authorgravatar for mitchell.hashimoto@gmail.comMitchell Hashimoto <mitchell.hashimoto@gmail.com> 2022-03-14 10:47:35-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-14 13:47:35-04:00
log1ebe3bd01d13b28b3ecd4962f0f70344fe75ba4d
tree602b9f3bac03851ee11dac3a9afe0b911c9d2530
parent5919b10048be6efa8c0ca6bcb259706098b2d5ec
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

stage2: reify structs and tuples (#11144)

Implements `@Type` for structs, anon structs, and tuples. This is another place that would probably benefit from a `.reified_struct` type tag but will defer for later in the interest of getting tests passing first.

2 files changed, 235 insertions(+), 11 deletions(-)

src/Sema.zig+190-10
...@@ -12459,7 +12459,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12459,7 +12459,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12459 const ty = try Type.array(sema.arena, len, sentinel, child_ty);12459 const ty = try Type.array(sema.arena, len, sentinel, child_ty);
12460 return sema.addType(ty);12460 return sema.addType(ty);
12461 },12461 },
12462 .Struct => return sema.fail(block, src, "TODO: Sema.zirReify for Struct", .{}),
12463 .Optional => {12462 .Optional => {
12464 const struct_val = union_val.val.castTag(.@"struct").?.data;12463 const struct_val = union_val.val.castTag(.@"struct").?.data;
12465 // TODO use reflection instead of magic numbers here12464 // TODO use reflection instead of magic numbers here
...@@ -12515,10 +12514,31 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12515,10 +12514,31 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12515 const ty = try Type.Tag.error_set_merged.create(sema.arena, names);12514 const ty = try Type.Tag.error_set_merged.create(sema.arena, names);
12516 return sema.addType(ty);12515 return sema.addType(ty);
12517 },12516 },
12517 .Struct => {
12518 // TODO use reflection instead of magic numbers here
12519 const struct_val = union_val.val.castTag(.@"struct").?.data;
12520 // layout: containerlayout,
12521 const layout_val = struct_val[0];
12522 // fields: []const enumfield,
12523 const fields_val = struct_val[1];
12524 // decls: []const declaration,
12525 const decls_val = struct_val[2];
12526 // is_tuple: bool,
12527 const is_tuple_val = struct_val[3];
12528
12529 // Decls
12530 if (decls_val.sliceLen() > 0) {
12531 return sema.fail(block, src, "reified structs must have no decls", .{});
12532 }
12533
12534 return if (is_tuple_val.toBool())
12535 try sema.reifyTuple(block, src, fields_val)
12536 else
12537 try sema.reifyStruct(block, inst, src, layout_val, fields_val);
12538 },
12518 .Enum => {12539 .Enum => {
12519 const struct_val = union_val.val.castTag(.@"struct").?.data;12540 const struct_val = union_val.val.castTag(.@"struct").?.data;
12520 // TODO use reflection instead of magic numbers here12541 // TODO use reflection instead of magic numbers here
12521 // error_set: type,
12522 // layout: ContainerLayout,12542 // layout: ContainerLayout,
12523 const layout_val = struct_val[0];12543 const layout_val = struct_val[0];
12524 // tag_type: type,12544 // tag_type: type,
...@@ -12537,10 +12557,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12537,10 +12557,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12537 }12557 }
1253812558
12539 // Decls12559 // Decls
12540 const decls_slice_val = decls_val.castTag(.slice).?.data;12560 if (decls_val.sliceLen() > 0) {
12541 const decls_decl = decls_slice_val.ptr.pointerDecl().?;
12542 try sema.ensureDeclAnalyzed(decls_decl);
12543 if (decls_decl.ty.arrayLen() > 0) {
12544 return sema.fail(block, src, "reified enums must have no decls", .{});12561 return sema.fail(block, src, "reified enums must have no decls", .{});
12545 }12562 }
1254612563
...@@ -12636,10 +12653,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12636,10 +12653,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12636 const decls_val = struct_val[0];12653 const decls_val = struct_val[0];
1263712654
12638 // Decls12655 // Decls
12639 const decls_slice_val = decls_val.castTag(.slice).?.data;12656 if (decls_val.sliceLen() > 0) {
12640 const decls_decl = decls_slice_val.ptr.pointerDecl().?;
12641 try sema.ensureDeclAnalyzed(decls_decl);
12642 if (decls_decl.ty.arrayLen() > 0) {
12643 return sema.fail(block, src, "reified opaque must have no decls", .{});12657 return sema.fail(block, src, "reified opaque must have no decls", .{});
12644 }12658 }
1264512659
...@@ -12684,6 +12698,172 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12684,6 +12698,172 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12684 }12698 }
12685}12699}
1268612700
12701fn reifyTuple(
12702 sema: *Sema,
12703 block: *Block,
12704 src: LazySrcLoc,
12705 fields_val: Value,
12706) CompileError!Air.Inst.Ref {
12707 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen());
12708 if (fields_len == 0) return sema.addType(Type.initTag(.empty_struct_literal));
12709
12710 const types = try sema.arena.alloc(Type, fields_len);
12711 const values = try sema.arena.alloc(Value, fields_len);
12712
12713 var used_fields: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
12714 defer used_fields.deinit(sema.gpa);
12715 try used_fields.ensureTotalCapacity(sema.gpa, fields_len);
12716
12717 var i: usize = 0;
12718 while (i < fields_len) : (i += 1) {
12719 const elem_val = try fields_val.elemValue(sema.arena, i);
12720 const field_struct_val = elem_val.castTag(.@"struct").?.data;
12721 // TODO use reflection instead of magic numbers here
12722 // name: []const u8
12723 const name_val = field_struct_val[0];
12724 // field_type: type,
12725 const field_type_val = field_struct_val[1];
12726 //default_value: ?*const anyopaque,
12727 const default_value_val = field_struct_val[2];
12728
12729 const field_name = try name_val.toAllocatedBytes(
12730 Type.initTag(.const_slice_u8),
12731 sema.arena,
12732 );
12733
12734 const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
12735 return sema.fail(
12736 block,
12737 src,
12738 "tuple cannot have non-numeric field '{s}': {}",
12739 .{ field_name, err },
12740 );
12741 };
12742
12743 if (field_index >= fields_len) {
12744 return sema.fail(
12745 block,
12746 src,
12747 "tuple field {} exceeds tuple field count",
12748 .{field_index},
12749 );
12750 }
12751
12752 const gop = used_fields.getOrPutAssumeCapacity(field_index);
12753 if (gop.found_existing) {
12754 // TODO: better source location
12755 return sema.fail(block, src, "duplicate tuple field {}", .{field_index});
12756 }
12757
12758 const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
12759 const payload_val = if (opt_val.pointerDecl()) |opt_decl|
12760 opt_decl.val
12761 else
12762 opt_val;
12763 break :blk try payload_val.copy(sema.arena);
12764 } else Value.initTag(.unreachable_value);
12765
12766 var buffer: Value.ToTypeBuffer = undefined;
12767 types[field_index] = try field_type_val.toType(&buffer).copy(sema.arena);
12768 values[field_index] = default_val;
12769 }
12770
12771 const ty = try Type.Tag.tuple.create(sema.arena, .{
12772 .types = types,
12773 .values = values,
12774 });
12775 return sema.addType(ty);
12776}
12777
12778fn reifyStruct(
12779 sema: *Sema,
12780 block: *Block,
12781 inst: Zir.Inst.Index,
12782 src: LazySrcLoc,
12783 layout_val: Value,
12784 fields_val: Value,
12785) CompileError!Air.Inst.Ref {
12786 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
12787 errdefer new_decl_arena.deinit();
12788 const new_decl_arena_allocator = new_decl_arena.allocator();
12789
12790 const struct_obj = try new_decl_arena_allocator.create(Module.Struct);
12791 const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);
12792 const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);
12793 const type_name = try sema.createTypeName(block, .anon);
12794 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
12795 .ty = Type.type,
12796 .val = new_struct_val,
12797 }, type_name);
12798 new_decl.owns_tv = true;
12799 errdefer sema.mod.abortAnonDecl(new_decl);
12800 struct_obj.* = .{
12801 .owner_decl = new_decl,
12802 .fields = .{},
12803 .node_offset = src.node_offset,
12804 .zir_index = inst,
12805 .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout),
12806 .status = .have_field_types,
12807 .known_non_opv = undefined,
12808 .namespace = .{
12809 .parent = block.namespace,
12810 .ty = struct_ty,
12811 .file_scope = block.getFileScope(),
12812 },
12813 };
12814
12815 // Fields
12816 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen());
12817 try struct_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
12818 var i: usize = 0;
12819 while (i < fields_len) : (i += 1) {
12820 const elem_val = try fields_val.elemValue(sema.arena, i);
12821 const field_struct_val = elem_val.castTag(.@"struct").?.data;
12822 // TODO use reflection instead of magic numbers here
12823 // name: []const u8
12824 const name_val = field_struct_val[0];
12825 // field_type: type,
12826 const field_type_val = field_struct_val[1];
12827 //default_value: ?*const anyopaque,
12828 const default_value_val = field_struct_val[2];
12829 // is_comptime: bool,
12830 const is_comptime_val = field_struct_val[3];
12831 // alignment: comptime_int,
12832 const alignment_val = field_struct_val[4];
12833
12834 const field_name = try name_val.toAllocatedBytes(
12835 Type.initTag(.const_slice_u8),
12836 new_decl_arena_allocator,
12837 );
12838
12839 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
12840 if (gop.found_existing) {
12841 // TODO: better source location
12842 return sema.fail(block, src, "duplicate struct field {s}", .{field_name});
12843 }
12844
12845 const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
12846 const payload_val = if (opt_val.pointerDecl()) |opt_decl|
12847 opt_decl.val
12848 else
12849 opt_val;
12850 break :blk try payload_val.copy(new_decl_arena_allocator);
12851 } else Value.initTag(.unreachable_value);
12852
12853 var buffer: Value.ToTypeBuffer = undefined;
12854 gop.value_ptr.* = .{
12855 .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator),
12856 .abi_align = try alignment_val.copy(new_decl_arena_allocator),
12857 .default_val = default_val,
12858 .is_comptime = is_comptime_val.toBool(),
12859 .offset = undefined,
12860 };
12861 }
12862
12863 try new_decl.finalizeNewArena(&new_decl_arena);
12864 return sema.analyzeDeclVal(block, src, new_decl);
12865}
12866
12687fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {12867fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
12688 const inst_data = sema.code.instructions.items(.data)[inst].un_node;12868 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
12689 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };12869 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
test/behavior/type.zig+45-1
...@@ -260,7 +260,11 @@ test "Type.ErrorSet" {...@@ -260,7 +260,11 @@ test "Type.ErrorSet" {
260}260}
261261
262test "Type.Struct" {262test "Type.Struct" {
263 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO263 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
264 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
265 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
266 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
267 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
264268
265 const A = @Type(@typeInfo(struct { x: u8, y: u32 }));269 const A = @Type(@typeInfo(struct { x: u8, y: u32 }));
266 const infoA = @typeInfo(A).Struct;270 const infoA = @typeInfo(A).Struct;
...@@ -303,6 +307,46 @@ test "Type.Struct" {...@@ -303,6 +307,46 @@ test "Type.Struct" {
303 try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoC.fields[1].default_value.?).*);307 try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoC.fields[1].default_value.?).*);
304 try testing.expectEqual(@as(usize, 0), infoC.decls.len);308 try testing.expectEqual(@as(usize, 0), infoC.decls.len);
305 try testing.expectEqual(@as(bool, false), infoC.is_tuple);309 try testing.expectEqual(@as(bool, false), infoC.is_tuple);
310
311 // anon structs
312 const D = @Type(@typeInfo(@TypeOf(.{ .x = 3, .y = 5 })));
313 const infoD = @typeInfo(D).Struct;
314 try testing.expectEqual(Type.ContainerLayout.Auto, infoD.layout);
315 try testing.expectEqualSlices(u8, "x", infoD.fields[0].name);
316 try testing.expectEqual(comptime_int, infoD.fields[0].field_type);
317 try testing.expectEqual(@as(comptime_int, 3), @ptrCast(*const comptime_int, infoD.fields[0].default_value.?).*);
318 try testing.expectEqualSlices(u8, "y", infoD.fields[1].name);
319 try testing.expectEqual(comptime_int, infoD.fields[1].field_type);
320 try testing.expectEqual(@as(comptime_int, 5), @ptrCast(*const comptime_int, infoD.fields[1].default_value.?).*);
321 try testing.expectEqual(@as(usize, 0), infoD.decls.len);
322 try testing.expectEqual(@as(bool, false), infoD.is_tuple);
323
324 // tuples
325 const E = @Type(@typeInfo(@TypeOf(.{ 1, 2 })));
326 const infoE = @typeInfo(E).Struct;
327 try testing.expectEqual(Type.ContainerLayout.Auto, infoE.layout);
328 try testing.expectEqualSlices(u8, "0", infoE.fields[0].name);
329 try testing.expectEqual(comptime_int, infoE.fields[0].field_type);
330 try testing.expectEqual(@as(comptime_int, 1), @ptrCast(*const comptime_int, infoE.fields[0].default_value.?).*);
331 try testing.expectEqualSlices(u8, "1", infoE.fields[1].name);
332 try testing.expectEqual(comptime_int, infoE.fields[1].field_type);
333 try testing.expectEqual(@as(comptime_int, 2), @ptrCast(*const comptime_int, infoE.fields[1].default_value.?).*);
334 try testing.expectEqual(@as(usize, 0), infoE.decls.len);
335 try testing.expectEqual(@as(bool, true), infoE.is_tuple);
336
337 // empty struct
338 const F = @Type(@typeInfo(struct {}));
339 const infoF = @typeInfo(F).Struct;
340 try testing.expectEqual(Type.ContainerLayout.Auto, infoF.layout);
341 try testing.expect(infoF.fields.len == 0);
342 try testing.expectEqual(@as(bool, false), infoF.is_tuple);
343
344 // empty tuple
345 const G = @Type(@typeInfo(@TypeOf(.{})));
346 const infoG = @typeInfo(G).Struct;
347 try testing.expectEqual(Type.ContainerLayout.Auto, infoG.layout);
348 try testing.expect(infoG.fields.len == 0);
349 try testing.expectEqual(@as(bool, true), infoG.is_tuple);
306}350}
307351
308test "Type.Enum" {352test "Type.Enum" {