From 147deb4324eb685d53922773ebaa09f322604dec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Jul 2022 22:34:03 -0700 Subject: [PATCH] stage2: lower each struct field type, align, init separately Previously, struct types, alignment values, and initialization expressions were all lowered into the same ZIR body, which caused false positive "depends on itself" errors when the initialization expression depended on the size of the struct. This also uses ResultLoc.coerced_ty for struct field alignment and initialization values. The resulting ZIR encoding ends up being roughly the same, neither smaller nor larger than previously. Closes #12029 --- src/AstGen.zig | 83 ++++++++---- src/Module.zig | 3 +- src/Sema.zig | 263 +++++++++++++++++++++++---------------- src/Zir.zig | 33 ++--- src/print_zir.zig | 139 +++++++++++++-------- test/behavior/struct.zig | 14 +++ 6 files changed, 331 insertions(+), 204 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index fd38ffbbdaa3..7220fe758dec 100644 --- a/src/AstGen.zig +++ b/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, diff --git a/src/Module.zig b/src/Module.zig index f9cfc5e54e63..67fc0ca619dd 100644 --- a/src/Module.zig +++ b/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, diff --git a/src/Sema.zig b/src/Sema.zig index 550f51d7c5e7..b139c3f89e9f 100644 --- a/src/Sema.zig +++ b/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 { diff --git a/src/Zir.zig b/src/Zir.zig index b2dde0df573e..b3126ea2f305 100644 --- a/src/Zir.zig +++ b/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]; diff --git a/src/print_zir.zig b/src/print_zir.zig index f2a11e20bd6c..8df8eaae07af 100644 --- a/src/print_zir.zig +++ b/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"); } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 624f1609d4ff..709c73807b0a 100644 --- a/test/behavior/struct.zig +++ b/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); +}