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(...@@ -398,7 +398,9 @@ pub fn fromZoirNodeAlloc(
398 .diag = diag,398 .diag = diag,
399 };399 };
400400
401 return parser.parseExpr(T, node);401 var out: T = undefined;
402 try parser.parseExprInto(node, &out);
403 return out;
402}404}
403405
404/// Frees ZON values.406/// Frees ZON values.
...@@ -488,34 +490,36 @@ const Parser = struct {...@@ -488,34 +490,36 @@ const Parser = struct {
488490
489 const ParseExprError = error{ ParseZon, OutOfMemory };491 const ParseExprError = error{ ParseZon, OutOfMemory };
490492
491 fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprError!T {493 fn parseExprInto(self: *@This(), node: Zoir.Node.Index, out: anytype) ParseExprError!void {
492 return self.parseExprInner(T, node) catch |err| switch (err) {494 return self.parseExprInnerInto(node, out) catch |err| switch (err) {
493 error.WrongType => return self.failExpectedType(T, node),495 error.WrongType => return self.failExpectedType(@TypeOf(out.*), node),
494 else => |e| return e,496 else => |e| return e,
495 };497 };
496 }498 }
497499
498 const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType };500 const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType };
499501
500 fn parseExprInner(502 fn parseExprInnerInto(
501 self: *@This(),503 self: *@This(),
502 T: type,
503 node: Zoir.Node.Index,504 node: Zoir.Node.Index,
504 ) ParseExprInnerError!T {505 out: anytype,
505 if (T == Zoir.Node.Index) {506 ) ParseExprInnerError!void {
506 return node;507 if (@TypeOf(out.*) == Zoir.Node.Index) {
508 out.* = node;
509 return;
507 }510 }
508511
509 switch (@typeInfo(T)) {512 switch (@typeInfo(@TypeOf(out.*))) {
510 .optional => |optional| if (node.get(self.zoir) == .null) {513 .optional => |optional| if (node.get(self.zoir) == .null) {
511 return null;514 out.* = null;
512 } else {515 } 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
514 },518 },
515 .bool => return self.parseBool(node),519 .bool => out.* = try self.parseBool(node),
516 .int => return self.parseInt(T, node),520 .int => out.* = try self.parseInt(@TypeOf(out.*), node),
517 .float => return self.parseFloat(T, node),521 .float => out.* = try self.parseFloat(@TypeOf(out.*), node),
518 .@"enum" => return self.parseEnumLiteral(T, node),522 .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node),
519 .pointer => |pointer| switch (pointer.size) {523 .pointer => |pointer| switch (pointer.size) {
520 .one => {524 .one => {
521 const slice = try self.gpa.alignedAlloc(525 const slice = try self.gpa.alignedAlloc(
...@@ -524,23 +528,20 @@ const Parser = struct {...@@ -524,23 +528,20 @@ const Parser = struct {
524 1,528 1,
525 );529 );
526 errdefer self.gpa.free(slice);530 errdefer self.gpa.free(slice);
527 const result = &slice[0];531 const ptr = &slice[0];
528 result.* = try self.parseExprInner(pointer.child, node);532 try self.parseExprInnerInto(node, ptr);
529 return result;533 out.* = ptr;
530 },534 },
531 .slice => return self.parseSlicePointer(T, node),535 .slice => out.* = try self.parseSlicePointer(@TypeOf(out.*), node),
532 else => comptime unreachable,536 else => comptime unreachable,
533 },537 },
534 .array => return self.parseArray(T, node),538 .array => try self.parseArrayInto(node, out),
535 .vector => |vector| {539 .vector => try self.parseVectorInto(node, out),
536 const A = [vector.len]vector.child;
537 return try self.parseArray(A, node);
538 },
539 .@"struct" => |@"struct"| if (@"struct".is_tuple)540 .@"struct" => |@"struct"| if (@"struct".is_tuple)
540 return self.parseTuple(T, node)541 try self.parseTupleInto(node, out)
541 else542 else
542 return self.parseStruct(T, node),543 try self.parseStructInto(node, out),
543 .@"union" => return self.parseUnion(T, node),544 .@"union" => try self.parseUnionInto(node, out),
544545
545 else => comptime unreachable,546 else => comptime unreachable,
546 }547 }
...@@ -758,53 +759,61 @@ const Parser = struct {...@@ -758,53 +759,61 @@ const Parser = struct {
758 free(self.gpa, item);759 free(self.gpa, item);
759 }760 }
760 };761 };
761 elem.* = try self.parseExpr(pointer.child, nodes.at(@intCast(i)));762 try self.parseExprInto(nodes.at(@intCast(i)), elem);
762 }763 }
763764
764 return slice;765 return slice;
765 }766 }
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 {
768 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {769 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
769 .array_literal => |nodes| nodes,770 .array_literal => |nodes| nodes,
770 .empty_literal => .{ .start = node, .len = 0 },771 .empty_literal => .{ .start = node, .len = 0 },
771 else => return error.WrongType,772 else => return error.WrongType,
772 };773 };
773774
774 const array_info = @typeInfo(T).array;775 const array = @typeInfo(@TypeOf(out.*)).array;
775776
776 // Check if the size matches777 // Check if the size matches
777 if (nodes.len < array_info.len) {778 if (nodes.len < array.len) {
778 return self.failNodeFmt(779 return self.failNodeFmt(
779 node,780 node,
780 "expected {} array elements; found {}",781 "expected {} array elements; found {}",
781 .{ array_info.len, nodes.len },782 .{ array.len, nodes.len },
782 );783 );
783 } else if (nodes.len > array_info.len) {784 } else if (nodes.len > array.len) {
784 return self.failNodeFmt(785 return self.failNodeFmt(
785 nodes.at(array_info.len),786 nodes.at(array.len),
786 "index {} outside of array of length {}",787 "index {} outside of array of length {}",
787 .{ array_info.len, array_info.len },788 .{ array.len, array.len },
788 );789 );
789 }790 }
790791
791 // Parse the elements and return the array792 // Parse the elements and return the array
792 var result: T = undefined;793 inline for (out, 0..) |*elem, i| {
793 for (&result, 0..) |*elem, i| {
794 // If we fail to parse this field, free all fields before it794 // If we fail to parse this field, free all fields before it
795 errdefer if (self.options.free_on_error) {795 errdefer if (self.options.free_on_error) {
796 for (result[0..i]) |item| {796 for (out[0..i]) |item| {
797 free(self.gpa, item);797 free(self.gpa, item);
798 }798 }
799 };799 };
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;
802 }806 }
803 if (array_info.sentinel()) |s| result[result.len] = s;
804 return result;
805 }807 }
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 {
808 const repr = node.get(self.zoir);817 const repr = node.get(self.zoir);
809 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) {818 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) {
810 .struct_literal => |nodes| nodes,819 .struct_literal => |nodes| nodes,
...@@ -812,7 +821,7 @@ const Parser = struct {...@@ -812,7 +821,7 @@ const Parser = struct {
812 else => return error.WrongType,821 else => return error.WrongType,
813 };822 };
814823
815 const info = @typeInfo(T).@"struct";824 const info = @typeInfo(@TypeOf(out.*)).@"struct";
816825
817 // Build a map from field name to index.826 // Build a map from field name to index.
818 // The special value `comptime_field` indicates that this is actually a comptime field.827 // The special value `comptime_field` indicates that this is actually a comptime field.
...@@ -826,7 +835,6 @@ const Parser = struct {...@@ -826,7 +835,6 @@ const Parser = struct {
826 };835 };
827836
828 // Parse the struct837 // Parse the struct
829 var result: T = undefined;
830 var field_found: [info.field_names.len]bool = @splat(false);838 var field_found: [info.field_names.len]bool = @splat(false);
831839
832 // If we fail partway through, free all already initialized fields840 // If we fail partway through, free all already initialized fields
...@@ -836,7 +844,7 @@ const Parser = struct {...@@ -836,7 +844,7 @@ const Parser = struct {
836 switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) {844 switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) {
837 inline 0...(info.field_names.len - 1) => |name_index| {845 inline 0...(info.field_names.len - 1) => |name_index| {
838 const name = info.field_names[name_index];846 const name = info.field_names[name_index];
839 free(self.gpa, @field(result, name));847 free(self.gpa, @field(out, name));
840 },848 },
841 else => unreachable, // Can't be out of bounds849 else => unreachable, // Can't be out of bounds
842 }850 }
...@@ -848,7 +856,7 @@ const Parser = struct {...@@ -848,7 +856,7 @@ const Parser = struct {
848 const name = fields.names[i].get(self.zoir);856 const name = fields.names[i].get(self.zoir);
849 const field_index = field_indices.get(name) orelse {857 const field_index = field_indices.get(name) orelse {
850 if (self.options.ignore_unknown_fields) continue;858 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);
852 };860 };
853 if (field_index == comptime_field) {861 if (field_index == comptime_field) {
854 return self.failComptimeField(node, i);862 return self.failComptimeField(node, i);
...@@ -863,9 +871,9 @@ const Parser = struct {...@@ -863,9 +871,9 @@ const Parser = struct {
863 inline 0...(info.field_names.len - 1) => |j| {871 inline 0...(info.field_names.len - 1) => |j| {
864 if (info.field_attrs[j].@"comptime") unreachable;872 if (info.field_attrs[j].@"comptime") unreachable;
865873
866 @field(result, info.field_names[j]) = try self.parseExpr(874 try self.parseExprInto(
867 info.field_types[j],
868 fields.vals.at(@intCast(i)),875 fields.vals.at(@intCast(i)),
876 &@field(out, info.field_names[j]),
869 );877 );
870 },878 },
871 else => unreachable, // Can't be out of bounds879 else => unreachable, // Can't be out of bounds
...@@ -879,7 +887,7 @@ const Parser = struct {...@@ -879,7 +887,7 @@ const Parser = struct {
879 if (!found) {887 if (!found) {
880 const field_attrs = info.field_attrs[i];888 const field_attrs = info.field_attrs[i];
881 if (field_attrs.defaultValue(info.field_types[i])) |default| {889 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;
883 } else {891 } else {
884 return self.failNodeFmt(892 return self.failNodeFmt(
885 node,893 node,
...@@ -889,19 +897,16 @@ const Parser = struct {...@@ -889,19 +897,16 @@ const Parser = struct {
889 }897 }
890 }898 }
891 }899 }
892
893 return result;
894 }900 }
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 {
897 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {903 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
898 .array_literal => |nodes| nodes,904 .array_literal => |nodes| nodes,
899 .empty_literal => .{ .start = node, .len = 0 },905 .empty_literal => .{ .start = node, .len = 0 },
900 else => return error.WrongType,906 else => return error.WrongType,
901 };907 };
902908
903 var result: T = undefined;909 const info = @typeInfo(@TypeOf(out.*)).@"struct";
904 const info = @typeInfo(T).@"struct";
905910
906 if (nodes.len > info.field_names.len) {911 if (nodes.len > info.field_names.len) {
907 return self.failNodeFmt(912 return self.failNodeFmt(
...@@ -915,7 +920,7 @@ const Parser = struct {...@@ -915,7 +920,7 @@ const Parser = struct {
915 // Check if we're out of bounds920 // Check if we're out of bounds
916 if (i >= nodes.len) {921 if (i >= nodes.len) {
917 if (info.field_attrs[i].defaultValue(info.field_types[i])) |default| {922 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;
919 } else {924 } else {
920 return self.failNodeFmt(node, "missing tuple field with index {}", .{i});925 return self.failNodeFmt(node, "missing tuple field with index {}", .{i});
921 }926 }
...@@ -924,23 +929,21 @@ const Parser = struct {...@@ -924,23 +929,21 @@ const Parser = struct {
924 errdefer if (self.options.free_on_error) {929 errdefer if (self.options.free_on_error) {
925 inline for (0..i) |j| {930 inline for (0..i) |j| {
926 if (j >= i) break;931 if (j >= i) break;
927 free(self.gpa, result[j]);932 free(self.gpa, out[j]);
928 }933 }
929 };934 };
930935
931 if (info.field_attrs[i].@"comptime") {936 if (info.field_attrs[i].@"comptime") {
932 return self.failComptimeField(node, i);937 return self.failComptimeField(node, i);
933 } else {938 } else {
934 result[i] = try self.parseExpr(info.field_types[i], nodes.at(i));939 try self.parseExprInto(nodes.at(i), &out[i]);
935 }940 }
936 }941 }
937 }942 }
938
939 return result;
940 }943 }
941944
942 fn parseUnion(self: *@This(), T: type, node: Zoir.Node.Index) !T {945 fn parseUnionInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void {
943 const @"union" = @typeInfo(T).@"union";946 const @"union" = @typeInfo(@TypeOf(out.*)).@"union";
944947
945 if (@"union".field_names.len == 0) comptime unreachable;948 if (@"union".field_names.len == 0) comptime unreachable;
946949
...@@ -966,7 +969,7 @@ const Parser = struct {...@@ -966,7 +969,7 @@ const Parser = struct {
966 const field_index = b: {969 const field_index = b: {
967 const field_name_str = field_name.get(self.zoir);970 const field_name_str = field_name.get(self.zoir);
968 break :b field_indices.get(field_name_str) orelse971 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);
970 };973 };
971974
972 // Initialize the union from the given field.975 // Initialize the union from the given field.
...@@ -977,7 +980,7 @@ const Parser = struct {...@@ -977,7 +980,7 @@ const Parser = struct {
977 return self.failNode(node, "expected union");980 return self.failNode(node, "expected union");
978981
979 // Instantiate the union982 // Instantiate the union
980 return @unionInit(T, @"union".field_names[i], {});983 out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], {});
981 },984 },
982 else => unreachable, // Can't be out of bounds985 else => unreachable, // Can't be out of bounds
983 }986 }
...@@ -992,15 +995,15 @@ const Parser = struct {...@@ -992,15 +995,15 @@ const Parser = struct {
992 const field_name_str = field_name.get(self.zoir);995 const field_name_str = field_name.get(self.zoir);
993 const field_val = struct_fields.vals.at(0);996 const field_val = struct_fields.vals.at(0);
994 const field_index = field_indices.get(field_name_str) orelse997 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
997 switch (field_index) {1000 switch (field_index) {
998 inline 0...@"union".field_names.len - 1 => |i| {1001 inline 0...@"union".field_names.len - 1 => |i| {
999 if (@"union".field_types[i] == void) {1002 if (@"union".field_types[i] == void) {
1000 return self.failNode(field_val, "expected type 'void'");1003 return self.failNode(field_val, "expected type 'void'");
1001 } else {1004 } else {
1002 const value = try self.parseExpr(@"union".field_types[i], field_val);1005 out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined);
1003 return @unionInit(T, @"union".field_names[i], value);1006 try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i]));
1004 }1007 }
1005 },1008 },
1006 else => unreachable, // Can't be out of bounds1009 else => unreachable, // Can't be out of bounds