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); +}