authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-07-16 20:21:54-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-09-26 12:35:38-07:00
logdb34b1f9ab306ab0568ad6e7409c0c577fb1e0e6
tree474dfbe3b909ba98cbf8093f3bb3eb97543cf39f
parenta4433d88b0a05b64a536a242d5d51d3371477e52

Parse nontrivial types into out pointer internally

This doesn't change any functionality, but it's a step towards supporting an update style API.

1 files changed, 70 insertions(+), 67 deletions(-)

lib/std/zon/parse.zig+70-67
......@@ -398,7 +398,9 @@ pub fn fromZoirNodeAlloc(
398398 .diag = diag,
399399 };
400400
401 return parser.parseExpr(T, node);
401 var out: T = undefined;
402 try parser.parseExprInto(node, &out);
403 return out;
402404}
403405
404406/// Frees ZON values.
......@@ -488,34 +490,36 @@ const Parser = struct {
488490
489491 const ParseExprError = error{ ParseZon, OutOfMemory };
490492
491 fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprError!T {
492 return self.parseExprInner(T, node) catch |err| switch (err) {
493 error.WrongType => return self.failExpectedType(T, node),
493 fn parseExprInto(self: *@This(), node: Zoir.Node.Index, out: anytype) ParseExprError!void {
494 return self.parseExprInnerInto(node, out) catch |err| switch (err) {
495 error.WrongType => return self.failExpectedType(@TypeOf(out.*), node),
494496 else => |e| return e,
495497 };
496498 }
497499
498500 const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType };
499501
500 fn parseExprInner(
502 fn parseExprInnerInto(
501503 self: *@This(),
502 T: type,
503504 node: Zoir.Node.Index,
504 ) ParseExprInnerError!T {
505 if (T == Zoir.Node.Index) {
506 return node;
505 out: anytype,
506 ) ParseExprInnerError!void {
507 if (@TypeOf(out.*) == Zoir.Node.Index) {
508 out.* = node;
509 return;
507510 }
508511
509 switch (@typeInfo(T)) {
512 switch (@typeInfo(@TypeOf(out.*))) {
510513 .optional => |optional| if (node.get(self.zoir) == .null) {
511 return null;
514 out.* = null;
512515 } else {
513 return try self.parseExprInner(optional.child, node);
516 out.* = @as(optional.child, undefined); // Make non-null
517 try self.parseExprInnerInto(node, &out.*.?); // Parse value into non-null pointer
514518 },
515 .bool => return self.parseBool(node),
516 .int => return self.parseInt(T, node),
517 .float => return self.parseFloat(T, node),
518 .@"enum" => return self.parseEnumLiteral(T, node),
519 .bool => out.* = try self.parseBool(node),
520 .int => out.* = try self.parseInt(@TypeOf(out.*), node),
521 .float => out.* = try self.parseFloat(@TypeOf(out.*), node),
522 .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node),
519523 .pointer => |pointer| switch (pointer.size) {
520524 .one => {
521525 const slice = try self.gpa.alignedAlloc(
......@@ -524,23 +528,20 @@ const Parser = struct {
524528 1,
525529 );
526530 errdefer self.gpa.free(slice);
527 const result = &slice[0];
528 result.* = try self.parseExprInner(pointer.child, node);
529 return result;
531 const ptr = &slice[0];
532 try self.parseExprInnerInto(node, ptr);
533 out.* = ptr;
530534 },
531 .slice => return self.parseSlicePointer(T, node),
535 .slice => out.* = try self.parseSlicePointer(@TypeOf(out.*), node),
532536 else => comptime unreachable,
533537 },
534 .array => return self.parseArray(T, node),
535 .vector => |vector| {
536 const A = [vector.len]vector.child;
537 return try self.parseArray(A, node);
538 },
538 .array => try self.parseArrayInto(node, out),
539 .vector => try self.parseVectorInto(node, out),
539540 .@"struct" => |@"struct"| if (@"struct".is_tuple)
540 return self.parseTuple(T, node)
541 try self.parseTupleInto(node, out)
541542 else
542 return self.parseStruct(T, node),
543 .@"union" => return self.parseUnion(T, node),
543 try self.parseStructInto(node, out),
544 .@"union" => try self.parseUnionInto(node, out),
544545
545546 else => comptime unreachable,
546547 }
......@@ -758,53 +759,61 @@ const Parser = struct {
758759 free(self.gpa, item);
759760 }
760761 };
761 elem.* = try self.parseExpr(pointer.child, nodes.at(@intCast(i)));
762 try self.parseExprInto(nodes.at(@intCast(i)), elem);
762763 }
763764
764765 return slice;
765766 }
766767
767 fn parseArray(self: *@This(), T: type, node: Zoir.Node.Index) !T {
768 fn parseArrayInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void {
768769 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
769770 .array_literal => |nodes| nodes,
770771 .empty_literal => .{ .start = node, .len = 0 },
771772 else => return error.WrongType,
772773 };
773774
774 const array_info = @typeInfo(T).array;
775 const array = @typeInfo(@TypeOf(out.*)).array;
775776
776777 // Check if the size matches
777 if (nodes.len < array_info.len) {
778 if (nodes.len < array.len) {
778779 return self.failNodeFmt(
779780 node,
780781 "expected {} array elements; found {}",
781 .{ array_info.len, nodes.len },
782 .{ array.len, nodes.len },
782783 );
783 } else if (nodes.len > array_info.len) {
784 } else if (nodes.len > array.len) {
784785 return self.failNodeFmt(
785 nodes.at(array_info.len),
786 nodes.at(array.len),
786787 "index {} outside of array of length {}",
787 .{ array_info.len, array_info.len },
788 .{ array.len, array.len },
788789 );
789790 }
790791
791792 // Parse the elements and return the array
792 var result: T = undefined;
793 for (&result, 0..) |*elem, i| {
793 inline for (out, 0..) |*elem, i| {
794794 // If we fail to parse this field, free all fields before it
795795 errdefer if (self.options.free_on_error) {
796 for (result[0..i]) |item| {
796 for (out[0..i]) |item| {
797797 free(self.gpa, item);
798798 }
799799 };
800800
801 elem.* = try self.parseExpr(array_info.child, nodes.at(@intCast(i)));
801 try self.parseExprInto(nodes.at(@intCast(i)), elem);
802 }
803
804 if (array.sentinel()) |s| {
805 out[array.len] = s;
802806 }
803 if (array_info.sentinel()) |s| result[result.len] = s;
804 return result;
805807 }
806808
807 fn parseStruct(self: *@This(), T: type, node: Zoir.Node.Index) !T {
809 fn parseVectorInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void {
810 const vector = @typeInfo(@TypeOf(out.*)).vector;
811 var array: [vector.len]vector.child = undefined;
812 try self.parseArrayInto(node, &array);
813 out.* = array;
814 }
815
816 fn parseStructInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void {
808817 const repr = node.get(self.zoir);
809818 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) {
810819 .struct_literal => |nodes| nodes,
......@@ -812,7 +821,7 @@ const Parser = struct {
812821 else => return error.WrongType,
813822 };
814823
815 const info = @typeInfo(T).@"struct";
824 const info = @typeInfo(@TypeOf(out.*)).@"struct";
816825
817826 // Build a map from field name to index.
818827 // The special value `comptime_field` indicates that this is actually a comptime field.
......@@ -826,7 +835,6 @@ const Parser = struct {
826835 };
827836
828837 // Parse the struct
829 var result: T = undefined;
830838 var field_found: [info.field_names.len]bool = @splat(false);
831839
832840 // If we fail partway through, free all already initialized fields
......@@ -836,7 +844,7 @@ const Parser = struct {
836844 switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) {
837845 inline 0...(info.field_names.len - 1) => |name_index| {
838846 const name = info.field_names[name_index];
839 free(self.gpa, @field(result, name));
847 free(self.gpa, @field(out, name));
840848 },
841849 else => unreachable, // Can't be out of bounds
842850 }
......@@ -848,7 +856,7 @@ const Parser = struct {
848856 const name = fields.names[i].get(self.zoir);
849857 const field_index = field_indices.get(name) orelse {
850858 if (self.options.ignore_unknown_fields) continue;
851 return self.failUnexpected(T, "field", node, i, name);
859 return self.failUnexpected(@TypeOf(out.*), "field", node, i, name);
852860 };
853861 if (field_index == comptime_field) {
854862 return self.failComptimeField(node, i);
......@@ -863,9 +871,9 @@ const Parser = struct {
863871 inline 0...(info.field_names.len - 1) => |j| {
864872 if (info.field_attrs[j].@"comptime") unreachable;
865873
866 @field(result, info.field_names[j]) = try self.parseExpr(
867 info.field_types[j],
874 try self.parseExprInto(
868875 fields.vals.at(@intCast(i)),
876 &@field(out, info.field_names[j]),
869877 );
870878 },
871879 else => unreachable, // Can't be out of bounds
......@@ -879,7 +887,7 @@ const Parser = struct {
879887 if (!found) {
880888 const field_attrs = info.field_attrs[i];
881889 if (field_attrs.defaultValue(info.field_types[i])) |default| {
882 @field(result, info.field_names[i]) = default;
890 @field(out, info.field_names[i]) = default;
883891 } else {
884892 return self.failNodeFmt(
885893 node,
......@@ -889,19 +897,16 @@ const Parser = struct {
889897 }
890898 }
891899 }
892
893 return result;
894900 }
895901
896 fn parseTuple(self: *@This(), T: type, node: Zoir.Node.Index) !T {
902 fn parseTupleInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void {
897903 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
898904 .array_literal => |nodes| nodes,
899905 .empty_literal => .{ .start = node, .len = 0 },
900906 else => return error.WrongType,
901907 };
902908
903 var result: T = undefined;
904 const info = @typeInfo(T).@"struct";
909 const info = @typeInfo(@TypeOf(out.*)).@"struct";
905910
906911 if (nodes.len > info.field_names.len) {
907912 return self.failNodeFmt(
......@@ -915,7 +920,7 @@ const Parser = struct {
915920 // Check if we're out of bounds
916921 if (i >= nodes.len) {
917922 if (info.field_attrs[i].defaultValue(info.field_types[i])) |default| {
918 @field(result, info.field_names[i]) = default;
923 @field(out.*, info.field_names[i]) = default;
919924 } else {
920925 return self.failNodeFmt(node, "missing tuple field with index {}", .{i});
921926 }
......@@ -924,23 +929,21 @@ const Parser = struct {
924929 errdefer if (self.options.free_on_error) {
925930 inline for (0..i) |j| {
926931 if (j >= i) break;
927 free(self.gpa, result[j]);
932 free(self.gpa, out[j]);
928933 }
929934 };
930935
931936 if (info.field_attrs[i].@"comptime") {
932937 return self.failComptimeField(node, i);
933938 } else {
934 result[i] = try self.parseExpr(info.field_types[i], nodes.at(i));
939 try self.parseExprInto(nodes.at(i), &out[i]);
935940 }
936941 }
937942 }
938
939 return result;
940943 }
941944
942 fn parseUnion(self: *@This(), T: type, node: Zoir.Node.Index) !T {
943 const @"union" = @typeInfo(T).@"union";
945 fn parseUnionInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void {
946 const @"union" = @typeInfo(@TypeOf(out.*)).@"union";
944947
945948 if (@"union".field_names.len == 0) comptime unreachable;
946949
......@@ -966,7 +969,7 @@ const Parser = struct {
966969 const field_index = b: {
967970 const field_name_str = field_name.get(self.zoir);
968971 break :b field_indices.get(field_name_str) orelse
969 return self.failUnexpected(T, "field", node, null, field_name_str);
972 return self.failUnexpected(@TypeOf(out.*), "field", node, null, field_name_str);
970973 };
971974
972975 // Initialize the union from the given field.
......@@ -977,7 +980,7 @@ const Parser = struct {
977980 return self.failNode(node, "expected union");
978981
979982 // Instantiate the union
980 return @unionInit(T, @"union".field_names[i], {});
983 out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], {});
981984 },
982985 else => unreachable, // Can't be out of bounds
983986 }
......@@ -992,15 +995,15 @@ const Parser = struct {
992995 const field_name_str = field_name.get(self.zoir);
993996 const field_val = struct_fields.vals.at(0);
994997 const field_index = field_indices.get(field_name_str) orelse
995 return self.failUnexpected(T, "field", node, 0, field_name_str);
998 return self.failUnexpected(@TypeOf(out.*), "field", node, 0, field_name_str);
996999
9971000 switch (field_index) {
9981001 inline 0...@"union".field_names.len - 1 => |i| {
9991002 if (@"union".field_types[i] == void) {
10001003 return self.failNode(field_val, "expected type 'void'");
10011004 } else {
1002 const value = try self.parseExpr(@"union".field_types[i], field_val);
1003 return @unionInit(T, @"union".field_names[i], value);
1005 out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined);
1006 try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i]));
10041007 }
10051008 },
10061009 else => unreachable, // Can't be out of bounds