authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-04-16 09:50:26+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-04-16 09:50:26+02:00
logd0226ac301140cb552dfc3da24cea7693d29c64f
treeaccc9db0c3b255655f111bbc25eaf227b9155ace
parent67a5b6e5e8280ef736d458c4596137c58c2c253a
parent6608b65100648a1ef5ac1951a744b4f73756378d

Merge pull request 'incremental: fix tracking of nested container declarations (and of opaque types)' (#31889) from dont-track-children-if-lost-parent into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31889 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

5 files changed, 169 insertions(+), 106 deletions(-)

lib/std/zig/Zir.zig+50-47
...@@ -4021,30 +4021,30 @@ pub const DeclContents = struct {...@@ -4021,30 +4021,30 @@ pub const DeclContents = struct {
4021 /// This is a simple optional because ZIR guarantees that a `func`/`func_inferred`/`func_fancy` instruction4021 /// This is a simple optional because ZIR guarantees that a `func`/`func_inferred`/`func_fancy` instruction
4022 /// can only occur once per `declaration`.4022 /// can only occur once per `declaration`.
4023 func_decl: ?Inst.Index,4023 func_decl: ?Inst.Index,
4024 explicit_types: std.ArrayList(Inst.Index),4024 type_decls: std.ArrayList(Inst.Index),
4025 other: std.ArrayList(Inst.Index),4025 other: std.ArrayList(Inst.Index),
40264026
4027 pub const init: DeclContents = .{4027 pub const init: DeclContents = .{
4028 .func_decl = null,4028 .func_decl = null,
4029 .explicit_types = .empty,4029 .type_decls = .empty,
4030 .other = .empty,4030 .other = .empty,
4031 };4031 };
40324032
4033 pub fn clear(contents: *DeclContents) void {4033 pub fn clear(contents: *DeclContents) void {
4034 contents.func_decl = null;4034 contents.func_decl = null;
4035 contents.explicit_types.clearRetainingCapacity();4035 contents.type_decls.clearRetainingCapacity();
4036 contents.other.clearRetainingCapacity();4036 contents.other.clearRetainingCapacity();
4037 }4037 }
40384038
4039 pub fn deinit(contents: *DeclContents, gpa: Allocator) void {4039 pub fn deinit(contents: *DeclContents, gpa: Allocator) void {
4040 contents.explicit_types.deinit(gpa);4040 contents.type_decls.deinit(gpa);
4041 contents.other.deinit(gpa);4041 contents.other.deinit(gpa);
4042 }4042 }
4043};4043};
40444044
4045/// Find all tracked ZIR instructions, recursively, within a `declaration` instruction. Does not recurse through4045/// Find all tracked ZIR instructions, recursively, within a `declaration` instruction. Does not recurse through
4046/// nested declarations; to find all declarations, call this function recursively on the type declarations discovered4046/// nested declarations; to find all declarations, call this function recursively on the type declarations discovered
4047/// in `contents.explicit_types`.4047/// in `contents.type_decls`.
4048///4048///
4049/// This populates an `ArrayList` because an iterator would need to allocate memory anyway.4049/// This populates an `ArrayList` because an iterator would need to allocate memory anyway.
4050pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_inst: Zir.Inst.Index) !void {4050pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_inst: Zir.Inst.Index) !void {
...@@ -4064,15 +4064,49 @@ pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_ins...@@ -4064,15 +4064,49 @@ pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_ins
4064 if (decl.value_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);4064 if (decl.value_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
4065}4065}
40664066
4067/// Like `findTrackable`, but only considers the `main_struct_inst` instruction. This may return more than4067/// `findTrackable` does not recurse into field expressions in a type. Instead, this function will
4068/// just that instruction because it will also traverse fields.4068/// scan specifically field expressions in a given type declaration for trackable ZIR instructions.
4069pub fn findTrackableRoot(zir: Zir, gpa: Allocator, contents: *DeclContents) !void {4069pub fn findTrackableFields(
4070 zir: *const Zir,
4071 gpa: Allocator,
4072 contents: *DeclContents,
4073 type_decl_inst: Zir.Inst.Index,
4074) Allocator.Error!void {
4070 contents.clear();4075 contents.clear();
40714076
4072 var found_defers: std.AutoHashMapUnmanaged(u32, void) = .empty;4077 var found_defers: std.AutoHashMapUnmanaged(u32, void) = .empty;
4073 defer found_defers.deinit(gpa);4078 defer found_defers.deinit(gpa);
40744079
4075 try zir.findTrackableInner(gpa, contents, &found_defers, .main_struct_inst);4080 assert(zir.instructions.items(.tag)[@intFromEnum(type_decl_inst)] == .extended);
4081 switch (zir.instructions.items(.data)[@intFromEnum(type_decl_inst)].extended.opcode) {
4082 .struct_decl => {
4083 const struct_decl = zir.getStructDecl(type_decl_inst);
4084 var it = struct_decl.iterateFields();
4085 while (it.next()) |field| {
4086 try zir.findTrackableBody(gpa, contents, &found_defers, field.type_body);
4087 if (field.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
4088 if (field.default_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
4089 }
4090 },
4091 .union_decl => {
4092 const union_decl = zir.getUnionDecl(type_decl_inst);
4093 var it = union_decl.iterateFields();
4094 while (it.next()) |field| {
4095 if (field.type_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
4096 if (field.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
4097 if (field.value_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
4098 }
4099 },
4100 .enum_decl => {
4101 const enum_decl = zir.getEnumDecl(type_decl_inst);
4102 var it = enum_decl.iterateFields();
4103 while (it.next()) |field| {
4104 if (field.value_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
4105 }
4106 },
4107 .opaque_decl => {},
4108 else => unreachable,
4109 }
4076}4110}
40774111
4078fn findTrackableInner(4112fn findTrackableInner(
...@@ -4396,49 +4430,18 @@ fn findTrackableInner(...@@ -4396,49 +4430,18 @@ fn findTrackableInner(
4396 try zir.findTrackableBody(gpa, contents, defers, body);4430 try zir.findTrackableBody(gpa, contents, defers, body);
4397 },4431 },
43984432
4399 // Reifications and opaque declarations need tracking, but have no bodies.4433 // Reifications need tracking.
4400 .reify_enum,4434 .reify_enum,
4401 .reify_struct,4435 .reify_struct,
4402 .reify_union,4436 .reify_union,
4403 .opaque_decl,
4404 => return contents.other.append(gpa, inst),4437 => return contents.other.append(gpa, inst),
44054438
4406 // Struct declarations need tracking and have bodies.4439 // Type declarations need tracking.
4407 .struct_decl => {4440 .struct_decl,
4408 try contents.explicit_types.append(gpa, inst);4441 .union_decl,
44094442 .enum_decl,
4410 const struct_decl = zir.getStructDecl(inst);4443 .opaque_decl,
4411 var it = struct_decl.iterateFields();4444 => return contents.type_decls.append(gpa, inst),
4412 while (it.next()) |field| {
4413 try zir.findTrackableBody(gpa, contents, defers, field.type_body);
4414 if (field.align_body) |b| try zir.findTrackableBody(gpa, contents, defers, b);
4415 if (field.default_body) |b| try zir.findTrackableBody(gpa, contents, defers, b);
4416 }
4417 },
4418
4419 // Union declarations need tracking and have bodies.
4420 .union_decl => {
4421 try contents.explicit_types.append(gpa, inst);
4422
4423 const union_decl = zir.getUnionDecl(inst);
4424 var it = union_decl.iterateFields();
4425 while (it.next()) |field| {
4426 if (field.type_body) |b| try zir.findTrackableBody(gpa, contents, defers, b);
4427 if (field.align_body) |b| try zir.findTrackableBody(gpa, contents, defers, b);
4428 if (field.value_body) |b| try zir.findTrackableBody(gpa, contents, defers, b);
4429 }
4430 },
4431
4432 // Enum declarations need tracking and have bodies.
4433 .enum_decl => {
4434 try contents.explicit_types.append(gpa, inst);
4435
4436 const enum_decl = zir.getEnumDecl(inst);
4437 var it = enum_decl.iterateFields();
4438 while (it.next()) |field| {
4439 if (field.value_body) |b| try zir.findTrackableBody(gpa, contents, defers, b);
4440 }
4441 },
4442 }4445 }
4443 },4446 },
44444447
src/Zcu.zig+46-44
...@@ -3361,8 +3361,8 @@ pub fn mapOldZirToNew(...@@ -3361,8 +3361,8 @@ pub fn mapOldZirToNew(
3361 old_inst: Zir.Inst.Index,3361 old_inst: Zir.Inst.Index,
3362 new_inst: Zir.Inst.Index,3362 new_inst: Zir.Inst.Index,
3363 };3363 };
3364 var match_stack: std.ArrayList(MatchedZirDecl) = .empty;3364 var pending_matched_type_decls: std.ArrayList(MatchedZirDecl) = .empty;
3365 defer match_stack.deinit(gpa);3365 defer pending_matched_type_decls.deinit(gpa);
33663366
3367 // Used as temporary buffers for namespace declaration instructions3367 // Used as temporary buffers for namespace declaration instructions
3368 var old_contents: Zir.DeclContents = .init;3368 var old_contents: Zir.DeclContents = .init;
...@@ -3370,42 +3370,13 @@ pub fn mapOldZirToNew(...@@ -3370,42 +3370,13 @@ pub fn mapOldZirToNew(
3370 var new_contents: Zir.DeclContents = .init;3370 var new_contents: Zir.DeclContents = .init;
3371 defer new_contents.deinit(gpa);3371 defer new_contents.deinit(gpa);
33723372
3373 // Map the main struct inst (and anything in its fields)3373 // Map the main struct inst to start off with.
3374 {3374 try pending_matched_type_decls.append(gpa, .{
3375 try old_zir.findTrackableRoot(gpa, &old_contents);3375 .old_inst = .main_struct_inst,
3376 try new_zir.findTrackableRoot(gpa, &new_contents);3376 .new_inst = .main_struct_inst,
33773377 });
3378 assert(old_contents.explicit_types.items[0] == .main_struct_inst);
3379 assert(new_contents.explicit_types.items[0] == .main_struct_inst);
3380
3381 assert(old_contents.func_decl == null);
3382 assert(new_contents.func_decl == null);
3383
3384 // We don't have any smart way of matching up these instructions, so we correlate them based on source order
3385 // in their respective arrays.
3386
3387 const num_explicit_types = @min(old_contents.explicit_types.items.len, new_contents.explicit_types.items.len);
3388 try match_stack.ensureUnusedCapacity(gpa, @intCast(num_explicit_types));
3389 for (
3390 old_contents.explicit_types.items[0..num_explicit_types],
3391 new_contents.explicit_types.items[0..num_explicit_types],
3392 ) |old_inst, new_inst| {
3393 // Here we use `match_stack`, so that we will recursively consider declarations on these types.
3394 match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
3395 }
3396
3397 const num_other = @min(old_contents.other.items.len, new_contents.other.items.len);
3398 try inst_map.ensureUnusedCapacity(gpa, @intCast(num_other));
3399 for (
3400 old_contents.other.items[0..num_other],
3401 new_contents.other.items[0..num_other],
3402 ) |old_inst, new_inst| {
3403 // These instructions don't have declarations, so we just modify `inst_map` directly.
3404 inst_map.putAssumeCapacity(old_inst, new_inst);
3405 }
3406 }
34073378
3408 while (match_stack.pop()) |match_item| {3379 while (pending_matched_type_decls.pop()) |match_item| {
3409 // There are some properties of type declarations which cannot change across incremental3380 // There are some properties of type declarations which cannot change across incremental
3410 // updates. If they have, we need to ignore this mapping. These properties are essentially3381 // updates. If they have, we need to ignore this mapping. These properties are essentially
3411 // everything passed into `InternPool.getDeclaredStructType` (likewise for unions, enums,3382 // everything passed into `InternPool.getDeclaredStructType` (likewise for unions, enums,
...@@ -3461,9 +3432,41 @@ pub fn mapOldZirToNew(...@@ -3461,9 +3432,41 @@ pub fn mapOldZirToNew(
3461 else => unreachable,3432 else => unreachable,
3462 }3433 }
34633434
3464 // Match the namespace declaration itself3435 // Match the container declaration itself
3465 try inst_map.put(gpa, match_item.old_inst, match_item.new_inst);3436 try inst_map.put(gpa, match_item.old_inst, match_item.new_inst);
34663437
3438 {
3439 // First, map the fields...
3440 try old_zir.findTrackableFields(gpa, &old_contents, match_item.old_inst);
3441 try new_zir.findTrackableFields(gpa, &new_contents, match_item.new_inst);
3442
3443 // This isn't a `.declaration`, so we shouldn't see a function declaration.
3444 assert(old_contents.func_decl == null);
3445 assert(new_contents.func_decl == null);
3446
3447 // We don't have any smart way of matching up these instructions, so we correlate them based on source order
3448 // in their respective arrays.
3449
3450 const num_type_decls = @min(old_contents.type_decls.items.len, new_contents.type_decls.items.len);
3451 try pending_matched_type_decls.ensureUnusedCapacity(gpa, @intCast(num_type_decls));
3452 for (
3453 old_contents.type_decls.items[0..num_type_decls],
3454 new_contents.type_decls.items[0..num_type_decls],
3455 ) |old_inst, new_inst| {
3456 pending_matched_type_decls.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
3457 }
3458
3459 const num_other = @min(old_contents.other.items.len, new_contents.other.items.len);
3460 try inst_map.ensureUnusedCapacity(gpa, @intCast(num_other));
3461 for (
3462 old_contents.other.items[0..num_other],
3463 new_contents.other.items[0..num_other],
3464 ) |old_inst, new_inst| {
3465 // These instructions don't have declarations, so we just modify `inst_map` directly.
3466 inst_map.putAssumeCapacity(old_inst, new_inst);
3467 }
3468 }
3469
3467 // Maps decl name to `declaration` instruction.3470 // Maps decl name to `declaration` instruction.
3468 var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .empty;3471 var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .empty;
3469 defer named_decls.deinit(gpa);3472 defer named_decls.deinit(gpa);
...@@ -3537,14 +3540,13 @@ pub fn mapOldZirToNew(...@@ -3537,14 +3540,13 @@ pub fn mapOldZirToNew(
3537 // We don't have any smart way of matching up these instructions, so we correlate them based on source order3540 // We don't have any smart way of matching up these instructions, so we correlate them based on source order
3538 // in their respective arrays.3541 // in their respective arrays.
35393542
3540 const num_explicit_types = @min(old_contents.explicit_types.items.len, new_contents.explicit_types.items.len);3543 const num_type_decls = @min(old_contents.type_decls.items.len, new_contents.type_decls.items.len);
3541 try match_stack.ensureUnusedCapacity(gpa, @intCast(num_explicit_types));3544 try pending_matched_type_decls.ensureUnusedCapacity(gpa, @intCast(num_type_decls));
3542 for (3545 for (
3543 old_contents.explicit_types.items[0..num_explicit_types],3546 old_contents.type_decls.items[0..num_type_decls],
3544 new_contents.explicit_types.items[0..num_explicit_types],3547 new_contents.type_decls.items[0..num_type_decls],
3545 ) |old_inst, new_inst| {3548 ) |old_inst, new_inst| {
3546 // Here we use `match_stack`, so that we will recursively consider declarations on these types.3549 pending_matched_type_decls.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
3547 match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
3548 }3550 }
35493551
3550 const num_other = @min(old_contents.other.items.len, new_contents.other.items.len);3552 const num_other = @min(old_contents.other.items.len, new_contents.other.items.len);
src/Zcu/PerThread.zig+7-15
...@@ -1073,8 +1073,6 @@ pub fn ensureMemoizedStateUpToDate(...@@ -1073,8 +1073,6 @@ pub fn ensureMemoizedStateUpToDate(
10731073
1074 const unit: AnalUnit = .wrap(.{ .memoized_state = stage });1074 const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
10751075
1076 log.debug("ensureMemoizedStateUpToDate", .{});
1077
1078 assert(!zcu.analysis_in_progress.contains(unit));1076 assert(!zcu.analysis_in_progress.contains(unit));
10791077
1080 const was_outdated = zcu.clearOutdatedState(unit);1078 const was_outdated = zcu.clearOutdatedState(unit);
...@@ -1142,6 +1140,8 @@ fn analyzeMemoizedState(...@@ -1142,6 +1140,8 @@ fn analyzeMemoizedState(
1142 const comp = zcu.comp;1140 const comp = zcu.comp;
1143 const gpa = comp.gpa;1141 const gpa = comp.gpa;
11441142
1143 log.debug("analyzeMemoizedState({t})", .{stage});
1144
1145 const unit: AnalUnit = .wrap(.{ .memoized_state = stage });1145 const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
11461146
1147 try zcu.analysis_in_progress.putNoClobber(gpa, unit, reason);1147 try zcu.analysis_in_progress.putNoClobber(gpa, unit, reason);
...@@ -1182,8 +1182,6 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU...@@ -1182,8 +1182,6 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
11821182
1183 const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });1183 const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });
11841184
1185 log.debug("ensureComptimeUnitUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
1186
1187 assert(!zcu.analysis_in_progress.contains(anal_unit));1185 assert(!zcu.analysis_in_progress.contains(anal_unit));
11881186
1189 // Determine whether or not this `ComptimeUnit` is outdated. For this kind of `AnalUnit`, that's1187 // Determine whether or not this `ComptimeUnit` is outdated. For this kind of `AnalUnit`, that's
...@@ -1345,8 +1343,6 @@ pub fn ensureTypeLayoutUpToDate(...@@ -1345,8 +1343,6 @@ pub fn ensureTypeLayoutUpToDate(
13451343
1346 const anal_unit: AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });1344 const anal_unit: AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });
13471345
1348 log.debug("ensureTypeLayoutUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
1349
1350 assert(!zcu.analysis_in_progress.contains(anal_unit));1346 assert(!zcu.analysis_in_progress.contains(anal_unit));
13511347
1352 const was_outdated: bool = outdated: {1348 const was_outdated: bool = outdated: {
...@@ -1413,6 +1409,8 @@ pub fn ensureTypeLayoutUpToDate(...@@ -1413,6 +1409,8 @@ pub fn ensureTypeLayoutUpToDate(
1413 };1409 };
1414 defer sema.deinit();1410 defer sema.deinit();
14151411
1412 log.debug("ensureTypeLayoutUpToDate {f} (out of date, resolving)", .{zcu.fmtAnalUnit(anal_unit)});
1413
1416 const result = switch (ty.zigTypeTag(zcu)) {1414 const result = switch (ty.zigTypeTag(zcu)) {
1417 .@"enum" => Sema.type_resolution.resolveEnumLayout(&sema, ty),1415 .@"enum" => Sema.type_resolution.resolveEnumLayout(&sema, ty),
1418 .@"struct" => Sema.type_resolution.resolveStructLayout(&sema, ty),1416 .@"struct" => Sema.type_resolution.resolveStructLayout(&sema, ty),
...@@ -1478,8 +1476,6 @@ pub fn ensureStructDefaultsUpToDate(...@@ -1478,8 +1476,6 @@ pub fn ensureStructDefaultsUpToDate(
14781476
1479 const anal_unit: AnalUnit = .wrap(.{ .struct_defaults = ty.toIntern() });1477 const anal_unit: AnalUnit = .wrap(.{ .struct_defaults = ty.toIntern() });
14801478
1481 log.debug("ensureStructDefaultsUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
1482
1483 assert(!zcu.analysis_in_progress.contains(anal_unit));1479 assert(!zcu.analysis_in_progress.contains(anal_unit));
14841480
1485 const was_outdated: bool = outdated: {1481 const was_outdated: bool = outdated: {
...@@ -1536,6 +1532,8 @@ pub fn ensureStructDefaultsUpToDate(...@@ -1536,6 +1532,8 @@ pub fn ensureStructDefaultsUpToDate(
1536 };1532 };
1537 defer sema.deinit();1533 defer sema.deinit();
15381534
1535 log.debug("ensureStructDefaultsUpToDate {f} (out of date, resolving)", .{zcu.fmtAnalUnit(anal_unit)});
1536
1539 const new_failed: bool = if (Sema.type_resolution.resolveStructDefaults(&sema, ty)) failed: {1537 const new_failed: bool = if (Sema.type_resolution.resolveStructDefaults(&sema, ty)) failed: {
1540 break :failed false;1538 break :failed false;
1541 } else |err| switch (err) {1539 } else |err| switch (err) {
...@@ -1584,8 +1582,6 @@ pub fn ensureNavValUpToDate(...@@ -1584,8 +1582,6 @@ pub fn ensureNavValUpToDate(
1584 const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });1582 const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
1585 const nav = ip.getNav(nav_id);1583 const nav = ip.getNav(nav_id);
15861584
1587 log.debug("ensureNavValUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
1588
1589 assert(!zcu.analysis_in_progress.contains(anal_unit));1585 assert(!zcu.analysis_in_progress.contains(anal_unit));
15901586
1591 try zcu.ensureNavValAnalysisQueued(nav_id);1587 try zcu.ensureNavValAnalysisQueued(nav_id);
...@@ -1946,8 +1942,6 @@ pub fn ensureNavTypeUpToDate(...@@ -1946,8 +1942,6 @@ pub fn ensureNavTypeUpToDate(
1946 const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });1942 const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
1947 const nav = ip.getNav(nav_id);1943 const nav = ip.getNav(nav_id);
19481944
1949 log.debug("ensureNavTypeUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
1950
1951 assert(!zcu.analysis_in_progress.contains(anal_unit));1945 assert(!zcu.analysis_in_progress.contains(anal_unit));
19521946
1953 try zcu.ensureNavValAnalysisQueued(nav_id);1947 try zcu.ensureNavValAnalysisQueued(nav_id);
...@@ -2191,8 +2185,6 @@ pub fn ensureFuncBodyUpToDate(...@@ -2191,8 +2185,6 @@ pub fn ensureFuncBodyUpToDate(
21912185
2192 const anal_unit: AnalUnit = .wrap(.{ .func = func_index });2186 const anal_unit: AnalUnit = .wrap(.{ .func = func_index });
21932187
2194 log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
2195
2196 assert(!zcu.analysis_in_progress.contains(anal_unit));2188 assert(!zcu.analysis_in_progress.contains(anal_unit));
21972189
2198 const func = zcu.funcInfo(func_index);2190 const func = zcu.funcInfo(func_index);
...@@ -2282,7 +2274,7 @@ fn analyzeFuncBody(...@@ -2282,7 +2274,7 @@ fn analyzeFuncBody(
2282 else2274 else
2283 .none;2275 .none;
22842276
2285 log.debug("analyze and generate fn body {f}", .{zcu.fmtAnalUnit(anal_unit)});2277 log.debug("analyzeFuncBody {f}", .{zcu.fmtAnalUnit(anal_unit)});
22862278
2287 var air = try pt.analyzeFuncBodyInner(func_index, reason);2279 var air = try pt.analyzeFuncBodyInner(func_index, reason);
2288 var air_owned = true;2280 var air_owned = true;
test/incremental/add_field_and_nested_struct_uses_changed_decl created+25
...@@ -0,0 +1,25 @@
1#update=initial version
2#file=main.zig
3pub fn main() void {
4 _ = @as(S, undefined);
5}
6// To reproduce the original bug, the inner struct must perform a namespace lookup
7// or a scope lookup when resolving its field type.
8const SomeType = u8;
9const S = struct {
10 foo: struct { inner: SomeType },
11};
12#expect_stdout=""
13#update=add field to outer struct, change decl used by inner struct
14#file=main.zig
15pub fn main() void {
16 _ = @as(S, undefined);
17}
18// To reproduce the original bug, the inner struct must perform a namespace lookup
19// or a scope lookup when resolving its field type.
20const SomeType = u16;
21const S = struct {
22 foo: struct { inner: SomeType },
23 bar: u32,
24};
25#expect_stdout=""
test/incremental/do_nothing created+41
...@@ -0,0 +1,41 @@
1// TODO: it'd be great if we could actually check that no analysis happened!
2#update=initial version
3#file=main.zig
4pub fn main() void {
5 const ptr: *const O = @ptrFromInt(0x1000);
6 _ = ptr;
7}
8const S = struct { foo: u32, nested: struct { x: u16 } };
9const U = union(enum) { a, b, c: S };
10const E = enum(u8) { a = @typeInfo(U).@"union".fields.len, b = 0, c };
11const O = opaque {
12 comptime {
13 _ = @as(S, undefined);
14 _ = @as(U, undefined);
15 _ = @as(E, undefined);
16 const Wrapper = struct { val: S };
17 const wrapper: Wrapper = .{ .val = .{ .foo = 123, .nested = .{ .x = 456 } } };
18 _ = wrapper;
19 }
20};
21#expect_stdout=""
22#update=do literally nothing
23#file=main.zig
24pub fn main() void {
25 const ptr: *const O = @ptrFromInt(0x1000);
26 _ = ptr;
27}
28const S = struct { foo: u32, nested: struct { x: u16 } };
29const U = union(enum) { a, b, c: S };
30const E = enum(u8) { a = @typeInfo(U).@"union".fields.len, b = 0, c };
31const O = opaque {
32 comptime {
33 _ = @as(S, undefined);
34 _ = @as(U, undefined);
35 _ = @as(E, undefined);
36 const Wrapper = struct { val: S };
37 const wrapper: Wrapper = .{ .val = .{ .foo = 123, .nested = .{ .x = 456 } } };
38 _ = wrapper;
39 }
40};
41#expect_stdout=""