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...@@ -9536,9 +9536,36 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
9536 const inst_data = sema.code.instructions.items(.data)[inst].un_node;9536 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
9537 const src = inst_data.src();9537 const src = inst_data.src();
9538 const obj_ty = try sema.resolveType(block, src, inst_data.operand);9538 const obj_ty = try sema.resolveType(block, src, inst_data.operand);
9539 const gpa = sema.gpa;
95399540
9540 switch (obj_ty.zigTypeTag()) {9541 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 },
9542 .Array => {9569 .Array => {
9543 if (obj_ty.sentinel()) |sentinel| {9570 if (obj_ty.sentinel()) |sentinel| {
9544 const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel);9571 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)...@@ -9572,6 +9599,7 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
9572 const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);9599 const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
95739600
9574 if (resolved_ty.castTag(.@"struct")) |struct_payload| {9601 if (resolved_ty.castTag(.@"struct")) |struct_payload| {
9602 // This logic must be synchronized with that in `zirStructInitEmpty`.
9575 const struct_obj = struct_payload.data;9603 const struct_obj = struct_payload.data;
95769604
9577 // Maps field index to field_type index of where it was already initialized.9605 // 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)...@@ -9633,37 +9661,7 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
9633 field_inits[i] = try sema.addConstant(field.ty, field.default_val);9661 field_inits[i] = try sema.addConstant(field.ty, field.default_val);
9634 }9662 }
9635 }9663 }
9636 if (root_msg) |msg| {9664 return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, resolved_ty, is_ref);
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", .{});
9667 } else if (resolved_ty.cast(Type.Payload.Union)) |union_payload| {9665 } else if (resolved_ty.cast(Type.Payload.Union)) |union_payload| {
9668 const union_obj = union_payload.data;9666 const union_obj = union_payload.data;
96699667
...@@ -9698,6 +9696,51 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)...@@ -9698,6 +9696,51 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
9698 unreachable;9696 unreachable;
9699}9697}
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
9701fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {9744fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
9702 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;9745 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9703 const src = inst_data.src();9746 const src = inst_data.src();
...@@ -14486,7 +14529,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp...@@ -14486,7 +14529,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
1448614529
14487 struct_obj.status = .field_types_wip;14530 struct_obj.status = .field_types_wip;
14488 try semaStructFields(sema.mod, struct_obj);14531 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
14491 return ty;14539 return ty;
14492 },14540 },
src/codegen/llvm.zig+2-1
...@@ -1487,7 +1487,6 @@ pub const DeclGen = struct {...@@ -1487,7 +1487,6 @@ pub const DeclGen = struct {
1487 return self.context.constStruct(&fields, fields.len, .False);1487 return self.context.constStruct(&fields, fields.len, .False);
1488 }1488 }
14891489
1490 const llvm_type = try self.llvmType(tv.ty);
1491 if (!tv.ty.childType().hasCodeGenBits() or !decl.ty.hasCodeGenBits()) {1490 if (!tv.ty.childType().hasCodeGenBits() or !decl.ty.hasCodeGenBits()) {
1492 return self.lowerPtrToVoid(tv.ty);1491 return self.lowerPtrToVoid(tv.ty);
1493 }1492 }
...@@ -1498,6 +1497,8 @@ pub const DeclGen = struct {...@@ -1498,6 +1497,8 @@ pub const DeclGen = struct {
1498 try self.resolveLlvmFunction(decl)1497 try self.resolveLlvmFunction(decl)
1499 else1498 else
1500 try self.resolveGlobalDecl(decl);1499 try self.resolveGlobalDecl(decl);
1500
1501 const llvm_type = try self.llvmType(tv.ty);
1501 return llvm_val.constBitCast(llvm_type);1502 return llvm_val.constBitCast(llvm_type);
1502 }1503 }
15031504
test/behavior/struct_llvm.zig+11
...@@ -66,3 +66,14 @@ test "self-referencing struct via array member" {...@@ -66,3 +66,14 @@ test "self-referencing struct via array member" {
66 x = T{ .children = .{&x} };66 x = T{ .children = .{&x} };
67 try expect(x.children[0] == &x);67 try expect(x.children[0] == &x);
68}68}
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;...@@ -6,32 +6,6 @@ const expectEqual = std.testing.expectEqual;
6const expectEqualSlices = std.testing.expectEqualSlices;6const expectEqualSlices = std.testing.expectEqualSlices;
7const maxInt = std.math.maxInt;7const 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
35const EmptyStruct2 = struct {};9const EmptyStruct2 = struct {};
36fn testReturnEmptyStructFromFn() EmptyStruct2 {10fn testReturnEmptyStructFromFn() EmptyStruct2 {
37 return EmptyStruct2{};11 return EmptyStruct2{};