authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-21 20:34:27-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-21 20:34:27-07:00
log88be5bd81decab792cdb5b749b4bb5cf6d71e877
treef5eed00970c7e97581e8382ae37d08ef177d431f
parent06d751dbb3c9cc4e09f2eb7250a242dc6b2423bc

Sema: fix empty struct init

* Extract common logic between `zirStructInitEmpty` and `zirStructInit`. * `resolveTypeFields` additionally sets status to `have_layout` if the total number of fields is 0.

4 files changed, 94 insertions(+), 60 deletions(-)

src/Sema.zig+81-33
......@@ -9536,9 +9536,36 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
95369536 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
95379537 const src = inst_data.src();
95389538 const obj_ty = try sema.resolveType(block, src, inst_data.operand);
9539 const gpa = sema.gpa;
95399540
95409541 switch (obj_ty.zigTypeTag()) {
9541 .Struct => return sema.addConstant(obj_ty, Value.initTag(.empty_struct_value)),
9542 .Struct => {
9543 // This logic must be synchronized with that in `zirStructInit`.
9544 const struct_ty = try sema.resolveTypeFields(block, src, obj_ty);
9545 const struct_obj = struct_ty.castTag(.@"struct").?.data;
9546
9547 // The init values to use for the struct instance.
9548 const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
9549 defer gpa.free(field_inits);
9550
9551 var root_msg: ?*Module.ErrorMsg = null;
9552
9553 for (struct_obj.fields.values()) |field, i| {
9554 if (field.default_val.tag() == .unreachable_value) {
9555 const field_name = struct_obj.fields.keys()[i];
9556 const template = "missing struct field: {s}";
9557 const args = .{field_name};
9558 if (root_msg) |msg| {
9559 try sema.errNote(block, src, msg, template, args);
9560 } else {
9561 root_msg = try sema.errMsg(block, src, template, args);
9562 }
9563 } else {
9564 field_inits[i] = try sema.addConstant(field.ty, field.default_val);
9565 }
9566 }
9567 return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, struct_ty, false);
9568 },
95429569 .Array => {
95439570 if (obj_ty.sentinel()) |sentinel| {
95449571 const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel);
......@@ -9572,6 +9599,7 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
95729599 const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
95739600
95749601 if (resolved_ty.castTag(.@"struct")) |struct_payload| {
9602 // This logic must be synchronized with that in `zirStructInitEmpty`.
95759603 const struct_obj = struct_payload.data;
95769604
95779605 // Maps field index to field_type index of where it was already initialized.
......@@ -9633,37 +9661,7 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
96339661 field_inits[i] = try sema.addConstant(field.ty, field.default_val);
96349662 }
96359663 }
9636 if (root_msg) |msg| {
9637 const fqn = try struct_obj.getFullyQualifiedName(gpa);
9638 defer gpa.free(fqn);
9639 try sema.mod.errNoteNonLazy(
9640 struct_obj.srcLoc(),
9641 msg,
9642 "struct '{s}' declared here",
9643 .{fqn},
9644 );
9645 return sema.failWithOwnedErrorMsg(msg);
9646 }
9647
9648 if (is_ref) {
9649 return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true", .{});
9650 }
9651
9652 const is_comptime = for (field_inits) |field_init| {
9653 if (!(try sema.isComptimeKnown(block, src, field_init))) {
9654 break false;
9655 }
9656 } else true;
9657
9658 if (is_comptime) {
9659 const values = try sema.arena.alloc(Value, field_inits.len);
9660 for (field_inits) |field_init, i| {
9661 values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
9662 }
9663 return sema.addConstant(resolved_ty, try Value.Tag.@"struct".create(sema.arena, values));
9664 }
9665
9666 return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
9664 return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, resolved_ty, is_ref);
96679665 } else if (resolved_ty.cast(Type.Payload.Union)) |union_payload| {
96689666 const union_obj = union_payload.data;
96699667
......@@ -9698,6 +9696,51 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
96989696 unreachable;
96999697}
97009698
9699fn finishStructInit(
9700 sema: *Sema,
9701 block: *Block,
9702 src: LazySrcLoc,
9703 field_inits: []const Air.Inst.Ref,
9704 root_msg: ?*Module.ErrorMsg,
9705 struct_obj: *Module.Struct,
9706 struct_ty: Type,
9707 is_ref: bool,
9708) !Air.Inst.Ref {
9709 const gpa = sema.gpa;
9710
9711 if (root_msg) |msg| {
9712 const fqn = try struct_obj.getFullyQualifiedName(gpa);
9713 defer gpa.free(fqn);
9714 try sema.mod.errNoteNonLazy(
9715 struct_obj.srcLoc(),
9716 msg,
9717 "struct '{s}' declared here",
9718 .{fqn},
9719 );
9720 return sema.failWithOwnedErrorMsg(msg);
9721 }
9722
9723 if (is_ref) {
9724 return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true", .{});
9725 }
9726
9727 const is_comptime = for (field_inits) |field_init| {
9728 if (!(try sema.isComptimeKnown(block, src, field_init))) {
9729 break false;
9730 }
9731 } else true;
9732
9733 if (is_comptime) {
9734 const values = try sema.arena.alloc(Value, field_inits.len);
9735 for (field_inits) |field_init, i| {
9736 values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
9737 }
9738 return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values));
9739 }
9740
9741 return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
9742}
9743
97019744fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
97029745 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
97039746 const src = inst_data.src();
......@@ -14486,7 +14529,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
1448614529
1448714530 struct_obj.status = .field_types_wip;
1448814531 try semaStructFields(sema.mod, struct_obj);
14489 struct_obj.status = .have_field_types;
14532
14533 if (struct_obj.fields.count() == 0) {
14534 struct_obj.status = .have_layout;
14535 } else {
14536 struct_obj.status = .have_field_types;
14537 }
1449014538
1449114539 return ty;
1449214540 },
src/codegen/llvm.zig+2-1
......@@ -1487,7 +1487,6 @@ pub const DeclGen = struct {
14871487 return self.context.constStruct(&fields, fields.len, .False);
14881488 }
14891489
1490 const llvm_type = try self.llvmType(tv.ty);
14911490 if (!tv.ty.childType().hasCodeGenBits() or !decl.ty.hasCodeGenBits()) {
14921491 return self.lowerPtrToVoid(tv.ty);
14931492 }
......@@ -1498,6 +1497,8 @@ pub const DeclGen = struct {
14981497 try self.resolveLlvmFunction(decl)
14991498 else
15001499 try self.resolveGlobalDecl(decl);
1500
1501 const llvm_type = try self.llvmType(tv.ty);
15011502 return llvm_val.constBitCast(llvm_type);
15021503 }
15031504
test/behavior/struct_llvm.zig+11
......@@ -66,3 +66,14 @@ test "self-referencing struct via array member" {
6666 x = T{ .children = .{&x} };
6767 try expect(x.children[0] == &x);
6868}
69
70test "empty struct method call" {
71 const es = EmptyStruct{};
72 try expect(es.method() == 1234);
73}
74const EmptyStruct = struct {
75 fn method(es: *const EmptyStruct) i32 {
76 _ = es;
77 return 1234;
78 }
79};
test/behavior/struct_stage1.zig-26
......@@ -6,32 +6,6 @@ const expectEqual = std.testing.expectEqual;
66const expectEqualSlices = std.testing.expectEqualSlices;
77const maxInt = std.math.maxInt;
88
9const StructFoo = struct {
10 a: i32,
11 b: bool,
12 c: f32,
13};
14
15const Node = struct {
16 val: Val,
17 next: *Node,
18};
19
20const Val = struct {
21 x: i32,
22};
23
24test "empty struct method call" {
25 const es = EmptyStruct{};
26 try expect(es.method() == 1234);
27}
28const EmptyStruct = struct {
29 fn method(es: *const EmptyStruct) i32 {
30 _ = es;
31 return 1234;
32 }
33};
34
359const EmptyStruct2 = struct {};
3610fn testReturnEmptyStructFromFn() EmptyStruct2 {
3711 return EmptyStruct2{};