| author | |
| committer | |
| log | b3462b7cec9931cd3747f10714954eb8efe00c04 |
| tree | 86c6f81f1bc4c4afb0d2b82a1245ebf3a6eb72e0 |
| parent | d78eda34c5de1ce869c55057b790081012e00bf5 |
| parent | 1acb6a53d04102ed028b73451df2250bd6d45cd9 |
| signature |
sema: analyze struct field bodies in a second pass, to allow them to use the layout of the struct itself10 files changed, 611 insertions(+), 105 deletions(-)
src/AstGen.zig+4-1| ... | @@ -4951,7 +4951,10 @@ fn structDeclInner( | ... | @@ -4951,7 +4951,10 @@ fn structDeclInner( |
| 4951 | 4951 | ||
| 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() } }; | ||
| 4955 | 4958 | ||
| 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 | ); |
| 3810 | 3810 | ||
| 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) { |
| 463 | 463 | ||
| 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 | } |
| 468 | 469 | ||
| ... | @@ -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 | } |
| 499 | 500 | ||
| 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 | } |
| 548 | 557 | ||
| 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 | } |
| 590 | 623 | ||
| 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 | }; |
| 3004 | 3059 | ||
| 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, and | 3116 | /// 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, |
| 3066 | 3126 | ||
| 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 | }; |
| 5352 | 5413 | ||
| ... | @@ -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)), |
| 6456 | 6525 | ||
| ... | @@ -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 | }); |
| 2704 | 2705 | ||
| ... | @@ -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; |
| 4720 | 4721 | ||
| 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); |
| 4775 | 4777 | ||
| 4778 | try sema.resolveStructFieldInits(struct_ty); | ||
| 4779 | |||
| 4776 | // We collect the comptime field values in case the struct initialization | 4780 | // 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); |
| 17640 | 17644 | ||
| 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 dupe | 17648 | // 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 | }; | ||
| 19222 | 19230 | ||
| 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 | } |
| 19228 | 19237 | ||
| 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 | } |
| 19378 | 19387 | ||
| 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 compilation | 21157 | // TODO: figure out InternPool removals for incremental compilation |
| ... | @@ -26640,6 +26652,7 @@ fn finishFieldCallBind( | ... | @@ -26640,6 +26652,7 @@ fn finishFieldCallBind( |
| 26640 | 26652 | ||
| 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); |
| 26856 | 26869 | ||
| 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); |
| 26892 | 26906 | ||
| 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) orelse | 26914 | 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 | } |
| 26904 | 26920 | ||
| ... | @@ -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); | ||
| 31293 | 31310 | ||
| 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 an | 34632 | // We'll guess "pointer-aligned", if the struct has an |
| 34614 | // underaligned pointer field then some allocations | 34633 | // 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 incorrect | 34635 | 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 an | 34644 | // We'll guess "pointer-aligned", if the struct has an |
| 34627 | // underaligned pointer field then some allocations | 34645 | // 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 incorrect | 34647 | 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 | } |
| 34720 | 34737 | ||
| 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); |
| 34723 | 34752 | ||
| ... | @@ -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 | } |
| 35339 | 35368 | ||
| 35369 | pub 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 | |||
| 35340 | pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void { | 35395 | pub 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 | } |
| 35520 | 35575 | ||
| 35521 | fn semaStructFields( | 35576 | fn 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; |
| 35537 | 35587 | ||
| 35538 | const src = LazySrcLoc.nodeOffset(0); | ||
| 35539 | extra_index += @intFromBool(small.has_src_node); | 35588 | extra_index += @intFromBool(small.has_src_node); |
| 35540 | 35589 | ||
| 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; |
| 35568 | 35617 | ||
| 35618 | return .{ fields_len, small, extra_index }; | ||
| 35619 | } | ||
| 35620 | |||
| 35621 | fn 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( |
| 35693 | 35761 | ||
| 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; | ||
| 35697 | 35764 | ||
| 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 | } |
| 35819 | 35886 | ||
| 35820 | // TODO: there seems to be no mechanism to catch when an init depends on | 35887 | 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` | ||
| 35897 | fn 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 | } | ||
| 35822 | 35996 | ||
| 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 | } | ||
| 1529 | 1557 | ||
| 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 | |||
| 1789 | test "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 | |||
| 1799 | test "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 | |||
| 1809 | test "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 | |||
| 1826 | test "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 | } |
| 1871 | 1871 | ||
| 1872 | test "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 | |||
| 1896 | test "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 | |||
| 1920 | test "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 | |||
| 1937 | test "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 | |||
| 1954 | test "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 | |||
| 1973 | test "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 | |||
| 1872 | test "union field is a pointer to an aligned version of itself" { | 1992 | test "union field is a pointer to an aligned version of itself" { |
| 1873 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1993 | 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 | |||
| 2026 | test "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 @@ | ||
| 1 | const S = struct { | ||
| 2 | next: ?*align(1) S align(128), | ||
| 3 | }; | ||
| 4 | |||
| 5 | export fn entry() usize { | ||
| 6 | return @alignOf(S); | ||
| 7 | } | ||
| 8 | |||
| 9 | // error | ||
| 10 | // | ||
| 11 | // :1:11: error: struct layout depends on being pointer aligned | ||