| ... | @@ -199,10 +199,8 @@ pub fn fromSliceAlloc(T: type, options: FromSliceAllocOptions) error{ OutOfMemor | ... | @@ -199,10 +199,8 @@ pub fn fromSliceAlloc(T: type, options: FromSliceAllocOptions) error{ OutOfMemor |
| 199 | .arena = options.arena, | 199 | .arena = options.arena, |
| 200 | .source = options.source, | 200 | .source = options.source, |
| 201 | .diag = options.diag, | 201 | .diag = options.diag, |
| 202 | .parser_options = .{ | 202 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 203 | .ignore_unknown_fields = options.ignore_unknown_fields, | 203 | .initialized = false, |
| 204 | .ignore_missing_fields = false, | | |
| 205 | }, | | |
| 206 | }); | 204 | }); |
| 207 | return value; | 205 | return value; |
| 208 | } | 206 | } |
| ... | @@ -218,10 +216,8 @@ pub fn updateFromSliceAlloc( | ... | @@ -218,10 +216,8 @@ pub fn updateFromSliceAlloc( |
| 218 | .arena = options.arena, | 216 | .arena = options.arena, |
| 219 | .source = options.source, | 217 | .source = options.source, |
| 220 | .diag = options.diag, | 218 | .diag = options.diag, |
| 221 | .parser_options = .{ | 219 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 222 | .ignore_unknown_fields = options.ignore_unknown_fields, | 220 | .initialized = true, |
| 223 | .ignore_missing_fields = true, | | |
| 224 | }, | | |
| 225 | }); | 221 | }); |
| 226 | } | 222 | } |
| 227 | | 223 | |
| ... | @@ -230,7 +226,8 @@ const ParseSliceAllocOptions = struct { | ... | @@ -230,7 +226,8 @@ const ParseSliceAllocOptions = struct { |
| 230 | arena: Allocator, | 226 | arena: Allocator, |
| 231 | source: [:0]const u8, | 227 | source: [:0]const u8, |
| 232 | diag: ?*Diagnostics, | 228 | diag: ?*Diagnostics, |
| 233 | parser_options: Parser.Options, | 229 | ignore_unknown_fields: bool, |
| | 230 | initialized: bool, |
| 234 | }; | 231 | }; |
| 235 | | 232 | |
| 236 | fn parseSliceAlloc( | 233 | fn parseSliceAlloc( |
| ... | @@ -250,7 +247,8 @@ fn parseSliceAlloc( | ... | @@ -250,7 +247,8 @@ fn parseSliceAlloc( |
| 250 | .zoir = &zoir, | 247 | .zoir = &zoir, |
| 251 | .node = .root, | 248 | .node = .root, |
| 252 | .diag = options.diag, | 249 | .diag = options.diag, |
| 253 | .parser_options = options.parser_options, | 250 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| | 251 | .initialized = options.initialized, |
| 254 | }); | 252 | }); |
| 255 | } | 253 | } |
| 256 | | 254 | |
| ... | @@ -327,10 +325,8 @@ pub fn fromZoirAlloc(T: type, options: FromZoirAllocOptions) error{ OutOfMemory, | ... | @@ -327,10 +325,8 @@ pub fn fromZoirAlloc(T: type, options: FromZoirAllocOptions) error{ OutOfMemory, |
| 327 | .zoir = options.zoir, | 325 | .zoir = options.zoir, |
| 328 | .node = options.node, | 326 | .node = options.node, |
| 329 | .diag = options.diag, | 327 | .diag = options.diag, |
| 330 | .parser_options = .{ | 328 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 331 | .ignore_unknown_fields = options.ignore_unknown_fields, | 329 | .initialized = false, |
| 332 | .ignore_missing_fields = false, | | |
| 333 | }, | | |
| 334 | }); | 330 | }); |
| 335 | return value; | 331 | return value; |
| 336 | } | 332 | } |
| ... | @@ -347,10 +343,8 @@ pub fn updateFromZoirAlloc( | ... | @@ -347,10 +343,8 @@ pub fn updateFromZoirAlloc( |
| 347 | .zoir = options.zoir, | 343 | .zoir = options.zoir, |
| 348 | .node = options.node, | 344 | .node = options.node, |
| 349 | .diag = options.diag, | 345 | .diag = options.diag, |
| 350 | .parser_options = .{ | 346 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 351 | .ignore_unknown_fields = options.ignore_unknown_fields, | 347 | .initialized = true, |
| 352 | .ignore_missing_fields = true, | | |
| 353 | }, | | |
| 354 | }); | 348 | }); |
| 355 | } | 349 | } |
| 356 | | 350 | |
| ... | @@ -360,7 +354,8 @@ const ParseZoirAllocOptions = struct { | ... | @@ -360,7 +354,8 @@ const ParseZoirAllocOptions = struct { |
| 360 | zoir: *const Zoir, | 354 | zoir: *const Zoir, |
| 361 | node: Zoir.Node.Index, | 355 | node: Zoir.Node.Index, |
| 362 | diag: ?*Diagnostics, | 356 | diag: ?*Diagnostics, |
| 363 | parser_options: Parser.Options, | 357 | ignore_unknown_fields: bool, |
| | 358 | initialized: bool, |
| 364 | }; | 359 | }; |
| 365 | | 360 | |
| 366 | fn parseZoirAlloc( | 361 | fn parseZoirAlloc( |
| ... | @@ -396,11 +391,11 @@ fn parseZoirAlloc( | ... | @@ -396,11 +391,11 @@ fn parseZoirAlloc( |
| 396 | .arena = options.arena, | 391 | .arena = options.arena, |
| 397 | .ast = options.ast, | 392 | .ast = options.ast, |
| 398 | .zoir = options.zoir, | 393 | .zoir = options.zoir, |
| 399 | .options = options.parser_options, | 394 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 400 | .diag = options.diag, | 395 | .diag = options.diag, |
| 401 | }; | 396 | }; |
| 402 | | 397 | |
| 403 | try parser.parseExprInto(options.node, value); | 398 | try parser.parseExprInto(options.node, value, options.initialized); |
| 404 | } | 399 | } |
| 405 | | 400 | |
| 406 | fn requiresAllocator(T: type) bool { | 401 | fn requiresAllocator(T: type) bool { |
| ... | @@ -429,18 +424,17 @@ const Parser = struct { | ... | @@ -429,18 +424,17 @@ const Parser = struct { |
| 429 | ast: *const Ast, | 424 | ast: *const Ast, |
| 430 | zoir: *const Zoir, | 425 | zoir: *const Zoir, |
| 431 | diag: ?*Diagnostics, | 426 | diag: ?*Diagnostics, |
| 432 | options: Parser.Options, | 427 | ignore_unknown_fields: bool, |
| 433 | pointer_depth: usize = 0, | | |
| 434 | | | |
| 435 | pub const Options = struct { | | |
| 436 | ignore_unknown_fields: bool, | | |
| 437 | ignore_missing_fields: bool, | | |
| 438 | }; | | |
| 439 | | 428 | |
| 440 | const ParseExprError = error{ ParseZon, OutOfMemory }; | 429 | const ParseExprError = error{ ParseZon, OutOfMemory }; |
| 441 | | 430 | |
| 442 | fn parseExprInto(self: *@This(), node: Zoir.Node.Index, out: anytype) ParseExprError!void { | 431 | fn parseExprInto( |
| 443 | return self.parseExprInnerInto(node, out) catch |err| switch (err) { | 432 | self: *@This(), |
| | 433 | node: Zoir.Node.Index, |
| | 434 | out: anytype, |
| | 435 | initialized: bool, |
| | 436 | ) ParseExprError!void { |
| | 437 | return self.parseExprInnerInto(node, out, initialized) catch |err| switch (err) { |
| 444 | error.WrongType => return self.failExpectedType(@TypeOf(out.*), node), | 438 | error.WrongType => return self.failExpectedType(@TypeOf(out.*), node), |
| 445 | else => |e| return e, | 439 | else => |e| return e, |
| 446 | }; | 440 | }; |
| ... | @@ -452,6 +446,7 @@ const Parser = struct { | ... | @@ -452,6 +446,7 @@ const Parser = struct { |
| 452 | self: *@This(), | 446 | self: *@This(), |
| 453 | node: Zoir.Node.Index, | 447 | node: Zoir.Node.Index, |
| 454 | out: anytype, | 448 | out: anytype, |
| | 449 | initialized: bool, |
| 455 | ) ParseExprInnerError!void { | 450 | ) ParseExprInnerError!void { |
| 456 | if (@TypeOf(out.*) == Zoir.Node.Index) { | 451 | if (@TypeOf(out.*) == Zoir.Node.Index) { |
| 457 | out.* = node; | 452 | out.* = node; |
| ... | @@ -462,9 +457,9 @@ const Parser = struct { | ... | @@ -462,9 +457,9 @@ const Parser = struct { |
| 462 | .optional => |optional| if (node.get(self.zoir) == .null) { | 457 | .optional => |optional| if (node.get(self.zoir) == .null) { |
| 463 | out.* = null; | 458 | out.* = null; |
| 464 | } else { | 459 | } else { |
| 465 | var new: optional.child = undefined; | 460 | const child_initialized = initialized and out.* != null; |
| 466 | try self.parseExprInnerInto(node, &new); | 461 | if (!child_initialized) out.* = @as(optional.child, undefined); |
| 467 | out.* = new; | 462 | try self.parseExprInnerInto(node, &out.*.?, child_initialized); |
| 468 | }, | 463 | }, |
| 469 | .bool => out.* = try self.parseBool(node), | 464 | .bool => out.* = try self.parseBool(node), |
| 470 | .int => out.* = try self.parseInt(@TypeOf(out.*), node), | 465 | .int => out.* = try self.parseInt(@TypeOf(out.*), node), |
| ... | @@ -472,23 +467,31 @@ const Parser = struct { | ... | @@ -472,23 +467,31 @@ const Parser = struct { |
| 472 | .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node), | 467 | .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node), |
| 473 | .pointer => |pointer| switch (pointer.size) { | 468 | .pointer => |pointer| switch (pointer.size) { |
| 474 | .one => { | 469 | .one => { |
| 475 | const slice = try self.arena.alignedAlloc( | 470 | // Get a pointer to update |
| 476 | pointer.child, | 471 | const new = b: { |
| 477 | if (pointer.attrs.@"align") |a| .fromByteUnits(a) else null, | 472 | // If the existing value is initialized and non const, use it |
| 478 | 1, | 473 | if (initialized and !pointer.attrs.@"const") break :b out.*; |
| | 474 | // Otherwise, allocate new memory |
| | 475 | const new = &(try self.arena.alignedAlloc( |
| | 476 | pointer.child, |
| | 477 | if (pointer.attrs.@"align") |a| .fromByteUnits(a) else null, |
| | 478 | 1, |
| | 479 | ))[0]; |
| | 480 | // If the original value was initialized, dupe it into the new memory |
| | 481 | if (initialized) new.* = out.*.*; |
| | 482 | break :b new; |
| | 483 | }; |
| | 484 | try self.parseExprInnerInto( |
| | 485 | node, |
| | 486 | new, |
| | 487 | initialized, |
| 479 | ); | 488 | ); |
| 480 | const new = &slice[0]; | | |
| 481 | { | | |
| 482 | self.pointer_depth += 1; | | |
| 483 | defer self.pointer_depth -= 1; | | |
| 484 | try self.parseExprInnerInto(node, new); | | |
| 485 | } | | |
| 486 | out.* = new; | 489 | out.* = new; |
| 487 | }, | 490 | }, |
| 488 | .slice => { | 491 | .slice => { |
| | 492 | // Slices are replaced wholesale since ZON doesn't have syntax for specifying |
| | 493 | // just a part of a slice. |
| 489 | const new = b: { | 494 | const new = b: { |
| 490 | self.pointer_depth += 1; | | |
| 491 | defer self.pointer_depth -= 1; | | |
| 492 | const new = try self.parseSlicePointer(@TypeOf(out.*), node); | 495 | const new = try self.parseSlicePointer(@TypeOf(out.*), node); |
| 493 | break :b new; | 496 | break :b new; |
| 494 | }; | 497 | }; |
| ... | @@ -501,8 +504,8 @@ const Parser = struct { | ... | @@ -501,8 +504,8 @@ const Parser = struct { |
| 501 | .@"struct" => |@"struct"| if (@"struct".is_tuple) | 504 | .@"struct" => |@"struct"| if (@"struct".is_tuple) |
| 502 | try self.parseTupleInto(node, out) | 505 | try self.parseTupleInto(node, out) |
| 503 | else | 506 | else |
| 504 | try self.parseStructInto(node, out), | 507 | try self.parseStructInto(node, out, initialized), |
| 505 | .@"union" => try self.parseUnionInto(node, out), | 508 | .@"union" => try self.parseUnionInto(node, out, initialized), |
| 506 | | 509 | |
| 507 | else => comptime unreachable, | 510 | else => comptime unreachable, |
| 508 | } | 511 | } |
| ... | @@ -714,7 +717,7 @@ const Parser = struct { | ... | @@ -714,7 +717,7 @@ const Parser = struct { |
| 714 | | 717 | |
| 715 | // Parse the elements and return the slice | 718 | // Parse the elements and return the slice |
| 716 | for (slice, 0..) |*elem, i| { | 719 | for (slice, 0..) |*elem, i| { |
| 717 | try self.parseExprInto(nodes.at(@intCast(i)), elem); | 720 | try self.parseExprInto(nodes.at(@intCast(i)), elem, false); |
| 718 | } | 721 | } |
| 719 | | 722 | |
| 720 | return slice; | 723 | return slice; |
| ... | @@ -746,7 +749,7 @@ const Parser = struct { | ... | @@ -746,7 +749,7 @@ const Parser = struct { |
| 746 | | 749 | |
| 747 | // Parse the elements and return the array | 750 | // Parse the elements and return the array |
| 748 | inline for (out, 0..) |*elem, i| { | 751 | inline for (out, 0..) |*elem, i| { |
| 749 | try self.parseExprInto(nodes.at(@intCast(i)), elem); | 752 | try self.parseExprInto(nodes.at(@intCast(i)), elem, false); |
| 750 | } | 753 | } |
| 751 | | 754 | |
| 752 | if (array.sentinel()) |s| { | 755 | if (array.sentinel()) |s| { |
| ... | @@ -761,7 +764,12 @@ const Parser = struct { | ... | @@ -761,7 +764,12 @@ const Parser = struct { |
| 761 | out.* = array; | 764 | out.* = array; |
| 762 | } | 765 | } |
| 763 | | 766 | |
| 764 | fn parseStructInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { | 767 | fn parseStructInto( |
| | 768 | self: *@This(), |
| | 769 | node: Zoir.Node.Index, |
| | 770 | out: anytype, |
| | 771 | initialized: bool, |
| | 772 | ) !void { |
| 765 | const repr = node.get(self.zoir); | 773 | const repr = node.get(self.zoir); |
| 766 | const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { | 774 | const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { |
| 767 | .struct_literal => |nodes| nodes, | 775 | .struct_literal => |nodes| nodes, |
| ... | @@ -787,7 +795,7 @@ const Parser = struct { | ... | @@ -787,7 +795,7 @@ const Parser = struct { |
| 787 | for (0..fields.names.len) |i| { | 795 | for (0..fields.names.len) |i| { |
| 788 | const name = fields.names[i].get(self.zoir); | 796 | const name = fields.names[i].get(self.zoir); |
| 789 | const field_index = field_indices.get(name) orelse { | 797 | const field_index = field_indices.get(name) orelse { |
| 790 | if (self.options.ignore_unknown_fields) continue; | 798 | if (self.ignore_unknown_fields) continue; |
| 791 | return self.failUnexpected(@TypeOf(out.*), "field", node, i, name); | 799 | return self.failUnexpected(@TypeOf(out.*), "field", node, i, name); |
| 792 | }; | 800 | }; |
| 793 | if (field_index == comptime_field) { | 801 | if (field_index == comptime_field) { |
| ... | @@ -802,10 +810,10 @@ const Parser = struct { | ... | @@ -802,10 +810,10 @@ const Parser = struct { |
| 802 | switch (field_index) { | 810 | switch (field_index) { |
| 803 | inline 0...(info.field_names.len - 1) => |j| { | 811 | inline 0...(info.field_names.len - 1) => |j| { |
| 804 | if (info.field_attrs[j].@"comptime") unreachable; | 812 | if (info.field_attrs[j].@"comptime") unreachable; |
| 805 | | | |
| 806 | try self.parseExprInto( | 813 | try self.parseExprInto( |
| 807 | fields.vals.at(@intCast(i)), | 814 | fields.vals.at(@intCast(i)), |
| 808 | &@field(out, info.field_names[j]), | 815 | &@field(out, info.field_names[j]), |
| | 816 | initialized, |
| 809 | ); | 817 | ); |
| 810 | }, | 818 | }, |
| 811 | else => unreachable, // Can't be out of bounds | 819 | else => unreachable, // Can't be out of bounds |
| ... | @@ -813,7 +821,7 @@ const Parser = struct { | ... | @@ -813,7 +821,7 @@ const Parser = struct { |
| 813 | } | 821 | } |
| 814 | | 822 | |
| 815 | // Fill in any missing default fields | 823 | // Fill in any missing default fields |
| 816 | if (!self.options.ignore_missing_fields or self.pointer_depth > 0) { | 824 | if (!initialized) { |
| 817 | inline for (field_found, 0..) |found, i| { | 825 | inline for (field_found, 0..) |found, i| { |
| 818 | if (!found) { | 826 | if (!found) { |
| 819 | const field_attrs = info.field_attrs[i]; | 827 | const field_attrs = info.field_attrs[i]; |
| ... | @@ -860,20 +868,28 @@ const Parser = struct { | ... | @@ -860,20 +868,28 @@ const Parser = struct { |
| 860 | if (info.field_attrs[i].@"comptime") { | 868 | if (info.field_attrs[i].@"comptime") { |
| 861 | return self.failComptimeField(node, i); | 869 | return self.failComptimeField(node, i); |
| 862 | } else { | 870 | } else { |
| 863 | try self.parseExprInto(nodes.at(i), &out[i]); | 871 | try self.parseExprInto(nodes.at(i), &out[i], false); |
| 864 | } | 872 | } |
| 865 | } | 873 | } |
| 866 | } | 874 | } |
| 867 | } | 875 | } |
| 868 | | 876 | |
| 869 | fn parseUnionInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { | 877 | fn parseUnionInto( |
| | 878 | self: *@This(), |
| | 879 | node: Zoir.Node.Index, |
| | 880 | out: anytype, |
| | 881 | initialized: bool, |
| | 882 | ) !void { |
| 870 | const @"union" = @typeInfo(@TypeOf(out.*)).@"union"; | 883 | const @"union" = @typeInfo(@TypeOf(out.*)).@"union"; |
| 871 | | 884 | |
| 872 | if (@"union".field_names.len == 0) comptime unreachable; | 885 | if (@"union".field_names.len == 0) comptime unreachable; |
| 873 | | 886 | |
| 874 | // Gather info on the fields | 887 | // Gather info on the fields |
| 875 | const field_indices = b: { | 888 | const field_indices = b: { |
| 876 | comptime var kvs_list: [@"union".field_names.len]struct { []const u8, usize } = undefined; | 889 | comptime var kvs_list: [@"union".field_names.len]struct { |
| | 890 | []const u8, |
| | 891 | usize, |
| | 892 | } = undefined; |
| 877 | inline for (@"union".field_names, 0..) |field_name, i| { | 893 | inline for (@"union".field_names, 0..) |field_name, i| { |
| 878 | kvs_list[i] = .{ field_name, i }; | 894 | kvs_list[i] = .{ field_name, i }; |
| 879 | } | 895 | } |
| ... | @@ -893,7 +909,13 @@ const Parser = struct { | ... | @@ -893,7 +909,13 @@ const Parser = struct { |
| 893 | const field_index = b: { | 909 | const field_index = b: { |
| 894 | const field_name_str = field_name.get(self.zoir); | 910 | const field_name_str = field_name.get(self.zoir); |
| 895 | break :b field_indices.get(field_name_str) orelse | 911 | break :b field_indices.get(field_name_str) orelse |
| 896 | return self.failUnexpected(@TypeOf(out.*), "field", node, null, field_name_str); | 912 | return self.failUnexpected( |
| | 913 | @TypeOf(out.*), |
| | 914 | "field", |
| | 915 | node, |
| | 916 | null, |
| | 917 | field_name_str, |
| | 918 | ); |
| 897 | }; | 919 | }; |
| 898 | | 920 | |
| 899 | // Initialize the union from the given field. | 921 | // Initialize the union from the given field. |
| ... | @@ -926,8 +948,25 @@ const Parser = struct { | ... | @@ -926,8 +948,25 @@ const Parser = struct { |
| 926 | if (@"union".field_types[i] == void) { | 948 | if (@"union".field_types[i] == void) { |
| 927 | return self.failNode(field_val, "expected type 'void'"); | 949 | return self.failNode(field_val, "expected type 'void'"); |
| 928 | } else { | 950 | } else { |
| 929 | out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined); | 951 | const field_initialized = b: { |
| 930 | try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i])); | 952 | if (!initialized) break :b false; |
| | 953 | if (@"union".tag_type == null) break :b false; |
| | 954 | const tag = @field(@"union".tag_type.?, @"union".field_names[i]); |
| | 955 | if (out.* != tag) break :b false; |
| | 956 | break :b true; |
| | 957 | }; |
| | 958 | if (!field_initialized) { |
| | 959 | out.* = @unionInit( |
| | 960 | @TypeOf(out.*), |
| | 961 | @"union".field_names[i], |
| | 962 | undefined, |
| | 963 | ); |
| | 964 | } |
| | 965 | try self.parseExprInto( |
| | 966 | field_val, |
| | 967 | &@field(out.*, @"union".field_names[i]), |
| | 968 | field_initialized, |
| | 969 | ); |
| 931 | } | 970 | } |
| 932 | }, | 971 | }, |
| 933 | else => unreachable, // Can't be out of bounds | 972 | else => unreachable, // Can't be out of bounds |
| ... | @@ -4250,7 +4289,7 @@ test "std.zon aligned pointers" { | ... | @@ -4250,7 +4289,7 @@ test "std.zon aligned pointers" { |
| 4250 | try std.testing.expectEqualDeep(expected.inner, found.inner); | 4289 | try std.testing.expectEqualDeep(expected.inner, found.inner); |
| 4251 | } | 4290 | } |
| 4252 | | 4291 | |
| 4253 | test "std.zon update" { | 4292 | test "std.zon update basic" { |
| 4254 | const gpa = std.testing.allocator; | 4293 | const gpa = std.testing.allocator; |
| 4255 | var arena_allocator: ArenaAllocator = .init(gpa); | 4294 | var arena_allocator: ArenaAllocator = .init(gpa); |
| 4256 | defer arena_allocator.deinit(); | 4295 | defer arena_allocator.deinit(); |
| ... | @@ -4362,6 +4401,178 @@ test "std.zon update" { | ... | @@ -4362,6 +4401,178 @@ test "std.zon update" { |
| 4362 | try std.testing.expectEqualDeep(expected, found); | 4401 | try std.testing.expectEqualDeep(expected, found); |
| 4363 | } | 4402 | } |
| 4364 | | 4403 | |
| | 4404 | test "std.zon update optionals" { |
| | 4405 | const gpa = std.testing.allocator; |
| | 4406 | var arena_allocator: ArenaAllocator = .init(gpa); |
| | 4407 | defer arena_allocator.deinit(); |
| | 4408 | const arena = arena_allocator.allocator(); |
| | 4409 | |
| | 4410 | const MyStruct = struct { |
| | 4411 | foo: ?struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, |
| | 4412 | }; |
| | 4413 | |
| | 4414 | // Updating an optional that starts out null should fill in any default fields we left off |
| | 4415 | { |
| | 4416 | var expected: MyStruct = .{ .foo = null }; |
| | 4417 | var found = expected; |
| | 4418 | try updateFromSlice(MyStruct, &found, .{ |
| | 4419 | .gpa = gpa, |
| | 4420 | .source = ".{ .foo = .{ .qux = 3 } }", |
| | 4421 | .diag = null, |
| | 4422 | }); |
| | 4423 | expected.foo = .{ .qux = 3 }; |
| | 4424 | try std.testing.expectEqual(expected, found); |
| | 4425 | } |
| | 4426 | |
| | 4427 | // Updating an optional that starts out null should error if we leave off required fields. |
| | 4428 | { |
| | 4429 | var found: MyStruct = .{ .foo = null }; |
| | 4430 | var diag: Diagnostics = .init(arena); |
| | 4431 | try std.testing.expectError(error.ParseZon, updateFromSlice(MyStruct, &found, .{ |
| | 4432 | .gpa = gpa, |
| | 4433 | .source = ".{ .foo = .{} }", |
| | 4434 | .diag = &diag, |
| | 4435 | })); |
| | 4436 | try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{diag}); |
| | 4437 | } |
| | 4438 | |
| | 4439 | // Updating an optional that starts out non-null should preserve any values we leave off. It's |
| | 4440 | // also okay to leave off required fields since they were already set. |
| | 4441 | { |
| | 4442 | var expected: MyStruct = .{ |
| | 4443 | .foo = .{ .bar = 10, .baz = 20, .qux = 30 }, |
| | 4444 | }; |
| | 4445 | var found = expected; |
| | 4446 | try updateFromSlice(MyStruct, &found, .{ |
| | 4447 | .gpa = gpa, |
| | 4448 | .source = ".{ .foo = .{ .baz = 200 } }", |
| | 4449 | .diag = null, |
| | 4450 | }); |
| | 4451 | expected.foo.?.baz = 200; |
| | 4452 | try std.testing.expectEqual(expected, found); |
| | 4453 | } |
| | 4454 | } |
| | 4455 | |
| | 4456 | test "std.zon update optional pointers" { |
| | 4457 | const gpa = std.testing.allocator; |
| | 4458 | var arena_allocator: ArenaAllocator = .init(gpa); |
| | 4459 | defer arena_allocator.deinit(); |
| | 4460 | const arena = arena_allocator.allocator(); |
| | 4461 | |
| | 4462 | const MyStruct = struct { |
| | 4463 | foo: ?*const struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, |
| | 4464 | }; |
| | 4465 | |
| | 4466 | // Just a smoke test since its the combination of behavior we've already tested separately |
| | 4467 | { |
| | 4468 | var expected: MyStruct = .{ .foo = null }; |
| | 4469 | var found = expected; |
| | 4470 | try updateFromSliceAlloc(MyStruct, &found, .{ |
| | 4471 | .gpa = gpa, |
| | 4472 | .arena = arena, |
| | 4473 | .source = ".{ .foo = .{ .qux = 3 } }", |
| | 4474 | .diag = null, |
| | 4475 | }); |
| | 4476 | expected.foo = &.{ .qux = 3 }; |
| | 4477 | try std.testing.expectEqualDeep(expected, found); |
| | 4478 | } |
| | 4479 | } |
| | 4480 | |
| | 4481 | test "std.zon update pointers" { |
| | 4482 | const gpa = std.testing.allocator; |
| | 4483 | var arena_allocator: ArenaAllocator = .init(gpa); |
| | 4484 | defer arena_allocator.deinit(); |
| | 4485 | const arena = arena_allocator.allocator(); |
| | 4486 | |
| | 4487 | // Updating a pointer should leave fields we don't specify unchanged |
| | 4488 | { |
| | 4489 | const MyStruct = struct { |
| | 4490 | foo: *struct { bar: u32 = 10, baz: u32 = 20, qux: u32 }, |
| | 4491 | }; |
| | 4492 | var ptr: @typeInfo(@FieldType(MyStruct, "foo")).pointer.child = .{ .qux = 3 }; |
| | 4493 | var expected: MyStruct = .{ .foo = &ptr }; |
| | 4494 | var found = expected; |
| | 4495 | try updateFromSliceAlloc(MyStruct, &found, .{ |
| | 4496 | .gpa = gpa, |
| | 4497 | .arena = arena, |
| | 4498 | .source = ".{ .foo = .{ .bar = 100 } }", |
| | 4499 | .diag = null, |
| | 4500 | }); |
| | 4501 | expected.foo.bar = 100; |
| | 4502 | try std.testing.expectEqualDeep(expected, found); |
| | 4503 | } |
| | 4504 | |
| | 4505 | // Same thing, but for const pointers |
| | 4506 | { |
| | 4507 | const MyStruct = struct { |
| | 4508 | foo: *const struct { bar: u32 = 10, baz: u32 = 20, qux: u32 }, |
| | 4509 | }; |
| | 4510 | var expected: MyStruct = .{ .foo = &.{ .qux = 3 } }; |
| | 4511 | var found = expected; |
| | 4512 | try updateFromSliceAlloc(MyStruct, &found, .{ |
| | 4513 | .gpa = gpa, |
| | 4514 | .arena = arena, |
| | 4515 | .source = ".{ .foo = .{ .bar = 100 } }", |
| | 4516 | .diag = null, |
| | 4517 | }); |
| | 4518 | expected.foo = &.{ .bar = 100, .baz = 20, .qux = 3 }; |
| | 4519 | try std.testing.expectEqualDeep(expected, found); |
| | 4520 | } |
| | 4521 | } |
| | 4522 | |
| | 4523 | test "std.zon update unions" { |
| | 4524 | const gpa = std.testing.allocator; |
| | 4525 | var arena_allocator: ArenaAllocator = .init(gpa); |
| | 4526 | defer arena_allocator.deinit(); |
| | 4527 | const arena = arena_allocator.allocator(); |
| | 4528 | |
| | 4529 | const MyUnion = union(enum) { |
| | 4530 | none: void, |
| | 4531 | foo: struct { bar: u32 = 10, baz: u32 = 20, qux: u32 }, |
| | 4532 | }; |
| | 4533 | |
| | 4534 | // Updating a union to a new field should fill in any default sub-fields we left off |
| | 4535 | { |
| | 4536 | var expected: MyUnion = .none; |
| | 4537 | var found = expected; |
| | 4538 | try updateFromSlice(MyUnion, &found, .{ |
| | 4539 | .gpa = gpa, |
| | 4540 | .source = ".{ .foo = .{ .qux = 3 } }", |
| | 4541 | .diag = null, |
| | 4542 | }); |
| | 4543 | expected = .{ .foo = .{ .qux = 3 } }; |
| | 4544 | try std.testing.expectEqual(expected, found); |
| | 4545 | } |
| | 4546 | |
| | 4547 | // Updating a union to a new field should error if we leave off required sub-fields |
| | 4548 | { |
| | 4549 | var found: MyUnion = .none; |
| | 4550 | var diag: Diagnostics = .init(arena); |
| | 4551 | try std.testing.expectError(error.ParseZon, updateFromSlice(MyUnion, &found, .{ |
| | 4552 | .gpa = gpa, |
| | 4553 | .source = ".{ .foo = .{} }", |
| | 4554 | .diag = &diag, |
| | 4555 | })); |
| | 4556 | try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{diag}); |
| | 4557 | } |
| | 4558 | |
| | 4559 | // Updating a union should preseve any sub-fields that we left off. It's also okay to leave off |
| | 4560 | // required fields since they were already set. |
| | 4561 | { |
| | 4562 | var expected: MyUnion = .{ |
| | 4563 | .foo = .{ .bar = 10, .baz = 20, .qux = 30 }, |
| | 4564 | }; |
| | 4565 | var found = expected; |
| | 4566 | try updateFromSlice(MyUnion, &found, .{ |
| | 4567 | .gpa = gpa, |
| | 4568 | .source = ".{ .foo = .{ .baz = 200 } }", |
| | 4569 | .diag = null, |
| | 4570 | }); |
| | 4571 | expected.foo.baz = 200; |
| | 4572 | try std.testing.expectEqual(expected, found); |
| | 4573 | } |
| | 4574 | } |
| | 4575 | |
| 4365 | test "std.zon variants" { | 4576 | test "std.zon variants" { |
| 4366 | // Just a smoke test, we don't need to test each variant thoroughly because they all call into | 4577 | // Just a smoke test, we don't need to test each variant thoroughly because they all call into |
| 4367 | // the same code. | 4578 | // the same code. |