authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-09-27 04:18:52-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-09-27 04:18:52-07:00
log20fd6977376c0f159a15a8b6264480f9875436c1
treec3819950214479d3636c7bbc75f4fa59d8d18708
parent9cbd85b3410952b1f931849044f707222df52270

Better API for reporting OOM


1 files changed, 75 insertions(+), 45 deletions(-)

lib/std/zon/parse.zig+75-45
......@@ -94,8 +94,12 @@ pub const Error = struct {
9494/// Errors encountered while parsing ZON. See `log` and `fatal` for reporting errors to the user.
9595pub const Errors = struct {
9696 items: []const Error,
97 oom: bool,
9798
98 pub const empty: Errors = .{ .items = &.{} };
99 pub const empty: Errors = .{
100 .items = &.{},
101 .oom = false,
102 };
99103
100104 /// Log the failure with `std.log.err`.
101105 pub fn log(self: *const Errors, path: []const u8) void {
......@@ -119,12 +123,9 @@ pub const Errors = struct {
119123 .arena = arena,
120124 .source = ".{ .foo = 1, .bar = 2 }",
121125 .errors = &errors,
122 }) catch |err| switch (err) {
123 error.OutOfMemory => return error.OutOfMemory,
124 error.ParseZon => b: {
125 errors.log("input.zon");
126 break :b .{ .foo = 0, .bar = 0 };
127 },
126 }) catch b: {
127 errors.log("input.zon");
128 break :b .{ .foo = 0, .bar = 0 };
128129 };
129130 _ = parsed;
130131 }
......@@ -151,16 +152,14 @@ pub const Errors = struct {
151152 .arena = arena,
152153 .source = ".{ .foo = 1, .bar = 2 }",
153154 .errors = &errors,
154 }) catch |err| switch (err) {
155 error.OutOfMemory => return error.OutOfMemory,
156 error.ParseZon => errors.fatal("input.zon"),
157 };
155 }) catch errors.fatal("input.zon");
158156 _ = parsed;
159157 }
160158
161159 pub fn fmt(self: *const Errors, path: []const u8) Formatter {
162160 return .{
163161 .items = self.items,
162 .oom = self.oom,
164163 .path = path,
165164 };
166165 }
......@@ -168,8 +167,12 @@ pub const Errors = struct {
168167 pub const Formatter = struct {
169168 path: []const u8,
170169 items: []const Error,
170 oom: bool,
171171
172172 pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void {
173 if (self.oom) {
174 try w.print("{s}: out of memory", .{self.path});
175 }
173176 for (self.items) |e| {
174177 try w.print("{s}:", .{self.path});
175178 try w.print("{d}:{d}: error: {s}\n", .{
......@@ -189,6 +192,11 @@ pub const Errors = struct {
189192 }
190193 }
191194 };
195
196 fn checkOom(self: *Errors, result: anytype) @TypeOf(result) {
197 self.oom |= result == error.OutOfMemory;
198 return result;
199 }
192200};
193201
194202pub const Options = struct {
......@@ -238,12 +246,9 @@ test fromSlice {
238246 .arena = arena,
239247 .source = source,
240248 .errors = &errors,
241 }) catch |err| switch (err) {
242 error.OutOfMemory => return err,
243 error.ParseZon => {
244 errors.log("texture_options.zon");
245 return err;
246 },
249 }) catch |err| {
250 errors.log("texture_options.zon");
251 return err;
247252 };
248253
249254 try std.testing.expectEqualDeep(TextureOptions{
......@@ -313,24 +318,18 @@ test updateFromSlice {
313318 .arena = arena,
314319 .source = global_config,
315320 .errors = &errors,
316 }) catch |err| switch (err) {
317 error.OutOfMemory => return err,
318 error.ParseZon => {
319 errors.log("global_config.zon");
320 return err;
321 },
321 }) catch |err| {
322 errors.log("global_config.zon");
323 return err;
322324 };
323325 updateFromSlice(MyTextEditorConfig, &config, .{
324326 .gpa = gpa,
325327 .arena = arena,
326328 .source = project_config,
327329 .errors = &errors,
328 }) catch |err| switch (err) {
329 error.OutOfMemory => return err,
330 error.ParseZon => {
331 errors.log("project_config.zon");
332 return err;
333 },
330 }) catch |err| {
331 errors.log("project_config.zon");
332 return err;
334333 };
335334
336335 try std.testing.expectEqualDeep(MyTextEditorConfig{
......@@ -358,23 +357,32 @@ fn fromSliceInner(
358357 initialized: bool,
359358 options: Options,
360359) error{ OutOfMemory, ParseZon }!void {
360 options.errors.oom = false;
361361 var errors: std.ArrayList(Error) = .empty;
362362 defer options.errors.items = errors.items;
363363
364 var ast = try std.zig.Ast.parse(options.gpa, options.source, .{ .mode = .zon });
364 var ast = try options.errors.checkOom(std.zig.Ast.parse(
365 options.gpa,
366 options.source,
367 .{ .mode = .zon },
368 ));
365369 defer ast.deinit(options.gpa);
366370
367 var zoir = try ZonGen.generate(options.gpa, ast, .{ .parse_str_lits = false });
371 var zoir = try options.errors.checkOom(ZonGen.generate(
372 options.gpa,
373 ast,
374 .{ .parse_str_lits = false },
375 ));
368376 defer zoir.deinit(options.gpa);
369377
370 try inner(T, value, initialized, .{
378 try options.errors.checkOom(inner(T, value, initialized, .{
371379 .arena = options.arena,
372380 .ast = &ast,
373381 .zoir = &zoir,
374382 .node = .root,
375383 .errors = &errors,
376384 .ignore_unknown_fields = options.ignore_unknown_fields,
377 });
385 }));
378386}
379387
380388pub const FromZoirOptions = struct {
......@@ -431,15 +439,16 @@ fn fromZoirInner(
431439 initialized: bool,
432440 options: FromZoirOptions,
433441) error{ OutOfMemory, ParseZon }!void {
442 options.errors.oom = false;
434443 var errors: std.ArrayList(Error) = .empty;
435444 defer options.errors.items = errors.items;
436 return inner(T, value, initialized, .{
445 return options.errors.checkOom(inner(T, value, initialized, .{
437446 .arena = options.arena,
438447 .ast = options.ast,
439448 .zoir = options.zoir,
440449 .errors = &errors,
441450 .ignore_unknown_fields = options.ignore_unknown_fields,
442 });
451 }));
443452}
444453
445454const InnerOptions = struct {
......@@ -1441,17 +1450,38 @@ test "std.zon failure/oom formatting" {
14411450 var arena_allocator: ArenaAllocator = .init(gpa);
14421451 defer arena_allocator.deinit();
14431452 const arena = arena_allocator.allocator();
1444 var errors: Errors = .empty;
14451453
1446 try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{
1447 .gpa = .failing,
1448 .arena = arena,
1449 .source = "\"foo\"",
1450 .errors = &errors,
1451 }));
1452 try std.testing.expectFmt(
1453 \\
1454 , "{f}", .{errors.fmt("input.zon")});
1454 {
1455 var errors: Errors = .empty;
1456 try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{
1457 .gpa = .failing,
1458 .arena = arena,
1459 .source = "\"foo\"",
1460 .errors = &errors,
1461 }));
1462 try std.testing.expectFmt(
1463 \\input.zon: out of memory
1464 , "{f}", .{errors.fmt("input.zon")});
1465 }
1466
1467 {
1468 var ast = try std.zig.Ast.parse(gpa, "\"foo\"", .{ .mode = .zon });
1469 defer ast.deinit(gpa);
1470
1471 var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false });
1472 defer zoir.deinit(gpa);
1473
1474 var errors: Errors = .empty;
1475 try std.testing.expectError(error.OutOfMemory, fromZoir([]const u8, .{
1476 .arena = .failing,
1477 .ast = &ast,
1478 .zoir = &zoir,
1479 .errors = &errors,
1480 }));
1481 try std.testing.expectFmt(
1482 \\input.zon: out of memory
1483 , "{f}", .{errors.fmt("input.zon")});
1484 }
14551485}
14561486
14571487test "std.zon fromSlice syntax error" {