authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-02-27 10:40:12+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:13+00:00
log51c23f7ba4f09f26f53b21299a456f2ed9d7839e
tree48ef4833fe4b90f8dd3866c9060cfeec3b227be3
parent0075c5a1d5ad1198b16e6a1c390c985a8e4354de
signaturelock-open Commit is signed but in an unrecognized format.

compiler: split default field values back out from layout resolution

I was trying out combining struct layout resolution with resolution of default field values, but it broke a few cases which it's not clear we want to break. The simplest such case was a struct with a field which was a slice of itself, with a default value of `&.{}`. So, at least for now, I'm accepting defeat and splitting this back out. This allows a couple of behavior tests which were removed to be re-introduced---I will do that in the commit following this one. I have *not* made this separate phase of resolution "lazy": instead, it is tied to layout resolution, in the sense that if a struct's layout is referenced, then its default field values are also referenced. I chose this approach for simplicity---not of the implementation (it's actually slightly *more* code to do it this way!), but in terms of the language specification. I think this behavior is easier to understand and keep in your head. It can be easily changed in future if we decide we want to. This partially reverts the commit titled "compiler: merge struct default value resolution into layout resolution".

9 files changed, 396 insertions(+), 72 deletions(-)

src/Compilation.zig+5-1
...@@ -3647,6 +3647,7 @@ const Header = extern struct {...@@ -3647,6 +3647,7 @@ const Header = extern struct {
3647 nav_val_deps_len: u32,3647 nav_val_deps_len: u32,
3648 nav_ty_deps_len: u32,3648 nav_ty_deps_len: u32,
3649 type_layout_deps_len: u32,3649 type_layout_deps_len: u32,
3650 struct_defaults_deps_len: u32,
3650 func_ies_deps_len: u32,3651 func_ies_deps_len: u32,
3651 zon_file_deps_len: u32,3652 zon_file_deps_len: u32,
3652 embed_file_deps_len: u32,3653 embed_file_deps_len: u32,
...@@ -3696,6 +3697,7 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3696,6 +3697,7 @@ pub fn saveState(comp: *Compilation) !void {
3696 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),3697 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
3697 .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),3698 .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
3698 .type_layout_deps_len = @intCast(ip.type_layout_deps.count()),3699 .type_layout_deps_len = @intCast(ip.type_layout_deps.count()),
3700 .struct_defaults_deps_len = @intCast(ip.struct_defaults_deps.count()),
3699 .func_ies_deps_len = @intCast(ip.func_ies_deps.count()),3701 .func_ies_deps_len = @intCast(ip.func_ies_deps.count()),
3700 .zon_file_deps_len = @intCast(ip.zon_file_deps.count()),3702 .zon_file_deps_len = @intCast(ip.zon_file_deps.count()),
3701 .embed_file_deps_len = @intCast(ip.embed_file_deps.count()),3703 .embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
...@@ -3720,7 +3722,7 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3720,7 +3722,7 @@ pub fn saveState(comp: *Compilation) !void {
3720 },3722 },
3721 });3723 });
37223724
3723 try bufs.ensureTotalCapacityPrecise(24 + 9 * pt_headers.items.len);3725 try bufs.ensureTotalCapacityPrecise(26 + 9 * pt_headers.items.len);
3724 addBuf(&bufs, mem.asBytes(&header));3726 addBuf(&bufs, mem.asBytes(&header));
3725 addBuf(&bufs, @ptrCast(pt_headers.items));3727 addBuf(&bufs, @ptrCast(pt_headers.items));
37263728
...@@ -3732,6 +3734,8 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3732,6 +3734,8 @@ pub fn saveState(comp: *Compilation) !void {
3732 addBuf(&bufs, @ptrCast(ip.nav_ty_deps.values()));3734 addBuf(&bufs, @ptrCast(ip.nav_ty_deps.values()));
3733 addBuf(&bufs, @ptrCast(ip.type_layout_deps.keys()));3735 addBuf(&bufs, @ptrCast(ip.type_layout_deps.keys()));
3734 addBuf(&bufs, @ptrCast(ip.type_layout_deps.values()));3736 addBuf(&bufs, @ptrCast(ip.type_layout_deps.values()));
3737 addBuf(&bufs, @ptrCast(ip.struct_defaults_deps.keys()));
3738 addBuf(&bufs, @ptrCast(ip.struct_defaults_deps.values()));
3735 addBuf(&bufs, @ptrCast(ip.func_ies_deps.keys()));3739 addBuf(&bufs, @ptrCast(ip.func_ies_deps.keys()));
3736 addBuf(&bufs, @ptrCast(ip.func_ies_deps.values()));3740 addBuf(&bufs, @ptrCast(ip.func_ies_deps.values()));
3737 addBuf(&bufs, @ptrCast(ip.zon_file_deps.keys()));3741 addBuf(&bufs, @ptrCast(ip.zon_file_deps.keys()));
src/IncrementalDebugServer.zig+3-1
...@@ -307,7 +307,7 @@ fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const...@@ -307,7 +307,7 @@ fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const
307 switch (dependee) {307 switch (dependee) {
308 .src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}),308 .src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}),
309 .nav_val, .nav_ty => |nav| try w.print("{t} {d}", .{ dependee, @intFromEnum(nav) }),309 .nav_val, .nav_ty => |nav| try w.print("{t} {d}", .{ dependee, @intFromEnum(nav) }),
310 .type_layout, .func_ies => |ip_index| try w.print("{t} {d}", .{ dependee, @intFromEnum(ip_index) }),310 .type_layout, .struct_defaults, .func_ies => |ip_index| try w.print("{t} {d}", .{ dependee, @intFromEnum(ip_index) }),
311 .memoized_state => |stage| try w.print("memoized_state {s}", .{@tagName(stage)}),311 .memoized_state => |stage| try w.print("memoized_state {s}", .{@tagName(stage)}),
312 }312 }
313 try w.writeByte('\n');313 try w.writeByte('\n');
...@@ -374,6 +374,8 @@ fn parseAnalUnit(str: []const u8) ?AnalUnit {...@@ -374,6 +374,8 @@ fn parseAnalUnit(str: []const u8) ?AnalUnit {
374 return .wrap(.{ .nav_ty = @enumFromInt(parseIndex(idx_str) orelse return null) });374 return .wrap(.{ .nav_ty = @enumFromInt(parseIndex(idx_str) orelse return null) });
375 } else if (std.mem.eql(u8, kind, "type_layout")) {375 } else if (std.mem.eql(u8, kind, "type_layout")) {
376 return .wrap(.{ .type_layout = @enumFromInt(parseIndex(idx_str) orelse return null) });376 return .wrap(.{ .type_layout = @enumFromInt(parseIndex(idx_str) orelse return null) });
377 } else if (std.mem.eql(u8, kind, "struct_defaults")) {
378 return .wrap(.{ .struct_defaults = @enumFromInt(parseIndex(idx_str) orelse return null) });
377 } else if (std.mem.eql(u8, kind, "func")) {379 } else if (std.mem.eql(u8, kind, "func")) {
378 return .wrap(.{ .func = @enumFromInt(parseIndex(idx_str) orelse return null) });380 return .wrap(.{ .func = @enumFromInt(parseIndex(idx_str) orelse return null) });
379 } else if (std.mem.eql(u8, kind, "memoized_state")) {381 } else if (std.mem.eql(u8, kind, "memoized_state")) {
src/InternPool.zig+17-1
...@@ -54,6 +54,9 @@ func_ies_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),...@@ -54,6 +54,9 @@ func_ies_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
54/// Dependencies on the resolved layout of a `struct`, `union`, or `enum` type.54/// Dependencies on the resolved layout of a `struct`, `union`, or `enum` type.
55/// Value is index into `dep_entries` of the first dependency on this type's layout.55/// Value is index into `dep_entries` of the first dependency on this type's layout.
56type_layout_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),56type_layout_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
57/// Dependencies on the resolved default field values of a `struct` type.
58/// Value is index into `dep_entries` of the first dependency on this type's inits.
59struct_defaults_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
57/// Dependencies on a ZON file. Triggered by `@import` of ZON.60/// Dependencies on a ZON file. Triggered by `@import` of ZON.
58/// Value is index into `dep_entries` of the first dependency on this ZON file.61/// Value is index into `dep_entries` of the first dependency on this ZON file.
59zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),62zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
...@@ -108,6 +111,7 @@ pub const empty: InternPool = .{...@@ -108,6 +111,7 @@ pub const empty: InternPool = .{
108 .nav_ty_deps = .empty,111 .nav_ty_deps = .empty,
109 .func_ies_deps = .empty,112 .func_ies_deps = .empty,
110 .type_layout_deps = .empty,113 .type_layout_deps = .empty,
114 .struct_defaults_deps = .empty,
111 .zon_file_deps = .empty,115 .zon_file_deps = .empty,
112 .embed_file_deps = .empty,116 .embed_file_deps = .empty,
113 .namespace_deps = .empty,117 .namespace_deps = .empty,
...@@ -419,6 +423,7 @@ pub const AnalUnit = packed struct(u64) {...@@ -419,6 +423,7 @@ pub const AnalUnit = packed struct(u64) {
419 nav_val,423 nav_val,
420 nav_ty,424 nav_ty,
421 type_layout,425 type_layout,
426 struct_defaults,
422 func,427 func,
423 memoized_state,428 memoized_state,
424 };429 };
...@@ -432,6 +437,8 @@ pub const AnalUnit = packed struct(u64) {...@@ -432,6 +437,8 @@ pub const AnalUnit = packed struct(u64) {
432 nav_ty: Nav.Index,437 nav_ty: Nav.Index,
433 /// This `AnalUnit` resolves the layout of the given `struct`, `union`, or `enum` type.438 /// This `AnalUnit` resolves the layout of the given `struct`, `union`, or `enum` type.
434 type_layout: InternPool.Index,439 type_layout: InternPool.Index,
440 /// This `AnalUnit` resolves the default field values of the given `struct` type.
441 struct_defaults: InternPool.Index,
435 /// This `AnalUnit` analyzes the body of the given runtime function.442 /// This `AnalUnit` analyzes the body of the given runtime function.
436 func: InternPool.Index,443 func: InternPool.Index,
437 /// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`.444 /// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`.
...@@ -851,6 +858,7 @@ pub const Dependee = union(enum) {...@@ -851,6 +858,7 @@ pub const Dependee = union(enum) {
851 /// Index is the function, not its IES.858 /// Index is the function, not its IES.
852 func_ies: Index,859 func_ies: Index,
853 type_layout: Index,860 type_layout: Index,
861 struct_defaults: Index,
854 zon_file: FileIndex,862 zon_file: FileIndex,
855 embed_file: Zcu.EmbedFile.Index,863 embed_file: Zcu.EmbedFile.Index,
856 namespace: TrackedInst.Index,864 namespace: TrackedInst.Index,
...@@ -904,6 +912,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI...@@ -904,6 +912,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
904 .nav_ty => |x| ip.nav_ty_deps.get(x),912 .nav_ty => |x| ip.nav_ty_deps.get(x),
905 .func_ies => |x| ip.func_ies_deps.get(x),913 .func_ies => |x| ip.func_ies_deps.get(x),
906 .type_layout => |x| ip.type_layout_deps.get(x),914 .type_layout => |x| ip.type_layout_deps.get(x),
915 .struct_defaults => |x| ip.struct_defaults_deps.get(x),
907 .zon_file => |x| ip.zon_file_deps.get(x),916 .zon_file => |x| ip.zon_file_deps.get(x),
908 .embed_file => |x| ip.embed_file_deps.get(x),917 .embed_file => |x| ip.embed_file_deps.get(x),
909 .namespace => |x| ip.namespace_deps.get(x),918 .namespace => |x| ip.namespace_deps.get(x),
...@@ -978,6 +987,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend...@@ -978,6 +987,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
978 .nav_ty => ip.nav_ty_deps,987 .nav_ty => ip.nav_ty_deps,
979 .func_ies => ip.func_ies_deps,988 .func_ies => ip.func_ies_deps,
980 .type_layout => ip.type_layout_deps,989 .type_layout => ip.type_layout_deps,
990 .struct_defaults => ip.struct_defaults_deps,
981 .zon_file => ip.zon_file_deps,991 .zon_file => ip.zon_file_deps,
982 .embed_file => ip.embed_file_deps,992 .embed_file => ip.embed_file_deps,
983 .namespace => ip.namespace_deps,993 .namespace => ip.namespace_deps,
...@@ -6454,6 +6464,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator, io: Io) void {...@@ -6454,6 +6464,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator, io: Io) void {
6454 ip.nav_ty_deps.deinit(gpa);6464 ip.nav_ty_deps.deinit(gpa);
6455 ip.func_ies_deps.deinit(gpa);6465 ip.func_ies_deps.deinit(gpa);
6456 ip.type_layout_deps.deinit(gpa);6466 ip.type_layout_deps.deinit(gpa);
6467 ip.struct_defaults_deps.deinit(gpa);
6457 ip.zon_file_deps.deinit(gpa);6468 ip.zon_file_deps.deinit(gpa);
6458 ip.embed_file_deps.deinit(gpa);6469 ip.embed_file_deps.deinit(gpa);
6459 ip.namespace_deps.deinit(gpa);6470 ip.namespace_deps.deinit(gpa);
...@@ -10619,6 +10630,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {...@@ -10619,6 +10630,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
10619 const nav_ty_deps_len = ip.nav_ty_deps.count();10630 const nav_ty_deps_len = ip.nav_ty_deps.count();
10620 const func_ies_deps_len = ip.func_ies_deps.count();10631 const func_ies_deps_len = ip.func_ies_deps.count();
10621 const type_layout_deps_len = ip.type_layout_deps.count();10632 const type_layout_deps_len = ip.type_layout_deps.count();
10633 const struct_defaults_deps_len = ip.struct_defaults_deps.count();
10622 const zon_file_deps_len = ip.zon_file_deps.count();10634 const zon_file_deps_len = ip.zon_file_deps.count();
10623 const embed_file_deps_len = ip.embed_file_deps.count();10635 const embed_file_deps_len = ip.embed_file_deps.count();
10624 const namespace_deps_len = ip.namespace_deps.count();10636 const namespace_deps_len = ip.namespace_deps.count();
...@@ -10629,6 +10641,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {...@@ -10629,6 +10641,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
10629 const nav_ty_deps_size = nav_ty_deps_len * 8;10641 const nav_ty_deps_size = nav_ty_deps_len * 8;
10630 const func_ies_deps_size = func_ies_deps_len * 8;10642 const func_ies_deps_size = func_ies_deps_len * 8;
10631 const type_layout_deps_size = type_layout_deps_len * 8;10643 const type_layout_deps_size = type_layout_deps_len * 8;
10644 const struct_defaults_deps_size = struct_defaults_deps_len * 8;
10632 const zon_file_deps_size = zon_file_deps_len * 8;10645 const zon_file_deps_size = zon_file_deps_len * 8;
10633 const embed_file_deps_size = embed_file_deps_len * 8;10646 const embed_file_deps_size = embed_file_deps_len * 8;
10634 const namespace_deps_size = namespace_deps_len * 8;10647 const namespace_deps_size = namespace_deps_len * 8;
...@@ -10642,6 +10655,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {...@@ -10642,6 +10655,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
10642 \\ {d} nav_ty: {d} bytes10655 \\ {d} nav_ty: {d} bytes
10643 \\ {d} func_ies: {d} bytes10656 \\ {d} func_ies: {d} bytes
10644 \\ {d} type_layout: {d} bytes10657 \\ {d} type_layout: {d} bytes
10658 \\ {d} struct_defaults: {d} bytes
10645 \\ {d} zon_file: {d} bytes10659 \\ {d} zon_file: {d} bytes
10646 \\ {d} embed_file: {d} bytes10660 \\ {d} embed_file: {d} bytes
10647 \\ {d} namespace: {d} bytes10661 \\ {d} namespace: {d} bytes
...@@ -10649,7 +10663,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {...@@ -10649,7 +10663,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
10649 \\10663 \\
10650 , .{10664 , .{
10651 dep_entries_size + src_hash_deps_size + nav_val_deps_size + nav_ty_deps_size +10665 dep_entries_size + src_hash_deps_size + nav_val_deps_size + nav_ty_deps_size +
10652 func_ies_deps_size + type_layout_deps_size + zon_file_deps_size +10666 func_ies_deps_size + type_layout_deps_size + struct_defaults_deps_size + zon_file_deps_size +
10653 embed_file_deps_size + namespace_deps_size + namespace_name_deps_size,10667 embed_file_deps_size + namespace_deps_size + namespace_name_deps_size,
10654 dep_entries_len,10668 dep_entries_len,
10655 dep_entries_size,10669 dep_entries_size,
...@@ -10663,6 +10677,8 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {...@@ -10663,6 +10677,8 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
10663 func_ies_deps_size,10677 func_ies_deps_size,
10664 type_layout_deps_len,10678 type_layout_deps_len,
10665 type_layout_deps_size,10679 type_layout_deps_size,
10680 struct_defaults_deps_len,
10681 struct_defaults_deps_size,
10666 zon_file_deps_len,10682 zon_file_deps_len,
10667 zon_file_deps_size,10683 zon_file_deps_size,
10668 embed_file_deps_len,10684 embed_file_deps_len,
src/Sema.zig+35-21
...@@ -4533,6 +4533,10 @@ fn validateStructInit(...@@ -4533,6 +4533,10 @@ fn validateStructInit(
4533 if (explicit) continue;4533 if (explicit) continue;
4534 if (struct_ty.structFieldIsComptime(i, zcu)) continue;4534 if (struct_ty.structFieldIsComptime(i, zcu)) continue;
45354535
4536 if (!struct_ty.isTuple(zcu)) {
4537 try sema.ensureStructDefaultsResolved(struct_ty, init_src);
4538 }
4539
4536 const default_val = struct_ty.structFieldDefaultValue(i, zcu) orelse {4540 const default_val = struct_ty.structFieldDefaultValue(i, zcu) orelse {
4537 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {4541 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
4538 const template = "missing tuple field with index {d}";4542 const template = "missing tuple field with index {d}";
...@@ -5850,6 +5854,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {...@@ -5850,6 +5854,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
5850 .nav_val,5854 .nav_val,
5851 .nav_ty,5855 .nav_ty,
5852 .type_layout,5856 .type_layout,
5857 .struct_defaults,
5853 .memoized_state,5858 .memoized_state,
5854 => return, // does nothing outside a function5859 => return, // does nothing outside a function
5855 };5860 };
...@@ -5868,6 +5873,7 @@ fn zirDisableIntrinsics(sema: *Sema) CompileError!void {...@@ -5868,6 +5873,7 @@ fn zirDisableIntrinsics(sema: *Sema) CompileError!void {
5868 .nav_val,5873 .nav_val,
5869 .nav_ty,5874 .nav_ty,
5870 .type_layout,5875 .type_layout,
5876 .struct_defaults,
5871 .memoized_state,5877 .memoized_state,
5872 => return, // does nothing outside a function5878 => return, // does nothing outside a function
5873 };5879 };
...@@ -7091,7 +7097,14 @@ fn analyzeCall(...@@ -7091,7 +7097,14 @@ fn analyzeCall(
7091 });7097 });
7092 if (func_ty_info.cc == .auto) {7098 if (func_ty_info.cc == .auto) {
7093 switch (sema.owner.unwrap()) {7099 switch (sema.owner.unwrap()) {
7094 .@"comptime", .nav_ty, .nav_val, .type_layout, .memoized_state => {},7100 .@"comptime",
7101 .nav_ty,
7102 .nav_val,
7103 .type_layout,
7104 .struct_defaults,
7105 .memoized_state,
7106 => {},
7107
7095 .func => |owner_func| ip.funcSetHasErrorTrace(io, owner_func, true),7108 .func => |owner_func| ip.funcSetHasErrorTrace(io, owner_func, true),
7096 }7109 }
7097 }7110 }
...@@ -16907,6 +16920,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -16907,6 +16920,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
16907 .struct_type => ip.loadStructType(ty.toIntern()),16920 .struct_type => ip.loadStructType(ty.toIntern()),
16908 else => unreachable,16921 else => unreachable,
16909 };16922 };
16923 try sema.ensureStructDefaultsResolved(ty, src); // can't do this sooner, since it's not allowed on tuples
16910 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);16924 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
1691116925
16912 for (struct_field_vals, 0..) |*field_val, field_index| {16926 for (struct_field_vals, 0..) |*field_val, field_index| {
...@@ -18788,6 +18802,8 @@ fn finishStructInit(...@@ -18788,6 +18802,8 @@ fn finishStructInit(
18788 continue;18802 continue;
18789 }18803 }
1879018804
18805 try sema.ensureStructDefaultsResolved(struct_ty, init_src);
18806
18791 const field_default: InternPool.Index = d: {18807 const field_default: InternPool.Index = d: {
18792 if (struct_type.field_defaults.len == 0) break :d .none;18808 if (struct_type.field_defaults.len == 0) break :d .none;
18793 break :d struct_type.field_defaults.get(ip)[i];18809 break :d struct_type.field_defaults.get(ip)[i];
...@@ -19454,7 +19470,14 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {...@@ -19454,7 +19470,14 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
19454 .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) {19470 .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) {
19455 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);19471 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
19456 },19472 },
19457 .@"comptime", .nav_ty, .nav_val, .type_layout, .memoized_state => {},19473
19474 .@"comptime",
19475 .nav_ty,
19476 .nav_val,
19477 .type_layout,
19478 .struct_defaults,
19479 .memoized_state,
19480 => {},
19458 }19481 }
19459 return Air.internedToRef(try pt.intern(.{ .opt = .{19482 return Air.internedToRef(try pt.intern(.{ .opt = .{
19460 .ty = opt_ptr_stack_trace_ty.toIntern(),19483 .ty = opt_ptr_stack_trace_ty.toIntern(),
...@@ -24784,7 +24807,7 @@ fn zirBuiltinExtern(...@@ -24784,7 +24807,7 @@ fn zirBuiltinExtern(
24784 // So, for now, just use our containing `declaration`.24807 // So, for now, just use our containing `declaration`.
24785 .zir_index = switch (sema.owner.unwrap()) {24808 .zir_index = switch (sema.owner.unwrap()) {
24786 .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,24809 .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
24787 .type_layout => |owner_ty| Type.fromInterned(owner_ty).typeDeclInstAllowGeneratedTag(zcu).?,24810 .type_layout, .struct_defaults => |owner_ty| Type.fromInterned(owner_ty).typeDeclInstAllowGeneratedTag(zcu).?,
24788 .memoized_state => unreachable,24811 .memoized_state => unreachable,
24789 .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,24812 .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
24790 .func => |func| zir_index: {24813 .func => |func| zir_index: {
...@@ -25276,7 +25299,14 @@ fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !In...@@ -25276,7 +25299,14 @@ fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !In
25276 try sema.ensureMemoizedStateResolved(src, .panic);25299 try sema.ensureMemoizedStateResolved(src, .panic);
25277 const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin());25300 const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin());
25278 switch (sema.owner.unwrap()) {25301 switch (sema.owner.unwrap()) {
25279 .@"comptime", .nav_ty, .nav_val, .type_layout, .memoized_state => {},25302 .@"comptime",
25303 .nav_ty,
25304 .nav_val,
25305 .type_layout,
25306 .struct_defaults,
25307 .memoized_state,
25308 => {},
25309
25280 .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(io, owner_func, true),25310 .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(io, owner_func, true),
25281 }25311 }
25282 return panic_fn_index;25312 return panic_fn_index;
...@@ -33541,23 +33571,6 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {...@@ -33541,23 +33571,6 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
33541 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);33571 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);
33542 if (gop.found_existing) return;33572 if (gop.found_existing) return;
3354333573
33544 // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields
33545 // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would
33546 // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
33547 // the loop.
33548 // Note that this also disallows a `nav_val`
33549 switch (sema.owner.unwrap()) {
33550 .nav_val => |this_nav| switch (dependee) {
33551 .nav_val => |other_nav| if (this_nav == other_nav) return,
33552 else => {},
33553 },
33554 .nav_ty => |this_nav| switch (dependee) {
33555 .nav_ty => |other_nav| if (this_nav == other_nav) return,
33556 else => {},
33557 },
33558 else => {},
33559 }
33560
33561 try pt.addDependency(sema.owner, dependee);33574 try pt.addDependency(sema.owner, dependee);
33562}33575}
3356333576
...@@ -33923,6 +33936,7 @@ const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStor...@@ -33923,6 +33936,7 @@ const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStor
3392333936
33924pub const type_resolution = @import("Sema/type_resolution.zig");33937pub const type_resolution = @import("Sema/type_resolution.zig");
33925pub const ensureLayoutResolved = type_resolution.ensureLayoutResolved;33938pub const ensureLayoutResolved = type_resolution.ensureLayoutResolved;
33939pub const ensureStructDefaultsResolved = type_resolution.ensureStructDefaultsResolved;
3392633940
33927pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!Type {33941pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!Type {
33928 assert(decl.kind() == .type);33942 assert(decl.kind() == .type);
src/Sema/LowerZon.zig+1
...@@ -758,6 +758,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool...@@ -758,6 +758,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
758 const ip = &pt.zcu.intern_pool;758 const ip = &pt.zcu.intern_pool;
759759
760 try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init);760 try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init);
761 try self.sema.ensureStructDefaultsResolved(res_ty, self.import_loc);
761 const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?;762 const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?;
762763
763 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {764 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
src/Sema/type_resolution.zig+158-40
...@@ -134,6 +134,33 @@ fn ensureLayoutResolvedInner(sema: *Sema, ty: Type, orig_ty: Type, reason: *cons...@@ -134,6 +134,33 @@ fn ensureLayoutResolvedInner(sema: *Sema, ty: Type, orig_ty: Type, reason: *cons
134 }134 }
135}135}
136136
137/// Asserts that `ty` is a non-tuple `struct` type, and ensures that its fields' default values
138/// are resolved. Adds incremental dependencies tracking the required type resolution.
139///
140/// It is not necessary to call this function to query the values of comptime fields: those values
141/// are available from type *layout* resolution, see `ensureLayoutResolved`.
142///
143/// Asserts that the *layout* of `ty` has already been resolved---see `ensureLayoutResolved`.
144pub fn ensureStructDefaultsResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!void {
145 const pt = sema.pt;
146 const zcu = pt.zcu;
147 const ip = &zcu.intern_pool;
148
149 assert(ip.indexToKey(ty.toIntern()) == .struct_type);
150 if (zcu.comp.config.incremental) assert(sema.dependencies.contains(.{ .type_layout = ty.toIntern() }));
151
152 try sema.declareDependency(.{ .struct_defaults = ty.toIntern() });
153 try sema.addReferenceEntry(null, src, .wrap(.{ .struct_defaults = ty.toIntern() }));
154
155 const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined };
156
157 if (zcu.analysis_in_progress.contains(.wrap(.{ .struct_defaults = ty.toIntern() }))) {
158 return sema.failWithDependencyLoop(.wrap(.{ .struct_defaults = ty.toIntern() }), &reason);
159 }
160
161 try pt.ensureStructDefaultsUpToDate(ty, &reason);
162}
163
137/// Asserts that `struct_ty` is a non-packed non-tuple struct, and that `sema.owner` is that type.164/// Asserts that `struct_ty` is a non-packed non-tuple struct, and that `sema.owner` is that type.
138/// This function *does* register the `src_hash` dependency on the struct.165/// This function *does* register the `src_hash` dependency on the struct.
139pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {166pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
...@@ -193,14 +220,8 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -193,14 +220,8 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
193 @memset(struct_obj.field_is_comptime_bits.getAll(ip), 0);220 @memset(struct_obj.field_is_comptime_bits.getAll(ip), 0);
194221
195 const zir_struct = sema.code.getStructDecl(zir_index);222 const zir_struct = sema.code.getStructDecl(zir_index);
196
197 // If we have any default values to resolve, we'll need to map the struct decl instruction
198 // to the result type.
199 if (zir_struct.field_default_body_lens != null) {
200 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index});
201 }
202
203 var field_it = zir_struct.iterateFields();223 var field_it = zir_struct.iterateFields();
224 var any_comptime_fields = false;
204 while (field_it.next()) |zir_field| {225 while (field_it.next()) |zir_field| {
205 {226 {
206 const name_slice = sema.code.nullTerminatedString(zir_field.name);227 const name_slice = sema.code.nullTerminatedString(zir_field.name);
...@@ -212,18 +233,21 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -212,18 +233,21 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
212 const bit_bag_index = zir_field.idx / 32;233 const bit_bag_index = zir_field.idx / 32;
213 const mask = @as(u32, 1) << @intCast(zir_field.idx % 32);234 const mask = @as(u32, 1) << @intCast(zir_field.idx % 32);
214 struct_obj.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask;235 struct_obj.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask;
236 any_comptime_fields = true;
215 }237 }
216238
217 const field_ty: Type = field_ty: {239 {
218 const field_ty_src = block.src(.{ .container_field_type = zir_field.idx });240 const field_ty_src = block.src(.{ .container_field_type = zir_field.idx });
219 block.comptime_reason = .{ .reason = .{241 const field_ty: Type = field_ty: {
220 .src = field_ty_src,242 block.comptime_reason = .{ .reason = .{
221 .r = .{ .simple = .struct_field_types },243 .src = field_ty_src,
222 } };244 .r = .{ .simple = .struct_field_types },
223 const type_ref = try sema.resolveInlineBody(&block, zir_field.type_body, zir_index);245 } };
224 break :field_ty try sema.analyzeAsType(&block, field_ty_src, .struct_field_types, type_ref);246 const type_ref = try sema.resolveInlineBody(&block, zir_field.type_body, zir_index);
225 };247 break :field_ty try sema.analyzeAsType(&block, field_ty_src, .struct_field_types, type_ref);
226 struct_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern();248 };
249 struct_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern();
250 }
227251
228 if (struct_obj.field_aligns.len == 0) {252 if (struct_obj.field_aligns.len == 0) {
229 assert(zir_field.align_body == null);253 assert(zir_field.align_body == null);
...@@ -240,31 +264,13 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -240,31 +264,13 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
240 };264 };
241 struct_obj.field_aligns.get(ip)[zir_field.idx] = field_align;265 struct_obj.field_aligns.get(ip)[zir_field.idx] = field_align;
242 }266 }
267 }
243268
244 if (struct_obj.field_defaults.len == 0) {269 // We also resolve the default values of any `comptime` fields now. This is not necessary in
245 assert(zir_field.default_body == null);270 // the case of a reified struct because the the default values were already poulated and
246 } else {271 // validated by `Sema.zirReifyStruct`.
247 const field_default_src = block.src(.{ .container_field_value = zir_field.idx });272 if (any_comptime_fields) {
248 const field_default: InternPool.Index = d: {273 try resolveStructDefaultsInner(sema, &block, &struct_obj, .comptime_fields);
249 block.comptime_reason = .{ .reason = .{
250 .src = field_default_src,
251 .r = .{ .simple = .struct_field_default_value },
252 } };
253 const default_body = zir_field.default_body orelse break :d .none;
254 // Provide the result type
255 sema.inst_map.putAssumeCapacity(zir_index, .fromType(field_ty));
256 defer assert(sema.inst_map.remove(zir_index));
257 const uncoerced_default_val = try sema.resolveInlineBody(&block, default_body, zir_index);
258 const coerced_default_val = try sema.coerce(&block, field_ty, uncoerced_default_val, field_default_src);
259 const default_val = try sema.resolveConstValue(&block, field_default_src, coerced_default_val, null);
260 if (default_val.canMutateComptimeVarState(zcu)) {
261 const field_name = struct_obj.field_names.get(ip)[zir_field.idx];
262 return sema.failWithContainsReferenceToComptimeVar(&block, field_default_src, field_name, "field default value", default_val);
263 }
264 break :d default_val.toIntern();
265 };
266 struct_obj.field_defaults.get(ip)[zir_field.idx] = field_default;
267 }
268 }274 }
269 }275 }
270276
...@@ -523,6 +529,118 @@ fn resolvePackedStructLayout(...@@ -523,6 +529,118 @@ fn resolvePackedStructLayout(
523 );529 );
524}530}
525531
532/// Asserts that `struct_ty` is a non-tuple struct, and that `sema.owner` is that type.
533///
534/// Also asserts that the layout of `struct_ty` has *already* been resolved (though it is okay for
535/// that resolution to have failed). This requirement exists to ensure better error messages in the
536/// event of a dependency loop.
537///
538/// This function *does* register the `src_hash` dependency on the struct.
539pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void {
540 const pt = sema.pt;
541 const zcu = pt.zcu;
542 const comp = zcu.comp;
543 const gpa = comp.gpa;
544 const ip = &zcu.intern_pool;
545
546 assert(sema.owner.unwrap().struct_defaults == struct_ty.toIntern());
547
548 // We always depend on the layout of `struct_ty`. However, we don't actually need to resolve it
549 // now, because the caller has done so for us. Just mark the dependency so that the incremental
550 // compilation handling understands the dependency graph.
551 try sema.declareDependency(.{ .type_layout = struct_ty.toIntern() });
552 struct_ty.assertHasLayout(zcu);
553 const layout_unit: InternPool.AnalUnit = .wrap(.{ .type_layout = struct_ty.toIntern() });
554 if (zcu.failed_analysis.contains(layout_unit) or zcu.transitive_failed_analysis.contains(layout_unit)) {
555 return error.AnalysisFail;
556 }
557
558 const struct_obj = ip.loadStructType(struct_ty.toIntern());
559 assert(struct_obj.want_layout);
560
561 if (struct_obj.is_reified) {
562 // `Sema.zirReifyStruct` has already populated the default field values *and* (by loading
563 // the default values from pointers) validated their types, so we have nothing to do.
564 return;
565 }
566
567 try sema.declareDependency(.{ .src_hash = struct_obj.zir_index });
568
569 if (struct_obj.field_defaults.len == 0) {
570 // The struct has no default field values, so the slice has been omitted.
571 return;
572 }
573
574 var block: Block = .{
575 .parent = null,
576 .sema = sema,
577 .namespace = struct_obj.namespace,
578 .instructions = .empty,
579 .inlining = null,
580 .comptime_reason = undefined, // always set before using `block`
581 .src_base_inst = struct_obj.zir_index,
582 .type_name_ctx = struct_obj.name,
583 };
584 defer block.instructions.deinit(gpa);
585
586 return resolveStructDefaultsInner(sema, &block, &struct_obj, .normal_fields);
587}
588
589/// Asserts that the struct is not reified, and that `struct_obj.field_defaults.len` is non-zero.
590fn resolveStructDefaultsInner(
591 sema: *Sema,
592 block: *Block,
593 struct_obj: *const InternPool.LoadedStructType,
594 mode: enum { comptime_fields, normal_fields },
595) CompileError!void {
596 const pt = sema.pt;
597 const zcu = pt.zcu;
598 const comp = zcu.comp;
599 const gpa = comp.gpa;
600 const ip = &zcu.intern_pool;
601
602 assert(struct_obj.field_defaults.len > 0);
603
604 // We'll need to map the struct decl instruction to provide result types
605 const zir_index = struct_obj.zir_index.resolve(ip) orelse return error.AnalysisFail;
606 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index});
607
608 const field_types = struct_obj.field_types.get(ip);
609
610 const zir_struct = sema.code.getStructDecl(zir_index);
611 var field_it = zir_struct.iterateFields();
612 while (field_it.next()) |zir_field| {
613 switch (mode) {
614 .comptime_fields => if (!zir_field.is_comptime) continue,
615 .normal_fields => if (zir_field.is_comptime) continue,
616 }
617
618 const default_val_src = block.src(.{ .container_field_value = zir_field.idx });
619 block.comptime_reason = .{ .reason = .{
620 .src = default_val_src,
621 .r = .{ .simple = .struct_field_default_value },
622 } };
623 const default_body = zir_field.default_body orelse {
624 struct_obj.field_defaults.get(ip)[zir_field.idx] = .none;
625 continue;
626 };
627 const field_ty: Type = .fromInterned(field_types[zir_field.idx]);
628 const uncoerced = ref: {
629 // Provide the result type
630 sema.inst_map.putAssumeCapacity(zir_index, .fromIntern(field_ty.toIntern()));
631 defer assert(sema.inst_map.remove(zir_index));
632 break :ref try sema.resolveInlineBody(block, default_body, zir_index);
633 };
634 const coerced = try sema.coerce(block, field_ty, uncoerced, default_val_src);
635 const default_val = try sema.resolveConstValue(block, default_val_src, coerced, null);
636 if (default_val.canMutateComptimeVarState(zcu)) {
637 const field_name = struct_obj.field_names.get(ip)[zir_field.idx];
638 return sema.failWithContainsReferenceToComptimeVar(block, default_val_src, field_name, "field default value", default_val);
639 }
640 struct_obj.field_defaults.get(ip)[zir_field.idx] = default_val.toIntern();
641 }
642}
643
526/// This logic must be kept in sync with `Type.getUnionLayout`.644/// This logic must be kept in sync with `Type.getUnionLayout`.
527pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {645pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
528 const pt = sema.pt;646 const pt = sema.pt;
src/Zcu.zig+24-4
...@@ -3175,6 +3175,7 @@ fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void {...@@ -3175,6 +3175,7 @@ fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3175 .nav_val => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_val = nav }),3175 .nav_val => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_val = nav }),
3176 .nav_ty => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_ty = nav }),3176 .nav_ty => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_ty = nav }),
3177 .type_layout => |ty| try zcu.markPoDependeeUpToDateInner(.{ .type_layout = ty }),3177 .type_layout => |ty| try zcu.markPoDependeeUpToDateInner(.{ .type_layout = ty }),
3178 .struct_defaults => |ty| try zcu.markPoDependeeUpToDateInner(.{ .struct_defaults = ty }),
3178 .func => |func| try zcu.markPoDependeeUpToDateInner(.{ .func_ies = func }),3179 .func => |func| try zcu.markPoDependeeUpToDateInner(.{ .func_ies = func }),
3179 .memoized_state => |stage| try zcu.markPoDependeeUpToDateInner(.{ .memoized_state = stage }),3180 .memoized_state => |stage| try zcu.markPoDependeeUpToDateInner(.{ .memoized_state = stage }),
3180 }3181 }
...@@ -3193,6 +3194,7 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni...@@ -3193,6 +3194,7 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
3193 .nav_val => |nav| .{ .nav_val = nav },3194 .nav_val => |nav| .{ .nav_val = nav },
3194 .nav_ty => |nav| .{ .nav_ty = nav },3195 .nav_ty => |nav| .{ .nav_ty = nav },
3195 .type_layout => |ty| .{ .type_layout = ty },3196 .type_layout => |ty| .{ .type_layout = ty },
3197 .struct_defaults => |ty| .{ .struct_defaults = ty },
3196 .func => |func_index| .{ .func_ies = func_index },3198 .func => |func_index| .{ .func_ies = func_index },
3197 .memoized_state => |stage| .{ .memoized_state = stage },3199 .memoized_state => |stage| .{ .memoized_state = stage },
3198 };3200 };
...@@ -4249,11 +4251,18 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.AutoArrayHashMapUnmanag...@@ -4249,11 +4251,18 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.AutoArrayHashMapUnmanag
4249 unit_idx += 1;4251 unit_idx += 1;
42504252
4251 // `nav_val` and `nav_ty` reference each other *implicitly* to save memory.4253 // `nav_val` and `nav_ty` reference each other *implicitly* to save memory.
4254 // Likewise for `type_layout` and `struct_defaults` of a struct type.
4252 queue_paired: {4255 queue_paired: {
4253 const other: AnalUnit = .wrap(switch (unit.unwrap()) {4256 const other: AnalUnit = .wrap(switch (unit.unwrap()) {
4254 .nav_val => |n| .{ .nav_ty = n },4257 .nav_val => |n| .{ .nav_ty = n },
4255 .nav_ty => |n| .{ .nav_val = n },4258 .nav_ty => |n| .{ .nav_val = n },
4256 .@"comptime", .type_layout, .func, .memoized_state => break :queue_paired,4259 .struct_defaults => |ty| .{ .type_layout = ty },
4260 .type_layout => |ty| switch (ip.indexToKey(ty)) {
4261 .struct_type => .{ .struct_defaults = ty },
4262 .union_type, .enum_type, .opaque_type => break :queue_paired,
4263 else => unreachable,
4264 },
4265 .@"comptime", .func, .memoized_state => break :queue_paired,
4257 });4266 });
4258 const gop = try units.getOrPut(gpa, other);4267 const gop = try units.getOrPut(gpa, other);
4259 if (gop.found_existing) break :queue_paired;4268 if (gop.found_existing) break :queue_paired;
...@@ -4406,7 +4415,7 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void...@@ -4406,7 +4415,7 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void
4406 }4415 }
4407 },4416 },
4408 .nav_val, .nav_ty => |nav, tag| return writer.print("{t}('{f}' [{}])", .{ tag, ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),4417 .nav_val, .nav_ty => |nav, tag| return writer.print("{t}('{f}' [{}])", .{ tag, ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
4409 .type_layout => |ty, tag| return writer.print("{t}('{f}' [{}])", .{ tag, Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }),4418 .type_layout, .struct_defaults => |ty, tag| return writer.print("{t}('{f}' [{}])", .{ tag, Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }),
4410 .func => |func| {4419 .func => |func| {
4411 const nav = zcu.funcInfo(func).owner_nav;4420 const nav = zcu.funcInfo(func).owner_nav;
4412 return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) });4421 return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) });
...@@ -4431,7 +4440,7 @@ fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void...@@ -4431,7 +4440,7 @@ fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void
4431 const fqn = ip.getNav(nav).fqn;4440 const fqn = ip.getNav(nav).fqn;
4432 return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) });4441 return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) });
4433 },4442 },
4434 .type_layout => |ip_index, tag| {4443 .type_layout, .struct_defaults => |ip_index, tag| {
4435 const name = Type.fromInterned(ip_index).containerTypeName(ip);4444 const name = Type.fromInterned(ip_index).containerTypeName(ip);
4436 return writer.print("{t}('{f}')", .{ tag, name.fmt(ip) });4445 return writer.print("{t}('{f}')", .{ tag, name.fmt(ip) });
4437 },4446 },
...@@ -4920,6 +4929,10 @@ fn addDependencyLoopErrorLine(...@@ -4920,6 +4929,10 @@ fn addDependencyLoopErrorLine(
4920 fmt_source,4929 fmt_source,
4921 dep_node.reason.type_layout_reason.msg(),4930 dep_node.reason.type_layout_reason.msg(),
4922 }),4931 }),
4932 .struct_defaults => |ty| try eb.printString(
4933 "default field values of '{f}' depend on themselves for initialization here",
4934 .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)},
4935 ),
4923 } else switch (dep_node.unit.unwrap()) {4936 } else switch (dep_node.unit.unwrap()) {
4924 .@"comptime" => unreachable, // cannot be involved in a dependency loop4937 .@"comptime" => unreachable, // cannot be involved in a dependency loop
4925 .nav_val => |nav| try eb.printString("{f} uses value of declaration '{f}' here", .{4938 .nav_val => |nav| try eb.printString("{f} uses value of declaration '{f}' here", .{
...@@ -4940,6 +4953,10 @@ fn addDependencyLoopErrorLine(...@@ -4940,6 +4953,10 @@ fn addDependencyLoopErrorLine(
4940 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),4953 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
4941 dep_node.reason.type_layout_reason.msg(),4954 dep_node.reason.type_layout_reason.msg(),
4942 }),4955 }),
4956 .struct_defaults => |ty| try eb.printString(
4957 "{f} uses default field values of '{f}' here",
4958 .{ fmt_source, Type.fromInterned(ty).containerTypeName(ip).fmt(ip) },
4959 ),
4943 };4960 };
49444961
4945 const src_loc = dep_node.reason.src.upgrade(zcu);4962 const src_loc = dep_node.reason.src.upgrade(zcu);
...@@ -4982,6 +4999,9 @@ fn formatDependencyLoopSourceUnit(data: FormatAnalUnit, w: *Io.Writer) Io.Writer...@@ -4982,6 +4999,9 @@ fn formatDependencyLoopSourceUnit(data: FormatAnalUnit, w: *Io.Writer) Io.Writer
4982 .type_layout => |ty| try w.print("type '{f}'", .{4999 .type_layout => |ty| try w.print("type '{f}'", .{
4983 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),5000 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
4984 }),5001 }),
5002 .struct_defaults => |ty| try w.print("default field value of '{f}'", .{
5003 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
5004 }),
4985 .func => |func| try w.print("function '{f}'", .{5005 .func => |func| try w.print("function '{f}'", .{
4986 ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip),5006 ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip),
4987 }),5007 }),
...@@ -5030,7 +5050,7 @@ pub fn populateReferenceTrace(...@@ -5030,7 +5050,7 @@ pub fn populateReferenceTrace(
5030 const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) {5050 const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) {
5031 .@"comptime" => "comptime",5051 .@"comptime" => "comptime",
5032 .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),5052 .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
5033 .type_layout => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),5053 .type_layout, .struct_defaults => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
5034 .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),5054 .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
5035 .memoized_state => null,5055 .memoized_state => null,
5036 };5056 };
src/Zcu/PerThread.zig+144-3
...@@ -324,6 +324,17 @@ pub fn update(...@@ -324,6 +324,17 @@ pub fn update(
324 .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav, null),324 .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav, null),
325 .nav_val => |nav| pt.ensureNavValUpToDate(nav, null),325 .nav_val => |nav| pt.ensureNavValUpToDate(nav, null),
326 .type_layout => |ty| pt.ensureTypeLayoutUpToDate(.fromInterned(ty), null),326 .type_layout => |ty| pt.ensureTypeLayoutUpToDate(.fromInterned(ty), null),
327 .struct_defaults => |ty| res: {
328 // Unlike the other functions, this one requires that the type layout is resolved first.
329 pt.ensureTypeLayoutUpToDate(.fromInterned(ty), null) catch |err| switch (err) {
330 error.OutOfMemory,
331 error.Canceled,
332 => |e| return e,
333
334 error.AnalysisFail => {}, // already reported
335 };
336 break :res pt.ensureStructDefaultsUpToDate(.fromInterned(ty), null);
337 },
327 .memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage, null),338 .memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage, null),
328 .func => |func| pt.ensureFuncBodyUpToDate(func, null),339 .func => |func| pt.ensureFuncBodyUpToDate(func, null),
329 };340 };
...@@ -1326,6 +1337,7 @@ pub fn ensureTypeLayoutUpToDate(...@@ -1326,6 +1337,7 @@ pub fn ensureTypeLayoutUpToDate(
1326 defer tracy_trace.end();1337 defer tracy_trace.end();
13271338
1328 const zcu = pt.zcu;1339 const zcu = pt.zcu;
1340 const ip = &zcu.intern_pool;
1329 const comp = zcu.comp;1341 const comp = zcu.comp;
1330 const gpa = comp.gpa;1342 const gpa = comp.gpa;
13311343
...@@ -1335,8 +1347,23 @@ pub fn ensureTypeLayoutUpToDate(...@@ -1335,8 +1347,23 @@ pub fn ensureTypeLayoutUpToDate(
13351347
1336 assert(!zcu.analysis_in_progress.contains(anal_unit));1348 assert(!zcu.analysis_in_progress.contains(anal_unit));
13371349
1338 const was_outdated = zcu.clearOutdatedState(anal_unit) or1350 const was_outdated: bool = outdated: {
1339 zcu.intern_pool.setWantTypeLayout(comp.io, ty.toIntern());1351 if (zcu.clearOutdatedState(anal_unit)) break :outdated true;
1352 if (ip.setWantTypeLayout(comp.io, ty.toIntern())) {
1353 // We'll analyze the layout for the first time, but if this is a struct type then its
1354 // default field values also need to be analyzed.
1355 if (ip.indexToKey(ty.toIntern()) == .struct_type) {
1356 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
1357 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
1358 try zcu.outdated.ensureUnusedCapacity(gpa, 1);
1359 try zcu.outdated_ready.other.ensureUnusedCapacity(gpa, 1);
1360 zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .struct_defaults = ty.toIntern() }), 0);
1361 zcu.outdated_ready.other.putAssumeCapacityNoClobber(.wrap(.{ .struct_defaults = ty.toIntern() }), {});
1362 }
1363 break :outdated true;
1364 }
1365 break :outdated false;
1366 };
13401367
1341 if (was_outdated) {1368 if (was_outdated) {
1342 // `was_outdated` is true in the initial update, so this isn't a `dev.check`.1369 // `was_outdated` is true in the initial update, so this isn't a `dev.check`.
...@@ -1359,7 +1386,7 @@ pub fn ensureTypeLayoutUpToDate(...@@ -1359,7 +1386,7 @@ pub fn ensureTypeLayoutUpToDate(
1359 info.deps.clearRetainingCapacity();1386 info.deps.clearRetainingCapacity();
1360 }1387 }
13611388
1362 const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(&zcu.intern_pool).toSlice(&zcu.intern_pool), null);1389 const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(ip).toSlice(ip), null);
1363 defer unit_tracking.end(zcu);1390 defer unit_tracking.end(zcu);
13641391
1365 try zcu.analysis_in_progress.put(gpa, anal_unit, reason);1392 try zcu.analysis_in_progress.put(gpa, anal_unit, reason);
...@@ -1428,6 +1455,120 @@ pub fn ensureTypeLayoutUpToDate(...@@ -1428,6 +1455,120 @@ pub fn ensureTypeLayoutUpToDate(
1428 if (new_failed) return error.AnalysisFail;1455 if (new_failed) return error.AnalysisFail;
1429}1456}
14301457
1458/// Ensures that the default field values of the given `struct` type are fully up-to-date,
1459/// performing re-analysis if necessary. Asserts that `ty` is a struct (not a tuple!) type. Unlike
1460/// the other "ensure X up to date" functions, this particular function also asserts that the
1461/// *layout* of `ty` is *already* up-to-date (though it is okay for that resolution to have failed).
1462/// Returns `error.AnalysisFail` if an analysis error is encountered while resolving the default
1463/// field values; the caller is free to ignore this, since the error is already registered.
1464pub fn ensureStructDefaultsUpToDate(
1465 pt: Zcu.PerThread,
1466 ty: Type,
1467 /// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`.
1468 reason: ?*const Zcu.DependencyReason,
1469) Zcu.SemaError!void {
1470 const tracy_trace = trace(@src());
1471 defer tracy_trace.end();
1472
1473 const zcu = pt.zcu;
1474 const ip = &zcu.intern_pool;
1475 const comp = zcu.comp;
1476 const gpa = comp.gpa;
1477
1478 assert(ip.indexToKey(ty.toIntern()) == .struct_type);
1479
1480 const anal_unit: AnalUnit = .wrap(.{ .struct_defaults = ty.toIntern() });
1481
1482 log.debug("ensureStructDefaultsUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
1483
1484 assert(!zcu.analysis_in_progress.contains(anal_unit));
1485
1486 const was_outdated: bool = outdated: {
1487 if (zcu.clearOutdatedState(anal_unit)) break :outdated true;
1488 // The type layout should already be marked as "wanted" by this point, because a struct's
1489 // layout must always be analyzed before its default values are.
1490 assert(!ip.setWantTypeLayout(comp.io, ty.toIntern()));
1491 break :outdated false;
1492 };
1493
1494 if (was_outdated) {
1495 // `was_outdated` is true in the initial update, so this isn't a `dev.check`.
1496 if (dev.env.supports(.incremental)) {
1497 zcu.resetUnit(anal_unit);
1498 }
1499 // For types, we already know that we have to invalidate all dependees.
1500 // TODO: we actually *could* detect whether everything was the same. should we bother?
1501 try zcu.markDependeeOutdated(.marked_po, .{ .struct_defaults = ty.toIntern() });
1502 } else {
1503 // We can trust the current information about this unit.
1504 if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail;
1505 if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail;
1506 return;
1507 }
1508
1509 if (zcu.comp.debugIncremental()) {
1510 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
1511 info.last_update_gen = zcu.generation;
1512 info.deps.clearRetainingCapacity();
1513 }
1514
1515 const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(ip).toSlice(ip), null);
1516 defer unit_tracking.end(zcu);
1517
1518 try zcu.analysis_in_progress.put(gpa, anal_unit, reason);
1519 defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
1520
1521 var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
1522 defer analysis_arena.deinit();
1523
1524 var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
1525 defer comptime_err_ret_trace.deinit();
1526
1527 const file = zcu.namespacePtr(ty.getNamespaceIndex(zcu)).fileScope(zcu);
1528
1529 var sema: Sema = .{
1530 .pt = pt,
1531 .gpa = gpa,
1532 .arena = analysis_arena.allocator(),
1533 .code = file.zir.?,
1534 .owner = anal_unit,
1535 .func_index = .none,
1536 .func_is_naked = false,
1537 .fn_ret_ty = .void,
1538 .fn_ret_ty_ies = null,
1539 .comptime_err_ret_trace = &comptime_err_ret_trace,
1540 };
1541 defer sema.deinit();
1542
1543 const new_failed: bool = if (Sema.type_resolution.resolveStructDefaults(&sema, ty)) failed: {
1544 break :failed false;
1545 } else |err| switch (err) {
1546 error.AnalysisFail => failed: {
1547 if (!zcu.failed_analysis.contains(anal_unit)) {
1548 // If this unit caused the error, it would have an entry in `failed_analysis`.
1549 // Since it does not, this must be a transitive failure.
1550 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
1551 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)});
1552 }
1553 break :failed true;
1554 },
1555 error.OutOfMemory,
1556 error.Canceled,
1557 => |e| return e,
1558 error.ComptimeReturn => unreachable,
1559 error.ComptimeBreak => unreachable,
1560 };
1561
1562 sema.flushExports() catch |err| switch (err) {
1563 error.OutOfMemory => |e| return e,
1564 };
1565
1566 // We don't need to `markDependeeOutdated`/`markPoDependeeUpToDate` here, because we already
1567 // marked the struct defaults as outdated at the top of this function.
1568
1569 if (new_failed) return error.AnalysisFail;
1570}
1571
1431/// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis1572/// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis
1432/// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is1573/// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
1433/// free to ignore this, since the error is already registered.1574/// free to ignore this, since the error is already registered.
src/link/Dwarf.zig+9-1
...@@ -3827,7 +3827,15 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co...@@ -3827,7 +3827,15 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co
3827 try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);3827 try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
3828 for (0..loaded_struct.field_types.len) |field_index| {3828 for (0..loaded_struct.field_types.len) |field_index| {
3829 const is_comptime = loaded_struct.field_is_comptime_bits.get(ip, field_index);3829 const is_comptime = loaded_struct.field_is_comptime_bits.get(ip, field_index);
3830 const field_init = loaded_struct.field_defaults.getOrNone(ip, field_index);3830 // TODO: we currently don't emit information about default values for
3831 // non-`comptime` fields, because these default values are resolved at a
3832 // separate time in the compiler frontend. To emit this information, the
3833 // frontend needs to tell us when the default values are available: like
3834 // how `Zcu.PerThread.ensureTypeLayoutUpToDate` enqueues a link task to
3835 // indicate completion of the type's layout, a task should be enqueued
3836 // by `Zcu.PerThread.ensureStructDefaultsUpToDate`, and upon receiving
3837 // it we should patch the correct default field values in.
3838 const field_init: InternPool.Index = if (is_comptime) loaded_struct.field_defaults.getOrNone(ip, field_index) else .none;
3831 assert(!(is_comptime and field_init == .none));3839 assert(!(is_comptime and field_init == .none));
3832 const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);3840 const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
3833 const has_runtime_bits, const has_comptime_state = switch (field_init) {3841 const has_runtime_bits, const has_comptime_state = switch (field_init) {