From 560e63d073e61c255f278097b440f27d3564c132 Mon Sep 17 00:00:00 2001 From: foxnne Date: Fri, 16 Oct 2026 08:11:08 -0500 Subject: [PATCH] zig: zon results come from their owner's arena Zig 0.17's std.zon.parse allocates results and diagnostics in an arena the caller passes, and has no free. Every parse now names the arena that owns its result: a local one where the old code parsed and freed in one scope, the caller's where the result is kept. Settings.parseOnly, Manifest.parse and layout_file.loadWindowFile take that arena; plugin settings copy their non-default strings out of it. The stand-in, its generated deep copy and free, and its rewrite-script step are gone. Co-Authored-By: Claude Opus 5.5 --- app/Recents.zig | 74 +---------- app/settings/Settings.zig | 100 ++------------ app/settings/SettingsMigration.zig | 90 ++----------- app/store/PluginLoader.zig | 18 +-- backend/src/Health.zig | 73 +---------- backend/src/SDLBackend.zig | 48 +------ backend/src/platform/geometry.zig | 48 +------ core/profile.zig | 46 +------ plugins/shared/build/helpers.zig | 47 +------ plugins/workbench/src/workbench_layout.zig | 73 +---------- scripts/zig-0.17/rewrite.sh | 7 +- scripts/zig-0.17/zon_standin.pl | 90 ------------- sdk/plugin_sdk.zig | 47 +------ sdk/replay/Snapshot.zig | 55 +------- sdk/src/Command.zig | 4 +- sdk/src/Manifest.zig | 125 +++++------------- sdk/src/events.zig | 46 +------ sdk/src/settings.zig | 81 +++++------- sdk/tape/Tape.zig | 46 +------ src/backend/backend_native.zig | 34 +---- src/backend/layout_file.zig | 101 +++------------ src/editor/Editor.zig | 7 +- src/editor/KeptDocuments.zig | 46 +------ src/editor/verdict.zig | 48 +------ tests/integration.zig | 144 ++++----------------- 25 files changed, 200 insertions(+), 1298 deletions(-) delete mode 100644 scripts/zig-0.17/zon_standin.pl diff --git a/app/Recents.zig b/app/Recents.zig index 07133b1cf..4b38e6b12 100644 --- a/app/Recents.zig +++ b/app/Recents.zig @@ -49,15 +49,10 @@ pub fn load(allocator: std.mem.Allocator, path: []const u8) !Recents { if (core.fs.readZ(allocator, dvui.io, path) catch null) |data| { defer allocator.free(data); - if ((zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Disk, .{ .gpa = allocator, .arena = zig017_arena.allocator(), .source = data, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Disk, allocator, zig017_v) else |zig017_e| zig017_e; - }) catch null) |disk| { - defer zig017ZonFree(allocator, disk); - + var disk_arena: std.heap.ArenaAllocator = .init(allocator); + defer disk_arena.deinit(); + var disk_diag: std.zon.parse.Diagnostics = undefined; + if (std.zon.parse.fromSlice(Disk, .{ .gpa = allocator, .arena = disk_arena.allocator(), .source = data, .diagnostics = &disk_diag, .ignore_unknown_fields = true }) catch null) |disk| { for (disk.folders) |folder| { // A folder on a mount (`gdrive://…`) is not the disk's to check: it exists // whenever its plugin is signed in, which is decided at open time. @@ -172,64 +167,3 @@ pub fn deinit(recents: *Recents, allocator: std.mem.Allocator) void { recents.commands.deinit(allocator); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -} diff --git a/app/settings/Settings.zig b/app/settings/Settings.zig index c4c7ffae3..5ede92e37 100644 --- a/app/settings/Settings.zig +++ b/app/settings/Settings.zig @@ -175,47 +175,36 @@ pub fn load(allocator: std.mem.Allocator, path: []const u8, plugins_dir: ?[]cons const data = core.fs.readZ(allocator, dvui.io, path) catch return default(allocator); defer allocator.free(data); - const parsed = parseOnly(allocator, data) catch |err| { + var arena: std.heap.ArenaAllocator = .init(allocator); + defer arena.deinit(); + const parsed = parseOnly(allocator, arena.allocator(), data) catch |err| { dvui.log.warn("Could not parse settings.zon ({s}); using defaults.", .{@errorName(err)}); return default(allocator); }; - defer freeParsed(allocator, parsed); - var result = parsed; // Own `theme` for the process lifetime (freed in `deinit`); every other field is scalar. result.theme = try allocator.dupe(u8, parsed.theme); return result; } -/// Frees a `parseOnly`/`load` result. Not `std.zon.parse.free` directly: since R12 the file only -/// records non-default fields, so a missing `theme` is filled by `std.zon.parse` with *this -/// struct's declared default* — a comptime string literal, not an allocation — and handing that -/// pointer to the allocator would be an invalid free. Zeroing it first makes the whole-struct -/// free a no-op for that field (`Allocator.free` returns early on an empty slice) while still -/// covering any other allocated field. -pub fn freeParsed(allocator: std.mem.Allocator, parsed: Settings) void { - var value = parsed; - if (value.theme.ptr == default_theme.ptr) value.theme = ""; - zig017ZonFree(allocator, value); -} - /// Parses `data` (already-read `settings.zon` bytes) into a `Settings` value, ignoring unknown /// fields (forward-compat + `.plugins` is handled separately — see `load`'s doc comment above). /// Unlike `load`, this returns the raw parse error on failure instead of falling back to /// defaults — defaulting is only correct when there's no existing live state to fall back *to* /// (startup); `Editor.reconcileExternalSettingsChange` (external hand-edit reconciliation, R11) /// has existing state and must not let a transient torn-write read reset every fizzy field and -/// every plugin's settings. Caller frees the result with `freeParsed` (not `Settings.deinit`, -/// which owns the live value's long-lived `theme`). -pub fn parseOnly(allocator: std.mem.Allocator, data: [:0]const u8) !Settings { +/// every plugin's settings. The result's strings live in `arena` (`allocator` is the parse's +/// scratch); `load` copies out the one it keeps. +pub fn parseOnly(allocator: std.mem.Allocator, arena: std.mem.Allocator, data: [:0]const u8) !Settings { @setEvalBranchQuota(10_000); - var parsed = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Settings, .{ .gpa = allocator, .arena = zig017_arena.allocator(), .source = data, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Settings, allocator, zig017_v) else |zig017_e| zig017_e; + var diag: std.zon.parse.Diagnostics = undefined; + var parsed = try std.zon.parse.fromSlice(Settings, .{ + .gpa = allocator, + .arena = arena, + .source = data, + .diagnostics = &diag, + .ignore_unknown_fields = true, }); SettingsMigration.windowGlass(allocator, data, &parsed); return parsed; @@ -238,7 +227,7 @@ fn fieldEqual(comptime FT: type, a: FT, b: FT) bool { /// halves of `settings.zon` record just what the user actually changed. An untouched fizzy /// serializes to `.{}`; a field hand-written back at its default drops out on the next write. /// Reading back is unaffected: `load`/`parseOnly` fill every missing field from these same -/// defaults (see `freeParsed` for the one ownership wrinkle that creates). +/// defaults (they are the declared defaults themselves: `load` copies `theme` before keeping it). /// /// This is *not* the whole `settings.zon` file — plugin settings are a separate /// `.plugins = .{ . = .{...}, ... }` field spliced on afterward by @@ -269,64 +258,3 @@ pub fn serialize(settings: *const Settings, allocator: std.mem.Allocator) ![]u8 pub fn deinit(settings: *Settings, allocator: std.mem.Allocator) void { allocator.free(settings.theme); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -} diff --git a/app/settings/SettingsMigration.zig b/app/settings/SettingsMigration.zig index 4592718a3..ae99eaa8f 100644 --- a/app/settings/SettingsMigration.zig +++ b/app/settings/SettingsMigration.zig @@ -75,17 +75,13 @@ pub fn migrateToPerPluginEnabled(allocator: std.mem.Allocator, settings_zon_path defer allocator.free(data); @setEvalBranchQuota(10_000); - const legacy = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(LegacyDisk, .{ .gpa = allocator, .arena = zig017_arena.allocator(), .source = data, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(LegacyDisk, allocator, zig017_v) else |zig017_e| zig017_e; - }) catch |err| { + var legacy_arena: std.heap.ArenaAllocator = .init(allocator); + defer legacy_arena.deinit(); + var legacy_diag: std.zon.parse.Diagnostics = undefined; + const legacy = std.zon.parse.fromSlice(LegacyDisk, .{ .gpa = allocator, .arena = legacy_arena.allocator(), .source = data, .diagnostics = &legacy_diag, .ignore_unknown_fields = true }) catch |err| { dvui.log.warn("settings: could not parse settings.zon for R12 enabled migration ({s}); skipping", .{@errorName(err)}); return; }; - defer zig017ZonFree(allocator, legacy); const blocks = SettingsPluginsZon.listPluginBlocks(allocator, data) catch return; defer SettingsPluginsZon.freeEntries(allocator, blocks); @@ -161,11 +157,12 @@ pub fn migrateToPerPluginEnabled(allocator: std.mem.Allocator, settings_zon_path // Regenerate fizzy's own fields from a Settings parse (unknown fields — including the legacy // `disabled_plugins` list — are ignored), so the rewritten file drops that list for free. - const parsed = Settings.parseOnly(allocator, data) catch |err| { + var parsed_arena: std.heap.ArenaAllocator = .init(allocator); + defer parsed_arena.deinit(); + const parsed = Settings.parseOnly(allocator, parsed_arena.allocator(), data) catch |err| { dvui.log.warn("settings: could not re-serialize fizzy's own fields for R12 migration ({s}); skipping", .{@errorName(err)}); return; }; - defer Settings.freeParsed(allocator, parsed); const fizzy_text = Settings.serialize(&parsed, allocator) catch |err| { dvui.log.warn("settings: could not serialize fizzy's own fields for R12 migration ({s}); skipping", .{@errorName(err)}); @@ -258,14 +255,10 @@ const LegacyGlass = struct { /// dialogs' blur goes with them, as the roughness that followed it does. pub fn windowGlass(allocator: std.mem.Allocator, data: [:0]const u8, settings: *Settings) void { @setEvalBranchQuota(10_000); - const legacy = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(LegacyGlass, .{ .gpa = allocator, .arena = zig017_arena.allocator(), .source = data, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(LegacyGlass, allocator, zig017_v) else |zig017_e| zig017_e; - }) catch return; - defer zig017ZonFree(allocator, legacy); + var legacy_arena: std.heap.ArenaAllocator = .init(allocator); + defer legacy_arena.deinit(); + var legacy_diag: std.zon.parse.Diagnostics = undefined; + const legacy = std.zon.parse.fromSlice(LegacyGlass, .{ .gpa = allocator, .arena = legacy_arena.allocator(), .source = data, .diagnostics = &legacy_diag, .ignore_unknown_fields = true }) catch return; if (legacy.dialog_opacity == null and legacy.dialog_blur == null and legacy.dialog_lift == null and legacy.dialog_detail == null and legacy.dialog_refraction == null) return; const opaque_window = comptime builtin.target.cpu.arch == .wasm32 or (builtin.target.os.tag != .macos and builtin.target.os.tag != .windows); @@ -274,64 +267,3 @@ pub fn windowGlass(allocator: std.mem.Allocator, data: [:0]const u8, settings: * settings.window_opacity_light = std.math.clamp(o, 0, 1); }; } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -} diff --git a/app/store/PluginLoader.zig b/app/store/PluginLoader.zig index fab9e8e04..6f1f12e23 100644 --- a/app/store/PluginLoader.zig +++ b/app/store/PluginLoader.zig @@ -532,8 +532,9 @@ pub fn probeName(allocator: std.mem.Allocator, path: []const u8) ?[]u8 { if (lib.lookup(dylib_api.GetManifestZonFn, dylib_api.symbol_manifest_zon)) |get_zon| { const zon = std.mem.span(get_zon()); if (zon.len > 0) { - if (sdk.Manifest.parse(allocator, zon)) |parsed| { - defer sdk.Manifest.free(allocator, parsed); + var arena: std.heap.ArenaAllocator = .init(allocator); + defer arena.deinit(); + if (sdk.Manifest.parse(allocator, arena.allocator(), zon)) |parsed| { if (parsed.name.len > 0) return allocator.dupe(u8, parsed.name) catch null; } else |_| {} } @@ -570,8 +571,9 @@ pub fn probeManifestInfo(allocator: std.mem.Allocator, path: []const u8) ?Probed const get_zon = lib.lookup(dylib_api.GetManifestZonFn, dylib_api.symbol_manifest_zon) orelse return null; const zon = std.mem.span(get_zon()); if (zon.len == 0) return null; - const parsed = sdk.Manifest.parse(allocator, zon) catch return null; - defer sdk.Manifest.free(allocator, parsed); + var arena: std.heap.ArenaAllocator = .init(allocator); + defer arena.deinit(); + const parsed = sdk.Manifest.parse(allocator, arena.allocator(), zon) catch return null; var out: ProbedManifest = .{}; errdefer freeProbedManifest(allocator, out); @@ -614,10 +616,10 @@ pub fn probeVersionInfo(path: []const u8) ?PluginVersionInfo { if (lib.lookup(dylib_api.GetManifestZonFn, dylib_api.symbol_manifest_zon)) |get_zon| { const zon = std.mem.span(get_zon()); if (zon.len > 0) { - // Stack/arena-free parse: use page allocator for a short-lived Manifest. - const gpa = std.heap.page_allocator; - if (sdk.Manifest.parse(gpa, zon)) |parsed| { - defer sdk.Manifest.free(gpa, parsed); + // A short-lived Manifest, in an arena over the page allocator. + var arena: std.heap.ArenaAllocator = .init(std.heap.page_allocator); + defer arena.deinit(); + if (sdk.Manifest.parse(std.heap.page_allocator, arena.allocator(), zon)) |parsed| { if (std.SemanticVersion.parse(parsed.version)) |sv| { plugin_version = .{ .major = @intCast(sv.major), .minor = @intCast(sv.minor), .patch = @intCast(sv.patch) }; } else |_| {} diff --git a/backend/src/Health.zig b/backend/src/Health.zig index 9b9c806cd..eeed51b86 100644 --- a/backend/src/Health.zig +++ b/backend/src/Health.zig @@ -314,14 +314,10 @@ test "a snapshot round-trips through ZON" { try std.zon.stringify.serialize(s, .{}, &out.writer); const text = try std.testing.allocator.dupeSentinel(u8, out.written(), 0); defer std.testing.allocator.free(text); - const back = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(std.testing.allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Snapshot, .{ .gpa = std.testing.allocator, .arena = zig017_arena.allocator(), .source = text, .diagnostics = &zig017_zd }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Snapshot, std.testing.allocator, zig017_v) else |zig017_e| zig017_e; - }); - defer zig017ZonFree(std.testing.allocator, back); + var back_arena: std.heap.ArenaAllocator = .init(std.testing.allocator); + defer back_arena.deinit(); + var back_diag: std.zon.parse.Diagnostics = undefined; + const back = try std.zon.parse.fromSlice(Snapshot, .{ .gpa = std.testing.allocator, .arena = back_arena.allocator(), .source = text, .diagnostics = &back_diag }); try std.testing.expectEqual(s.presents, back.presents); try std.testing.expectEqual(s.os_windows, back.os_windows); try std.testing.expectEqual(s.ns_windows, back.ns_windows); @@ -330,64 +326,3 @@ test "a snapshot round-trips through ZON" { try std.testing.expectEqual(s.last_frame, back.last_frame); try std.testing.expectEqualStrings(s.first_sdl_error, back.first_sdl_error); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -} diff --git a/backend/src/SDLBackend.zig b/backend/src/SDLBackend.zig index a5f5bdd3c..6e8beb16c 100644 --- a/backend/src/SDLBackend.zig +++ b/backend/src/SDLBackend.zig @@ -700,13 +700,10 @@ pub const WindowGeometry = struct { if (data.len >= nul_buf.len) return null; @memcpy(nul_buf[0..data.len], data); nul_buf[data.len] = 0; - const saved = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(std.heap.page_allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Saved, .{ .gpa = std.heap.page_allocator, .arena = zig017_arena.allocator(), .source = nul_buf[0..data.len :0], .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Saved, std.heap.page_allocator, zig017_v) else |zig017_e| zig017_e; - }) catch return null; + var arena: std.heap.ArenaAllocator = .init(std.heap.page_allocator); + defer arena.deinit(); + var diag: std.zon.parse.Diagnostics = undefined; + const saved = std.zon.parse.fromSliceNoAlloc(Saved, .{ .gpa = std.heap.page_allocator, .arena = arena.allocator(), .source = nul_buf[0..data.len :0], .diagnostics = &diag, .ignore_unknown_fields = true }) catch return null; return fromSaved(saved); } @@ -3759,40 +3756,3 @@ const live_resize_trace = struct { (dvui.Rect.Physical{ .x = 200, .y = r.h - 6, .w = r.w - 400, .h = 6 }).fill(.all(0), .{ .color = .{ .color = yellow } }); } }; - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/backend/src/platform/geometry.zig b/backend/src/platform/geometry.zig index f8fb06aa7..e40da2221 100644 --- a/backend/src/platform/geometry.zig +++ b/backend/src/platform/geometry.zig @@ -75,13 +75,10 @@ fn fileLoad(_: ?*anyopaque) ?Geometry { defer gpa.free(data); const z = gpa.dupeSentinel(u8, data, 0) catch return null; defer gpa.free(z); - const g = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Geometry, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = z, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Geometry, gpa, zig017_v) else |zig017_e| zig017_e; - }) catch return null; + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var diag: std.zon.parse.Diagnostics = undefined; + const g = std.zon.parse.fromSliceNoAlloc(Geometry, .{ .gpa = gpa, .arena = arena.allocator(), .source = z, .diagnostics = &diag, .ignore_unknown_fields = true }) catch return null; return if (g.valid()) g else null; } @@ -265,40 +262,3 @@ fn trackLinux() void { if (w < 1 or h < 1) return; linux_normal = .{ .x = @floatFromInt(x), .y = @floatFromInt(y), .w = @floatFromInt(w), .h = @floatFromInt(h) }; } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/core/profile.zig b/core/profile.zig index 8e414551d..60f9d28a7 100644 --- a/core/profile.zig +++ b/core/profile.zig @@ -867,13 +867,8 @@ test "a report says what each owner and scope cost, as ZON that reads back" { }; var arena: std.heap.ArenaAllocator = .init(std.testing.allocator); defer arena.deinit(); - const r = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(arena.allocator()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Report, .{ .gpa = arena.allocator(), .arena = zig017_arena.allocator(), .source = try arena.allocator().dupeSentinel(u8, text, 0), .diagnostics = &zig017_zd }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Report, arena.allocator(), zig017_v) else |zig017_e| zig017_e; - }); + var diag: std.zon.parse.Diagnostics = undefined; + const r = try std.zon.parse.fromSlice(Report, .{ .gpa = std.testing.allocator, .arena = arena.allocator(), .source = try arena.allocator().dupeSentinel(u8, text, 0), .diagnostics = &diag }); // The plugin's own time is what it spent; fizzy's own is what was left once that is taken out. try std.testing.expectEqualStrings("text", r.owners[0].owner); try std.testing.expect(r.owners[0].self_ms >= 1.5); @@ -957,40 +952,3 @@ test "a frame's GPU counts land in its own slot and the window's average, not be p.frameEnd(); try std.testing.expectEqual(@as(f64, 2), p.stats.gpu.?.targets); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/plugins/shared/build/helpers.zig b/plugins/shared/build/helpers.zig index ba1d37c3a..5beda65ff 100644 --- a/plugins/shared/build/helpers.zig +++ b/plugins/shared/build/helpers.zig @@ -66,13 +66,9 @@ pub fn readManifestAt(b: *std.Build, zon_rel_path: []const u8) IdentityManifest version: []const u8, min_sdk_version: []const u8 = "", }; - const parsed = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(b.allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Target, .{ .gpa = b.allocator, .arena = zig017_arena.allocator(), .source = src, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Target, b.allocator, zig017_v) else |zig017_e| zig017_e; - }) catch |e| + // The build's allocator is freed only when the build ends: the manifest is read into it. + var diag: std.zon.parse.Diagnostics = undefined; + const parsed = std.zon.parse.fromSlice(Target, .{ .gpa = b.allocator, .arena = b.allocator, .source = src, .diagnostics = &diag, .ignore_unknown_fields = true }) catch |e| std.debug.panic("fizzy: failed to parse {s}: {}", .{ zon_rel_path, e }); _ = std.SemanticVersion.parse(parsed.version) catch std.debug.panic("fizzy: {s} .version \"{s}\" is not valid semver", .{ zon_rel_path, parsed.version }); @@ -280,40 +276,3 @@ pub fn installDylib(b: *std.Build, lib: *std.Build.Step.Compile, name: []const u }); b.getInstallStep().dependOn(&install_step.step); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/plugins/workbench/src/workbench_layout.zig b/plugins/workbench/src/workbench_layout.zig index 1e64ba59e..5648edffa 100644 --- a/plugins/workbench/src/workbench_layout.zig +++ b/plugins/workbench/src/workbench_layout.zig @@ -176,17 +176,13 @@ fn loadPanes(wb: *Workbench) void { defer wb.allocator.free(data); const bytes = wb.allocator.dupeSentinel(u8, data, 0) catch return; defer wb.allocator.free(bytes); - const snap = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(wb.allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Panes.Snapshot, .{ .gpa = wb.allocator, .arena = zig017_arena.allocator(), .source = bytes, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Panes.Snapshot, wb.allocator, zig017_v) else |zig017_e| zig017_e; - }) catch |err| { + var snap_arena: std.heap.ArenaAllocator = .init(wb.allocator); + defer snap_arena.deinit(); + var snap_diag: std.zon.parse.Diagnostics = undefined; + const snap = std.zon.parse.fromSlice(Panes.Snapshot, .{ .gpa = wb.allocator, .arena = snap_arena.allocator(), .source = bytes, .diagnostics = &snap_diag, .ignore_unknown_fields = true }) catch |err| { dvui.log.warn("workbench: ignoring {s}: {s}", .{ path, @errorName(err) }); return; }; - defer zig017ZonFree(wb.allocator, snap); var loaded = Panes.fromSnapshot(wb.allocator, snap) catch return; loaded.animated = true; @@ -236,64 +232,3 @@ fn panesPath(wb: *Workbench) ?[]u8 { std.Io.Dir.cwd().createDirPath(dvui.io, dir) catch return null; return std.fs.path.join(wb.allocator, &.{ dir, panes_file }) catch null; } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -} diff --git a/scripts/zig-0.17/rewrite.sh b/scripts/zig-0.17/rewrite.sh index 5bbe76ff6..357f2c3fe 100755 --- a/scripts/zig-0.17/rewrite.sh +++ b/scripts/zig-0.17/rewrite.sh @@ -4,8 +4,7 @@ # rewritten too, and delete it in the PR that merges the branch. Idempotent. # # Hand migrations are not here. What is here either `zig fmt` does, or is a rename with one -# right answer. `zon_standin.pl` is the exception: a stand-in for the zon arena migration, -# marked ZIG017 wherever it writes, until that PR replaces it. +# right answer. set -euo pipefail cd "$(jj root 2>/dev/null || git rev-parse --show-toplevel)" here=scripts/zig-0.17 @@ -36,8 +35,8 @@ perl -0pi -e 's/\.dupeZ(\((?:[^()]++|(?1))*\))/my $a=$1; $a =~ s{\)\z}{, 0)}; ". python3 $here/fields.py $files >/dev/null python3 $here/unused.py $here $files >/dev/null -# Stand-in until the zon arena PR: 0.17's std.zon.parse, results in the old allocator. -perl $here/zon_standin.pl $files >/dev/null +# Not std.zon.parse: each call is ported by hand to its owner's arena. A 0.16 call that a merge +# from main brings fails to compile, which is the reminder. # Last: zig fmt's own upgrades (@intFromEnum -> @backingInt, ...). jj file list | grep -E "\.(zig|zon)$" | xargs "${zig[@]}" fmt >/dev/null # everything, spikes too diff --git a/scripts/zig-0.17/zon_standin.pl b/scripts/zig-0.17/zon_standin.pl deleted file mode 100644 index 880f80db5..000000000 --- a/scripts/zig-0.17/zon_standin.pl +++ /dev/null @@ -1,90 +0,0 @@ -# ZIG017 stand-in: 0.16 std.zon.parse calls on 0.17's API, results in the given allocator. Replaced by the zon arena PR. -use strict; use warnings; -local $/; -our $NEED_DUPE = 0; -my $DUPE = <<'ZIGDUPE'; - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -ZIGDUPE -my $FREE = <<'ZIGFREE'; -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -} -ZIGFREE -for my $f (@ARGV) { - open my $fh, '<', $f or die; my $s = <$fh>; close $fh; my $o = $s; - $NEED_DUPE = 0; - $s =~ s{std\.zon\.parse\.(fromSliceAlloc|fromSlice)(\((?:[^()]++|(?2))*\))}{conv($2)}ge; - $s =~ s{std\.zon\.parse\.free\(}{zig017ZonFree(}g; - $s .= $DUPE if $NEED_DUPE && $s !~ /fn zig017ZonDupe\(/; - $s .= $FREE if $s =~ /zig017ZonFree\(/ && $s !~ /fn zig017ZonFree\(/; - if ($s ne $o) { open my $w, '>', $f or die; print $w $s; close $w; print "$f\n"; } -} -sub split_args { my ($a)=@_; $a =~ s/^\(//; $a =~ s/\)$//; my @p; my $d=0; my $cur=''; - for my $c (split //, $a) { if ($c eq ',' && $d==0) { push @p, $cur; $cur=''; next } $d++ if $c =~ /[\(\[\{]/; $d-- if $c =~ /[\)\]\}]/; $cur.=$c } push @p, $cur if $cur =~ /\S/; - s/^\s+|\s+$//g for @p; return @p } -sub conv { my ($a)=@_; my @p=split_args($a); return "std.zon.parse.fromSlice$a" unless @p==5; - my ($t,$al,$src,$diag,$opts)=@p; my $iuf = $opts =~ /ignore_unknown_fields\s*=\s*(\w+)/ ? ", .ignore_unknown_fields = $1" : ""; - if ($diag ne 'null') { - return "(zig017_zon: { break :zig017_zon std.zon.parse.fromSlice($t, .{ .gpa = $al, .arena = $al, .source = $src, .diagnostics = $diag$iuf }); })"; - } - $NEED_DUPE = 1; - return "(zig017_zon: { var zig017_zd: std.zon.parse.Diagnostics = undefined; var zig017_arena: std.heap.ArenaAllocator = .init($al); defer zig017_arena.deinit(); " - . "const zig017_r = std.zon.parse.fromSlice($t, .{ .gpa = $al, .arena = zig017_arena.allocator(), .source = $src, .diagnostics = &zig017_zd$iuf }); " - . "break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe($t, $al, zig017_v) else |zig017_e| zig017_e; })" } diff --git a/sdk/plugin_sdk.zig b/sdk/plugin_sdk.zig index 6802c6d10..6025a3d2e 100644 --- a/sdk/plugin_sdk.zig +++ b/sdk/plugin_sdk.zig @@ -111,13 +111,9 @@ fn readManifestAt(b: *std.Build, rel_path: []const u8) IdentityManifest { version: []const u8, min_sdk_version: []const u8 = "", }; - const parsed = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(b.allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Target, .{ .gpa = b.allocator, .arena = zig017_arena.allocator(), .source = src, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Target, b.allocator, zig017_v) else |zig017_e| zig017_e; - }) catch |e| + // The build's allocator is freed only when the build ends: the manifest is read into it. + var diag: std.zon.parse.Diagnostics = undefined; + const parsed = std.zon.parse.fromSlice(Target, .{ .gpa = b.allocator, .arena = b.allocator, .source = src, .diagnostics = &diag, .ignore_unknown_fields = true }) catch |e| std.debug.panic("fizzy: failed to parse {s}: {}", .{ rel_path, e }); _ = std.SemanticVersion.parse(parsed.version) catch std.debug.panic("fizzy: {s} .version \"{s}\" is not valid semver (expected e.g. 1.2.3)", .{ rel_path, parsed.version }); @@ -625,43 +621,6 @@ pub fn installBuiltinPlugin( }); } -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} - /// Declare to Zig 0.17's configure cache that the build read whether `rel` exists: the file itself /// when it does, and otherwise the nearest directory that exists, since the maker cannot load a /// configuration that depends on a path inside a missing directory ("configuration failed: diff --git a/sdk/replay/Snapshot.zig b/sdk/replay/Snapshot.zig index 9c7eb9b58..3dea87645 100644 --- a/sdk/replay/Snapshot.zig +++ b/sdk/replay/Snapshot.zig @@ -382,13 +382,8 @@ test "a snapshot names what a person acts on, and what a tape aims at" { // It reads back as what it says: two buttons by name, the tagged one by its tag, the mark. var arena: std.heap.ArenaAllocator = .init(std.testing.allocator); defer arena.deinit(); - const nodes = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(arena.allocator()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice([]const Node, .{ .gpa = arena.allocator(), .arena = zig017_arena.allocator(), .source = try arena.allocator().dupeSentinel(u8, text, 0), .diagnostics = &zig017_zd }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe([]const Node, arena.allocator(), zig017_v) else |zig017_e| zig017_e; - }); + var diag: std.zon.parse.Diagnostics = undefined; + const nodes = try std.zon.parse.fromSlice([]const Node, .{ .gpa = std.testing.allocator, .arena = arena.allocator(), .source = try arena.allocator().dupeSentinel(u8, text, 0), .diagnostics = &diag }); var buttons: usize = 0; var save = false; var close = false; @@ -472,13 +467,8 @@ test "a snapshot has the dialogs dvui shows after its capture, and says which wi var arena: std.heap.ArenaAllocator = .init(std.testing.allocator); defer arena.deinit(); - const nodes = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(arena.allocator()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice([]const Node, .{ .gpa = arena.allocator(), .arena = zig017_arena.allocator(), .source = try arena.allocator().dupeSentinel(u8, text, 0), .diagnostics = &zig017_zd }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe([]const Node, arena.allocator(), zig017_v) else |zig017_e| zig017_e; - }); + var diag: std.zon.parse.Diagnostics = undefined; + const nodes = try std.zon.parse.fromSlice([]const Node, .{ .gpa = std.testing.allocator, .arena = arena.allocator(), .source = try arena.allocator().dupeSentinel(u8, text, 0), .diagnostics = &diag }); // The windows first, each a node. try std.testing.expect(nodes.len > 3); for (nodes[0..3], [_][]const u8{ "app.window:main", "app.window:band", "app.window:dialog" }) |node, tag| { @@ -510,40 +500,3 @@ test "a snapshot has the dialogs dvui shows after its capture, and says which wi // Taken, nothing more is captured: a dialog drawn in a frame nobody asked for is free. try std.testing.expect(!taking()); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/sdk/src/Command.zig b/sdk/src/Command.zig index 1da84a7a0..f911790d4 100644 --- a/sdk/src/Command.zig +++ b/sdk/src/Command.zig @@ -198,9 +198,7 @@ pub fn Params(comptime T: type) type { const text = std.mem.trim(u8, call.args, " \t\r\n"); const source = try call.arena.dupeSentinel(u8, if (text.len == 0) ".{}" else text, 0); var diag: std.zon.parse.Diagnostics = undefined; - return (zig017_zon: { - break :zig017_zon std.zon.parse.fromSlice(Args, .{ .gpa = call.arena, .arena = call.arena, .source = source, .diagnostics = &diag }); - }) catch |err| switch (err) { + return std.zon.parse.fromSlice(Args, .{ .gpa = call.arena, .arena = call.arena, .source = source, .diagnostics = &diag }) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.ParseZon => { call.bad_args = true; diff --git a/sdk/src/Manifest.zig b/sdk/src/Manifest.zig index 7cf87ae29..ee31afbce 100644 --- a/sdk/src/Manifest.zig +++ b/sdk/src/Manifest.zig @@ -52,7 +52,7 @@ author_url: []const u8 = "", /// `dupeZ`/`readFileAllocOptions` with a sentinel) into a `Manifest`. Validates `version` (and /// `min_sdk_version`, when non-empty) are well-formed semver post-parse — zon itself has no /// semver type, so this is the manifest's own integrity check on top of the structural parse. -/// Free the result with `free`. +/// The result, its strings included, lives in `arena`; `gpa` is the parse's scratch. /// /// **Unknown fields are ignored on purpose**, which is what makes this format forward-compatible: /// `Manifest` is explicitly a growing set of optional fields (R16 added `description`, R17 added @@ -63,15 +63,15 @@ author_url: []const u8 = "", /// (`probeName`/`probeDescription`/`probeTags`/`probeVersionInfo`) would silently lose data it /// could otherwise still read. The tradeoff accepted here is that a misspelled field name is /// ignored rather than diagnosed; the visible symptom is a store listing missing that value. -pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8) !Manifest { - const m = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Manifest, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = source, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Manifest, gpa, zig017_v) else |zig017_e| zig017_e; +pub fn parse(gpa: std.mem.Allocator, arena: std.mem.Allocator, source: [:0]const u8) !Manifest { + var diag: std.zon.parse.Diagnostics = undefined; + const m = try std.zon.parse.fromSlice(Manifest, .{ + .gpa = gpa, + .arena = arena, + .source = source, + .diagnostics = &diag, + .ignore_unknown_fields = true, }); - errdefer free(gpa, m); _ = std.SemanticVersion.parse(m.version) catch return error.InvalidVersion; if (m.min_sdk_version.len > 0) { _ = std.SemanticVersion.parse(m.min_sdk_version) catch return error.InvalidMinSdkVersion; @@ -79,11 +79,6 @@ pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8) !Manifest { return m; } -/// Free a `Manifest` returned by `parse`. -pub fn free(gpa: std.mem.Allocator, m: Manifest) void { - zig017ZonFree(gpa, m); -} - test "Manifest parse round-trips identity" { const gpa = std.testing.allocator; const source: [:0]const u8 = @@ -95,8 +90,11 @@ test "Manifest parse round-trips identity" { \\} ; - const m = try parse(gpa, source); - defer free(gpa, m); + var arena: std.heap.ArenaAllocator = .init(gpa); + + defer arena.deinit(); + + const m = try parse(gpa, arena.allocator(), source); try std.testing.expectEqualStrings("example", m.id); try std.testing.expectEqualStrings("Example", m.name); @@ -113,8 +111,9 @@ test "parse defaults min_sdk_version when omitted" { \\ .version = "0.1.0", \\} ; - const m = try parse(gpa, source); - defer free(gpa, m); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const m = try parse(gpa, arena.allocator(), source); try std.testing.expectEqualStrings("", m.min_sdk_version); } @@ -128,8 +127,9 @@ test "parse defaults tags to empty when omitted" { \\ .version = "0.1.0", \\} ; - const m = try parse(gpa, source); - defer free(gpa, m); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const m = try parse(gpa, arena.allocator(), source); try std.testing.expectEqual(@as(usize, 0), m.tags.len); } @@ -144,8 +144,9 @@ test "parse round-trips tags" { \\ .tags = .{ "editor", "pixel-art" }, \\} ; - const m = try parse(gpa, source); - defer free(gpa, m); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const m = try parse(gpa, arena.allocator(), source); try std.testing.expectEqual(@as(usize, 2), m.tags.len); try std.testing.expectEqualStrings("editor", m.tags[0]); @@ -163,8 +164,9 @@ test "parse round-trips author and author_url" { \\ .author_url = "https://example.test/~somebody", \\} ; - const m = try parse(gpa, source); - defer free(gpa, m); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const m = try parse(gpa, arena.allocator(), source); try std.testing.expectEqualStrings("somebody", m.author); try std.testing.expectEqualStrings("https://example.test/~somebody", m.author_url); @@ -175,8 +177,9 @@ test "parse defaults author and author_url to empty" { const source: [:0]const u8 = \\.{ .id = "example", .name = "Example", .version = "0.1.0" } ; - const m = try parse(gpa, source); - defer free(gpa, m); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const m = try parse(gpa, arena.allocator(), source); try std.testing.expectEqualStrings("", m.author); try std.testing.expectEqualStrings("", m.author_url); @@ -195,8 +198,9 @@ test "parse ignores fields a newer SDK added" { \\ .another = .{ "a", "b" }, \\} ; - const m = try parse(gpa, source); - defer free(gpa, m); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const m = try parse(gpa, arena.allocator(), source); try std.testing.expectEqualStrings("example", m.id); try std.testing.expectEqualStrings("0.1.0", m.version); @@ -211,66 +215,7 @@ test "parse rejects a non-semver version" { \\ .version = "not-a-version", \\} ; - try std.testing.expectError(error.InvalidVersion, parse(gpa, source)); -} - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + try std.testing.expectError(error.InvalidVersion, parse(gpa, arena.allocator(), source)); } diff --git a/sdk/src/events.zig b/sdk/src/events.zig index 1b66e4aaa..3bd1e270e 100644 --- a/sdk/src/events.zig +++ b/sdk/src/events.zig @@ -53,13 +53,8 @@ pub fn matches(pattern: []const u8, name: []const u8) bool { pub fn parse(comptime T: type, arena: std.mem.Allocator, event: Event) !T { const text = std.mem.trim(u8, event.payload, " \t\r\n"); const source = try arena.dupeSentinel(u8, if (text.len == 0) ".{}" else text, 0); - return (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(arena); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(T, .{ .gpa = arena, .arena = zig017_arena.allocator(), .source = source, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(T, arena, zig017_v) else |zig017_e| zig017_e; - }); + var diag: std.zon.parse.Diagnostics = undefined; + return std.zon.parse.fromSlice(T, .{ .gpa = arena, .arena = arena, .source = source, .diagnostics = &diag, .ignore_unknown_fields = true }); } // ---- what fizzy announces ---- @@ -145,40 +140,3 @@ test "a subscription takes its name, its prefix, or everything" { try std.testing.expect(!matches("fizzy.document.*", "fizzy.folder.opened")); try std.testing.expect(matches("*", "chat.turn_ended")); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/sdk/src/settings.zig b/sdk/src/settings.zig index 9e345b693..4a4b4f77e 100644 --- a/sdk/src/settings.zig +++ b/sdk/src/settings.zig @@ -464,16 +464,32 @@ pub fn Schema(comptime T: type) type { }; defer gpa.free(blob_z); - const parsed = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Payloads, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = blob_z, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Payloads, gpa, zig017_v) else |zig017_e| zig017_e; + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var diag: std.zon.parse.Diagnostics = undefined; + var parsed = std.zon.parse.fromSlice(Payloads, .{ + .gpa = gpa, + .arena = arena.allocator(), + .source = blob_z, + .diagnostics = &diag, + .ignore_unknown_fields = true, }) catch |err| { dvui.log.warn("sdk.settings: failed to parse settings: {s}", .{@errorName(err)}); return; }; + // The parse's strings are in `arena`. One that is not the declared default is copied + // out, so it is this schema's to free (`freeOwned`); a default stays the default. + inline for (struct_fields) |f| { + if (comptime typeTagFor(f.type.Payload) == .string) { + const parsed_str = @field(parsed, f.name); + if (parsed_str.ptr != @field(default_payloads, f.name).ptr) { + @field(parsed, f.name) = gpa.dupe(u8, parsed_str) catch |err| { + dvui.log.warn("sdk.settings: out of memory keeping settings: {s}", .{@errorName(err)}); + return; + }; + } + } + } freeOwned(gpa, out.*); fromPayloads(out, parsed); } @@ -641,16 +657,14 @@ pub fn Schema(comptime T: type) type { const P = f.type.Payload; const text_z = gpa.dupeSentinel(u8, text, 0) catch return false; defer gpa.free(text_z); - const parsed = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(P, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = text_z, .diagnostics = &zig017_zd }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(P, gpa, zig017_v) else |zig017_e| zig017_e; - }) catch return false; - // Strings follow the same ownership rule as `setString`; other payloads are - // plain values the parse allocated nothing for. + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var diag: std.zon.parse.Diagnostics = undefined; + var parsed = std.zon.parse.fromSlice(P, .{ .gpa = gpa, .arena = arena.allocator(), .source = text_z, .diagnostics = &diag }) catch return false; + // Strings follow the same ownership rule as `setString`: copied out of the + // parse's arena, the schema's to free. Other payloads are plain values. if (comptime typeTagFor(P) == .string) { + parsed = gpa.dupe(u8, parsed) catch return false; if (fieldIsOwned(f.name, v.*)) gpa.free(@field(v.*, f.name).v); } payloadPtr(v, f.name).* = parsed; @@ -1010,40 +1024,3 @@ test "a type with no dedicated control is still a legal setting, edited as zon t // `.other` payloads take part in diff-vs-default like anything else. try testing.expect(try S.diffSerialize(testing.allocator, .{}) == null); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/sdk/tape/Tape.zig b/sdk/tape/Tape.zig index be9ce34ed..360251170 100644 --- a/sdk/tape/Tape.zig +++ b/sdk/tape/Tape.zig @@ -449,13 +449,8 @@ pub fn load(gpa: std.mem.Allocator, bytes: []const u8, check: Check) !Owned { pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8, check: Check) !Owned { var arena: std.heap.ArenaAllocator = .init(gpa); errdefer arena.deinit(); - const tape = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(arena.allocator()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Tape, .{ .gpa = arena.allocator(), .arena = zig017_arena.allocator(), .source = source, .diagnostics = &zig017_zd }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Tape, arena.allocator(), zig017_v) else |zig017_e| zig017_e; - }); + var diag: std.zon.parse.Diagnostics = undefined; + const tape = try std.zon.parse.fromSlice(Tape, .{ .gpa = gpa, .arena = arena.allocator(), .source = source, .diagnostics = &diag }); try tape.validate(check); return .{ .arena = arena, .tape = tape }; } @@ -677,40 +672,3 @@ test "a hand-written tape parses with every default left out" { try testing.expect(owned.tape.ops[2].do.wait.until == .idle); try testing.expectEqual(@as(u32, 10_000), owned.tape.ops[2].do.wait.timeout); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/src/backend/backend_native.zig b/src/backend/backend_native.zig index 21d4c74bb..612eedfc2 100644 --- a/src/backend/backend_native.zig +++ b/src/backend/backend_native.zig @@ -158,8 +158,9 @@ var layout_store_dir: []const u8 = ""; fn layoutStoreLoad(_: ?*anyopaque) ?platform.geometry.Geometry { const gpa = std.heap.page_allocator; - const f = loadWindowFile(gpa, layout_store_dir); - defer zig017ZonFree(gpa, f); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const f = loadWindowFile(gpa, arena.allocator(), layout_store_dir); if (f.w < 1 or f.h < 1) return null; return .{ .x = f.x, .y = f.y, .w = f.w, .h = f.h, .state = if (f.maximized) .maximized else .normal }; } @@ -167,8 +168,9 @@ fn layoutStoreLoad(_: ?*anyopaque) ?platform.geometry.Geometry { fn layoutStoreSave(_: ?*anyopaque, g: platform.geometry.Geometry) void { // Read-modify-write: the regions and tree on disk stay as they are. const gpa = std.heap.page_allocator; - var f = loadWindowFile(gpa, layout_store_dir); - defer zig017ZonFree(gpa, f); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var f = loadWindowFile(gpa, arena.allocator(), layout_store_dir); f.x = g.x; f.y = g.y; f.w = g.w; @@ -396,27 +398,3 @@ pub fn setDynamicNativeMenuShortcut(index: usize, key: ?[]const u8, modifier_mas pub fn setSdlAppMetadata(name: [*:0]const u8, version: [*:0]const u8, identifier: [*:0]const u8) void { _ = sdl3.SDL_SetAppMetadata(name, version, identifier); } -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -} diff --git a/src/backend/layout_file.zig b/src/backend/layout_file.zig index 3254f0c57..3192238ae 100644 --- a/src/backend/layout_file.zig +++ b/src/backend/layout_file.zig @@ -97,9 +97,8 @@ fn windowFilePath(buf: []u8, dir: []const u8, name: []const u8) ?[:0]const u8 { /// Reads every field of `window.zon`, falling back to `SavedFrame`'s own defaults for whatever /// is missing or unparseable (never null — simplifies every caller, which only cares about the /// subset of fields it owns). -/// Reads `window.zon` into `gpa`-owned memory. Free with `zig017ZonFree(gpa, frame)` — the -/// region list holds allocated names now, so the old return-by-value-and-forget will not do. -pub fn loadWindowFile(gpa: std.mem.Allocator, dir: []const u8) SavedFrame { +/// Reads `window.zon` into `arena` (the region list holds allocated names); `gpa` is scratch. +pub fn loadWindowFile(gpa: std.mem.Allocator, arena: std.mem.Allocator, dir: []const u8) SavedFrame { var path_buf: [1024]u8 = undefined; const path = windowFilePath(&path_buf, dir, layout_file) orelse return .{}; const data = readCapped(gpa, path) catch blk: { @@ -111,14 +110,13 @@ pub fn loadWindowFile(gpa: std.mem.Allocator, dir: []const u8) SavedFrame { defer gpa.free(data); const data_z = gpa.dupeSentinel(u8, data, 0) catch return .{}; defer gpa.free(data_z); - // `fromSliceAlloc`, not `fromSlice`: the region list holds allocated names, and `fromSlice` - // asserts at comptime that the result contains no pointers. - return (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(SavedFrame, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = data_z, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(SavedFrame, gpa, zig017_v) else |zig017_e| zig017_e; + var diag: std.zon.parse.Diagnostics = undefined; + return std.zon.parse.fromSlice(SavedFrame, .{ + .gpa = gpa, + .arena = arena, + .source = data_z, + .diagnostics = &diag, + .ignore_unknown_fields = true, }) catch .{}; } @@ -140,8 +138,9 @@ pub fn writeWindowFile(dir: []const u8, f: SavedFrame) void { /// Read-modify-write: keeps whatever frame geometry is on disk, replaces the region list. pub fn saveRegions(dir: []const u8, regions: []const SavedRegion) void { const gpa = std.heap.page_allocator; - var f = loadWindowFile(gpa, dir); - defer zig017ZonFree(gpa, f); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var f = loadWindowFile(gpa, arena.allocator(), dir); const keep = f.regions; f.regions = regions; writeWindowFile(dir, f); @@ -153,8 +152,9 @@ pub fn saveRegions(dir: []const u8, regions: []const SavedRegion) void { /// this, so an existing tree on disk is left alone. pub fn saveTree(dir: []const u8, tree: ?core.widgets.DockLayout.Snapshot) void { const gpa = std.heap.page_allocator; - var f = loadWindowFile(gpa, dir); - defer zig017ZonFree(gpa, f); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var f = loadWindowFile(gpa, arena.allocator(), dir); const keep = f.tree; f.tree = tree; writeWindowFile(dir, f); @@ -164,8 +164,9 @@ pub fn saveTree(dir: []const u8, tree: ?core.widgets.DockLayout.Snapshot) void { /// The saved seed-tree, rebuilt as a live `DockLayout`, or null when the file has none. /// Caller owns the result (`DockLayout.deinit`). pub fn loadTree(gpa: std.mem.Allocator, dir: []const u8) ?core.widgets.DockLayout { - const f = loadWindowFile(gpa, dir); - defer zig017ZonFree(gpa, f); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const f = loadWindowFile(gpa, arena.allocator(), dir); const snap = f.tree orelse return null; var dock = core.widgets.DockLayout.fromSnapshot(gpa, snap) catch return null; dock.animated = true; @@ -175,8 +176,9 @@ pub fn loadTree(gpa: std.mem.Allocator, dir: []const u8) ?core.widgets.DockLayou /// Every region `layout.zon` remembers, in `gpa`-owned memory. Call once at startup; free with /// `freeRegions`. pub fn loadRegions(gpa: std.mem.Allocator, dir: []const u8) []SavedRegion { - const f = loadWindowFile(gpa, dir); - defer zig017ZonFree(gpa, f); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + const f = loadWindowFile(gpa, arena.allocator(), dir); const out = gpa.alloc(SavedRegion, f.regions.len) catch return &.{}; var n: usize = 0; for (f.regions) |r| { @@ -234,64 +236,3 @@ pub fn freeRegions(gpa: std.mem.Allocator, regions: []SavedRegion) void { } gpa.free(regions); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -} diff --git a/src/editor/Editor.zig b/src/editor/Editor.zig index 9d53529e1..350fe287c 100644 --- a/src/editor/Editor.zig +++ b/src/editor/Editor.zig @@ -871,7 +871,7 @@ pub fn builtinManifest(editor: *Editor, id: []const u8) ?sdk.Manifest { if (std.mem.eql(u8, m.plugin_id, id)) { const frame_gpa = editor.app.arena.allocator(); const zon = frame_gpa.dupeSentinel(u8, m.plugin_options.manifest_zon, 0) catch return null; - return sdk.Manifest.parse(frame_gpa, zon) catch return null; + return sdk.Manifest.parse(frame_gpa, frame_gpa, zon) catch return null; } } return null; @@ -3457,11 +3457,12 @@ pub fn reconcileExternalSettingsChange(editor: *Editor) void { // plugin's settings. Left unreconciled until another external change retriggers a read // (rare in practice: the watcher's debounce already gives a normal atomic save time to // settle before this runs at all). - const parsed = Settings.parseOnly(gpa, data) catch |err| { + var parsed_arena: std.heap.ArenaAllocator = .init(gpa); + defer parsed_arena.deinit(); + const parsed = Settings.parseOnly(gpa, parsed_arena.allocator(), data) catch |err| { dvui.log.warn("settings watcher: could not parse external settings.zon change ({s}); skipping this reconciliation", .{@errorName(err)}); return; }; - defer Settings.freeParsed(gpa, parsed); // Fizzy fields, one at a time rather than `editor.app.settings = parsed` wholesale: `theme` is // runtime-owned (see its doc comment on `Settings`) and must not be clobbered by the raw diff --git a/src/editor/KeptDocuments.zig b/src/editor/KeptDocuments.zig index b4b4c7bc8..2c3196b96 100644 --- a/src/editor/KeptDocuments.zig +++ b/src/editor/KeptDocuments.zig @@ -359,13 +359,8 @@ pub fn loadSession(gpa: std.mem.Allocator, dir: []const u8) ?KeptDocuments { defer gpa.free(text); var arena: std.heap.ArenaAllocator = .init(gpa); defer arena.deinit(); - const saved = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(arena.allocator()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Saved, .{ .gpa = arena.allocator(), .arena = zig017_arena.allocator(), .source = text, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Saved, arena.allocator(), zig017_v) else |zig017_e| zig017_e; - }) catch |err| { + var diag: std.zon.parse.Diagnostics = undefined; + const saved = std.zon.parse.fromSlice(Saved, .{ .gpa = gpa, .arena = arena.allocator(), .source = text, .diagnostics = &diag, .ignore_unknown_fields = true }) catch |err| { dvui.log.warn("restart: the last session could not be read ({t}); documents open from disk", .{err}); return null; }; @@ -495,40 +490,3 @@ fn recover(editor: *Editor, path: []const u8, unsaved: []const u8) void { dvui.log.warn("reload: the unsaved contents of {s} are in {s}", .{ path, out }); dvui.toast(@src(), .{ .message = std.fmt.allocPrint(editor.app.arena.allocator(), "Unsaved changes to {s} were kept in {s}", .{ std.fs.path.basename(path), out }) catch "Unsaved changes were kept in fizzy's recovered folder." }); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/src/editor/verdict.zig b/src/editor/verdict.zig index 0e690246a..b1b697574 100644 --- a/src/editor/verdict.zig +++ b/src/editor/verdict.zig @@ -190,13 +190,10 @@ fn readExpect(gpa: std.mem.Allocator, file: []const u8) !Expect { defer gpa.free(bytes); const source = try gpa.dupeSentinel(u8, bytes, 0); defer gpa.free(source); - return (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Expect, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = source, .diagnostics = &zig017_zd }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Expect, gpa, zig017_v) else |zig017_e| zig017_e; - }); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var diag: std.zon.parse.Diagnostics = undefined; + return std.zon.parse.fromSliceNoAlloc(Expect, .{ .gpa = gpa, .arena = arena.allocator(), .source = source, .diagnostics = &diag }); } /// Once a displayed frame, after the app's: watch the tape start and end. True: the tape has @@ -366,40 +363,3 @@ fn over(limit: ?u32, value: u32) bool { const max = limit orelse return false; return value > max; } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} diff --git a/tests/integration.zig b/tests/integration.zig index 16872511f..d428ebe69 100644 --- a/tests/integration.zig +++ b/tests/integration.zig @@ -5162,13 +5162,10 @@ test "verdict: the soak tape is a demo, and its expectations read" { // It ends on a wait, so what closed last has time to close before the windows are counted. try std.testing.expect(owned.tape.ops[owned.tape.ops.len - 1].do == .wait); - const expect = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(std.testing.allocator); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(fizzy.verdict.Expect, .{ .gpa = std.testing.allocator, .arena = zig017_arena.allocator(), .source = @embedFile("soak_expect"), .diagnostics = &zig017_zd }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(fizzy.verdict.Expect, std.testing.allocator, zig017_v) else |zig017_e| zig017_e; - }); + var expect_arena: std.heap.ArenaAllocator = .init(std.testing.allocator); + defer expect_arena.deinit(); + var expect_diag: std.zon.parse.Diagnostics = undefined; + const expect = try std.zon.parse.fromSliceNoAlloc(fizzy.verdict.Expect, .{ .gpa = std.testing.allocator, .arena = expect_arena.allocator(), .source = @embedFile("soak_expect"), .diagnostics = &expect_diag }); try std.testing.expect(!expect.allow_leaks); try std.testing.expectEqual(@as(u32, 0), expect.max_sdl_errors); try std.testing.expectEqual(@as(?u32, null), expect.os_windows); @@ -5338,14 +5335,10 @@ test "float: the saved layout brings it back, and Reset Layout takes it away" { gpa.free(regions); const text = try gpa.dupeSentinel(u8, out.written(), 0); defer gpa.free(text); - const saved = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice([]fizzy.backend.SavedRegion, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = text, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe([]fizzy.backend.SavedRegion, gpa, zig017_v) else |zig017_e| zig017_e; - }); - defer zig017ZonFree(gpa, saved); + var saved_arena: std.heap.ArenaAllocator = .init(gpa); + defer saved_arena.deinit(); + var saved_diag: std.zon.parse.Diagnostics = undefined; + const saved = try std.zon.parse.fromSlice([]fizzy.backend.SavedRegion, .{ .gpa = gpa, .arena = saved_arena.allocator(), .source = text, .diagnostics = &saved_diag, .ignore_unknown_fields = true }); editor.app.layout.resetLayout(gpa); try dvui.testing.settle(ManyPanelFrame.frame); @@ -6672,25 +6665,17 @@ const HeardEvents = struct { defer gpa.free(src); // A batch of disk changes is heard by its first path, everything else by its `.path`. const path = if (std.mem.eql(u8, e.name, "fizzy.files.changed")) files: { - const changed = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(sdk.events.FilesChanged, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = src, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(sdk.events.FilesChanged, gpa, zig017_v) else |zig017_e| zig017_e; - }) catch return; - defer zig017ZonFree(gpa, changed); + var changed_arena: std.heap.ArenaAllocator = .init(gpa); + defer changed_arena.deinit(); + var changed_diag: std.zon.parse.Diagnostics = undefined; + const changed = std.zon.parse.fromSlice(sdk.events.FilesChanged, .{ .gpa = gpa, .arena = changed_arena.allocator(), .source = src, .diagnostics = &changed_diag, .ignore_unknown_fields = true }) catch return; if (changed.events.len == 0) return; break :files gpa.dupe(u8, changed.events[0].path) catch return; } else plain: { - const payload = (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(gpa); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(sdk.events.Path, .{ .gpa = gpa, .arena = zig017_arena.allocator(), .source = src, .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(sdk.events.Path, gpa, zig017_v) else |zig017_e| zig017_e; - }) catch return; - defer zig017ZonFree(gpa, payload); + var payload_arena: std.heap.ArenaAllocator = .init(gpa); + defer payload_arena.deinit(); + var payload_diag: std.zon.parse.Diagnostics = undefined; + const payload = std.zon.parse.fromSlice(sdk.events.Path, .{ .gpa = gpa, .arena = payload_arena.allocator(), .source = src, .diagnostics = &payload_diag, .ignore_unknown_fields = true }) catch return; break :plain gpa.dupe(u8, payload.path) catch return; }; self.events.append(gpa, .{ .name = gpa.dupe(u8, e.name) catch return, .path = path }) catch gpa.free(path); @@ -6993,13 +6978,8 @@ test "headless: the whole editor comes up, opens a file, plays the tour and goes .ok => |r| r orelse return error.TestUnexpectedResult, else => return error.TestUnexpectedResult, }; - return (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(h.arena()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Read, .{ .gpa = h.arena(), .arena = zig017_arena.allocator(), .source = try h.arena().dupeSentinel(u8, zon, 0), .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Read, h.arena(), zig017_v) else |zig017_e| zig017_e; - }); + var diag: std.zon.parse.Diagnostics = undefined; + return std.zon.parse.fromSlice(Read, .{ .gpa = h.arena(), .arena = h.arena(), .source = try h.arena().dupeSentinel(u8, zon, 0), .diagnostics = &diag, .ignore_unknown_fields = true }); } }.read; @@ -7037,13 +7017,8 @@ test "headless: the whole editor comes up, opens a file, plays the tour and goes try std.testing.expectEqualStrings(notes, front.owner.documentPath(front)); const Caret = struct { caret: struct { line: u32, column: u32 } }; const out = host.callCommand("text.read", notes_args, host.arena()).ok.?; - const at = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(host.arena()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Caret, .{ .gpa = host.arena(), .arena = zig017_arena.allocator(), .source = try host.arena().dupeSentinel(u8, out, 0), .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Caret, host.arena(), zig017_v) else |zig017_e| zig017_e; - }); + var at_diag: std.zon.parse.Diagnostics = undefined; + const at = try std.zon.parse.fromSlice(Caret, .{ .gpa = host.arena(), .arena = host.arena(), .source = try host.arena().dupeSentinel(u8, out, 0), .diagnostics = &at_diag, .ignore_unknown_fields = true }); try std.testing.expectEqual(@as(u32, 3), at.caret.line); try std.testing.expectEqual(@as(u32, 4), at.caret.column); } @@ -7271,13 +7246,8 @@ test "headless: the whole editor comes up, opens a file, plays the tour and goes // has passed on screen, which a few test frames are not. profile: struct { frame: struct { fps: f64 } }, }; - const stats = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(host.arena()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Stats, .{ .gpa = host.arena(), .arena = zig017_arena.allocator(), .source = try host.arena().dupeSentinel(u8, zon, 0), .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Stats, host.arena(), zig017_v) else |zig017_e| zig017_e; - }); + var stats_diag: std.zon.parse.Diagnostics = undefined; + const stats = try std.zon.parse.fromSlice(Stats, .{ .gpa = host.arena(), .arena = host.arena(), .source = try host.arena().dupeSentinel(u8, zon, 0), .diagnostics = &stats_diag, .ignore_unknown_fields = true }); try std.testing.expect(for (stats.plugins) |pl| { if (std.mem.eql(u8, pl.id, "devtools")) break pl.link == .bundled; } else false); @@ -7297,13 +7267,8 @@ test "headless: the whole editor comes up, opens a file, plays the tour and goes else => return error.TestUnexpectedResult, }; const Written = struct { path: []const u8, frames: u64 }; - const written = try (zig017_zon: { - var zig017_zd: std.zon.parse.Diagnostics = undefined; - var zig017_arena: std.heap.ArenaAllocator = .init(host.arena()); - defer zig017_arena.deinit(); - const zig017_r = std.zon.parse.fromSlice(Written, .{ .gpa = host.arena(), .arena = zig017_arena.allocator(), .source = try host.arena().dupeSentinel(u8, zon, 0), .diagnostics = &zig017_zd, .ignore_unknown_fields = true }); - break :zig017_zon if (zig017_r) |zig017_v| zig017ZonDupe(Written, host.arena(), zig017_v) else |zig017_e| zig017_e; - }); + var written_diag: std.zon.parse.Diagnostics = undefined; + const written = try std.zon.parse.fromSlice(Written, .{ .gpa = host.arena(), .arena = host.arena(), .source = try host.arena().dupeSentinel(u8, zon, 0), .diagnostics = &written_diag, .ignore_unknown_fields = true }); try std.testing.expect(written.frames > 0); const input_path = try std.fs.path.join(host.arena(), &.{ written.path, "input.bin" }); const bytes = try std.Io.Dir.cwd().readFileAlloc(dvui.io, input_path, host.arena(), .unlimited); @@ -7923,64 +7888,3 @@ test "a live tape drags a floating window by its header" { std.debug.print("float x {d:.1} (from 300) after {d} frames\n", .{ live_float.x, frames }); try std.testing.expectApproxEqAbs(@as(f32, 150), live_float.x, 2); } - -// ZIG017 stand-in: a deep copy of a zon result out of the parse's arena into the caller's -// allocator, where the code before 0.17 expects to free it. Goes with the zon arena PR. -fn zig017ZonDupe(comptime T: type, gpa: std.mem.Allocator, value: T) error{OutOfMemory}!T { - switch (@typeInfo(T)) { - .pointer => |p| switch (p.size) { - .one => { - const out = try gpa.create(p.child); - out.* = try zig017ZonDupe(p.child, gpa, value.*); - return out; - }, - .slice => { - const out = if (comptime p.sentinel()) |s| try gpa.allocSentinel(p.child, value.len, s) else try gpa.alloc(p.child, value.len); - for (out, value) |*o, v| o.* = try zig017ZonDupe(p.child, gpa, v); - return out; - }, - else => comptime unreachable, - }, - .array => |a| { - var out: T = undefined; - for (&out, value) |*o, v| o.* = try zig017ZonDupe(a.child, gpa, v); - return out; - }, - .@"struct" => |s| { - var out: T = value; - inline for (s.field_names, s.field_types, s.field_attrs) |n, FT, attrs| { - if (!attrs.@"comptime") @field(out, n) = try zig017ZonDupe(FT, gpa, @field(value, n)); - } - return out; - }, - .@"union" => |u| if (u.tag_type == null) return value else switch (value) { - inline else => |payload, tag| return @unionInit(T, @tagName(tag), try zig017ZonDupe(@TypeOf(payload), gpa, payload)), - }, - .optional => |o| return if (value) |v| try zig017ZonDupe(o.child, gpa, v) else null, - else => return value, - } -} -// ZIG017 stand-in: 0.16's `std.zon.parse.free`, which 0.17 removed (results now come from an -// arena the caller owns). Goes with the zon arena PR. -fn zig017ZonFree(gpa: std.mem.Allocator, value: anytype) void { - switch (@typeInfo(@TypeOf(value))) { - .pointer => |p| switch (p.size) { - .one => { - zig017ZonFree(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| zig017ZonFree(gpa, item); - gpa.free(value); - }, - else => comptime unreachable, - }, - .array => for (value) |item| zig017ZonFree(gpa, item), - .@"struct" => |s| inline for (s.field_names) |n| zig017ZonFree(gpa, @field(value, n)), - .@"union" => |u| if (u.tag_type != null) switch (value) { - inline else => |payload| zig017ZonFree(gpa, payload), - }, - .optional => if (value) |some| zig017ZonFree(gpa, some), - else => {}, - } -}