authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-11-07 07:44:32+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-07 07:44:32+00:00
logb3462b7cec9931cd3747f10714954eb8efe00c04
tree86c6f81f1bc4c4afb0d2b82a1245ebf3a6eb72e0
parentd78eda34c5de1ce869c55057b790081012e00bf5
parent1acb6a53d04102ed028b73451df2250bd6d45cd9
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17692 from kcbanner/struct_field_init_pass

sema: analyze struct field bodies in a second pass, to allow them to use the layout of the struct itself

10 files changed, 611 insertions(+), 105 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));
...@@ -17638,6 +17642,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -17638,6 +17642,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
17638 };17642 };
17639 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);17643 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
1764017644
17645 try sema.resolveStructFieldInits(ty);
17646
17641 for (struct_field_vals, 0..) |*field_val, i| {17647 for (struct_field_vals, 0..) |*field_val, i| {
17642 // TODO: write something like getCoercedInts to avoid needing to dupe17648 // TODO: write something like getCoercedInts to avoid needing to dupe
17643 const name = if (struct_type.fieldName(ip, i).unwrap()) |name_nts|17649 const name = if (struct_type.fieldName(ip, i).unwrap()) |name_nts|
...@@ -19213,17 +19219,20 @@ fn zirStructInit(...@@ -19213,17 +19219,20 @@ fn zirStructInit(
19213 const uncoerced_init = try sema.resolveInst(item.data.init);19219 const uncoerced_init = try sema.resolveInst(item.data.init);
19214 const field_ty = resolved_ty.structFieldType(field_index, mod);19220 const field_ty = resolved_ty.structFieldType(field_index, mod);
19215 field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);19221 field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);
19216 if (!is_packed) if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| {19222 if (!is_packed) {
19217 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {19223 try sema.resolveStructFieldInits(resolved_ty);
19218 return sema.failWithNeededComptime(block, field_src, .{19224 if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| {
19219 .needed_comptime_reason = "value stored in comptime field must be comptime-known",19225 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
19220 });19226 return sema.failWithNeededComptime(block, field_src, .{
19221 };19227 .needed_comptime_reason = "value stored in comptime field must be comptime-known",
19228 });
19229 };
1922219230
19223 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) {19231 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) {
19224 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);19232 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
19233 }
19225 }19234 }
19226 };19235 }
19227 }19236 }
1922819237
19229 return sema.finishStructInit(block, src, src, field_inits, resolved_ty, result_ty, is_ref);19238 return sema.finishStructInit(block, src, src, field_inits, resolved_ty, result_ty, is_ref);
...@@ -19376,6 +19385,8 @@ fn finishStructInit(...@@ -19376,6 +19385,8 @@ fn finishStructInit(
19376 continue;19385 continue;
19377 }19386 }
1937819387
19388 try sema.resolveStructFieldInits(struct_ty);
19389
19379 const field_init = struct_type.fieldInit(ip, i);19390 const field_init = struct_type.fieldInit(ip, i);
19380 if (field_init == .none) {19391 if (field_init == .none) {
19381 const field_name = struct_type.field_names.get(ip)[i];19392 const field_name = struct_type.field_names.get(ip)[i];
...@@ -21140,6 +21151,7 @@ fn reifyStruct(...@@ -21140,6 +21151,7 @@ fn reifyStruct(
21140 // struct types.21151 // struct types.
21141 .any_comptime_fields = true,21152 .any_comptime_fields = true,
21142 .any_default_inits = true,21153 .any_default_inits = true,
21154 .inits_resolved = true,
21143 .any_aligned_fields = true,21155 .any_aligned_fields = true,
21144 });21156 });
21145 // TODO: figure out InternPool removals for incremental compilation21157 // TODO: figure out InternPool removals for incremental compilation
...@@ -26640,6 +26652,7 @@ fn finishFieldCallBind(...@@ -26640,6 +26652,7 @@ fn finishFieldCallBind(
2664026652
26641 const container_ty = ptr_ty.childType(mod);26653 const container_ty = ptr_ty.childType(mod);
26642 if (container_ty.zigTypeTag(mod) == .Struct) {26654 if (container_ty.zigTypeTag(mod) == .Struct) {
26655 try sema.resolveStructFieldInits(container_ty);
26643 if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| {26656 if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| {
26644 return .{ .direct = Air.internedToRef(default_val.toIntern()) };26657 return .{ .direct = Air.internedToRef(default_val.toIntern()) };
26645 }26658 }
...@@ -26855,6 +26868,7 @@ fn structFieldPtrByIndex(...@@ -26855,6 +26868,7 @@ fn structFieldPtrByIndex(
26855 const ptr_field_ty = try sema.ptrType(ptr_ty_data);26868 const ptr_field_ty = try sema.ptrType(ptr_ty_data);
2685626869
26857 if (struct_type.fieldIsComptime(ip, field_index)) {26870 if (struct_type.fieldIsComptime(ip, field_index)) {
26871 try sema.resolveStructFieldInits(struct_ty);
26858 const val = try mod.intern(.{ .ptr = .{26872 const val = try mod.intern(.{ .ptr = .{
26859 .ty = ptr_field_ty.toIntern(),26873 .ty = ptr_field_ty.toIntern(),
26860 .addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },26874 .addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },
...@@ -26891,6 +26905,7 @@ fn structFieldVal(...@@ -26891,6 +26905,7 @@ fn structFieldVal(
26891 assert(struct_ty.zigTypeTag(mod) == .Struct);26905 assert(struct_ty.zigTypeTag(mod) == .Struct);
2689226906
26893 try sema.resolveTypeFields(struct_ty);26907 try sema.resolveTypeFields(struct_ty);
26908
26894 switch (ip.indexToKey(struct_ty.toIntern())) {26909 switch (ip.indexToKey(struct_ty.toIntern())) {
26895 .struct_type => |struct_type| {26910 .struct_type => |struct_type| {
26896 if (struct_type.isTuple(ip))26911 if (struct_type.isTuple(ip))
...@@ -26899,6 +26914,7 @@ fn structFieldVal(...@@ -26899,6 +26914,7 @@ fn structFieldVal(
26899 const field_index = struct_type.nameIndex(ip, field_name) orelse26914 const field_index = struct_type.nameIndex(ip, field_name) orelse
26900 return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name);26915 return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name);
26901 if (struct_type.fieldIsComptime(ip, field_index)) {26916 if (struct_type.fieldIsComptime(ip, field_index)) {
26917 try sema.resolveStructFieldInits(struct_ty);
26902 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);26918 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);
26903 }26919 }
2690426920
...@@ -31290,6 +31306,7 @@ fn coerceTupleToStruct(...@@ -31290,6 +31306,7 @@ fn coerceTupleToStruct(
31290 const mod = sema.mod;31306 const mod = sema.mod;
31291 const ip = &mod.intern_pool;31307 const ip = &mod.intern_pool;
31292 try sema.resolveTypeFields(struct_ty);31308 try sema.resolveTypeFields(struct_ty);
31309 try sema.resolveStructFieldInits(struct_ty);
3129331310
31294 if (struct_ty.isTupleOrAnonStruct(mod)) {31311 if (struct_ty.isTupleOrAnonStruct(mod)) {
31295 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);31312 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);
...@@ -34272,6 +34289,8 @@ fn resolvePeerTypesInner(...@@ -34272,6 +34289,8 @@ fn resolvePeerTypesInner(
34272 var comptime_val: ?Value = null;34289 var comptime_val: ?Value = null;
34273 for (peer_tys) |opt_ty| {34290 for (peer_tys) |opt_ty| {
34274 const struct_ty = opt_ty orelse continue;34291 const struct_ty = opt_ty orelse continue;
34292 try sema.resolveStructFieldInits(struct_ty);
34293
34275 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {34294 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse {
34276 comptime_val = null;34295 comptime_val = null;
34277 break;34296 break;
...@@ -34613,8 +34632,7 @@ pub fn resolveStructAlignment(...@@ -34613,8 +34632,7 @@ pub fn resolveStructAlignment(
34613 // We'll guess "pointer-aligned", if the struct has an34632 // We'll guess "pointer-aligned", if the struct has an
34614 // underaligned pointer field then some allocations34633 // underaligned pointer field then some allocations
34615 // might require explicit alignment.34634 // might require explicit alignment.
34616 //TODO write this bit and emit an error later if incorrect34635 struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34617 //struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34618 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));34636 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
34619 struct_type.flagsPtr(ip).alignment = result;34637 struct_type.flagsPtr(ip).alignment = result;
34620 return result;34638 return result;
...@@ -34626,8 +34644,7 @@ pub fn resolveStructAlignment(...@@ -34626,8 +34644,7 @@ pub fn resolveStructAlignment(
34626 // We'll guess "pointer-aligned", if the struct has an34644 // We'll guess "pointer-aligned", if the struct has an
34627 // underaligned pointer field then some allocations34645 // underaligned pointer field then some allocations
34628 // might require explicit alignment.34646 // might require explicit alignment.
34629 //TODO write this bit and emit an error later if incorrect34647 struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34630 //struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
34631 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));34648 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
34632 struct_type.flagsPtr(ip).alignment = result;34649 struct_type.flagsPtr(ip).alignment = result;
34633 return result;34650 return result;
...@@ -34718,6 +34735,18 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {...@@ -34718,6 +34735,18 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
34718 return sema.failWithOwnedErrorMsg(null, msg);34735 return sema.failWithOwnedErrorMsg(null, msg);
34719 }34736 }
3472034737
34738 if (struct_type.flagsPtr(ip).assumed_pointer_aligned and
34739 big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(mod.getTarget().ptrBitWidth(), 8))))
34740 {
34741 const msg = try Module.ErrorMsg.create(
34742 sema.gpa,
34743 mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod),
34744 "struct layout depends on being pointer aligned",
34745 .{},
34746 );
34747 return sema.failWithOwnedErrorMsg(null, msg);
34748 }
34749
34721 if (struct_type.hasReorderedFields()) {34750 if (struct_type.hasReorderedFields()) {
34722 const runtime_order = struct_type.runtime_order.get(ip);34751 const runtime_order = struct_type.runtime_order.get(ip);
3472334752
...@@ -35337,6 +35366,32 @@ pub fn resolveTypeFieldsStruct(...@@ -35337,6 +35366,32 @@ pub fn resolveTypeFieldsStruct(
35337 try semaStructFields(mod, sema.arena, struct_type);35366 try semaStructFields(mod, sema.arena, struct_type);
35338}35367}
3533935368
35369pub fn resolveStructFieldInits(sema: *Sema, ty: Type) CompileError!void {
35370 const mod = sema.mod;
35371 const ip = &mod.intern_pool;
35372 const struct_type = mod.typeToStruct(ty) orelse return;
35373 const owner_decl = struct_type.decl.unwrap() orelse return;
35374
35375 // Inits can start as resolved
35376 if (struct_type.haveFieldInits(ip)) return;
35377
35378 try sema.resolveStructLayout(ty);
35379
35380 if (struct_type.setInitsWip(ip)) {
35381 const msg = try Module.ErrorMsg.create(
35382 sema.gpa,
35383 mod.declPtr(owner_decl).srcLoc(mod),
35384 "struct '{}' depends on itself",
35385 .{ty.fmt(mod)},
35386 );
35387 return sema.failWithOwnedErrorMsg(null, msg);
35388 }
35389 defer struct_type.clearInitsWip(ip);
35390
35391 try semaStructFieldInits(mod, sema.arena, struct_type);
35392 struct_type.setHaveFieldInits(ip);
35393}
35394
35340pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void {35395pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void {
35341 const mod = sema.mod;35396 const mod = sema.mod;
35342 const ip = &mod.intern_pool;35397 const ip = &mod.intern_pool;
...@@ -35518,24 +35573,18 @@ fn resolveInferredErrorSetTy(...@@ -35518,24 +35573,18 @@ fn resolveInferredErrorSetTy(
35518 }35573 }
35519}35574}
3552035575
35521fn semaStructFields(35576fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct {
35522 mod: *Module,35577 /// fields_len
35523 arena: Allocator,35578 usize,
35524 struct_type: InternPool.Key.StructType,35579 Zir.Inst.StructDecl.Small,
35525) CompileError!void {35580 /// extra_index
35526 const gpa = mod.gpa;35581 usize,
35527 const ip = &mod.intern_pool;35582} {
35528 const decl_index = struct_type.decl.unwrap() orelse return;
35529 const decl = mod.declPtr(decl_index);
35530 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
35531 const zir = mod.namespacePtr(namespace_index).file_scope.zir;
35532 const zir_index = struct_type.zir_index;
35533 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;35583 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
35534 assert(extended.opcode == .struct_decl);35584 assert(extended.opcode == .struct_decl);
35535 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);35585 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
35536 var extra_index: usize = extended.operand;35586 var extra_index: usize = extended.operand;
3553735587
35538 const src = LazySrcLoc.nodeOffset(0);
35539 extra_index += @intFromBool(small.has_src_node);35588 extra_index += @intFromBool(small.has_src_node);
3554035589
35541 const fields_len = if (small.has_fields_len) blk: {35590 const fields_len = if (small.has_fields_len) blk: {
...@@ -35566,6 +35615,25 @@ fn semaStructFields(...@@ -35566,6 +35615,25 @@ fn semaStructFields(
35566 while (decls_it.next()) |_| {}35615 while (decls_it.next()) |_| {}
35567 extra_index = decls_it.extra_index;35616 extra_index = decls_it.extra_index;
3556835617
35618 return .{ fields_len, small, extra_index };
35619}
35620
35621fn semaStructFields(
35622 mod: *Module,
35623 arena: Allocator,
35624 struct_type: InternPool.Key.StructType,
35625) CompileError!void {
35626 const gpa = mod.gpa;
35627 const ip = &mod.intern_pool;
35628 const decl_index = struct_type.decl.unwrap() orelse return;
35629 const decl = mod.declPtr(decl_index);
35630 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
35631 const zir = mod.namespacePtr(namespace_index).file_scope.zir;
35632 const zir_index = struct_type.zir_index;
35633
35634 const src = LazySrcLoc.nodeOffset(0);
35635 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
35636
35569 if (fields_len == 0) switch (struct_type.layout) {35637 if (fields_len == 0) switch (struct_type.layout) {
35570 .Packed => {35638 .Packed => {
35571 try semaBackingIntType(mod, struct_type);35639 try semaBackingIntType(mod, struct_type);
...@@ -35693,7 +35761,6 @@ fn semaStructFields(...@@ -35693,7 +35761,6 @@ fn semaStructFields(
3569335761
35694 // Next we do only types and alignments, saving the inits for a second pass,35762 // Next we do only types and alignments, saving the inits for a second pass,
35695 // so that init values may depend on type layout.35763 // so that init values may depend on type layout.
35696 const bodies_index = extra_index;
3569735764
35698 for (fields, 0..) |zir_field, field_i| {35765 for (fields, 0..) |zir_field, field_i| {
35699 const field_ty: Type = ty: {35766 const field_ty: Type = ty: {
...@@ -35817,44 +35884,161 @@ fn semaStructFields(...@@ -35817,44 +35884,161 @@ fn semaStructFields(
35817 extra_index += zir_field.init_body_len;35884 extra_index += zir_field.init_body_len;
35818 }35885 }
3581935886
35820 // TODO: there seems to be no mechanism to catch when an init depends on35887 struct_type.clearTypesWip(ip);
35821 // another init that hasn't been resolved.35888 if (!any_inits) struct_type.setHaveFieldInits(ip);
35889
35890 for (comptime_mutable_decls.items) |ct_decl_index| {
35891 const ct_decl = mod.declPtr(ct_decl_index);
35892 _ = try ct_decl.internValue(mod);
35893 }
35894}
35895
35896// This logic must be kept in sync with `semaStructFields`
35897fn semaStructFieldInits(
35898 mod: *Module,
35899 arena: Allocator,
35900 struct_type: InternPool.Key.StructType,
35901) CompileError!void {
35902 const gpa = mod.gpa;
35903 const ip = &mod.intern_pool;
35904
35905 assert(!struct_type.haveFieldInits(ip));
35906
35907 const decl_index = struct_type.decl.unwrap() orelse return;
35908 const decl = mod.declPtr(decl_index);
35909 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
35910 const zir = mod.namespacePtr(namespace_index).file_scope.zir;
35911 const zir_index = struct_type.zir_index;
35912 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
35913
35914 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
35915 defer comptime_mutable_decls.deinit();
35916
35917 var sema: Sema = .{
35918 .mod = mod,
35919 .gpa = gpa,
35920 .arena = arena,
35921 .code = zir,
35922 .owner_decl = decl,
35923 .owner_decl_index = decl_index,
35924 .func_index = .none,
35925 .func_is_naked = false,
35926 .fn_ret_ty = Type.void,
35927 .fn_ret_ty_ies = null,
35928 .owner_func_index = .none,
35929 .comptime_mutable_decls = &comptime_mutable_decls,
35930 };
35931 defer sema.deinit();
35932
35933 var block_scope: Block = .{
35934 .parent = null,
35935 .sema = &sema,
35936 .src_decl = decl_index,
35937 .namespace = namespace_index,
35938 .wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
35939 .instructions = .{},
35940 .inlining = null,
35941 .is_comptime = true,
35942 };
35943 defer assert(block_scope.instructions.items.len == 0);
35944
35945 const Field = struct {
35946 type_body_len: u32 = 0,
35947 align_body_len: u32 = 0,
35948 init_body_len: u32 = 0,
35949 };
35950 const fields = try sema.arena.alloc(Field, fields_len);
35951
35952 var any_inits = false;
35953
35954 {
35955 const bits_per_field = 4;
35956 const fields_per_u32 = 32 / bits_per_field;
35957 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
35958 const flags_index = extra_index;
35959 var bit_bag_index: usize = flags_index;
35960 extra_index += bit_bags_count;
35961 var cur_bit_bag: u32 = undefined;
35962 var field_i: u32 = 0;
35963 while (field_i < fields_len) : (field_i += 1) {
35964 if (field_i % fields_per_u32 == 0) {
35965 cur_bit_bag = zir.extra[bit_bag_index];
35966 bit_bag_index += 1;
35967 }
35968 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
35969 cur_bit_bag >>= 1;
35970 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
35971 cur_bit_bag >>= 2;
35972 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
35973 cur_bit_bag >>= 1;
35974
35975 if (!small.is_tuple) {
35976 extra_index += 1;
35977 }
35978 extra_index += 1; // doc_comment
35979
35980 fields[field_i] = .{};
35981
35982 if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index];
35983 extra_index += 1;
35984
35985 if (has_align) {
35986 fields[field_i].align_body_len = zir.extra[extra_index];
35987 extra_index += 1;
35988 }
35989 if (has_init) {
35990 fields[field_i].init_body_len = zir.extra[extra_index];
35991 extra_index += 1;
35992 any_inits = true;
35993 }
35994 }
35995 }
3582235996
35823 if (any_inits) {35997 if (any_inits) {
35824 extra_index = bodies_index;
35825 for (fields, 0..) |zir_field, field_i| {35998 for (fields, 0..) |zir_field, field_i| {
35826 const field_ty = struct_type.field_types.get(ip)[field_i].toType();
35827 extra_index += zir_field.type_body_len;35999 extra_index += zir_field.type_body_len;
35828 extra_index += zir_field.align_body_len;36000 extra_index += zir_field.align_body_len;
35829 if (zir_field.init_body_len > 0) {36001 const body = zir.bodySlice(extra_index, zir_field.init_body_len);
35830 const body = zir.bodySlice(extra_index, zir_field.init_body_len);36002 extra_index += zir_field.init_body_len;
35831 extra_index += body.len;36003
35832 const init = try sema.resolveBody(&block_scope, body, zir_index);36004 if (body.len == 0) continue;
35833 const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {36005
35834 error.NeededSourceLocation => {36006 // Pre-populate the type mapping the body expects to be there.
35835 const init_src = mod.fieldSrcLoc(decl_index, .{36007 // In init bodies, the zir index of the struct itself is used
35836 .index = field_i,36008 // to refer to the current field type.
35837 .range = .value,36009
35838 }).lazy;36010 const field_ty = struct_type.field_types.get(ip)[field_i].toType();
35839 _ = try sema.coerce(&block_scope, field_ty, init, init_src);36011 const type_ref = Air.internedToRef(field_ty.toIntern());
35840 unreachable;36012 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index});
35841 },36013 sema.inst_map.putAssumeCapacity(zir_index, type_ref);
35842 else => |e| return e,36014
35843 };36015 const init = try sema.resolveBody(&block_scope, body, zir_index);
35844 const default_val = (try sema.resolveValue(coerced)) orelse {36016 const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {
36017 error.NeededSourceLocation => {
35845 const init_src = mod.fieldSrcLoc(decl_index, .{36018 const init_src = mod.fieldSrcLoc(decl_index, .{
35846 .index = field_i,36019 .index = field_i,
35847 .range = .value,36020 .range = .value,
35848 }).lazy;36021 }).lazy;
35849 return sema.failWithNeededComptime(&block_scope, init_src, .{36022 _ = try sema.coerce(&block_scope, field_ty, init, init_src);
35850 .needed_comptime_reason = "struct field default value must be comptime-known",36023 unreachable;
35851 });36024 },
35852 };36025 else => |e| return e,
35853 const field_init = try default_val.intern(field_ty, mod);36026 };
35854 struct_type.field_inits.get(ip)[field_i] = field_init;36027 const default_val = (try sema.resolveValue(coerced)) orelse {
35855 }36028 const init_src = mod.fieldSrcLoc(decl_index, .{
36029 .index = field_i,
36030 .range = .value,
36031 }).lazy;
36032 return sema.failWithNeededComptime(&block_scope, init_src, .{
36033 .needed_comptime_reason = "struct field default value must be comptime-known",
36034 });
36035 };
36036
36037 const field_init = try default_val.intern(field_ty, mod);
36038 struct_type.field_inits.get(ip)[field_i] = field_init;
35856 }36039 }
35857 }36040 }
36041
35858 for (comptime_mutable_decls.items) |ct_decl_index| {36042 for (comptime_mutable_decls.items) |ct_decl_index| {
35859 const ct_decl = mod.declPtr(ct_decl_index);36043 const ct_decl = mod.declPtr(ct_decl_index);
35860 _ = try ct_decl.internValue(mod);36044 _ = try ct_decl.internValue(mod);
...@@ -36682,6 +36866,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {...@@ -36682,6 +36866,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
36682 );36866 );
36683 for (field_vals, 0..) |*field_val, i| {36867 for (field_vals, 0..) |*field_val, i| {
36684 if (struct_type.fieldIsComptime(ip, i)) {36868 if (struct_type.fieldIsComptime(ip, i)) {
36869 try sema.resolveStructFieldInits(ty);
36685 field_val.* = struct_type.field_inits.get(ip)[i];36870 field_val.* = struct_type.field_inits.get(ip)[i];
36686 continue;36871 continue;
36687 }36872 }
src/arch/wasm/CodeGen.zig+8-4
...@@ -3372,10 +3372,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3372,10 +3372,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3372 },3372 },
3373 .un => |un| {3373 .un => |un| {
3374 // in this case we have a packed union which will not be passed by reference.3374 // in this case we have a packed union which will not be passed by reference.
3375 const union_obj = mod.typeToUnion(ty).?;3375 const constant_ty = if (un.tag == .none)
3376 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;3376 try ty.unionBackingType(mod)
3377 const field_ty = union_obj.field_types.get(ip)[field_index].toType();3377 else field_ty: {
3378 return func.lowerConstant(un.val.toValue(), field_ty);3378 const union_obj = mod.typeToUnion(ty).?;
3379 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
3380 break :field_ty union_obj.field_types.get(ip)[field_index].toType();
3381 };
3382 return func.lowerConstant(un.val.toValue(), constant_ty);
3379 },3383 },
3380 .memoized_call => unreachable,3384 .memoized_call => unreachable,
3381 }3385 }
src/codegen/c.zig+73-44
...@@ -1499,56 +1499,85 @@ pub const DeclGen = struct {...@@ -1499,56 +1499,85 @@ pub const DeclGen = struct {
1499 else => unreachable,1499 else => unreachable,
1500 },1500 },
1501 .un => |un| {1501 .un => |un| {
1502 if (!location.isInitializer()) {
1503 try writer.writeByte('(');
1504 try dg.renderType(writer, ty);
1505 try writer.writeByte(')');
1506 }
1507
1508 const union_obj = mod.typeToUnion(ty).?;1502 const union_obj = mod.typeToUnion(ty).?;
1509 const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;1503 if (un.tag == .none) {
1510 const field_ty = union_obj.field_types.get(ip)[field_i].toType();1504 const backing_ty = try ty.unionBackingType(mod);
1511 const field_name = union_obj.field_names.get(ip)[field_i];1505 switch (union_obj.getLayout(ip)) {
1512 if (union_obj.getLayout(ip) == .Packed) {1506 .Packed => {
1513 if (field_ty.hasRuntimeBits(mod)) {1507 if (!location.isInitializer()) {
1514 if (field_ty.isPtrAtRuntime(mod)) {1508 try writer.writeByte('(');
1515 try writer.writeByte('(');1509 try dg.renderType(writer, backing_ty);
1516 try dg.renderType(writer, ty);1510 try writer.writeByte(')');
1517 try writer.writeByte(')');1511 }
1518 } else if (field_ty.zigTypeTag(mod) == .Float) {1512 try dg.renderValue(writer, backing_ty, un.val.toValue(), initializer_type);
1519 try writer.writeByte('(');1513 },
1520 try dg.renderType(writer, ty);1514 .Extern => {
1521 try writer.writeByte(')');1515 if (location == .StaticInitializer) {
1516 return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});
1517 }
1518
1519 const ptr_ty = try mod.singleConstPtrType(ty);
1520 try writer.writeAll("*((");
1521 try dg.renderType(writer, ptr_ty);
1522 try writer.writeAll(")(");
1523 try dg.renderType(writer, backing_ty);
1524 try writer.writeAll("){");
1525 try dg.renderValue(writer, backing_ty, un.val.toValue(), initializer_type);
1526 try writer.writeAll("})");
1527 },
1528 else => unreachable,
1529 }
1530 } else {
1531 if (!location.isInitializer()) {
1532 try writer.writeByte('(');
1533 try dg.renderType(writer, ty);
1534 try writer.writeByte(')');
1535 }
1536
1537 const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
1538 const field_ty = union_obj.field_types.get(ip)[field_i].toType();
1539 const field_name = union_obj.field_names.get(ip)[field_i];
1540 if (union_obj.getLayout(ip) == .Packed) {
1541 if (field_ty.hasRuntimeBits(mod)) {
1542 if (field_ty.isPtrAtRuntime(mod)) {
1543 try writer.writeByte('(');
1544 try dg.renderType(writer, ty);
1545 try writer.writeByte(')');
1546 } else if (field_ty.zigTypeTag(mod) == .Float) {
1547 try writer.writeByte('(');
1548 try dg.renderType(writer, ty);
1549 try writer.writeByte(')');
1550 }
1551 try dg.renderValue(writer, field_ty, un.val.toValue(), initializer_type);
1552 } else {
1553 try writer.writeAll("0");
1522 }1554 }
1523 try dg.renderValue(writer, field_ty, un.val.toValue(), initializer_type);1555 return;
1524 } else {
1525 try writer.writeAll("0");
1526 }1556 }
1527 return;
1528 }
15291557
1530 try writer.writeByte('{');1558 try writer.writeByte('{');
1531 if (ty.unionTagTypeSafety(mod)) |tag_ty| {1559 if (ty.unionTagTypeSafety(mod)) |tag_ty| {
1532 const layout = mod.getUnionLayout(union_obj);1560 const layout = mod.getUnionLayout(union_obj);
1533 if (layout.tag_size != 0) {1561 if (layout.tag_size != 0) {
1534 try writer.writeAll(" .tag = ");1562 try writer.writeAll(" .tag = ");
1535 try dg.renderValue(writer, tag_ty, un.tag.toValue(), initializer_type);1563 try dg.renderValue(writer, tag_ty, un.tag.toValue(), initializer_type);
1564 }
1565 if (ty.unionHasAllZeroBitFieldTypes(mod)) return try writer.writeByte('}');
1566 if (layout.tag_size != 0) try writer.writeByte(',');
1567 try writer.writeAll(" .payload = {");
1536 }1568 }
1537 if (ty.unionHasAllZeroBitFieldTypes(mod)) return try writer.writeByte('}');1569 if (field_ty.hasRuntimeBits(mod)) {
1538 if (layout.tag_size != 0) try writer.writeByte(',');1570 try writer.print(" .{ } = ", .{fmtIdent(ip.stringToSlice(field_name))});
1539 try writer.writeAll(" .payload = {");1571 try dg.renderValue(writer, field_ty, un.val.toValue(), initializer_type);
1540 }1572 try writer.writeByte(' ');
1541 if (field_ty.hasRuntimeBits(mod)) {1573 } else for (union_obj.field_types.get(ip)) |this_field_ty| {
1542 try writer.print(" .{ } = ", .{fmtIdent(ip.stringToSlice(field_name))});1574 if (!this_field_ty.toType().hasRuntimeBits(mod)) continue;
1543 try dg.renderValue(writer, field_ty, un.val.toValue(), initializer_type);1575 try dg.renderValue(writer, this_field_ty.toType(), Value.undef, initializer_type);
1544 try writer.writeByte(' ');1576 break;
1545 } else for (union_obj.field_types.get(ip)) |this_field_ty| {1577 }
1546 if (!this_field_ty.toType().hasRuntimeBits(mod)) continue;1578 if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}');
1547 try dg.renderValue(writer, this_field_ty.toType(), Value.undef, initializer_type);1579 try writer.writeByte('}');
1548 break;
1549 }1580 }
1550 if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}');
1551 try writer.writeByte('}');
1552 },1581 },
1553 }1582 }
1554 }1583 }
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