Commit 4a28c1d5c3
Changed files (6)
test
behavior
src/AstGen.zig
@@ -4148,7 +4148,6 @@ fn structDeclInner(
.src_node = node,
.layout = layout,
.fields_len = 0,
- .body_len = 0,
.decls_len = 0,
.known_non_opv = false,
.known_comptime_only = false,
@@ -4192,6 +4191,19 @@ fn structDeclInner(
var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
defer wip_members.deinit();
+ // We will use the scratch buffer, starting here, for the bodies:
+ // bodies: { // for every fields_len
+ // field_type_body_inst: Inst, // for each field_type_body_len
+ // align_body_inst: Inst, // for each align_body_len
+ // init_body_inst: Inst, // for each init_body_len
+ // }
+ // Note that the scratch buffer is simultaneously being used by WipMembers, however
+ // it will not access any elements beyond this point in the ArrayList. It also
+ // accesses via the ArrayList items field so it can handle the scratch buffer being
+ // reallocated.
+ // No defer needed here because it is handled by `wip_members.deinit()` above.
+ const bodies_start = astgen.scratch.items.len;
+
var known_non_opv = false;
var known_comptime_only = false;
for (container_decl.ast.members) |member_node| {
@@ -4203,20 +4215,18 @@ fn structDeclInner(
const field_name = try astgen.identAsString(member.ast.name_token);
wip_members.appendToField(field_name);
+ const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
+ wip_members.appendToField(doc_comment_index);
+
if (member.ast.type_expr == 0) {
return astgen.failTok(member.ast.name_token, "struct field missing type", .{});
}
const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
- wip_members.appendToField(@enumToInt(field_type));
-
- const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
- wip_members.appendToField(doc_comment_index);
-
+ const have_type_body = !block_scope.isEmpty();
const have_align = member.ast.align_expr != 0;
const have_value = member.ast.value_expr != 0;
const is_comptime = member.comptime_token != null;
- const unused = false;
if (!is_comptime) {
known_non_opv = known_non_opv or
@@ -4224,36 +4234,59 @@ fn structDeclInner(
known_comptime_only = known_comptime_only or
nodeImpliesComptimeOnly(tree, member.ast.type_expr);
}
- wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, unused });
+ wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body });
+
+ if (have_type_body) {
+ if (!block_scope.endsWithNoReturn()) {
+ _ = try block_scope.addBreak(.break_inline, decl_inst, field_type);
+ }
+ const body = block_scope.instructionsSlice();
+ const old_scratch_len = astgen.scratch.items.len;
+ try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
+ appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
+ wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
+ block_scope.instructions.items.len = block_scope.instructions_top;
+ } else {
+ wip_members.appendToField(@enumToInt(field_type));
+ }
if (have_align) {
if (layout == .Packed) {
try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
}
- const align_inst = try expr(&block_scope, &namespace.base, align_rl, member.ast.align_expr);
- wip_members.appendToField(@enumToInt(align_inst));
+ const align_ref = try expr(&block_scope, &namespace.base, coerced_align_rl, member.ast.align_expr);
+ if (!block_scope.endsWithNoReturn()) {
+ _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
+ }
+ const body = block_scope.instructionsSlice();
+ const old_scratch_len = astgen.scratch.items.len;
+ try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
+ appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
+ wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
+ block_scope.instructions.items.len = block_scope.instructions_top;
}
+
if (have_value) {
- const rl: ResultLoc = if (field_type == .none) .none else .{ .ty = field_type };
+ const rl: ResultLoc = if (field_type == .none) .none else .{ .coerced_ty = field_type };
const default_inst = try expr(&block_scope, &namespace.base, rl, member.ast.value_expr);
- wip_members.appendToField(@enumToInt(default_inst));
+ if (!block_scope.endsWithNoReturn()) {
+ _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst);
+ }
+ const body = block_scope.instructionsSlice();
+ const old_scratch_len = astgen.scratch.items.len;
+ try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
+ appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
+ wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
+ block_scope.instructions.items.len = block_scope.instructions_top;
} else if (member.comptime_token) |comptime_token| {
return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
}
}
- if (!block_scope.isEmpty()) {
- _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
- }
-
- const body = block_scope.instructionsSlice();
- const body_len = astgen.countBodyLenAfterFixups(body);
-
try gz.setStruct(decl_inst, .{
.src_node = node,
.layout = layout,
- .body_len = body_len,
.fields_len = field_count,
.decls_len = decl_count,
.known_non_opv = known_non_opv,
@@ -4263,10 +4296,11 @@ fn structDeclInner(
wip_members.finishBits(bits_per_field);
const decls_slice = wip_members.declsSlice();
const fields_slice = wip_members.fieldsSlice();
- try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
+ const bodies_slice = astgen.scratch.items[bodies_start..];
+ try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + fields_slice.len + bodies_slice.len);
astgen.extra.appendSliceAssumeCapacity(decls_slice);
- astgen.appendBodyWithFixups(body);
astgen.extra.appendSliceAssumeCapacity(fields_slice);
+ astgen.extra.appendSliceAssumeCapacity(bodies_slice);
block_scope.unstack();
try gz.addNamespaceCaptures(&namespace);
@@ -10981,7 +11015,6 @@ const GenZir = struct {
fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
src_node: Ast.Node.Index,
- body_len: u32,
fields_len: u32,
decls_len: u32,
layout: std.builtin.Type.ContainerLayout,
@@ -10998,9 +11031,6 @@ const GenZir = struct {
const node_offset = gz.nodeIndexToRelative(args.src_node);
astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
}
- if (args.body_len != 0) {
- astgen.extra.appendAssumeCapacity(args.body_len);
- }
if (args.fields_len != 0) {
astgen.extra.appendAssumeCapacity(args.fields_len);
}
@@ -11013,7 +11043,6 @@ const GenZir = struct {
.opcode = .struct_decl,
.small = @bitCast(u16, Zir.Inst.StructDecl.Small{
.has_src_node = args.src_node != 0,
- .has_body_len = args.body_len != 0,
.has_fields_len = args.fields_len != 0,
.has_decls_len = args.decls_len != 0,
.known_non_opv = args.known_non_opv,
src/Module.zig
@@ -916,13 +916,14 @@ pub const Struct = struct {
/// one possible value.
known_non_opv: bool,
requires_comptime: PropertyBoolean = .unknown,
+ have_field_inits: bool = false,
pub const Fields = std.StringArrayHashMapUnmanaged(Field);
/// The `Type` and `Value` memory is owned by the arena of the Struct's owner_decl.
pub const Field = struct {
/// Uses `noreturn` to indicate `anytype`.
- /// undefined until `status` is `have_field_types` or `have_layout`.
+ /// undefined until `status` is >= `have_field_types`.
ty: Type,
/// Uses `unreachable_value` to indicate no default.
default_val: Value,
src/print_zir.zig
@@ -1227,12 +1227,6 @@ const Writer = struct {
break :blk src_node;
} else null;
- const body_len = if (small.has_body_len) blk: {
- const body_len = self.code.extra[extra_index];
- extra_index += 1;
- break :blk body_len;
- } else 0;
-
const fields_len = if (small.has_fields_len) blk: {
const fields_len = self.code.extra[extra_index];
extra_index += 1;
@@ -1262,71 +1256,114 @@ const Writer = struct {
try stream.writeAll("}, ");
}
- const body = self.code.extra[extra_index..][0..body_len];
- extra_index += body.len;
-
if (fields_len == 0) {
- assert(body.len == 0);
try stream.writeAll("{}, {})");
} else {
- const prev_parent_decl_node = self.parent_decl_node;
- if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off);
- try self.writeBracedDecl(stream, body);
- try stream.writeAll(", {\n");
-
- self.indent += 2;
const bits_per_field = 4;
const fields_per_u32 = 32 / bits_per_field;
const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
- var bit_bag_index: usize = extra_index;
- extra_index += bit_bags_count;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % fields_per_u32 == 0) {
- cur_bit_bag = self.code.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_align = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const has_default = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const is_comptime = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const unused = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
+ const Field = struct {
+ doc_comment_index: u32,
+ type_len: u32 = 0,
+ align_len: u32 = 0,
+ init_len: u32 = 0,
+ field_type: Zir.Inst.Ref = .none,
+ name: u32,
+ is_comptime: bool,
+ };
+ const fields = try self.arena.alloc(Field, fields_len);
+ {
+ var bit_bag_index: usize = extra_index;
+ extra_index += bit_bags_count;
+ var cur_bit_bag: u32 = undefined;
+ var field_i: u32 = 0;
+ while (field_i < fields_len) : (field_i += 1) {
+ if (field_i % fields_per_u32 == 0) {
+ cur_bit_bag = self.code.extra[bit_bag_index];
+ bit_bag_index += 1;
+ }
+ const has_align = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const has_default = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const is_comptime = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const has_type_body = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+
+ const field_name = self.code.extra[extra_index];
+ extra_index += 1;
+ const doc_comment_index = self.code.extra[extra_index];
+ extra_index += 1;
- _ = unused;
+ fields[field_i] = .{
+ .doc_comment_index = doc_comment_index,
+ .is_comptime = is_comptime,
+ .name = field_name,
+ };
- const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
- extra_index += 1;
- const field_type = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
- extra_index += 1;
- const doc_comment_index = self.code.extra[extra_index];
- extra_index += 1;
+ if (has_type_body) {
+ fields[field_i].type_len = self.code.extra[extra_index];
+ } else {
+ fields[field_i].field_type = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
+ }
+ extra_index += 1;
- try self.writeDocComment(stream, doc_comment_index);
+ if (has_align) {
+ fields[field_i].align_len = self.code.extra[extra_index];
+ extra_index += 1;
+ }
+
+ if (has_default) {
+ fields[field_i].init_len = self.code.extra[extra_index];
+ extra_index += 1;
+ }
+ }
+ }
+
+ const prev_parent_decl_node = self.parent_decl_node;
+ if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off);
+ try stream.writeAll("{\n");
+ self.indent += 2;
+ for (fields) |field| {
+ const field_name = self.code.nullTerminatedString(field.name);
+
+ try self.writeDocComment(stream, field.doc_comment_index);
try stream.writeByteNTimes(' ', self.indent);
- try self.writeFlag(stream, "comptime ", is_comptime);
+ try self.writeFlag(stream, "comptime ", field.is_comptime);
try stream.print("{}: ", .{std.zig.fmtId(field_name)});
- try self.writeInstRef(stream, field_type);
+ if (field.field_type != .none) {
+ try self.writeInstRef(stream, field.field_type);
+ }
- if (has_align) {
- const align_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
- extra_index += 1;
+ if (field.type_len > 0) {
+ const body = self.code.extra[extra_index..][0..field.type_len];
+ extra_index += body.len;
+ self.indent += 2;
+ try self.writeBracedDecl(stream, body);
+ self.indent -= 2;
+ }
+ if (field.align_len > 0) {
+ const body = self.code.extra[extra_index..][0..field.align_len];
+ extra_index += body.len;
+ self.indent += 2;
try stream.writeAll(" align(");
- try self.writeInstRef(stream, align_ref);
+ try self.writeBracedDecl(stream, body);
try stream.writeAll(")");
+ self.indent -= 2;
}
- if (has_default) {
- const default_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
- extra_index += 1;
+ if (field.init_len > 0) {
+ const body = self.code.extra[extra_index..][0..field.init_len];
+ extra_index += body.len;
+ self.indent += 2;
try stream.writeAll(" = ");
- try self.writeInstRef(stream, default_ref);
+ try self.writeBracedDecl(stream, body);
+ self.indent -= 2;
}
+
try stream.writeAll(",\n");
}
src/Sema.zig
@@ -1862,13 +1862,15 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg)
return error.AnalysisFail;
}
-pub fn resolveAlign(
+const align_ty = Type.u29;
+
+fn analyzeAsAlign(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- zir_ref: Zir.Inst.Ref,
+ air_ref: Air.Inst.Ref,
) !u32 {
- const alignment_big = try sema.resolveInt(block, src, zir_ref, Type.initTag(.u29));
+ const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty);
const alignment = @intCast(u32, alignment_big); // We coerce to u16 in the prev line.
if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{});
if (!std.math.isPowerOfTwo(alignment)) {
@@ -1879,6 +1881,16 @@ pub fn resolveAlign(
return alignment;
}
+pub fn resolveAlign(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ zir_ref: Zir.Inst.Ref,
+) !u32 {
+ const air_ref = try sema.resolveInst(zir_ref);
+ return analyzeAsAlign(sema, block, src, air_ref);
+}
+
fn resolveInt(
sema: *Sema,
block: *Block,
@@ -1886,8 +1898,18 @@ fn resolveInt(
zir_ref: Zir.Inst.Ref,
dest_ty: Type,
) !u64 {
- const air_inst = try sema.resolveInst(zir_ref);
- const coerced = try sema.coerce(block, dest_ty, air_inst, src);
+ const air_ref = try sema.resolveInst(zir_ref);
+ return analyzeAsInt(sema, block, src, air_ref, dest_ty);
+}
+
+fn analyzeAsInt(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ air_ref: Air.Inst.Ref,
+ dest_ty: Type,
+) !u64 {
+ const coerced = try sema.coerce(block, dest_ty, air_ref, src);
const val = try sema.resolveConstValue(block, src, coerced);
const target = sema.mod.getTarget();
return (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?;
@@ -2097,7 +2119,6 @@ pub fn analyzeStructDecl(
var extra_index: usize = extended.operand;
extra_index += @boolToInt(small.has_src_node);
- extra_index += @boolToInt(small.has_body_len);
extra_index += @boolToInt(small.has_fields_len);
const decls_len = if (small.has_decls_len) blk: {
const decls_len = sema.code.extra[extra_index];
@@ -24858,12 +24879,6 @@ fn resolveTypeFieldsStruct(
struct_obj.status = .field_types_wip;
try semaStructFields(sema.mod, struct_obj);
-
- if (struct_obj.fields.count() == 0) {
- struct_obj.status = .have_layout;
- } else {
- struct_obj.status = .have_field_types;
- }
}
fn resolveTypeFieldsUnion(
@@ -24954,13 +24969,7 @@ fn resolveInferredErrorSetTy(
}
}
-fn semaStructFields(
- mod: *Module,
- struct_obj: *Module.Struct,
-) CompileError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
+fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void {
const gpa = mod.gpa;
const decl_index = struct_obj.owner_decl;
const zir = struct_obj.namespace.file_scope.zir;
@@ -24972,12 +24981,6 @@ fn semaStructFields(
const src = LazySrcLoc.nodeOffset(struct_obj.node_offset);
extra_index += @boolToInt(small.has_src_node);
- const body_len = if (small.has_body_len) blk: {
- const body_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk body_len;
- } else 0;
-
const fields_len = if (small.has_fields_len) blk: {
const fields_len = zir.extra[extra_index];
extra_index += 1;
@@ -24995,12 +24998,10 @@ fn semaStructFields(
while (decls_it.next()) |_| {}
extra_index = decls_it.extra_index;
- const body = zir.extra[extra_index..][0..body_len];
if (fields_len == 0) {
- assert(body.len == 0);
+ struct_obj.status = .have_layout;
return;
}
- extra_index += body.len;
const decl = mod.declPtr(decl_index);
var decl_arena = decl.value_arena.?.promote(gpa);
@@ -25042,106 +25043,150 @@ fn semaStructFields(
block_scope.params.deinit(gpa);
}
- if (body.len != 0) {
- try sema.analyzeBody(&block_scope, body);
- }
+ try struct_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len);
- try wip_captures.finalize();
+ const Field = struct {
+ type_body_len: u32 = 0,
+ align_body_len: u32 = 0,
+ init_body_len: u32 = 0,
+ type_ref: Air.Inst.Ref = .none,
+ };
+ const fields = try sema.arena.alloc(Field, fields_len);
+ var any_inits = false;
- try struct_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len);
+ {
+ const bits_per_field = 4;
+ const fields_per_u32 = 32 / bits_per_field;
+ const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
+ const flags_index = extra_index;
+ var bit_bag_index: usize = flags_index;
+ extra_index += bit_bags_count;
+ var cur_bit_bag: u32 = undefined;
+ var field_i: u32 = 0;
+ while (field_i < fields_len) : (field_i += 1) {
+ if (field_i % fields_per_u32 == 0) {
+ cur_bit_bag = zir.extra[bit_bag_index];
+ bit_bag_index += 1;
+ }
+ const has_align = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const has_init = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const is_comptime = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const has_type_body = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+
+ const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
+ extra_index += 1;
+ extra_index += 1; // doc_comment
- const bits_per_field = 4;
- const fields_per_u32 = 32 / bits_per_field;
- const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
- var bit_bag_index: usize = extra_index;
- extra_index += bit_bags_count;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % fields_per_u32 == 0) {
- cur_bit_bag = zir.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_align = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const has_default = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const is_comptime = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const unused = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
+ fields[field_i] = .{};
- _ = unused;
+ if (has_type_body) {
+ fields[field_i].type_body_len = zir.extra[extra_index];
+ } else {
+ fields[field_i].type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ }
+ extra_index += 1;
- const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
- extra_index += 1;
- const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
- extra_index += 1;
+ // This string needs to outlive the ZIR code.
+ const field_name = try decl_arena_allocator.dupe(u8, field_name_zir);
- // doc_comment
- extra_index += 1;
+ const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
+ if (gop.found_existing) {
+ const msg = msg: {
+ const tree = try sema.getAstTree(&block_scope);
+ const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, field_i);
+ const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name});
+ errdefer msg.destroy(gpa);
- // This string needs to outlive the ZIR code.
- const field_name = try decl_arena_allocator.dupe(u8, field_name_zir);
- const field_ty: Type = if (field_type_ref == .none)
- Type.initTag(.noreturn)
- else
- // TODO: if we need to report an error here, use a source location
- // that points to this type expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- try sema.resolveType(&block_scope, src, field_type_ref);
+ const prev_field_index = struct_obj.fields.getIndex(field_name).?;
+ const prev_field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, prev_field_index);
+ try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
+ try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(&block_scope, msg);
+ }
+ gop.value_ptr.* = .{
+ .ty = Type.initTag(.noreturn),
+ .abi_align = 0,
+ .default_val = Value.initTag(.unreachable_value),
+ .is_comptime = is_comptime,
+ .offset = undefined,
+ };
+
+ if (has_align) {
+ fields[field_i].align_body_len = zir.extra[extra_index];
+ extra_index += 1;
+ }
+ if (has_init) {
+ fields[field_i].init_body_len = zir.extra[extra_index];
+ extra_index += 1;
+ any_inits = true;
+ }
+ }
+ }
+
+ // Next we do only types and alignments, saving the inits for a second pass,
+ // so that init values may depend on type layout.
+ const bodies_index = extra_index;
+ for (fields) |zir_field, i| {
// TODO emit compile errors for invalid field types
// such as arrays and pointers inside packed structs.
-
+ const field_ty: Type = ty: {
+ if (zir_field.type_ref != .none) {
+ // TODO: if we need to report an error here, use a source location
+ // that points to this type expression rather than the struct.
+ // But only resolve the source location if we need to emit a compile error.
+ break :ty try sema.resolveType(&block_scope, src, zir_field.type_ref);
+ }
+ assert(zir_field.type_body_len != 0);
+ const body = zir.extra[extra_index..][0..zir_field.type_body_len];
+ extra_index += body.len;
+ const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
+ break :ty try sema.analyzeAsType(&block_scope, src, ty_ref);
+ };
if (field_ty.tag() == .generic_poison) {
return error.GenericPoison;
}
- const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
- if (gop.found_existing) {
- const msg = msg: {
- const tree = try sema.getAstTree(&block_scope);
- const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, field_i);
- const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name});
- errdefer msg.destroy(gpa);
+ const field = &struct_obj.fields.values()[i];
+ field.ty = try field_ty.copy(decl_arena_allocator);
- const prev_field_index = struct_obj.fields.getIndex(field_name).?;
- const prev_field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, prev_field_index);
- try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
- try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(&block_scope, msg);
+ if (zir_field.align_body_len > 0) {
+ const body = zir.extra[extra_index..][0..zir_field.align_body_len];
+ extra_index += body.len;
+ const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
+ field.abi_align = try sema.analyzeAsAlign(&block_scope, src, align_ref);
}
- gop.value_ptr.* = .{
- .ty = try field_ty.copy(decl_arena_allocator),
- .abi_align = 0,
- .default_val = Value.initTag(.unreachable_value),
- .is_comptime = is_comptime,
- .offset = undefined,
- };
- if (has_align) {
- const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
- extra_index += 1;
- // TODO: if we need to report an error here, use a source location
- // that points to this alignment expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- gop.value_ptr.abi_align = try sema.resolveAlign(&block_scope, src, align_ref);
- }
- if (has_default) {
- const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
- extra_index += 1;
- const default_inst = try sema.resolveInst(default_ref);
- // TODO: if we need to report an error here, use a source location
- // that points to this default value expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- const default_val = (try sema.resolveMaybeUndefVal(&block_scope, src, default_inst)) orelse
- return sema.failWithNeededComptime(&block_scope, src);
- gop.value_ptr.default_val = try default_val.copy(decl_arena_allocator);
+ extra_index += zir_field.init_body_len;
+ }
+
+ struct_obj.status = .have_field_types;
+
+ if (any_inits) {
+ extra_index = bodies_index;
+ for (fields) |zir_field, i| {
+ extra_index += zir_field.type_body_len;
+ extra_index += zir_field.align_body_len;
+ if (zir_field.init_body_len > 0) {
+ const body = zir.extra[extra_index..][0..zir_field.init_body_len];
+ extra_index += body.len;
+ const init = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
+ const field = &struct_obj.fields.values()[i];
+ const coerced = try sema.coerce(&block_scope, field.ty, init, src);
+ const default_val = (try sema.resolveMaybeUndefVal(&block_scope, src, coerced)) orelse
+ return sema.failWithNeededComptime(&block_scope, src);
+ field.default_val = try default_val.copy(decl_arena_allocator);
+ }
}
}
+
+ struct_obj.have_field_inits = true;
}
fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void {
src/Zir.zig
@@ -3093,16 +3093,15 @@ pub const Inst = struct {
/// Trailing:
/// 0. src_node: i32, // if has_src_node
- /// 1. body_len: u32, // if has_body_len
- /// 2. fields_len: u32, // if has_fields_len
- /// 3. decls_len: u32, // if has_decls_len
- /// 4. decl_bits: u32 // for every 8 decls
+ /// 1. fields_len: u32, // if has_fields_len
+ /// 2. decls_len: u32, // if has_decls_len
+ /// 3. decl_bits: u32 // for every 8 decls
/// - sets of 4 bits:
/// 0b000X: whether corresponding decl is pub
/// 0b00X0: whether corresponding decl is exported
/// 0b0X00: whether corresponding decl has an align expression
/// 0bX000: whether corresponding decl has a linksection or an address space expression
- /// 5. decl: { // for every decls_len
+ /// 4. decl: { // for every decls_len
/// src_hash: [4]u32, // hash of source bytes
/// line: u32, // line number of decl, relative to parent
/// name: u32, // null terminated string index
@@ -3120,32 +3119,35 @@ pub const Inst = struct {
/// address_space: Ref,
/// }
/// }
- /// 6. inst: Index // for every body_len
- /// 7. flags: u32 // for every 8 fields
+ /// 5. flags: u32 // for every 8 fields
/// - sets of 4 bits:
/// 0b000X: whether corresponding field has an align expression
/// 0b00X0: whether corresponding field has a default expression
/// 0b0X00: whether corresponding field is comptime
- /// 0bX000: unused
- /// 8. fields: { // for every fields_len
+ /// 0bX000: whether corresponding field has a type expression
+ /// 6. fields: { // for every fields_len
/// field_name: u32,
- /// field_type: Ref,
- /// - if none, means `anytype`.
/// doc_comment: u32, // 0 if no doc comment
- /// align: Ref, // if corresponding bit is set
- /// default_value: Ref, // if corresponding bit is set
+ /// field_type: Ref, // if corresponding bit is not set. none means anytype.
+ /// field_type_body_len: u32, // if corresponding bit is set
+ /// align_body_len: u32, // if corresponding bit is set
+ /// init_body_len: u32, // if corresponding bit is set
+ /// }
+ /// 7. bodies: { // for every fields_len
+ /// field_type_body_inst: Inst, // for each field_type_body_len
+ /// align_body_inst: Inst, // for each align_body_len
+ /// init_body_inst: Inst, // for each init_body_len
/// }
pub const StructDecl = struct {
pub const Small = packed struct {
has_src_node: bool,
- has_body_len: bool,
has_fields_len: bool,
has_decls_len: bool,
known_non_opv: bool,
known_comptime_only: bool,
name_strategy: NameStrategy,
layout: std.builtin.Type.ContainerLayout,
- _: u6 = undefined,
+ _: u7 = undefined,
};
};
@@ -3594,7 +3596,6 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {
const small = @bitCast(Inst.StructDecl.Small, extended.small);
var extra_index: usize = extended.operand;
extra_index += @boolToInt(small.has_src_node);
- extra_index += @boolToInt(small.has_body_len);
extra_index += @boolToInt(small.has_fields_len);
const decls_len = if (small.has_decls_len) decls_len: {
const decls_len = zir.extra[extra_index];
test/behavior/struct.zig
@@ -1358,3 +1358,17 @@ test "store to comptime field" {
s.a.a = 1;
}
}
+
+test "struct field init value is size of the struct" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+ const namespace = struct {
+ const S = extern struct {
+ size: u8 = @sizeOf(S),
+ blah: u16,
+ };
+ };
+ var s: namespace.S = .{ .blah = 1234 };
+ try expect(s.size == 4);
+}