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
signaturelock-open 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(...@@ -4951,7 +4951,10 @@ fn structDeclInner(
49514951
4952 if (have_value) {4952 if (have_value) {
4953 any_default_inits = true;4953 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
4956 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);4959 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
4957 if (!block_scope.endsWithNoReturn()) {4960 if (!block_scope.endsWithNoReturn()) {
src/Autodoc.zig+5
...@@ -3808,6 +3808,11 @@ fn walkInstruction(...@@ -3808,6 +3808,11 @@ fn walkInstruction(
3808 call_ctx,3808 call_ctx,
3809 );3809 );
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
3811 var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{};3816 var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{};
3812 var field_default_refs: std.ArrayListUnmanaged(?DocData.Expr) = .{};3817 var field_default_refs: std.ArrayListUnmanaged(?DocData.Expr) = .{};
3813 var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};3818 var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
src/InternPool.zig+72-2
...@@ -463,6 +463,7 @@ pub const Key = union(enum) {...@@ -463,6 +463,7 @@ pub const Key = union(enum) {
463463
464 pub fn fieldInit(s: @This(), ip: *const InternPool, i: usize) Index {464 pub fn fieldInit(s: @This(), ip: *const InternPool, i: usize) Index {
465 if (s.field_inits.len == 0) return .none;465 if (s.field_inits.len == 0) return .none;
466 assert(s.haveFieldInits(ip));
466 return s.field_inits.get(ip)[i];467 return s.field_inits.get(ip)[i];
467 }468 }
468469
...@@ -497,6 +498,14 @@ pub const Key = union(enum) {...@@ -497,6 +498,14 @@ pub const Key = union(enum) {
497 return @ptrCast(&ip.extra.items[self.extra_index + flags_field_index]);498 return @ptrCast(&ip.extra.items[self.extra_index + flags_field_index]);
498 }499 }
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
500 pub fn assumeRuntimeBitsIfFieldTypesWip(s: @This(), ip: *InternPool) bool {509 pub fn assumeRuntimeBitsIfFieldTypesWip(s: @This(), ip: *InternPool) bool {
501 if (s.layout == .Packed) return false;510 if (s.layout == .Packed) return false;
502 const flags_ptr = s.flagsPtr(ip);511 const flags_ptr = s.flagsPtr(ip);
...@@ -546,6 +555,30 @@ pub const Key = union(enum) {...@@ -546,6 +555,30 @@ pub const Key = union(enum) {
546 s.flagsPtr(ip).alignment_wip = false;555 s.flagsPtr(ip).alignment_wip = false;
547 }556 }
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
549 pub fn setFullyResolved(s: @This(), ip: *InternPool) bool {582 pub fn setFullyResolved(s: @This(), ip: *InternPool) bool {
550 if (s.layout == .Packed) return true;583 if (s.layout == .Packed) return true;
551 const flags_ptr = s.flagsPtr(ip);584 const flags_ptr = s.flagsPtr(ip);
...@@ -588,6 +621,20 @@ pub const Key = union(enum) {...@@ -588,6 +621,20 @@ pub const Key = union(enum) {
588 return types.len == 0 or types[0] != .none;621 return types.len == 0 or types[0] != .none;
589 }622 }
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
591 pub fn haveLayout(s: @This(), ip: *InternPool) bool {638 pub fn haveLayout(s: @This(), ip: *InternPool) bool {
592 return switch (s.layout) {639 return switch (s.layout) {
593 .Packed => s.backingIntType(ip).* != .none,640 .Packed => s.backingIntType(ip).* != .none,
...@@ -3000,6 +3047,14 @@ pub const Tag = enum(u8) {...@@ -3000,6 +3047,14 @@ pub const Tag = enum(u8) {
3000 namespace: Module.Namespace.OptionalIndex,3047 namespace: Module.Namespace.OptionalIndex,
3001 backing_int_ty: Index,3048 backing_int_ty: Index,
3002 names_map: MapIndex,3049 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 };
3003 };3058 };
30043059
3005 /// At first I thought of storing the denormalized data externally, such as...3060 /// At first I thought of storing the denormalized data externally, such as...
...@@ -3045,6 +3100,7 @@ pub const Tag = enum(u8) {...@@ -3045,6 +3100,7 @@ pub const Tag = enum(u8) {
3045 requires_comptime: RequiresComptime,3100 requires_comptime: RequiresComptime,
3046 is_tuple: bool,3101 is_tuple: bool,
3047 assumed_runtime_bits: bool,3102 assumed_runtime_bits: bool,
3103 assumed_pointer_aligned: bool,
3048 has_namespace: bool,3104 has_namespace: bool,
3049 any_comptime_fields: bool,3105 any_comptime_fields: bool,
3050 any_default_inits: bool,3106 any_default_inits: bool,
...@@ -3057,14 +3113,18 @@ pub const Tag = enum(u8) {...@@ -3057,14 +3113,18 @@ pub const Tag = enum(u8) {
3057 field_types_wip: bool,3113 field_types_wip: bool,
3058 /// Dependency loop detection when resolving struct layout.3114 /// Dependency loop detection when resolving struct layout.
3059 layout_wip: bool,3115 layout_wip: bool,
3060 /// Determines whether `size`, `alignment`, runtime field order, and3116 /// Indicates whether `size`, `alignment`, runtime field order, and
3061 /// field offets are populated.3117 /// field offets are populated.
3062 layout_resolved: bool,3118 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,
3063 // The types and all its fields have had their layout resolved. Even through pointer,3123 // The types and all its fields have had their layout resolved. Even through pointer,
3064 // which `layout_resolved` does not ensure.3124 // which `layout_resolved` does not ensure.
3065 fully_resolved: bool,3125 fully_resolved: bool,
30663126
3067 _: u11 = 0,3127 _: u8 = 0,
3068 };3128 };
3069 };3129 };
3070};3130};
...@@ -5347,6 +5407,7 @@ pub const StructTypeInit = struct {...@@ -5347,6 +5407,7 @@ pub const StructTypeInit = struct {
5347 is_tuple: bool,5407 is_tuple: bool,
5348 any_comptime_fields: bool,5408 any_comptime_fields: bool,
5349 any_default_inits: bool,5409 any_default_inits: bool,
5410 inits_resolved: bool,
5350 any_aligned_fields: bool,5411 any_aligned_fields: bool,
5351};5412};
53525413
...@@ -5399,6 +5460,10 @@ pub fn getStructType(...@@ -5399,6 +5460,10 @@ pub fn getStructType(
5399 .namespace = ini.namespace,5460 .namespace = ini.namespace,
5400 .backing_int_ty = .none,5461 .backing_int_ty = .none,
5401 .names_map = names_map,5462 .names_map = names_map,
5463 .flags = .{
5464 .field_inits_wip = false,
5465 .inits_resolved = ini.inits_resolved,
5466 },
5402 }),5467 }),
5403 });5468 });
5404 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);5469 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
...@@ -5431,6 +5496,7 @@ pub fn getStructType(...@@ -5431,6 +5496,7 @@ pub fn getStructType(
5431 .requires_comptime = ini.requires_comptime,5496 .requires_comptime = ini.requires_comptime,
5432 .is_tuple = ini.is_tuple,5497 .is_tuple = ini.is_tuple,
5433 .assumed_runtime_bits = false,5498 .assumed_runtime_bits = false,
5499 .assumed_pointer_aligned = false,
5434 .has_namespace = ini.namespace != .none,5500 .has_namespace = ini.namespace != .none,
5435 .any_comptime_fields = ini.any_comptime_fields,5501 .any_comptime_fields = ini.any_comptime_fields,
5436 .any_default_inits = ini.any_default_inits,5502 .any_default_inits = ini.any_default_inits,
...@@ -5440,6 +5506,8 @@ pub fn getStructType(...@@ -5440,6 +5506,8 @@ pub fn getStructType(
5440 .field_types_wip = false,5506 .field_types_wip = false,
5441 .layout_wip = false,5507 .layout_wip = false,
5442 .layout_resolved = false,5508 .layout_resolved = false,
5509 .field_inits_wip = false,
5510 .inits_resolved = ini.inits_resolved,
5443 .fully_resolved = false,5511 .fully_resolved = false,
5444 },5512 },
5445 }),5513 }),
...@@ -6451,6 +6519,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {...@@ -6451,6 +6519,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
6451 Tag.TypePointer.PackedOffset,6519 Tag.TypePointer.PackedOffset,
6452 Tag.TypeUnion.Flags,6520 Tag.TypeUnion.Flags,
6453 Tag.TypeStruct.Flags,6521 Tag.TypeStruct.Flags,
6522 Tag.TypeStructPacked.Flags,
6454 Tag.Variable.Flags,6523 Tag.Variable.Flags,
6455 => @bitCast(@field(extra, field.name)),6524 => @bitCast(@field(extra, field.name)),
64566525
...@@ -6525,6 +6594,7 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct...@@ -6525,6 +6594,7 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct
6525 Tag.TypePointer.PackedOffset,6594 Tag.TypePointer.PackedOffset,
6526 Tag.TypeUnion.Flags,6595 Tag.TypeUnion.Flags,
6527 Tag.TypeStruct.Flags,6596 Tag.TypeStruct.Flags,
6597 Tag.TypeStructPacked.Flags,
6528 Tag.Variable.Flags,6598 Tag.Variable.Flags,
6529 FuncAnalysis,6599 FuncAnalysis,
6530 => @bitCast(int32),6600 => @bitCast(int32),
src/Sema.zig+239-54
...@@ -2699,6 +2699,7 @@ pub fn getStructType(...@@ -2699,6 +2699,7 @@ pub fn getStructType(
2699 .requires_comptime = if (small.known_comptime_only) .yes else .unknown,2699 .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
2700 .any_default_inits = small.any_default_inits,2700 .any_default_inits = small.any_default_inits,
2701 .any_comptime_fields = small.any_comptime_fields,2701 .any_comptime_fields = small.any_comptime_fields,
2702 .inits_resolved = false,
2702 .any_aligned_fields = small.any_aligned_fields,2703 .any_aligned_fields = small.any_aligned_fields,
2703 });2704 });
27042705
...@@ -4718,6 +4719,7 @@ fn validateStructInit(...@@ -4718,6 +4719,7 @@ fn validateStructInit(
4718 const i: u32 = @intCast(i_usize);4719 const i: u32 = @intCast(i_usize);
4719 if (field_ptr != .none) continue;4720 if (field_ptr != .none) continue;
47204721
4722 try sema.resolveStructFieldInits(struct_ty);
4721 const default_val = struct_ty.structFieldDefaultValue(i, mod);4723 const default_val = struct_ty.structFieldDefaultValue(i, mod);
4722 if (default_val.toIntern() == .unreachable_value) {4724 if (default_val.toIntern() == .unreachable_value) {
4723 const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse {4725 const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse {
...@@ -4773,6 +4775,8 @@ fn validateStructInit(...@@ -4773,6 +4775,8 @@ fn validateStructInit(
4773 const air_tags = sema.air_instructions.items(.tag);4775 const air_tags = sema.air_instructions.items(.tag);
4774 const air_datas = sema.air_instructions.items(.data);4776 const air_datas = sema.air_instructions.items(.data);
47754777
4778 try sema.resolveStructFieldInits(struct_ty);
4779
4776 // We collect the comptime field values in case the struct initialization4780 // We collect the comptime field values in case the struct initialization
4777 // ends up being comptime-known.4781 // ends up being comptime-known.
4778 const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod));4782 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...@@ -17630,6 +17634,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
17630 };17634 };
17631 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);17635 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
1763217636
17637 try sema.resolveStructFieldInits(ty);
17638
17633 for (struct_field_vals, 0..) |*field_val, i| {17639 for (struct_field_vals, 0..) |*field_val, i| {
17634 // TODO: write something like getCoercedInts to avoid needing to dupe17640 // TODO: write something like getCoercedInts to avoid needing to dupe
17635 const name = if (struct_type.fieldName(ip, i).unwrap()) |name_nts|17641 const name = if (struct_type.fieldName(ip, i).unwrap()) |name_nts|
...@@ -19205,17 +19211,20 @@ fn zirStructInit(...@@ -19205,17 +19211,20 @@ fn zirStructInit(
19205 const uncoerced_init = try sema.resolveInst(item.data.init);19211 const uncoerced_init = try sema.resolveInst(item.data.init);
19206 const field_ty = resolved_ty.structFieldType(field_index, mod);19212 const field_ty = resolved_ty.structFieldType(field_index, mod);
19207 field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);19213 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| {19214 if (!is_packed) {
19209 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {19215 try sema.resolveStructFieldInits(resolved_ty);
19210 return sema.failWithNeededComptime(block, field_src, .{19216 if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| {
19211 .needed_comptime_reason = "value stored in comptime field must be comptime-known",19217 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
19212 });19218 return sema.failWithNeededComptime(block, field_src, .{
19213 };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)) {19223 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) {
19216 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);19224 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
19225 }
19217 }19226 }
19218 };19227 }
19219 }19228 }
1922019229
19221 return sema.finishStructInit(block, src, src, field_inits, resolved_ty, result_ty, is_ref);19230 return sema.finishStructInit(block, src, src, field_inits, resolved_ty, result_ty, is_ref);
...@@ -19368,6 +19377,8 @@ fn finishStructInit(...@@ -19368,6 +19377,8 @@ fn finishStructInit(
19368 continue;19377 continue;
19369 }19378 }
1937019379
19380 try sema.resolveStructFieldInits(struct_ty);
19381
19371 const field_init = struct_type.fieldInit(ip, i);19382 const field_init = struct_type.fieldInit(ip, i);
19372 if (field_init == .none) {19383 if (field_init == .none) {
19373 const field_name = struct_type.field_names.get(ip)[i];19384 const field_name = struct_type.field_names.get(ip)[i];
...@@ -21132,6 +21143,7 @@ fn reifyStruct(...@@ -21132,6 +21143,7 @@ fn reifyStruct(
21132 // struct types.21143 // struct types.
21133 .any_comptime_fields = true,21144 .any_comptime_fields = true,
21134 .any_default_inits = true,21145 .any_default_inits = true,
21146 .inits_resolved = true,
21135 .any_aligned_fields = true,21147 .any_aligned_fields = true,
21136 });21148 });
21137 // TODO: figure out InternPool removals for incremental compilation21149 // TODO: figure out InternPool removals for incremental compilation
...@@ -26632,6 +26644,7 @@ fn finishFieldCallBind(...@@ -26632,6 +26644,7 @@ fn finishFieldCallBind(
2663226644
26633 const container_ty = ptr_ty.childType(mod);26645 const container_ty = ptr_ty.childType(mod);
26634 if (container_ty.zigTypeTag(mod) == .Struct) {26646 if (container_ty.zigTypeTag(mod) == .Struct) {
26647 try sema.resolveStructFieldInits(container_ty);
26635 if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| {26648 if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| {
26636 return .{ .direct = Air.internedToRef(default_val.toIntern()) };26649 return .{ .direct = Air.internedToRef(default_val.toIntern()) };
26637 }26650 }
...@@ -26847,6 +26860,7 @@ fn structFieldPtrByIndex(...@@ -26847,6 +26860,7 @@ fn structFieldPtrByIndex(
26847 const ptr_field_ty = try sema.ptrType(ptr_ty_data);26860 const ptr_field_ty = try sema.ptrType(ptr_ty_data);
2684826861
26849 if (struct_type.fieldIsComptime(ip, field_index)) {26862 if (struct_type.fieldIsComptime(ip, field_index)) {
26863 try sema.resolveStructFieldInits(struct_ty);
26850 const val = try mod.intern(.{ .ptr = .{26864 const val = try mod.intern(.{ .ptr = .{
26851 .ty = ptr_field_ty.toIntern(),26865 .ty = ptr_field_ty.toIntern(),
26852 .addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },26866 .addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },
...@@ -26883,6 +26897,7 @@ fn structFieldVal(...@@ -26883,6 +26897,7 @@ fn structFieldVal(
26883 assert(struct_ty.zigTypeTag(mod) == .Struct);26897 assert(struct_ty.zigTypeTag(mod) == .Struct);
2688426898
26885 try sema.resolveTypeFields(struct_ty);26899 try sema.resolveTypeFields(struct_ty);
26900
26886 switch (ip.indexToKey(struct_ty.toIntern())) {26901 switch (ip.indexToKey(struct_ty.toIntern())) {
26887 .struct_type => |struct_type| {26902 .struct_type => |struct_type| {
26888 if (struct_type.isTuple(ip))26903 if (struct_type.isTuple(ip))
...@@ -26891,6 +26906,7 @@ fn structFieldVal(...@@ -26891,6 +26906,7 @@ fn structFieldVal(
26891 const field_index = struct_type.nameIndex(ip, field_name) orelse26906 const field_index = struct_type.nameIndex(ip, field_name) orelse
26892 return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name);26907 return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name);
26893 if (struct_type.fieldIsComptime(ip, field_index)) {26908 if (struct_type.fieldIsComptime(ip, field_index)) {
26909 try sema.resolveStructFieldInits(struct_ty);
26894 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);26910 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);
26895 }26911 }
2689626912
...@@ -31282,6 +31298,7 @@ fn coerceTupleToStruct(...@@ -31282,6 +31298,7 @@ fn coerceTupleToStruct(
31282 const mod = sema.mod;31298 const mod = sema.mod;
31283 const ip = &mod.intern_pool;31299 const ip = &mod.intern_pool;
31284 try sema.resolveTypeFields(struct_ty);31300 try sema.resolveTypeFields(struct_ty);
31301 try sema.resolveStructFieldInits(struct_ty);
3128531302
31286 if (struct_ty.isTupleOrAnonStruct(mod)) {31303 if (struct_ty.isTupleOrAnonStruct(mod)) {
31287 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);31304 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);
...@@ -34264,6 +34281,8 @@ fn resolvePeerTypesInner(...@@ -34264,6 +34281,8 @@ fn resolvePeerTypesInner(
34264 var comptime_val: ?Value = null;34281 var comptime_val: ?Value = null;
34265 for (peer_tys) |opt_ty| {34282 for (peer_tys) |opt_ty| {
34266 const struct_ty = opt_ty orelse continue;34283 const struct_ty = opt_ty orelse continue;
34284 try sema.resolveStructFieldInits(struct_ty);
34285
34267 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {34286 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {
34268 comptime_val = null;34287 comptime_val = null;
34269 break;34288 break;
...@@ -34605,8 +34624,7 @@ pub fn resolveStructAlignment(...@@ -34605,8 +34624,7 @@ pub fn resolveStructAlignment(
34605 // We'll guess "pointer-aligned", if the struct has an34624 // We'll guess "pointer-aligned", if the struct has an
34606 // underaligned pointer field then some allocations34625 // underaligned pointer field then some allocations
34607 // might require explicit alignment.34626 // might require explicit alignment.
34608 //TODO write this bit and emit an error later if incorrect34627 struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34609 //struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34610 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));34628 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
34611 struct_type.flagsPtr(ip).alignment = result;34629 struct_type.flagsPtr(ip).alignment = result;
34612 return result;34630 return result;
...@@ -34618,8 +34636,7 @@ pub fn resolveStructAlignment(...@@ -34618,8 +34636,7 @@ pub fn resolveStructAlignment(
34618 // We'll guess "pointer-aligned", if the struct has an34636 // We'll guess "pointer-aligned", if the struct has an
34619 // underaligned pointer field then some allocations34637 // underaligned pointer field then some allocations
34620 // might require explicit alignment.34638 // might require explicit alignment.
34621 //TODO write this bit and emit an error later if incorrect34639 struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34622 //struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34623 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));34640 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
34624 struct_type.flagsPtr(ip).alignment = result;34641 struct_type.flagsPtr(ip).alignment = result;
34625 return result;34642 return result;
...@@ -34710,6 +34727,18 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {...@@ -34710,6 +34727,18 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
34710 return sema.failWithOwnedErrorMsg(null, msg);34727 return sema.failWithOwnedErrorMsg(null, msg);
34711 }34728 }
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
34713 if (struct_type.hasReorderedFields()) {34742 if (struct_type.hasReorderedFields()) {
34714 const runtime_order = struct_type.runtime_order.get(ip);34743 const runtime_order = struct_type.runtime_order.get(ip);
3471534744
...@@ -35329,6 +35358,32 @@ pub fn resolveTypeFieldsStruct(...@@ -35329,6 +35358,32 @@ pub fn resolveTypeFieldsStruct(
35329 try semaStructFields(mod, sema.arena, struct_type);35358 try semaStructFields(mod, sema.arena, struct_type);
35330}35359}
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
35332pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void {35387pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void {
35333 const mod = sema.mod;35388 const mod = sema.mod;
35334 const ip = &mod.intern_pool;35389 const ip = &mod.intern_pool;
...@@ -35510,24 +35565,18 @@ fn resolveInferredErrorSetTy(...@@ -35510,24 +35565,18 @@ fn resolveInferredErrorSetTy(
35510 }35565 }
35511}35566}
3551235567
35513fn semaStructFields(35568fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct {
35514 mod: *Module,35569 /// fields_len
35515 arena: Allocator,35570 usize,
35516 struct_type: InternPool.Key.StructType,35571 Zir.Inst.StructDecl.Small,
35517) CompileError!void {35572 /// extra_index
35518 const gpa = mod.gpa;35573 usize,
35519 const ip = &mod.intern_pool;35574} {
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;
35525 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;35575 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
35526 assert(extended.opcode == .struct_decl);35576 assert(extended.opcode == .struct_decl);
35527 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);35577 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
35528 var extra_index: usize = extended.operand;35578 var extra_index: usize = extended.operand;
3552935579
35530 const src = LazySrcLoc.nodeOffset(0);
35531 extra_index += @intFromBool(small.has_src_node);35580 extra_index += @intFromBool(small.has_src_node);
3553235581
35533 const fields_len = if (small.has_fields_len) blk: {35582 const fields_len = if (small.has_fields_len) blk: {
...@@ -35558,6 +35607,25 @@ fn semaStructFields(...@@ -35558,6 +35607,25 @@ fn semaStructFields(
35558 while (decls_it.next()) |_| {}35607 while (decls_it.next()) |_| {}
35559 extra_index = decls_it.extra_index;35608 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
35561 if (fields_len == 0) switch (struct_type.layout) {35629 if (fields_len == 0) switch (struct_type.layout) {
35562 .Packed => {35630 .Packed => {
35563 try semaBackingIntType(mod, struct_type);35631 try semaBackingIntType(mod, struct_type);
...@@ -35685,7 +35753,6 @@ fn semaStructFields(...@@ -35685,7 +35753,6 @@ fn semaStructFields(
3568535753
35686 // Next we do only types and alignments, saving the inits for a second pass,35754 // Next we do only types and alignments, saving the inits for a second pass,
35687 // so that init values may depend on type layout.35755 // so that init values may depend on type layout.
35688 const bodies_index = extra_index;
3568935756
35690 for (fields, 0..) |zir_field, field_i| {35757 for (fields, 0..) |zir_field, field_i| {
35691 const field_ty: Type = ty: {35758 const field_ty: Type = ty: {
...@@ -35809,44 +35876,161 @@ fn semaStructFields(...@@ -35809,44 +35876,161 @@ fn semaStructFields(
35809 extra_index += zir_field.init_body_len;35876 extra_index += zir_field.init_body_len;
35810 }35877 }
3581135878
35812 // TODO: there seems to be no mechanism to catch when an init depends on35879 struct_type.clearTypesWip(ip);
35813 // another init that hasn't been resolved.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
35815 if (any_inits) {35989 if (any_inits) {
35816 extra_index = bodies_index;
35817 for (fields, 0..) |zir_field, field_i| {35990 for (fields, 0..) |zir_field, field_i| {
35818 const field_ty = struct_type.field_types.get(ip)[field_i].toType();
35819 extra_index += zir_field.type_body_len;35991 extra_index += zir_field.type_body_len;
35820 extra_index += zir_field.align_body_len;35992 extra_index += zir_field.align_body_len;
35821 if (zir_field.init_body_len > 0) {35993 const body = zir.bodySlice(extra_index, zir_field.init_body_len);
35822 const body = zir.bodySlice(extra_index, zir_field.init_body_len);35994 extra_index += zir_field.init_body_len;
35823 extra_index += body.len;35995
35824 const init = try sema.resolveBody(&block_scope, body, zir_index);35996 if (body.len == 0) continue;
35825 const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {35997
35826 error.NeededSourceLocation => {35998 // Pre-populate the type mapping the body expects to be there.
35827 const init_src = mod.fieldSrcLoc(decl_index, .{35999 // In init bodies, the zir index of the struct itself is used
35828 .index = field_i,36000 // to refer to the current field type.
35829 .range = .value,36001
35830 }).lazy;36002 const field_ty = struct_type.field_types.get(ip)[field_i].toType();
35831 _ = try sema.coerce(&block_scope, field_ty, init, init_src);36003 const type_ref = Air.internedToRef(field_ty.toIntern());
35832 unreachable;36004 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index});
35833 },36005 sema.inst_map.putAssumeCapacity(zir_index, type_ref);
35834 else => |e| return e,36006
35835 };36007 const init = try sema.resolveBody(&block_scope, body, zir_index);
35836 const default_val = (try sema.resolveValue(coerced)) orelse {36008 const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {
36009 error.NeededSourceLocation => {
35837 const init_src = mod.fieldSrcLoc(decl_index, .{36010 const init_src = mod.fieldSrcLoc(decl_index, .{
35838 .index = field_i,36011 .index = field_i,
35839 .range = .value,36012 .range = .value,
35840 }).lazy;36013 }).lazy;
35841 return sema.failWithNeededComptime(&block_scope, init_src, .{36014 _ = try sema.coerce(&block_scope, field_ty, init, init_src);
35842 .needed_comptime_reason = "struct field default value must be comptime-known",36015 unreachable;
35843 });36016 },
35844 };36017 else => |e| return e,
35845 const field_init = try default_val.intern(field_ty, mod);36018 };
35846 struct_type.field_inits.get(ip)[field_i] = field_init;36019 const default_val = (try sema.resolveValue(coerced)) orelse {
35847 }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;
35848 }36031 }
35849 }36032 }
36033
35850 for (comptime_mutable_decls.items) |ct_decl_index| {36034 for (comptime_mutable_decls.items) |ct_decl_index| {
35851 const ct_decl = mod.declPtr(ct_decl_index);36035 const ct_decl = mod.declPtr(ct_decl_index);
35852 _ = try ct_decl.internValue(mod);36036 _ = try ct_decl.internValue(mod);
...@@ -36674,6 +36858,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {...@@ -36674,6 +36858,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
36674 );36858 );
36675 for (field_vals, 0..) |*field_val, i| {36859 for (field_vals, 0..) |*field_val, i| {
36676 if (struct_type.fieldIsComptime(ip, i)) {36860 if (struct_type.fieldIsComptime(ip, i)) {
36861 try sema.resolveStructFieldInits(ty);
36677 field_val.* = struct_type.field_inits.get(ip)[i];36862 field_val.* = struct_type.field_inits.get(ip)[i];
36678 continue;36863 continue;
36679 }36864 }
src/type.zig+2
...@@ -2415,6 +2415,7 @@ pub const Type = struct {...@@ -2415,6 +2415,7 @@ pub const Type = struct {
2415 for (field_vals, 0..) |*field_val, i_usize| {2415 for (field_vals, 0..) |*field_val, i_usize| {
2416 const i: u32 = @intCast(i_usize);2416 const i: u32 = @intCast(i_usize);
2417 if (struct_type.fieldIsComptime(ip, i)) {2417 if (struct_type.fieldIsComptime(ip, i)) {
2418 assert(struct_type.haveFieldInits(ip));
2418 field_val.* = struct_type.field_inits.get(ip)[i];2419 field_val.* = struct_type.field_inits.get(ip)[i];
2419 continue;2420 continue;
2420 }2421 }
...@@ -3014,6 +3015,7 @@ pub const Type = struct {...@@ -3014,6 +3015,7 @@ pub const Type = struct {
3014 const ip = &mod.intern_pool;3015 const ip = &mod.intern_pool;
3015 switch (ip.indexToKey(ty.toIntern())) {3016 switch (ip.indexToKey(ty.toIntern())) {
3016 .struct_type => |struct_type| {3017 .struct_type => |struct_type| {
3018 assert(struct_type.haveFieldInits(ip));
3017 if (struct_type.fieldIsComptime(ip, index)) {3019 if (struct_type.fieldIsComptime(ip, index)) {
3018 return struct_type.field_inits.get(ip)[index].toValue();3020 return struct_type.field_inits.get(ip)[index].toValue();
3019 } else {3021 } else {
test/behavior/struct.zig+57
...@@ -1785,3 +1785,60 @@ test "comptimeness of optional and error union payload is analyzed properly" {...@@ -1785,3 +1785,60 @@ test "comptimeness of optional and error union payload is analyzed properly" {
1785 const x = (try c).?.x;1785 const x = (try c).?.x;
1786 try std.testing.expectEqual(3, x);1786 try std.testing.expectEqual(3, x);
1787}1787}
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" {...@@ -1869,6 +1869,126 @@ test "reinterpret packed union inside packed struct" {
1869 try S.doTheTest();1869 try S.doTheTest();
1870}1870}
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
1872test "union field is a pointer to an aligned version of itself" {1992test "union field is a pointer to an aligned version of itself" {
1873 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1993 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1874 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;1994 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
...@@ -1902,3 +2022,23 @@ test "pass register-sized field as non-register-sized union" {...@@ -1902,3 +2022,23 @@ test "pass register-sized field as non-register-sized union" {
1902 try S.untaggedUnion(.{ .x = x });2022 try S.untaggedUnion(.{ .x = x });
1903 try S.externUnion(.{ .x = x });2023 try S.externUnion(.{ .x = x });
1904}2024}
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