Skip to content

Commit f8e93f5

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

File tree

7 files changed

+396
-49
lines changed

7 files changed

+396
-49
lines changed

src/AstGen.zig

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4948,7 +4948,10 @@ fn structDeclInner(
49484948

49494949
if (have_value) {
49504950
any_default_inits = true;
4951-
const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = field_type } };
4951+
4952+
// The decl_inst is used as here so that we can easily reconstruct a mapping
4953+
// between it and the field type when the fields inits are analzyed.
4954+
const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = Zir.indexToRef(decl_inst) } };
49524955

49534956
const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
49544957
if (!block_scope.endsWithNoReturn()) {

src/InternPool.zig

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ pub const Key = union(enum) {
370370
layout: std.builtin.Type.ContainerLayout,
371371
field_names: NullTerminatedString.Slice,
372372
field_types: Index.Slice,
373+
field_inits_bodies: InitBody.Slice,
373374
field_inits: Index.Slice,
374375
field_aligns: Alignment.Slice,
375376
runtime_order: RuntimeOrder.Slice,
@@ -400,6 +401,23 @@ pub const Key = union(enum) {
400401
}
401402
};
402403

404+
pub const InitBody = struct {
405+
start: Zir.Inst.Index,
406+
len: u32,
407+
408+
pub const Slice = struct {
409+
start: u32,
410+
len: u32,
411+
412+
pub fn get(this: @This(), ip: *const InternPool) []InitBody {
413+
// Workaround for not being able to case between slices of different lengths
414+
assert(ip.extra.items.len >= this.start + this.len * 2);
415+
const bodies_ptr: [*]InitBody = @ptrCast(ip.extra.items[this.start..].ptr);
416+
return bodies_ptr[0..this.len];
417+
}
418+
};
419+
};
420+
403421
pub const Offsets = struct {
404422
start: u32,
405423
len: u32,
@@ -463,6 +481,7 @@ pub const Key = union(enum) {
463481

464482
pub fn fieldInit(s: @This(), ip: *const InternPool, i: usize) Index {
465483
if (s.field_inits.len == 0) return .none;
484+
assert(s.haveFieldInits(ip));
466485
return s.field_inits.get(ip)[i];
467486
}
468487

@@ -497,6 +516,14 @@ pub const Key = union(enum) {
497516
return @ptrCast(&ip.extra.items[self.extra_index + flags_field_index]);
498517
}
499518

519+
/// The returned pointer expires with any addition to the `InternPool`.
520+
/// Asserts the is not packed.
521+
pub fn packedFlagsPtr(self: @This(), ip: *const InternPool) *Tag.TypeStructPacked.Flags {
522+
assert(self.layout == .Packed);
523+
const flags_field_index = std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?;
524+
return @ptrCast(&ip.extra.items[self.extra_index + flags_field_index]);
525+
}
526+
500527
pub fn assumeRuntimeBitsIfFieldTypesWip(s: @This(), ip: *InternPool) bool {
501528
if (s.layout == .Packed) return false;
502529
const flags_ptr = s.flagsPtr(ip);
@@ -546,6 +573,28 @@ pub const Key = union(enum) {
546573
s.flagsPtr(ip).alignment_wip = false;
547574
}
548575

576+
pub fn setInitsWip(s: @This(), ip: *InternPool) bool {
577+
switch (s.layout) {
578+
inline else => |layout| {
579+
const flag = if (layout == .Packed)
580+
&s.packedFlagsPtr(ip).field_inits_wip
581+
else
582+
&s.flagsPtr(ip).field_inits_wip;
583+
584+
if (flag.*) return true;
585+
flag.* = true;
586+
return false;
587+
},
588+
}
589+
}
590+
591+
pub fn clearInitsWip(s: @This(), ip: *InternPool) void {
592+
switch (s.layout) {
593+
.Packed => s.packedFlagsPtr(ip).field_inits_wip = false,
594+
.Auto, .Extern => s.flagsPtr(ip).field_inits_wip = false,
595+
}
596+
}
597+
549598
pub fn setFullyResolved(s: @This(), ip: *InternPool) bool {
550599
if (s.layout == .Packed) return true;
551600
const flags_ptr = s.flagsPtr(ip);
@@ -588,6 +637,20 @@ pub const Key = union(enum) {
588637
return types.len == 0 or types[0] != .none;
589638
}
590639

640+
pub fn haveFieldInits(s: @This(), ip: *const InternPool) bool {
641+
return switch (s.layout) {
642+
.Packed => s.packedFlagsPtr(ip).inits_resolved,
643+
.Auto, .Extern => s.flagsPtr(ip).inits_resolved,
644+
};
645+
}
646+
647+
pub fn setHaveFieldInits(s: @This(), ip: *InternPool) void {
648+
return switch (s.layout) {
649+
.Packed => s.packedFlagsPtr(ip).inits_resolved = true,
650+
.Auto, .Extern => s.flagsPtr(ip).inits_resolved = true,
651+
};
652+
}
653+
591654
pub fn haveLayout(s: @This(), ip: *InternPool) bool {
592655
return switch (s.layout) {
593656
.Packed => s.backingIntType(ip).* != .none,
@@ -2985,14 +3048,25 @@ pub const Tag = enum(u8) {
29853048
/// Trailing:
29863049
/// 0. type: Index for each fields_len
29873050
/// 1. name: NullTerminatedString for each fields_len
2988-
/// 2. init: Index for each fields_len // if tag is type_struct_packed_inits
3051+
///
3052+
/// if tag is type_struct_packed_inits:
3053+
/// 2. body_indices: Zir.Inst.Index // for each field in declared order, a pair of start index and length.
3054+
/// 3. init: Index for each fields_len
29893055
pub const TypeStructPacked = struct {
29903056
decl: Module.Decl.Index,
29913057
zir_index: Zir.Inst.Index,
29923058
fields_len: u32,
29933059
namespace: Module.Namespace.OptionalIndex,
29943060
backing_int_ty: Index,
29953061
names_map: MapIndex,
3062+
flags: Flags,
3063+
3064+
pub const Flags = packed struct(u32) {
3065+
/// Dependency loop detection when resolving field inits.
3066+
field_inits_wip: bool,
3067+
inits_resolved: bool,
3068+
_: u30 = 0,
3069+
};
29963070
};
29973071

29983072
/// At first I thought of storing the denormalized data externally, such as...
@@ -3015,6 +3089,7 @@ pub const Tag = enum(u8) {
30153089
/// names_map: MapIndex,
30163090
/// name: NullTerminatedString // for each field in declared order
30173091
/// 2. if any_default_inits:
3092+
/// body_indices: Zir.Inst.Index // for each field in declared order, a pair of start index and length.
30183093
/// init: Index // for each field in declared order
30193094
/// 3. if has_namespace:
30203095
/// namespace: Module.Namespace.Index
@@ -3038,6 +3113,7 @@ pub const Tag = enum(u8) {
30383113
requires_comptime: RequiresComptime,
30393114
is_tuple: bool,
30403115
assumed_runtime_bits: bool,
3116+
assumed_pointer_aligned: bool,
30413117
has_namespace: bool,
30423118
any_comptime_fields: bool,
30433119
any_default_inits: bool,
@@ -3050,14 +3126,18 @@ pub const Tag = enum(u8) {
30503126
field_types_wip: bool,
30513127
/// Dependency loop detection when resolving struct layout.
30523128
layout_wip: bool,
3053-
/// Determines whether `size`, `alignment`, runtime field order, and
3129+
/// Indicates whether `size`, `alignment`, runtime field order, and
30543130
/// field offets are populated.
30553131
layout_resolved: bool,
3132+
/// Dependency loop detection when resolving field inits.
3133+
field_inits_wip: bool,
3134+
/// Indicates whether `field_inits` has been resolved.
3135+
inits_resolved: bool,
30563136
// The types and all its fields have had their layout resolved. Even through pointer,
30573137
// which `layout_resolved` does not ensure.
30583138
fully_resolved: bool,
30593139

3060-
_: u11 = 0,
3140+
_: u8 = 0,
30613141
};
30623142
};
30633143
};
@@ -3690,6 +3770,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
36903770
.layout = .Auto,
36913771
.field_names = .{ .start = 0, .len = 0 },
36923772
.field_types = .{ .start = 0, .len = 0 },
3773+
.field_inits_bodies = .{ .start = 0, .len = 0 },
36933774
.field_inits = .{ .start = 0, .len = 0 },
36943775
.field_aligns = .{ .start = 0, .len = 0 },
36953776
.runtime_order = .{ .start = 0, .len = 0 },
@@ -3706,6 +3787,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
37063787
.layout = .Auto,
37073788
.field_names = .{ .start = 0, .len = 0 },
37083789
.field_types = .{ .start = 0, .len = 0 },
3790+
.field_inits_bodies = .{ .start = 0, .len = 0 },
37093791
.field_inits = .{ .start = 0, .len = 0 },
37103792
.field_aligns = .{ .start = 0, .len = 0 },
37113793
.runtime_order = .{ .start = 0, .len = 0 },
@@ -4218,6 +4300,12 @@ fn extraStructType(ip: *const InternPool, extra_index: u32) Key.StructType {
42184300
index += fields_len;
42194301
break :t .{ names_map.toOptional(), names };
42204302
};
4303+
const field_inits_bodies: Key.StructType.InitBody.Slice = t: {
4304+
if (!s.data.flags.any_default_inits) break :t .{ .start = 0, .len = 0 };
4305+
const offsets: Key.StructType.InitBody.Slice = .{ .start = index, .len = fields_len };
4306+
index += fields_len * 2;
4307+
break :t offsets;
4308+
};
42214309
const field_inits: Index.Slice = t: {
42224310
if (!s.data.flags.any_default_inits) break :t .{ .start = 0, .len = 0 };
42234311
const inits: Index.Slice = .{ .start = index, .len = fields_len };
@@ -4261,6 +4349,7 @@ fn extraStructType(ip: *const InternPool, extra_index: u32) Key.StructType {
42614349
.field_types = field_types,
42624350
.names_map = names_map,
42634351
.field_names = field_names,
4352+
.field_inits_bodies = field_inits_bodies,
42644353
.field_inits = field_inits,
42654354
.namespace = namespace,
42664355
.field_aligns = field_aligns,
@@ -4287,13 +4376,20 @@ fn extraPackedStructType(ip: *const InternPool, extra_index: u32, inits: bool) K
42874376
.start = type_struct_packed.end + fields_len,
42884377
.len = fields_len,
42894378
},
4290-
.field_inits = if (inits) .{
4379+
.field_inits_bodies = if (inits) .{
42914380
.start = type_struct_packed.end + fields_len * 2,
42924381
.len = fields_len,
42934382
} else .{
42944383
.start = 0,
42954384
.len = 0,
42964385
},
4386+
.field_inits = if (inits) .{
4387+
.start = type_struct_packed.end + fields_len * 4,
4388+
.len = fields_len,
4389+
} else .{
4390+
.start = 0,
4391+
.len = 0,
4392+
},
42974393
.field_aligns = .{ .start = 0, .len = 0 },
42984394
.runtime_order = .{ .start = 0, .len = 0 },
42994395
.comptime_bits = .{ .start = 0, .len = 0 },
@@ -5340,6 +5436,7 @@ pub const StructTypeInit = struct {
53405436
is_tuple: bool,
53415437
any_comptime_fields: bool,
53425438
any_default_inits: bool,
5439+
inits_resolved: bool,
53435440
any_aligned_fields: bool,
53445441
};
53455442

@@ -5360,6 +5457,7 @@ pub fn getStructType(
53605457
.layout = undefined,
53615458
.field_names = undefined,
53625459
.field_types = undefined,
5460+
.field_inits_bodies = undefined,
53635461
.field_inits = undefined,
53645462
.field_aligns = undefined,
53655463
.runtime_order = undefined,
@@ -5382,6 +5480,7 @@ pub fn getStructType(
53825480
try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStructPacked).Struct.fields.len +
53835481
ini.fields_len + // types
53845482
ini.fields_len + // names
5483+
ini.fields_len * 2 + // init bodies
53855484
ini.fields_len); // inits
53865485
try ip.items.append(gpa, .{
53875486
.tag = if (ini.any_default_inits) .type_struct_packed_inits else .type_struct_packed,
@@ -5392,11 +5491,16 @@ pub fn getStructType(
53925491
.namespace = ini.namespace,
53935492
.backing_int_ty = .none,
53945493
.names_map = names_map,
5494+
.flags = .{
5495+
.field_inits_wip = false,
5496+
.inits_resolved = ini.inits_resolved,
5497+
},
53955498
}),
53965499
});
53975500
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
53985501
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalNullTerminatedString.none), ini.fields_len);
53995502
if (ini.any_default_inits) {
5503+
ip.extra.appendNTimesAssumeCapacity(0, ini.fields_len * 2);
54005504
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
54015505
}
54025506
return @enumFromInt(ip.items.len - 1);
@@ -5408,7 +5512,7 @@ pub fn getStructType(
54085512
const comptime_elements_len = if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0;
54095513

54105514
try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStruct).Struct.fields.len +
5411-
(ini.fields_len * 5) + // types, names, inits, runtime order, offsets
5515+
(ini.fields_len * 7) + // types, names, init bodies, inits, runtime order, offsets
54125516
align_elements_len + comptime_elements_len +
54135517
2); // names_map + namespace
54145518
try ip.items.append(gpa, .{
@@ -5424,6 +5528,7 @@ pub fn getStructType(
54245528
.requires_comptime = ini.requires_comptime,
54255529
.is_tuple = ini.is_tuple,
54265530
.assumed_runtime_bits = false,
5531+
.assumed_pointer_aligned = false,
54275532
.has_namespace = ini.namespace != .none,
54285533
.any_comptime_fields = ini.any_comptime_fields,
54295534
.any_default_inits = ini.any_default_inits,
@@ -5433,6 +5538,8 @@ pub fn getStructType(
54335538
.field_types_wip = false,
54345539
.layout_wip = false,
54355540
.layout_resolved = false,
5541+
.field_inits_wip = false,
5542+
.inits_resolved = ini.inits_resolved,
54365543
.fully_resolved = false,
54375544
},
54385545
}),
@@ -5443,6 +5550,7 @@ pub fn getStructType(
54435550
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalNullTerminatedString.none), ini.fields_len);
54445551
}
54455552
if (ini.any_default_inits) {
5553+
ip.extra.appendNTimesAssumeCapacity(0, ini.fields_len * 2);
54465554
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
54475555
}
54485556
if (ini.namespace.unwrap()) |namespace| {
@@ -6443,6 +6551,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
64436551
Tag.TypePointer.PackedOffset,
64446552
Tag.TypeUnion.Flags,
64456553
Tag.TypeStruct.Flags,
6554+
Tag.TypeStructPacked.Flags,
64466555
Tag.Variable.Flags,
64476556
=> @bitCast(@field(extra, field.name)),
64486557

@@ -6516,6 +6625,7 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct
65166625
Tag.TypePointer.PackedOffset,
65176626
Tag.TypeUnion.Flags,
65186627
Tag.TypeStruct.Flags,
6628+
Tag.TypeStructPacked.Flags,
65196629
Tag.Variable.Flags,
65206630
FuncAnalysis,
65216631
=> @bitCast(int32),

0 commit comments

Comments
 (0)