authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-10-21 16:49:30-04:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-11-07 00:49:35+00:00
logf10499be0a16ec58d98387b49189401f2af2094f
tree1dee8acd698e9bb489a9926004d4cdf16f7ce72d
parent234693bcbba6f55ff6e975ddbedf0fad4dfaa8f1
signature Commit is signed but in an unrecognized format.

sema: analyze field init bodies in a second pass

This change allows struct field inits to use layout information of their own struct without causing a circular dependency. `semaStructFields` caches the ranges of the init bodies in the `StructType` trailing data. The init bodies are then resolved by `resolveStructFieldInits`, which is called before the inits are actually required. Within the init bodies, the struct decl's instruction is repurposed to refer to the field type itself. This is to allow us to easily rebuild the inst_map mapping required for the init body instructions to refer to the field type. Thanks to @mlugg for the guidance on this one!

8 files changed, 530 insertions(+), 57 deletions(-)

src/AstGen.zig+4-1
......@@ -4951,7 +4951,10 @@ fn structDeclInner(
49514951
49524952 if (have_value) {
49534953 any_default_inits = true;
4954 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = field_type } };
4954
4955 // The decl_inst is used as here so that we can easily reconstruct a mapping
4956 // between it and the field type when the fields inits are analzyed.
4957 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } };
49554958
49564959 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
49574960 if (!block_scope.endsWithNoReturn()) {
src/Autodoc.zig+5
......@@ -3808,6 +3808,11 @@ fn walkInstruction(
38083808 call_ctx,
38093809 );
38103810
3811 // Inside field init bodies, the struct decl instruction is used to refer to the
3812 // field type during the second pass of analysis.
3813 try self.repurposed_insts.put(self.arena, inst, {});
3814 defer _ = self.repurposed_insts.remove(inst);
3815
38113816 var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{};
38123817 var field_default_refs: std.ArrayListUnmanaged(?DocData.Expr) = .{};
38133818 var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
src/InternPool.zig+72-2
......@@ -463,6 +463,7 @@ pub const Key = union(enum) {
463463
464464 pub fn fieldInit(s: @This(), ip: *const InternPool, i: usize) Index {
465465 if (s.field_inits.len == 0) return .none;
466 assert(s.haveFieldInits(ip));
466467 return s.field_inits.get(ip)[i];
467468 }
468469
......@@ -497,6 +498,14 @@ pub const Key = union(enum) {
497498 return @ptrCast(&ip.extra.items[self.extra_index + flags_field_index]);
498499 }
499500
501 /// The returned pointer expires with any addition to the `InternPool`.
502 /// Asserts that the struct is packed.
503 pub fn packedFlagsPtr(self: @This(), ip: *const InternPool) *Tag.TypeStructPacked.Flags {
504 assert(self.layout == .Packed);
505 const flags_field_index = std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?;
506 return @ptrCast(&ip.extra.items[self.extra_index + flags_field_index]);
507 }
508
500509 pub fn assumeRuntimeBitsIfFieldTypesWip(s: @This(), ip: *InternPool) bool {
501510 if (s.layout == .Packed) return false;
502511 const flags_ptr = s.flagsPtr(ip);
......@@ -546,6 +555,30 @@ pub const Key = union(enum) {
546555 s.flagsPtr(ip).alignment_wip = false;
547556 }
548557
558 pub fn setInitsWip(s: @This(), ip: *InternPool) bool {
559 switch (s.layout) {
560 .Packed => {
561 const flag = &s.packedFlagsPtr(ip).field_inits_wip;
562 if (flag.*) return true;
563 flag.* = true;
564 return false;
565 },
566 .Auto, .Extern => {
567 const flag = &s.flagsPtr(ip).field_inits_wip;
568 if (flag.*) return true;
569 flag.* = true;
570 return false;
571 },
572 }
573 }
574
575 pub fn clearInitsWip(s: @This(), ip: *InternPool) void {
576 switch (s.layout) {
577 .Packed => s.packedFlagsPtr(ip).field_inits_wip = false,
578 .Auto, .Extern => s.flagsPtr(ip).field_inits_wip = false,
579 }
580 }
581
549582 pub fn setFullyResolved(s: @This(), ip: *InternPool) bool {
550583 if (s.layout == .Packed) return true;
551584 const flags_ptr = s.flagsPtr(ip);
......@@ -588,6 +621,20 @@ pub const Key = union(enum) {
588621 return types.len == 0 or types[0] != .none;
589622 }
590623
624 pub fn haveFieldInits(s: @This(), ip: *const InternPool) bool {
625 return switch (s.layout) {
626 .Packed => s.packedFlagsPtr(ip).inits_resolved,
627 .Auto, .Extern => s.flagsPtr(ip).inits_resolved,
628 };
629 }
630
631 pub fn setHaveFieldInits(s: @This(), ip: *InternPool) void {
632 switch (s.layout) {
633 .Packed => s.packedFlagsPtr(ip).inits_resolved = true,
634 .Auto, .Extern => s.flagsPtr(ip).inits_resolved = true,
635 }
636 }
637
591638 pub fn haveLayout(s: @This(), ip: *InternPool) bool {
592639 return switch (s.layout) {
593640 .Packed => s.backingIntType(ip).* != .none,
......@@ -3000,6 +3047,14 @@ pub const Tag = enum(u8) {
30003047 namespace: Module.Namespace.OptionalIndex,
30013048 backing_int_ty: Index,
30023049 names_map: MapIndex,
3050 flags: Flags,
3051
3052 pub const Flags = packed struct(u32) {
3053 /// Dependency loop detection when resolving field inits.
3054 field_inits_wip: bool,
3055 inits_resolved: bool,
3056 _: u30 = 0,
3057 };
30033058 };
30043059
30053060 /// At first I thought of storing the denormalized data externally, such as...
......@@ -3045,6 +3100,7 @@ pub const Tag = enum(u8) {
30453100 requires_comptime: RequiresComptime,
30463101 is_tuple: bool,
30473102 assumed_runtime_bits: bool,
3103 assumed_pointer_aligned: bool,
30483104 has_namespace: bool,
30493105 any_comptime_fields: bool,
30503106 any_default_inits: bool,
......@@ -3057,14 +3113,18 @@ pub const Tag = enum(u8) {
30573113 field_types_wip: bool,
30583114 /// Dependency loop detection when resolving struct layout.
30593115 layout_wip: bool,
3060 /// Determines whether `size`, `alignment`, runtime field order, and
3116 /// Indicates whether `size`, `alignment`, runtime field order, and
30613117 /// field offets are populated.
30623118 layout_resolved: bool,
3119 /// Dependency loop detection when resolving field inits.
3120 field_inits_wip: bool,
3121 /// Indicates whether `field_inits` has been resolved.
3122 inits_resolved: bool,
30633123 // The types and all its fields have had their layout resolved. Even through pointer,
30643124 // which `layout_resolved` does not ensure.
30653125 fully_resolved: bool,
30663126
3067 _: u11 = 0,
3127 _: u8 = 0,
30683128 };
30693129 };
30703130};
......@@ -5347,6 +5407,7 @@ pub const StructTypeInit = struct {
53475407 is_tuple: bool,
53485408 any_comptime_fields: bool,
53495409 any_default_inits: bool,
5410 inits_resolved: bool,
53505411 any_aligned_fields: bool,
53515412};
53525413
......@@ -5399,6 +5460,10 @@ pub fn getStructType(
53995460 .namespace = ini.namespace,
54005461 .backing_int_ty = .none,
54015462 .names_map = names_map,
5463 .flags = .{
5464 .field_inits_wip = false,
5465 .inits_resolved = ini.inits_resolved,
5466 },
54025467 }),
54035468 });
54045469 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
......@@ -5431,6 +5496,7 @@ pub fn getStructType(
54315496 .requires_comptime = ini.requires_comptime,
54325497 .is_tuple = ini.is_tuple,
54335498 .assumed_runtime_bits = false,
5499 .assumed_pointer_aligned = false,
54345500 .has_namespace = ini.namespace != .none,
54355501 .any_comptime_fields = ini.any_comptime_fields,
54365502 .any_default_inits = ini.any_default_inits,
......@@ -5440,6 +5506,8 @@ pub fn getStructType(
54405506 .field_types_wip = false,
54415507 .layout_wip = false,
54425508 .layout_resolved = false,
5509 .field_inits_wip = false,
5510 .inits_resolved = ini.inits_resolved,
54435511 .fully_resolved = false,
54445512 },
54455513 }),
......@@ -6451,6 +6519,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
64516519 Tag.TypePointer.PackedOffset,
64526520 Tag.TypeUnion.Flags,
64536521 Tag.TypeStruct.Flags,
6522 Tag.TypeStructPacked.Flags,
64546523 Tag.Variable.Flags,
64556524 => @bitCast(@field(extra, field.name)),
64566525
......@@ -6525,6 +6594,7 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct
65256594 Tag.TypePointer.PackedOffset,
65266595 Tag.TypeUnion.Flags,
65276596 Tag.TypeStruct.Flags,
6597 Tag.TypeStructPacked.Flags,
65286598 Tag.Variable.Flags,
65296599 FuncAnalysis,
65306600 => @bitCast(int32),
src/Sema.zig+239-54
......@@ -2699,6 +2699,7 @@ pub fn getStructType(
26992699 .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
27002700 .any_default_inits = small.any_default_inits,
27012701 .any_comptime_fields = small.any_comptime_fields,
2702 .inits_resolved = false,
27022703 .any_aligned_fields = small.any_aligned_fields,
27032704 });
27042705
......@@ -4718,6 +4719,7 @@ fn validateStructInit(
47184719 const i: u32 = @intCast(i_usize);
47194720 if (field_ptr != .none) continue;
47204721
4722 try sema.resolveStructFieldInits(struct_ty);
47214723 const default_val = struct_ty.structFieldDefaultValue(i, mod);
47224724 if (default_val.toIntern() == .unreachable_value) {
47234725 const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse {
......@@ -4773,6 +4775,8 @@ fn validateStructInit(
47734775 const air_tags = sema.air_instructions.items(.tag);
47744776 const air_datas = sema.air_instructions.items(.data);
47754777
4778 try sema.resolveStructFieldInits(struct_ty);
4779
47764780 // We collect the comptime field values in case the struct initialization
47774781 // ends up being comptime-known.
47784782 const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod));
......@@ -17630,6 +17634,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1763017634 };
1763117635 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
1763217636
17637 try sema.resolveStructFieldInits(ty);
17638
1763317639 for (struct_field_vals, 0..) |*field_val, i| {
1763417640 // TODO: write something like getCoercedInts to avoid needing to dupe
1763517641 const name = if (struct_type.fieldName(ip, i).unwrap()) |name_nts|
......@@ -19205,17 +19211,20 @@ fn zirStructInit(
1920519211 const uncoerced_init = try sema.resolveInst(item.data.init);
1920619212 const field_ty = resolved_ty.structFieldType(field_index, mod);
1920719213 field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);
19208 if (!is_packed) if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| {
19209 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
19210 return sema.failWithNeededComptime(block, field_src, .{
19211 .needed_comptime_reason = "value stored in comptime field must be comptime-known",
19212 });
19213 };
19214 if (!is_packed) {
19215 try sema.resolveStructFieldInits(resolved_ty);
19216 if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| {
19217 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
19218 return sema.failWithNeededComptime(block, field_src, .{
19219 .needed_comptime_reason = "value stored in comptime field must be comptime-known",
19220 });
19221 };
1921419222
19215 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) {
19216 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
19223 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) {
19224 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
19225 }
1921719226 }
19218 };
19227 }
1921919228 }
1922019229
1922119230 return sema.finishStructInit(block, src, src, field_inits, resolved_ty, result_ty, is_ref);
......@@ -19368,6 +19377,8 @@ fn finishStructInit(
1936819377 continue;
1936919378 }
1937019379
19380 try sema.resolveStructFieldInits(struct_ty);
19381
1937119382 const field_init = struct_type.fieldInit(ip, i);
1937219383 if (field_init == .none) {
1937319384 const field_name = struct_type.field_names.get(ip)[i];
......@@ -21132,6 +21143,7 @@ fn reifyStruct(
2113221143 // struct types.
2113321144 .any_comptime_fields = true,
2113421145 .any_default_inits = true,
21146 .inits_resolved = true,
2113521147 .any_aligned_fields = true,
2113621148 });
2113721149 // TODO: figure out InternPool removals for incremental compilation
......@@ -26632,6 +26644,7 @@ fn finishFieldCallBind(
2663226644
2663326645 const container_ty = ptr_ty.childType(mod);
2663426646 if (container_ty.zigTypeTag(mod) == .Struct) {
26647 try sema.resolveStructFieldInits(container_ty);
2663526648 if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| {
2663626649 return .{ .direct = Air.internedToRef(default_val.toIntern()) };
2663726650 }
......@@ -26847,6 +26860,7 @@ fn structFieldPtrByIndex(
2684726860 const ptr_field_ty = try sema.ptrType(ptr_ty_data);
2684826861
2684926862 if (struct_type.fieldIsComptime(ip, field_index)) {
26863 try sema.resolveStructFieldInits(struct_ty);
2685026864 const val = try mod.intern(.{ .ptr = .{
2685126865 .ty = ptr_field_ty.toIntern(),
2685226866 .addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },
......@@ -26883,6 +26897,7 @@ fn structFieldVal(
2688326897 assert(struct_ty.zigTypeTag(mod) == .Struct);
2688426898
2688526899 try sema.resolveTypeFields(struct_ty);
26900
2688626901 switch (ip.indexToKey(struct_ty.toIntern())) {
2688726902 .struct_type => |struct_type| {
2688826903 if (struct_type.isTuple(ip))
......@@ -26891,6 +26906,7 @@ fn structFieldVal(
2689126906 const field_index = struct_type.nameIndex(ip, field_name) orelse
2689226907 return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name);
2689326908 if (struct_type.fieldIsComptime(ip, field_index)) {
26909 try sema.resolveStructFieldInits(struct_ty);
2689426910 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);
2689526911 }
2689626912
......@@ -31282,6 +31298,7 @@ fn coerceTupleToStruct(
3128231298 const mod = sema.mod;
3128331299 const ip = &mod.intern_pool;
3128431300 try sema.resolveTypeFields(struct_ty);
31301 try sema.resolveStructFieldInits(struct_ty);
3128531302
3128631303 if (struct_ty.isTupleOrAnonStruct(mod)) {
3128731304 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);
......@@ -34264,6 +34281,8 @@ fn resolvePeerTypesInner(
3426434281 var comptime_val: ?Value = null;
3426534282 for (peer_tys) |opt_ty| {
3426634283 const struct_ty = opt_ty orelse continue;
34284 try sema.resolveStructFieldInits(struct_ty);
34285
3426734286 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {
3426834287 comptime_val = null;
3426934288 break;
......@@ -34605,8 +34624,7 @@ pub fn resolveStructAlignment(
3460534624 // We'll guess "pointer-aligned", if the struct has an
3460634625 // underaligned pointer field then some allocations
3460734626 // might require explicit alignment.
34608 //TODO write this bit and emit an error later if incorrect
34609 //struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34627 struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
3461034628 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
3461134629 struct_type.flagsPtr(ip).alignment = result;
3461234630 return result;
......@@ -34618,8 +34636,7 @@ pub fn resolveStructAlignment(
3461834636 // We'll guess "pointer-aligned", if the struct has an
3461934637 // underaligned pointer field then some allocations
3462034638 // might require explicit alignment.
34621 //TODO write this bit and emit an error later if incorrect
34622 //struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34639 struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
3462334640 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
3462434641 struct_type.flagsPtr(ip).alignment = result;
3462534642 return result;
......@@ -34710,6 +34727,18 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
3471034727 return sema.failWithOwnedErrorMsg(null, msg);
3471134728 }
3471234729
34730 if (struct_type.flagsPtr(ip).assumed_pointer_aligned and
34731 big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(mod.getTarget().ptrBitWidth(), 8))))
34732 {
34733 const msg = try Module.ErrorMsg.create(
34734 sema.gpa,
34735 mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod),
34736 "struct layout depends on being pointer aligned",
34737 .{},
34738 );
34739 return sema.failWithOwnedErrorMsg(null, msg);
34740 }
34741
3471334742 if (struct_type.hasReorderedFields()) {
3471434743 const runtime_order = struct_type.runtime_order.get(ip);
3471534744
......@@ -35329,6 +35358,32 @@ pub fn resolveTypeFieldsStruct(
3532935358 try semaStructFields(mod, sema.arena, struct_type);
3533035359}
3533135360
35361pub fn resolveStructFieldInits(sema: *Sema, ty: Type) CompileError!void {
35362 const mod = sema.mod;
35363 const ip = &mod.intern_pool;
35364 const struct_type = mod.typeToStruct(ty) orelse return;
35365 const owner_decl = struct_type.decl.unwrap() orelse return;
35366
35367 // Inits can start as resolved
35368 if (struct_type.haveFieldInits(ip)) return;
35369
35370 try sema.resolveStructLayout(ty);
35371
35372 if (struct_type.setInitsWip(ip)) {
35373 const msg = try Module.ErrorMsg.create(
35374 sema.gpa,
35375 mod.declPtr(owner_decl).srcLoc(mod),
35376 "struct '{}' depends on itself",
35377 .{ty.fmt(mod)},
35378 );
35379 return sema.failWithOwnedErrorMsg(null, msg);
35380 }
35381 defer struct_type.clearInitsWip(ip);
35382
35383 try semaStructFieldInits(mod, sema.arena, struct_type);
35384 struct_type.setHaveFieldInits(ip);
35385}
35386
3533235387pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void {
3533335388 const mod = sema.mod;
3533435389 const ip = &mod.intern_pool;
......@@ -35510,24 +35565,18 @@ fn resolveInferredErrorSetTy(
3551035565 }
3551135566}
3551235567
35513fn semaStructFields(
35514 mod: *Module,
35515 arena: Allocator,
35516 struct_type: InternPool.Key.StructType,
35517) CompileError!void {
35518 const gpa = mod.gpa;
35519 const ip = &mod.intern_pool;
35520 const decl_index = struct_type.decl.unwrap() orelse return;
35521 const decl = mod.declPtr(decl_index);
35522 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
35523 const zir = mod.namespacePtr(namespace_index).file_scope.zir;
35524 const zir_index = struct_type.zir_index;
35568fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct {
35569 /// fields_len
35570 usize,
35571 Zir.Inst.StructDecl.Small,
35572 /// extra_index
35573 usize,
35574} {
3552535575 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
3552635576 assert(extended.opcode == .struct_decl);
3552735577 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
3552835578 var extra_index: usize = extended.operand;
3552935579
35530 const src = LazySrcLoc.nodeOffset(0);
3553135580 extra_index += @intFromBool(small.has_src_node);
3553235581
3553335582 const fields_len = if (small.has_fields_len) blk: {
......@@ -35558,6 +35607,25 @@ fn semaStructFields(
3555835607 while (decls_it.next()) |_| {}
3555935608 extra_index = decls_it.extra_index;
3556035609
35610 return .{ fields_len, small, extra_index };
35611}
35612
35613fn semaStructFields(
35614 mod: *Module,
35615 arena: Allocator,
35616 struct_type: InternPool.Key.StructType,
35617) CompileError!void {
35618 const gpa = mod.gpa;
35619 const ip = &mod.intern_pool;
35620 const decl_index = struct_type.decl.unwrap() orelse return;
35621 const decl = mod.declPtr(decl_index);
35622 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
35623 const zir = mod.namespacePtr(namespace_index).file_scope.zir;
35624 const zir_index = struct_type.zir_index;
35625
35626 const src = LazySrcLoc.nodeOffset(0);
35627 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
35628
3556135629 if (fields_len == 0) switch (struct_type.layout) {
3556235630 .Packed => {
3556335631 try semaBackingIntType(mod, struct_type);
......@@ -35685,7 +35753,6 @@ fn semaStructFields(
3568535753
3568635754 // Next we do only types and alignments, saving the inits for a second pass,
3568735755 // so that init values may depend on type layout.
35688 const bodies_index = extra_index;
3568935756
3569035757 for (fields, 0..) |zir_field, field_i| {
3569135758 const field_ty: Type = ty: {
......@@ -35809,44 +35876,161 @@ fn semaStructFields(
3580935876 extra_index += zir_field.init_body_len;
3581035877 }
3581135878
35812 // TODO: there seems to be no mechanism to catch when an init depends on
35813 // another init that hasn't been resolved.
35879 struct_type.clearTypesWip(ip);
35880 if (!any_inits) struct_type.setHaveFieldInits(ip);
35881
35882 for (comptime_mutable_decls.items) |ct_decl_index| {
35883 const ct_decl = mod.declPtr(ct_decl_index);
35884 _ = try ct_decl.internValue(mod);
35885 }
35886}
35887
35888// This logic must be kept in sync with `semaStructFields`
35889fn semaStructFieldInits(
35890 mod: *Module,
35891 arena: Allocator,
35892 struct_type: InternPool.Key.StructType,
35893) CompileError!void {
35894 const gpa = mod.gpa;
35895 const ip = &mod.intern_pool;
35896
35897 assert(!struct_type.haveFieldInits(ip));
35898
35899 const decl_index = struct_type.decl.unwrap() orelse return;
35900 const decl = mod.declPtr(decl_index);
35901 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
35902 const zir = mod.namespacePtr(namespace_index).file_scope.zir;
35903 const zir_index = struct_type.zir_index;
35904 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
35905
35906 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
35907 defer comptime_mutable_decls.deinit();
35908
35909 var sema: Sema = .{
35910 .mod = mod,
35911 .gpa = gpa,
35912 .arena = arena,
35913 .code = zir,
35914 .owner_decl = decl,
35915 .owner_decl_index = decl_index,
35916 .func_index = .none,
35917 .func_is_naked = false,
35918 .fn_ret_ty = Type.void,
35919 .fn_ret_ty_ies = null,
35920 .owner_func_index = .none,
35921 .comptime_mutable_decls = &comptime_mutable_decls,
35922 };
35923 defer sema.deinit();
35924
35925 var block_scope: Block = .{
35926 .parent = null,
35927 .sema = &sema,
35928 .src_decl = decl_index,
35929 .namespace = namespace_index,
35930 .wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
35931 .instructions = .{},
35932 .inlining = null,
35933 .is_comptime = true,
35934 };
35935 defer assert(block_scope.instructions.items.len == 0);
35936
35937 const Field = struct {
35938 type_body_len: u32 = 0,
35939 align_body_len: u32 = 0,
35940 init_body_len: u32 = 0,
35941 };
35942 const fields = try sema.arena.alloc(Field, fields_len);
35943
35944 var any_inits = false;
35945
35946 {
35947 const bits_per_field = 4;
35948 const fields_per_u32 = 32 / bits_per_field;
35949 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
35950 const flags_index = extra_index;
35951 var bit_bag_index: usize = flags_index;
35952 extra_index += bit_bags_count;
35953 var cur_bit_bag: u32 = undefined;
35954 var field_i: u32 = 0;
35955 while (field_i < fields_len) : (field_i += 1) {
35956 if (field_i % fields_per_u32 == 0) {
35957 cur_bit_bag = zir.extra[bit_bag_index];
35958 bit_bag_index += 1;
35959 }
35960 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
35961 cur_bit_bag >>= 1;
35962 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
35963 cur_bit_bag >>= 2;
35964 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
35965 cur_bit_bag >>= 1;
35966
35967 if (!small.is_tuple) {
35968 extra_index += 1;
35969 }
35970 extra_index += 1; // doc_comment
35971
35972 fields[field_i] = .{};
35973
35974 if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index];
35975 extra_index += 1;
35976
35977 if (has_align) {
35978 fields[field_i].align_body_len = zir.extra[extra_index];
35979 extra_index += 1;
35980 }
35981 if (has_init) {
35982 fields[field_i].init_body_len = zir.extra[extra_index];
35983 extra_index += 1;
35984 any_inits = true;
35985 }
35986 }
35987 }
3581435988
3581535989 if (any_inits) {
35816 extra_index = bodies_index;
3581735990 for (fields, 0..) |zir_field, field_i| {
35818 const field_ty = struct_type.field_types.get(ip)[field_i].toType();
3581935991 extra_index += zir_field.type_body_len;
3582035992 extra_index += zir_field.align_body_len;
35821 if (zir_field.init_body_len > 0) {
35822 const body = zir.bodySlice(extra_index, zir_field.init_body_len);
35823 extra_index += body.len;
35824 const init = try sema.resolveBody(&block_scope, body, zir_index);
35825 const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {
35826 error.NeededSourceLocation => {
35827 const init_src = mod.fieldSrcLoc(decl_index, .{
35828 .index = field_i,
35829 .range = .value,
35830 }).lazy;
35831 _ = try sema.coerce(&block_scope, field_ty, init, init_src);
35832 unreachable;
35833 },
35834 else => |e| return e,
35835 };
35836 const default_val = (try sema.resolveValue(coerced)) orelse {
35993 const body = zir.bodySlice(extra_index, zir_field.init_body_len);
35994 extra_index += zir_field.init_body_len;
35995
35996 if (body.len == 0) continue;
35997
35998 // Pre-populate the type mapping the body expects to be there.
35999 // In init bodies, the zir index of the struct itself is used
36000 // to refer to the current field type.
36001
36002 const field_ty = struct_type.field_types.get(ip)[field_i].toType();
36003 const type_ref = Air.internedToRef(field_ty.toIntern());
36004 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index});
36005 sema.inst_map.putAssumeCapacity(zir_index, type_ref);
36006
36007 const init = try sema.resolveBody(&block_scope, body, zir_index);
36008 const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {
36009 error.NeededSourceLocation => {
3583736010 const init_src = mod.fieldSrcLoc(decl_index, .{
3583836011 .index = field_i,
3583936012 .range = .value,
3584036013 }).lazy;
35841 return sema.failWithNeededComptime(&block_scope, init_src, .{
35842 .needed_comptime_reason = "struct field default value must be comptime-known",
35843 });
35844 };
35845 const field_init = try default_val.intern(field_ty, mod);
35846 struct_type.field_inits.get(ip)[field_i] = field_init;
35847 }
36014 _ = try sema.coerce(&block_scope, field_ty, init, init_src);
36015 unreachable;
36016 },
36017 else => |e| return e,
36018 };
36019 const default_val = (try sema.resolveValue(coerced)) orelse {
36020 const init_src = mod.fieldSrcLoc(decl_index, .{
36021 .index = field_i,
36022 .range = .value,
36023 }).lazy;
36024 return sema.failWithNeededComptime(&block_scope, init_src, .{
36025 .needed_comptime_reason = "struct field default value must be comptime-known",
36026 });
36027 };
36028
36029 const field_init = try default_val.intern(field_ty, mod);
36030 struct_type.field_inits.get(ip)[field_i] = field_init;
3584836031 }
3584936032 }
36033
3585036034 for (comptime_mutable_decls.items) |ct_decl_index| {
3585136035 const ct_decl = mod.declPtr(ct_decl_index);
3585236036 _ = try ct_decl.internValue(mod);
......@@ -36674,6 +36858,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
3667436858 );
3667536859 for (field_vals, 0..) |*field_val, i| {
3667636860 if (struct_type.fieldIsComptime(ip, i)) {
36861 try sema.resolveStructFieldInits(ty);
3667736862 field_val.* = struct_type.field_inits.get(ip)[i];
3667836863 continue;
3667936864 }
src/type.zig+2
......@@ -2415,6 +2415,7 @@ pub const Type = struct {
24152415 for (field_vals, 0..) |*field_val, i_usize| {
24162416 const i: u32 = @intCast(i_usize);
24172417 if (struct_type.fieldIsComptime(ip, i)) {
2418 assert(struct_type.haveFieldInits(ip));
24182419 field_val.* = struct_type.field_inits.get(ip)[i];
24192420 continue;
24202421 }
......@@ -3014,6 +3015,7 @@ pub const Type = struct {
30143015 const ip = &mod.intern_pool;
30153016 switch (ip.indexToKey(ty.toIntern())) {
30163017 .struct_type => |struct_type| {
3018 assert(struct_type.haveFieldInits(ip));
30173019 if (struct_type.fieldIsComptime(ip, index)) {
30183020 return struct_type.field_inits.get(ip)[index].toValue();
30193021 } else {
test/behavior/struct.zig+57
......@@ -1785,3 +1785,60 @@ test "comptimeness of optional and error union payload is analyzed properly" {
17851785 const x = (try c).?.x;
17861786 try std.testing.expectEqual(3, x);
17871787}
1788
1789test "initializer uses own alignment" {
1790 const S = struct {
1791 x: u32 = @alignOf(@This()) + 1,
1792 };
1793
1794 var s: S = .{};
1795 try expectEqual(4, @alignOf(S));
1796 try expectEqual(@as(usize, 5), s.x);
1797}
1798
1799test "initializer uses own size" {
1800 const S = struct {
1801 x: u32 = @sizeOf(@This()) + 1,
1802 };
1803
1804 var s: S = .{};
1805 try expectEqual(4, @sizeOf(S));
1806 try expectEqual(@as(usize, 5), s.x);
1807}
1808
1809test "initializer takes a pointer to a variable inside its struct" {
1810 const namespace = struct {
1811 const S = struct {
1812 s: *S = &S.instance,
1813 var instance: S = undefined;
1814 };
1815
1816 fn doTheTest() !void {
1817 var foo: S = .{};
1818 try expectEqual(&S.instance, foo.s);
1819 }
1820 };
1821
1822 try namespace.doTheTest();
1823 comptime try namespace.doTheTest();
1824}
1825
1826test "circular dependency through pointer field of a struct" {
1827 const S = struct {
1828 const StructInner = extern struct {
1829 outer: StructOuter = std.mem.zeroes(StructOuter),
1830 };
1831
1832 const StructMiddle = extern struct {
1833 outer: ?*StructInner,
1834 inner: ?*StructOuter,
1835 };
1836
1837 const StructOuter = extern struct {
1838 middle: StructMiddle = std.mem.zeroes(StructMiddle),
1839 };
1840 };
1841 var outer: S.StructOuter = .{};
1842 try expect(outer.middle.outer == null);
1843 try expect(outer.middle.inner == null);
1844}
test/behavior/union.zig+140
......@@ -1869,6 +1869,126 @@ test "reinterpret packed union inside packed struct" {
18691869 try S.doTheTest();
18701870}
18711871
1872test "inner struct initializer uses union layout" {
1873 const namespace = struct {
1874 const U = union {
1875 a: struct {
1876 x: u32 = @alignOf(U) + 1,
1877 },
1878 b: struct {
1879 y: u16 = @sizeOf(U) + 2,
1880 },
1881 };
1882 };
1883
1884 {
1885 const u: namespace.U = .{ .a = .{} };
1886 try expectEqual(4, @alignOf(namespace.U));
1887 try expectEqual(@as(usize, 5), u.a.x);
1888 }
1889
1890 {
1891 const u: namespace.U = .{ .b = .{} };
1892 try expectEqual(@as(usize, @sizeOf(namespace.U) + 2), u.b.y);
1893 }
1894}
1895
1896test "inner struct initializer uses packed union layout" {
1897 const namespace = struct {
1898 const U = packed union {
1899 a: packed struct {
1900 x: u32 = @alignOf(U) + 1,
1901 },
1902 b: packed struct {
1903 y: u16 = @sizeOf(U) + 2,
1904 },
1905 };
1906 };
1907
1908 {
1909 const u: namespace.U = .{ .a = .{} };
1910 try expectEqual(4, @alignOf(namespace.U));
1911 try expectEqual(@as(usize, 5), u.a.x);
1912 }
1913
1914 {
1915 const u: namespace.U = .{ .b = .{} };
1916 try expectEqual(@as(usize, @sizeOf(namespace.U) + 2), u.b.y);
1917 }
1918}
1919
1920test "extern union initialized via reintepreted struct field initializer" {
1921 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
1922
1923 const U = extern union {
1924 a: u32,
1925 b: u8,
1926 };
1927
1928 const S = extern struct {
1929 u: U = std.mem.bytesAsValue(U, &bytes).*,
1930 };
1931
1932 const s: S = .{};
1933 try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa));
1934 try expect(s.u.b == 0xaa);
1935}
1936
1937test "packed union initialized via reintepreted struct field initializer" {
1938 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
1939
1940 const U = packed union {
1941 a: u32,
1942 b: u8,
1943 };
1944
1945 const S = packed struct {
1946 u: U = std.mem.bytesAsValue(U, &bytes).*,
1947 };
1948
1949 var s: S = .{};
1950 try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa));
1951 try expect(s.u.b == if (endian == .little) 0xaa else 0xdd);
1952}
1953
1954test "store of comptime reinterpreted memory to extern union" {
1955 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
1956
1957 const U = extern union {
1958 a: u32,
1959 b: u8,
1960 };
1961
1962 const reinterpreted = comptime b: {
1963 var u: U = undefined;
1964 u = std.mem.bytesAsValue(U, &bytes).*;
1965 break :b u;
1966 };
1967
1968 var u: U = reinterpreted;
1969 try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa));
1970 try expect(u.b == 0xaa);
1971}
1972
1973test "store of comptime reinterpreted memory to packed union" {
1974 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
1975
1976 const U = packed union {
1977 a: u32,
1978 b: u8,
1979 };
1980
1981 const reinterpreted = comptime b: {
1982 var u: U = undefined;
1983 u = std.mem.bytesAsValue(U, &bytes).*;
1984 break :b u;
1985 };
1986
1987 var u: U = reinterpreted;
1988 try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa));
1989 try expect(u.b == if (endian == .little) 0xaa else 0xdd);
1990}
1991
18721992test "union field is a pointer to an aligned version of itself" {
18731993 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
18741994 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
......@@ -1902,3 +2022,23 @@ test "pass register-sized field as non-register-sized union" {
19022022 try S.untaggedUnion(.{ .x = x });
19032023 try S.externUnion(.{ .x = x });
19042024}
2025
2026test "circular dependency through pointer field of a union" {
2027 const S = struct {
2028 const UnionInner = extern struct {
2029 outer: UnionOuter = std.mem.zeroes(UnionOuter),
2030 };
2031
2032 const UnionMiddle = extern union {
2033 outer: ?*UnionOuter,
2034 inner: ?*UnionInner,
2035 };
2036
2037 const UnionOuter = extern struct {
2038 u: UnionMiddle = std.mem.zeroes(UnionMiddle),
2039 };
2040 };
2041 var outer: S.UnionOuter = .{};
2042 try expect(outer.u.outer == null);
2043 try expect(outer.u.inner == null);
2044}
test/cases/compile_errors/struct_depends_on_pointer_alignment.zig created+11
......@@ -0,0 +1,11 @@
1const S = struct {
2 next: ?*align(1) S align(128),
3};
4
5export fn entry() usize {
6 return @alignOf(S);
7}
8
9// error
10//
11// :1:11: error: struct layout depends on being pointer aligned