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 {...@@ -94,8 +94,12 @@ pub const Error = struct {
94/// Errors encountered while parsing ZON. See `log` and `fatal` for reporting errors to the user.94/// Errors encountered while parsing ZON. See `log` and `fatal` for reporting errors to the user.
95pub const Errors = struct {95pub const Errors = struct {
96 items: []const Error,96 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
100 /// Log the failure with `std.log.err`.104 /// Log the failure with `std.log.err`.
101 pub fn log(self: *const Errors, path: []const u8) void {105 pub fn log(self: *const Errors, path: []const u8) void {
...@@ -119,12 +123,9 @@ pub const Errors = struct {...@@ -119,12 +123,9 @@ pub const Errors = struct {
119 .arena = arena,123 .arena = arena,
120 .source = ".{ .foo = 1, .bar = 2 }",124 .source = ".{ .foo = 1, .bar = 2 }",
121 .errors = &errors,125 .errors = &errors,
122 }) catch |err| switch (err) {126 }) catch b: {
123 error.OutOfMemory => return error.OutOfMemory,127 errors.log("input.zon");
124 error.ParseZon => b: {128 break :b .{ .foo = 0, .bar = 0 };
125 errors.log("input.zon");
126 break :b .{ .foo = 0, .bar = 0 };
127 },
128 };129 };
129 _ = parsed;130 _ = parsed;
130 }131 }
...@@ -151,16 +152,14 @@ pub const Errors = struct {...@@ -151,16 +152,14 @@ pub const Errors = struct {
151 .arena = arena,152 .arena = arena,
152 .source = ".{ .foo = 1, .bar = 2 }",153 .source = ".{ .foo = 1, .bar = 2 }",
153 .errors = &errors,154 .errors = &errors,
154 }) catch |err| switch (err) {155 }) catch errors.fatal("input.zon");
155 error.OutOfMemory => return error.OutOfMemory,
156 error.ParseZon => errors.fatal("input.zon"),
157 };
158 _ = parsed;156 _ = parsed;
159 }157 }
160158
161 pub fn fmt(self: *const Errors, path: []const u8) Formatter {159 pub fn fmt(self: *const Errors, path: []const u8) Formatter {
162 return .{160 return .{
163 .items = self.items,161 .items = self.items,
162 .oom = self.oom,
164 .path = path,163 .path = path,
165 };164 };
166 }165 }
...@@ -168,8 +167,12 @@ pub const Errors = struct {...@@ -168,8 +167,12 @@ pub const Errors = struct {
168 pub const Formatter = struct {167 pub const Formatter = struct {
169 path: []const u8,168 path: []const u8,
170 items: []const Error,169 items: []const Error,
170 oom: bool,
171171
172 pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void {172 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 }
173 for (self.items) |e| {176 for (self.items) |e| {
174 try w.print("{s}:", .{self.path});177 try w.print("{s}:", .{self.path});
175 try w.print("{d}:{d}: error: {s}\n", .{178 try w.print("{d}:{d}: error: {s}\n", .{
...@@ -189,6 +192,11 @@ pub const Errors = struct {...@@ -189,6 +192,11 @@ pub const Errors = struct {
189 }192 }
190 }193 }
191 };194 };
195
196 fn checkOom(self: *Errors, result: anytype) @TypeOf(result) {
197 self.oom |= result == error.OutOfMemory;
198 return result;
199 }
192};200};
193201
194pub const Options = struct {202pub const Options = struct {
...@@ -238,12 +246,9 @@ test fromSlice {...@@ -238,12 +246,9 @@ test fromSlice {
238 .arena = arena,246 .arena = arena,
239 .source = source,247 .source = source,
240 .errors = &errors,248 .errors = &errors,
241 }) catch |err| switch (err) {249 }) catch |err| {
242 error.OutOfMemory => return err,250 errors.log("texture_options.zon");
243 error.ParseZon => {251 return err;
244 errors.log("texture_options.zon");
245 return err;
246 },
247 };252 };
248253
249 try std.testing.expectEqualDeep(TextureOptions{254 try std.testing.expectEqualDeep(TextureOptions{
...@@ -313,24 +318,18 @@ test updateFromSlice {...@@ -313,24 +318,18 @@ test updateFromSlice {
313 .arena = arena,318 .arena = arena,
314 .source = global_config,319 .source = global_config,
315 .errors = &errors,320 .errors = &errors,
316 }) catch |err| switch (err) {321 }) catch |err| {
317 error.OutOfMemory => return err,322 errors.log("global_config.zon");
318 error.ParseZon => {323 return err;
319 errors.log("global_config.zon");
320 return err;
321 },
322 };324 };
323 updateFromSlice(MyTextEditorConfig, &config, .{325 updateFromSlice(MyTextEditorConfig, &config, .{
324 .gpa = gpa,326 .gpa = gpa,
325 .arena = arena,327 .arena = arena,
326 .source = project_config,328 .source = project_config,
327 .errors = &errors,329 .errors = &errors,
328 }) catch |err| switch (err) {330 }) catch |err| {
329 error.OutOfMemory => return err,331 errors.log("project_config.zon");
330 error.ParseZon => {332 return err;
331 errors.log("project_config.zon");
332 return err;
333 },
334 };333 };
335334
336 try std.testing.expectEqualDeep(MyTextEditorConfig{335 try std.testing.expectEqualDeep(MyTextEditorConfig{
...@@ -358,23 +357,32 @@ fn fromSliceInner(...@@ -358,23 +357,32 @@ fn fromSliceInner(
358 initialized: bool,357 initialized: bool,
359 options: Options,358 options: Options,
360) error{ OutOfMemory, ParseZon }!void {359) error{ OutOfMemory, ParseZon }!void {
360 options.errors.oom = false;
361 var errors: std.ArrayList(Error) = .empty;361 var errors: std.ArrayList(Error) = .empty;
362 defer options.errors.items = errors.items;362 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 ));
365 defer ast.deinit(options.gpa);369 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 ));
368 defer zoir.deinit(options.gpa);376 defer zoir.deinit(options.gpa);
369377
370 try inner(T, value, initialized, .{378 try options.errors.checkOom(inner(T, value, initialized, .{
371 .arena = options.arena,379 .arena = options.arena,
372 .ast = &ast,380 .ast = &ast,
373 .zoir = &zoir,381 .zoir = &zoir,
374 .node = .root,382 .node = .root,
375 .errors = &errors,383 .errors = &errors,
376 .ignore_unknown_fields = options.ignore_unknown_fields,384 .ignore_unknown_fields = options.ignore_unknown_fields,
377 });385 }));
378}386}
379387
380pub const FromZoirOptions = struct {388pub const FromZoirOptions = struct {
...@@ -431,15 +439,16 @@ fn fromZoirInner(...@@ -431,15 +439,16 @@ fn fromZoirInner(
431 initialized: bool,439 initialized: bool,
432 options: FromZoirOptions,440 options: FromZoirOptions,
433) error{ OutOfMemory, ParseZon }!void {441) error{ OutOfMemory, ParseZon }!void {
442 options.errors.oom = false;
434 var errors: std.ArrayList(Error) = .empty;443 var errors: std.ArrayList(Error) = .empty;
435 defer options.errors.items = errors.items;444 defer options.errors.items = errors.items;
436 return inner(T, value, initialized, .{445 return options.errors.checkOom(inner(T, value, initialized, .{
437 .arena = options.arena,446 .arena = options.arena,
438 .ast = options.ast,447 .ast = options.ast,
439 .zoir = options.zoir,448 .zoir = options.zoir,
440 .errors = &errors,449 .errors = &errors,
441 .ignore_unknown_fields = options.ignore_unknown_fields,450 .ignore_unknown_fields = options.ignore_unknown_fields,
442 });451 }));
443}452}
444453
445const InnerOptions = struct {454const InnerOptions = struct {
...@@ -1441,17 +1450,38 @@ test "std.zon failure/oom formatting" {...@@ -1441,17 +1450,38 @@ test "std.zon failure/oom formatting" {
1441 var arena_allocator: ArenaAllocator = .init(gpa);1450 var arena_allocator: ArenaAllocator = .init(gpa);
1442 defer arena_allocator.deinit();1451 defer arena_allocator.deinit();
1443 const arena = arena_allocator.allocator();1452 const arena = arena_allocator.allocator();
1444 var errors: Errors = .empty;
14451453
1446 try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{1454 {
1447 .gpa = .failing,1455 var errors: Errors = .empty;
1448 .arena = arena,1456 try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{
1449 .source = "\"foo\"",1457 .gpa = .failing,
1450 .errors = &errors,1458 .arena = arena,
1451 }));1459 .source = "\"foo\"",
1452 try std.testing.expectFmt(1460 .errors = &errors,
1453 \\1461 }));
1454 , "{f}", .{errors.fmt("input.zon")});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 }
1455}1485}
14561486
1457test "std.zon fromSlice syntax error" {1487test "std.zon fromSlice syntax error" {