| ... | ... | @@ -199,10 +199,8 @@ pub fn fromSliceAlloc(T: type, options: FromSliceAllocOptions) error{ OutOfMemor |
| 199 | 199 | .arena = options.arena, |
| 200 | 200 | .source = options.source, |
| 201 | 201 | .diag = options.diag, |
| 202 | | .parser_options = .{ |
| 203 | | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 204 | | .ignore_missing_fields = false, |
| 205 | | }, |
| 202 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 203 | .initialized = false, |
| 206 | 204 | }); |
| 207 | 205 | return value; |
| 208 | 206 | } |
| ... | ... | @@ -218,10 +216,8 @@ pub fn updateFromSliceAlloc( |
| 218 | 216 | .arena = options.arena, |
| 219 | 217 | .source = options.source, |
| 220 | 218 | .diag = options.diag, |
| 221 | | .parser_options = .{ |
| 222 | | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 223 | | .ignore_missing_fields = true, |
| 224 | | }, |
| 219 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 220 | .initialized = true, |
| 225 | 221 | }); |
| 226 | 222 | } |
| 227 | 223 | |
| ... | ... | @@ -230,7 +226,8 @@ const ParseSliceAllocOptions = struct { |
| 230 | 226 | arena: Allocator, |
| 231 | 227 | source: [:0]const u8, |
| 232 | 228 | diag: ?*Diagnostics, |
| 233 | | parser_options: Parser.Options, |
| 229 | ignore_unknown_fields: bool, |
| 230 | initialized: bool, |
| 234 | 231 | }; |
| 235 | 232 | |
| 236 | 233 | fn parseSliceAlloc( |
| ... | ... | @@ -250,7 +247,8 @@ fn parseSliceAlloc( |
| 250 | 247 | .zoir = &zoir, |
| 251 | 248 | .node = .root, |
| 252 | 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 | 325 | .zoir = options.zoir, |
| 328 | 326 | .node = options.node, |
| 329 | 327 | .diag = options.diag, |
| 330 | | .parser_options = .{ |
| 331 | | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 332 | | .ignore_missing_fields = false, |
| 333 | | }, |
| 328 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 329 | .initialized = false, |
| 334 | 330 | }); |
| 335 | 331 | return value; |
| 336 | 332 | } |
| ... | ... | @@ -347,10 +343,8 @@ pub fn updateFromZoirAlloc( |
| 347 | 343 | .zoir = options.zoir, |
| 348 | 344 | .node = options.node, |
| 349 | 345 | .diag = options.diag, |
| 350 | | .parser_options = .{ |
| 351 | | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 352 | | .ignore_missing_fields = true, |
| 353 | | }, |
| 346 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 347 | .initialized = true, |
| 354 | 348 | }); |
| 355 | 349 | } |
| 356 | 350 | |
| ... | ... | @@ -360,7 +354,8 @@ const ParseZoirAllocOptions = struct { |
| 360 | 354 | zoir: *const Zoir, |
| 361 | 355 | node: Zoir.Node.Index, |
| 362 | 356 | diag: ?*Diagnostics, |
| 363 | | parser_options: Parser.Options, |
| 357 | ignore_unknown_fields: bool, |
| 358 | initialized: bool, |
| 364 | 359 | }; |
| 365 | 360 | |
| 366 | 361 | fn parseZoirAlloc( |
| ... | ... | @@ -396,11 +391,11 @@ fn parseZoirAlloc( |
| 396 | 391 | .arena = options.arena, |
| 397 | 392 | .ast = options.ast, |
| 398 | 393 | .zoir = options.zoir, |
| 399 | | .options = options.parser_options, |
| 394 | .ignore_unknown_fields = options.ignore_unknown_fields, |
| 400 | 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 | 401 | fn requiresAllocator(T: type) bool { |
| ... | ... | @@ -429,18 +424,17 @@ const Parser = struct { |
| 429 | 424 | ast: *const Ast, |
| 430 | 425 | zoir: *const Zoir, |
| 431 | 426 | diag: ?*Diagnostics, |
| 432 | | options: Parser.Options, |
| 433 | | pointer_depth: usize = 0, |
| 434 | | |
| 435 | | pub const Options = struct { |
| 436 | | ignore_unknown_fields: bool, |
| 437 | | ignore_missing_fields: bool, |
| 438 | | }; |
| 427 | ignore_unknown_fields: bool, |
| 439 | 428 | |
| 440 | 429 | const ParseExprError = error{ ParseZon, OutOfMemory }; |
| 441 | 430 | |
| 442 | | fn parseExprInto(self: *@This(), node: Zoir.Node.Index, out: anytype) ParseExprError!void { |
| 443 | | return self.parseExprInnerInto(node, out) catch |err| switch (err) { |
| 431 | fn parseExprInto( |
| 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 | 438 | error.WrongType => return self.failExpectedType(@TypeOf(out.*), node), |
| 445 | 439 | else => |e| return e, |
| 446 | 440 | }; |
| ... | ... | @@ -452,6 +446,7 @@ const Parser = struct { |
| 452 | 446 | self: *@This(), |
| 453 | 447 | node: Zoir.Node.Index, |
| 454 | 448 | out: anytype, |
| 449 | initialized: bool, |
| 455 | 450 | ) ParseExprInnerError!void { |
| 456 | 451 | if (@TypeOf(out.*) == Zoir.Node.Index) { |
| 457 | 452 | out.* = node; |
| ... | ... | @@ -462,9 +457,9 @@ const Parser = struct { |
| 462 | 457 | .optional => |optional| if (node.get(self.zoir) == .null) { |
| 463 | 458 | out.* = null; |
| 464 | 459 | } else { |
| 465 | | var new: optional.child = undefined; |
| 466 | | try self.parseExprInnerInto(node, &new); |
| 467 | | out.* = new; |
| 460 | const child_initialized = initialized and out.* != null; |
| 461 | if (!child_initialized) out.* = @as(optional.child, undefined); |
| 462 | try self.parseExprInnerInto(node, &out.*.?, child_initialized); |
| 468 | 463 | }, |
| 469 | 464 | .bool => out.* = try self.parseBool(node), |
| 470 | 465 | .int => out.* = try self.parseInt(@TypeOf(out.*), node), |
| ... | ... | @@ -472,23 +467,31 @@ const Parser = struct { |
| 472 | 467 | .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node), |
| 473 | 468 | .pointer => |pointer| switch (pointer.size) { |
| 474 | 469 | .one => { |
| 475 | | const slice = try self.arena.alignedAlloc( |
| 476 | | pointer.child, |
| 477 | | if (pointer.attrs.@"align") |a| .fromByteUnits(a) else null, |
| 478 | | 1, |
| 470 | // Get a pointer to update |
| 471 | const new = b: { |
| 472 | // If the existing value is initialized and non const, use it |
| 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 | 489 | out.* = new; |
| 487 | 490 | }, |
| 488 | 491 | .slice => { |
| 492 | // Slices are replaced wholesale since ZON doesn't have syntax for specifying |
| 493 | // just a part of a slice. |
| 489 | 494 | const new = b: { |
| 490 | | self.pointer_depth += 1; |
| 491 | | defer self.pointer_depth -= 1; |
| 492 | 495 | const new = try self.parseSlicePointer(@TypeOf(out.*), node); |
| 493 | 496 | break :b new; |
| 494 | 497 | }; |
| ... | ... | @@ -501,8 +504,8 @@ const Parser = struct { |
| 501 | 504 | .@"struct" => |@"struct"| if (@"struct".is_tuple) |
| 502 | 505 | try self.parseTupleInto(node, out) |
| 503 | 506 | else |
| 504 | | try self.parseStructInto(node, out), |
| 505 | | .@"union" => try self.parseUnionInto(node, out), |
| 507 | try self.parseStructInto(node, out, initialized), |
| 508 | .@"union" => try self.parseUnionInto(node, out, initialized), |
| 506 | 509 | |
| 507 | 510 | else => comptime unreachable, |
| 508 | 511 | } |
| ... | ... | @@ -714,7 +717,7 @@ const Parser = struct { |
| 714 | 717 | |
| 715 | 718 | // Parse the elements and return the slice |
| 716 | 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 | 723 | return slice; |
| ... | ... | @@ -746,7 +749,7 @@ const Parser = struct { |
| 746 | 749 | |
| 747 | 750 | // Parse the elements and return the array |
| 748 | 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 | 755 | if (array.sentinel()) |s| { |
| ... | ... | @@ -761,7 +764,12 @@ const Parser = struct { |
| 761 | 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 | 773 | const repr = node.get(self.zoir); |
| 766 | 774 | const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { |
| 767 | 775 | .struct_literal => |nodes| nodes, |
| ... | ... | @@ -787,7 +795,7 @@ const Parser = struct { |
| 787 | 795 | for (0..fields.names.len) |i| { |
| 788 | 796 | const name = fields.names[i].get(self.zoir); |
| 789 | 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 | 799 | return self.failUnexpected(@TypeOf(out.*), "field", node, i, name); |
| 792 | 800 | }; |
| 793 | 801 | if (field_index == comptime_field) { |
| ... | ... | @@ -802,10 +810,10 @@ const Parser = struct { |
| 802 | 810 | switch (field_index) { |
| 803 | 811 | inline 0...(info.field_names.len - 1) => |j| { |
| 804 | 812 | if (info.field_attrs[j].@"comptime") unreachable; |
| 805 | | |
| 806 | 813 | try self.parseExprInto( |
| 807 | 814 | fields.vals.at(@intCast(i)), |
| 808 | 815 | &@field(out, info.field_names[j]), |
| 816 | initialized, |
| 809 | 817 | ); |
| 810 | 818 | }, |
| 811 | 819 | else => unreachable, // Can't be out of bounds |
| ... | ... | @@ -813,7 +821,7 @@ const Parser = struct { |
| 813 | 821 | } |
| 814 | 822 | |
| 815 | 823 | // Fill in any missing default fields |
| 816 | | if (!self.options.ignore_missing_fields or self.pointer_depth > 0) { |
| 824 | if (!initialized) { |
| 817 | 825 | inline for (field_found, 0..) |found, i| { |
| 818 | 826 | if (!found) { |
| 819 | 827 | const field_attrs = info.field_attrs[i]; |
| ... | ... | @@ -860,20 +868,28 @@ const Parser = struct { |
| 860 | 868 | if (info.field_attrs[i].@"comptime") { |
| 861 | 869 | return self.failComptimeField(node, i); |
| 862 | 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 | 883 | const @"union" = @typeInfo(@TypeOf(out.*)).@"union"; |
| 871 | 884 | |
| 872 | 885 | if (@"union".field_names.len == 0) comptime unreachable; |
| 873 | 886 | |
| 874 | 887 | // Gather info on the fields |
| 875 | 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 | 893 | inline for (@"union".field_names, 0..) |field_name, i| { |
| 878 | 894 | kvs_list[i] = .{ field_name, i }; |
| 879 | 895 | } |
| ... | ... | @@ -893,7 +909,13 @@ const Parser = struct { |
| 893 | 909 | const field_index = b: { |
| 894 | 910 | const field_name_str = field_name.get(self.zoir); |
| 895 | 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 | 921 | // Initialize the union from the given field. |
| ... | ... | @@ -926,8 +948,25 @@ const Parser = struct { |
| 926 | 948 | if (@"union".field_types[i] == void) { |
| 927 | 949 | return self.failNode(field_val, "expected type 'void'"); |
| 928 | 950 | } else { |
| 929 | | out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined); |
| 930 | | try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i])); |
| 951 | const field_initialized = b: { |
| 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 | 972 | else => unreachable, // Can't be out of bounds |
| ... | ... | @@ -4250,7 +4289,7 @@ test "std.zon aligned pointers" { |
| 4250 | 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 | 4293 | const gpa = std.testing.allocator; |
| 4255 | 4294 | var arena_allocator: ArenaAllocator = .init(gpa); |
| 4256 | 4295 | defer arena_allocator.deinit(); |
| ... | ... | @@ -4362,6 +4401,178 @@ test "std.zon update" { |
| 4362 | 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 | 4576 | test "std.zon variants" { |
| 4366 | 4577 | // Just a smoke test, we don't need to test each variant thoroughly because they all call into |
| 4367 | 4578 | // the same code. |