From 88be5bd81decab792cdb5b749b4bb5cf6d71e877 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Dec 2021 20:34:27 -0700 Subject: 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. --- src/Sema.zig | 114 ++++++++++++++++++++++++++++++++++++--------------- src/codegen/llvm.zig | 3 +- 2 files changed, 83 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/Sema.zig b/src/Sema.zig index 2ac4d327b1..e030090979 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9536,9 +9536,36 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const obj_ty = try sema.resolveType(block, src, inst_data.operand); + const gpa = sema.gpa; switch (obj_ty.zigTypeTag()) { - .Struct => return sema.addConstant(obj_ty, Value.initTag(.empty_struct_value)), + .Struct => { + // This logic must be synchronized with that in `zirStructInit`. + const struct_ty = try sema.resolveTypeFields(block, src, obj_ty); + const struct_obj = struct_ty.castTag(.@"struct").?.data; + + // The init values to use for the struct instance. + const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count()); + defer gpa.free(field_inits); + + var root_msg: ?*Module.ErrorMsg = null; + + for (struct_obj.fields.values()) |field, i| { + if (field.default_val.tag() == .unreachable_value) { + const field_name = struct_obj.fields.keys()[i]; + const template = "missing struct field: {s}"; + const args = .{field_name}; + if (root_msg) |msg| { + try sema.errNote(block, src, msg, template, args); + } else { + root_msg = try sema.errMsg(block, src, template, args); + } + } else { + field_inits[i] = try sema.addConstant(field.ty, field.default_val); + } + } + return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, struct_ty, false); + }, .Array => { if (obj_ty.sentinel()) |sentinel| { 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) const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type); if (resolved_ty.castTag(.@"struct")) |struct_payload| { + // This logic must be synchronized with that in `zirStructInitEmpty`. const struct_obj = struct_payload.data; // 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) field_inits[i] = try sema.addConstant(field.ty, field.default_val); } } - if (root_msg) |msg| { - const fqn = try struct_obj.getFullyQualifiedName(gpa); - defer gpa.free(fqn); - try sema.mod.errNoteNonLazy( - struct_obj.srcLoc(), - msg, - "struct '{s}' declared here", - .{fqn}, - ); - return sema.failWithOwnedErrorMsg(msg); - } - - if (is_ref) { - return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true", .{}); - } - - const is_comptime = for (field_inits) |field_init| { - if (!(try sema.isComptimeKnown(block, src, field_init))) { - break false; - } - } else true; - - if (is_comptime) { - const values = try sema.arena.alloc(Value, field_inits.len); - for (field_inits) |field_init, i| { - values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; - } - return sema.addConstant(resolved_ty, try Value.Tag.@"struct".create(sema.arena, values)); - } - - return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{}); + return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, resolved_ty, is_ref); } else if (resolved_ty.cast(Type.Payload.Union)) |union_payload| { const union_obj = union_payload.data; @@ -9698,6 +9696,51 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) unreachable; } +fn finishStructInit( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + field_inits: []const Air.Inst.Ref, + root_msg: ?*Module.ErrorMsg, + struct_obj: *Module.Struct, + struct_ty: Type, + is_ref: bool, +) !Air.Inst.Ref { + const gpa = sema.gpa; + + if (root_msg) |msg| { + const fqn = try struct_obj.getFullyQualifiedName(gpa); + defer gpa.free(fqn); + try sema.mod.errNoteNonLazy( + struct_obj.srcLoc(), + msg, + "struct '{s}' declared here", + .{fqn}, + ); + return sema.failWithOwnedErrorMsg(msg); + } + + if (is_ref) { + return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true", .{}); + } + + const is_comptime = for (field_inits) |field_init| { + if (!(try sema.isComptimeKnown(block, src, field_init))) { + break false; + } + } else true; + + if (is_comptime) { + const values = try sema.arena.alloc(Value, field_inits.len); + for (field_inits) |field_init, i| { + values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; + } + return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values)); + } + + return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{}); +} + fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); @@ -14486,7 +14529,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp struct_obj.status = .field_types_wip; try semaStructFields(sema.mod, struct_obj); - struct_obj.status = .have_field_types; + + if (struct_obj.fields.count() == 0) { + struct_obj.status = .have_layout; + } else { + struct_obj.status = .have_field_types; + } return ty; }, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 78848bee28..02de622cf2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1487,7 +1487,6 @@ pub const DeclGen = struct { return self.context.constStruct(&fields, fields.len, .False); } - const llvm_type = try self.llvmType(tv.ty); if (!tv.ty.childType().hasCodeGenBits() or !decl.ty.hasCodeGenBits()) { return self.lowerPtrToVoid(tv.ty); } @@ -1498,6 +1497,8 @@ pub const DeclGen = struct { try self.resolveLlvmFunction(decl) else try self.resolveGlobalDecl(decl); + + const llvm_type = try self.llvmType(tv.ty); return llvm_val.constBitCast(llvm_type); } -- cgit v1.2.3