authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-06 17:20:45-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-06 17:20:45-07:00
logcdea22f5d7f1ef7a44cf871eeafab3383495ab6c
treec71de1d6255bab7673b6f44337429c4417f69423
parent3791cd6781ce92478b8a17c8d56c5cf66d4ecfb0

stage2: wire up outdated/deleted decl detection

we're back to incremental compilation working smoothly

5 files changed, 151 insertions(+), 146 deletions(-)

BRANCH_TODO-8
......@@ -1,11 +1,3 @@
1 * implement the iterators that updateZirRefs needs
2 - for iterating over ZIR decls
3 - for iterating over ZIR instructions within a decl to find decl instructions
4 * implement `zig zirdiff a.zig b.zig` for showing debug output for how a particular
5 source transformation will be seen by the change detection algorithm.
6 * communicate the changelist back to the driver code and process it in semantic analysis,
7 handling deletions and outdatings.
8
91 * namespace decls table can't reference ZIR memory because it can get modified on updates
102 - change it for astgen worker to compare old and new ZIR, updating existing
113 namespaces & decls, and creating a changelist.
src/Compilation.zig+5
......@@ -1878,6 +1878,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
18781878 }
18791879 }
18801880
1881 // Iterate over all the files and look for outdated and deleted declarations.
1882 if (self.bin_file.options.module) |mod| {
1883 try mod.processOutdatedAndDeletedDecls();
1884 }
1885
18811886 while (self.work_queue.readItem()) |work_item| switch (work_item) {
18821887 .codegen_decl => |decl| switch (decl.analysis) {
18831888 .unreferenced => unreachable,
src/Module.zig+118-133
......@@ -285,8 +285,7 @@ pub const Decl = struct {
285285 log.debug("destroy Decl {*} ({s})", .{ decl, decl.name });
286286 decl.clearName(gpa);
287287 if (decl.has_tv) {
288 if (decl.val.castTag(.function)) |payload| {
289 const func = payload.data;
288 if (decl.getFunction()) |func| {
290289 func.deinit(gpa);
291290 gpa.destroy(func);
292291 } else if (decl.val.getTypeNamespace()) |namespace| {
......@@ -308,6 +307,10 @@ pub const Decl = struct {
308307 }
309308
310309 pub fn clearValues(decl: *Decl, gpa: *Allocator) void {
310 if (decl.getFunction()) |func| {
311 func.deinit(gpa);
312 gpa.destroy(func);
313 }
311314 if (decl.value_arena) |arena_state| {
312315 arena_state.promote(gpa).deinit();
313316 decl.value_arena = null;
......@@ -348,7 +351,7 @@ pub const Decl = struct {
348351 return contents_hash;
349352 }
350353
351 pub fn zirBlockIndex(decl: Decl) Zir.Inst.Index {
354 pub fn zirBlockIndex(decl: *const Decl) Zir.Inst.Index {
352355 assert(decl.zir_decl_index != 0);
353356 const zir = decl.namespace.file_scope.zir;
354357 return zir.extra[decl.zir_decl_index + 6];
......@@ -369,6 +372,13 @@ pub const Decl = struct {
369372 return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
370373 }
371374
375 /// Returns true if and only if the Decl is the top level struct associated with a File.
376 pub fn isRoot(decl: *const Decl) bool {
377 if (decl.namespace.parent != null)
378 return false;
379 return decl == decl.namespace.ty.getOwnerDecl();
380 }
381
372382 pub fn relativeToLine(decl: Decl, offset: u32) u32 {
373383 return decl.src_line + offset;
374384 }
......@@ -832,6 +842,14 @@ pub const Scope = struct {
832842 /// Owned by its owner Decl Value.
833843 namespace: *Namespace,
834844
845 /// Used by change detection algorithm, after astgen, contains the
846 /// set of decls that existed in the previous ZIR but not in the new one.
847 deleted_decls: std.ArrayListUnmanaged(*Decl) = .{},
848 /// Used by change detection algorithm, after astgen, contains the
849 /// set of decls that existed both in the previous ZIR and in the new one,
850 /// but their source code has been modified.
851 outdated_decls: std.ArrayListUnmanaged(*Decl) = .{},
852
835853 pub fn unload(file: *File, gpa: *Allocator) void {
836854 file.unloadTree(gpa);
837855 file.unloadSource(gpa);
......@@ -862,6 +880,8 @@ pub const Scope = struct {
862880 pub fn deinit(file: *File, mod: *Module) void {
863881 const gpa = mod.gpa;
864882 log.debug("deinit File {s}", .{file.sub_file_path});
883 file.deleted_decls.deinit(gpa);
884 file.outdated_decls.deinit(gpa);
865885 if (file.status == .success_air) {
866886 file.namespace.getDecl().destroy(mod);
867887 }
......@@ -2361,8 +2381,10 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
23612381 // We do not need to hold any locks at this time because all the Decl and Namespace
23622382 // objects being touched are specific to this File, and the only other concurrent
23632383 // tasks are touching other File objects.
2364 const change_list = try updateZirRefs(gpa, file, prev_zir);
2365 @panic("TODO do something with change_list");
2384 try updateZirRefs(gpa, file, prev_zir);
2385
2386 // At this point, `file.outdated_decls` and `file.deleted_decls` are populated,
2387 // and semantic analysis will deal with them properly.
23662388 }
23672389
23682390 // TODO don't report compile errors until Sema @importFile
......@@ -2377,23 +2399,13 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
23772399 }
23782400}
23792401
2380const UpdateChangeList = struct {
2381 deleted: []*const Decl,
2382 outdated: []*const Decl,
2383
2384 fn deinit(self: *UpdateChangeList, gpa: *Allocator) void {
2385 gpa.free(self.deleted);
2386 gpa.free(self.outdated);
2387 }
2388};
2389
23902402/// Patch ups:
23912403/// * Struct.zir_index
23922404/// * Fn.zir_body_inst
23932405/// * Decl.zir_decl_index
23942406/// * Decl.name
23952407/// * Namespace.decl keys
2396fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChangeList {
2408fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
23972409 const new_zir = file.zir;
23982410
23992411 // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which
......@@ -2419,7 +2431,8 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange
24192431 }
24202432 }
24212433
2422 // Walk the Decl graph.
2434 // Walk the Decl graph, updating ZIR indexes, strings, and populating
2435 // the deleted and outdated lists.
24232436
24242437 var decl_stack: std.ArrayListUnmanaged(*Decl) = .{};
24252438 defer decl_stack.deinit(gpa);
......@@ -2427,48 +2440,48 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange
24272440 const root_decl = file.namespace.getDecl();
24282441 try decl_stack.append(gpa, root_decl);
24292442
2430 var deleted_decls: std.ArrayListUnmanaged(*Decl) = .{};
2431 defer deleted_decls.deinit(gpa);
2432 var outdated_decls: std.ArrayListUnmanaged(*Decl) = .{};
2433 defer outdated_decls.deinit(gpa);
2443 file.deleted_decls.clearRetainingCapacity();
2444 file.outdated_decls.clearRetainingCapacity();
2445
2446 // The root decl is always outdated; otherwise we would not have had
2447 // to re-generate ZIR for the File.
2448 try file.outdated_decls.append(gpa, root_decl);
24342449
24352450 while (decl_stack.popOrNull()) |decl| {
24362451 // Anonymous decls and the root decl have this set to 0. We still need
24372452 // to walk them but we do not need to modify this value.
2453 // Anonymous decls should not be marked outdated. They will be re-generated
2454 // if their owner decl is marked outdated.
24382455 if (decl.zir_decl_index != 0) {
24392456 const old_hash = decl.contentsHashZir(old_zir);
24402457 decl.zir_decl_index = extra_map.get(decl.zir_decl_index) orelse {
2441 try deleted_decls.append(gpa, decl);
2458 try file.deleted_decls.append(gpa, decl);
24422459 continue;
24432460 };
24442461 const new_name_index = string_table.get(mem.spanZ(decl.name)) orelse {
2445 try deleted_decls.append(gpa, decl);
2462 try file.deleted_decls.append(gpa, decl);
24462463 continue;
24472464 };
24482465 decl.name = new_zir.nullTerminatedString(new_name_index).ptr;
24492466
24502467 const new_hash = decl.contentsHashZir(new_zir);
24512468 if (!std.zig.srcHashEql(old_hash, new_hash)) {
2452 try outdated_decls.append(gpa, decl);
2469 try file.outdated_decls.append(gpa, decl);
24532470 }
2454 } else {
2455 // TODO all decls should probably store source hash. Without this,
2456 // we currently unnecessarily mark all anon decls outdated here.
2457 try outdated_decls.append(gpa, decl);
24582471 }
24592472
24602473 if (!decl.has_tv) continue;
24612474
24622475 if (decl.getStruct()) |struct_obj| {
24632476 struct_obj.zir_index = inst_map.get(struct_obj.zir_index) orelse {
2464 try deleted_decls.append(gpa, decl);
2477 try file.deleted_decls.append(gpa, decl);
24652478 continue;
24662479 };
24672480 }
24682481
24692482 if (decl.getFunction()) |func| {
24702483 func.zir_body_inst = inst_map.get(func.zir_body_inst) orelse {
2471 try deleted_decls.append(gpa, decl);
2484 try file.deleted_decls.append(gpa, decl);
24722485 continue;
24732486 };
24742487 }
......@@ -2478,7 +2491,7 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange
24782491 const sub_decl = entry.value;
24792492 if (sub_decl.zir_decl_index != 0) {
24802493 const new_key_index = string_table.get(entry.key) orelse {
2481 try deleted_decls.append(gpa, sub_decl);
2494 try file.deleted_decls.append(gpa, sub_decl);
24822495 continue;
24832496 };
24842497 entry.key = new_zir.nullTerminatedString(new_key_index);
......@@ -2487,14 +2500,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange
24872500 }
24882501 }
24892502 }
2490
2491 const outdated_slice = outdated_decls.toOwnedSlice(gpa);
2492 const deleted_slice = deleted_decls.toOwnedSlice(gpa);
2493
2494 return UpdateChangeList{
2495 .outdated = outdated_slice,
2496 .deleted = deleted_slice,
2497 };
24982503}
24992504
25002505pub fn mapOldZirToNew(
......@@ -2512,10 +2517,8 @@ pub fn mapOldZirToNew(
25122517 var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{};
25132518 defer match_stack.deinit(gpa);
25142519
2515 const old_main_struct_inst = old_zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
2516 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
2517 const new_main_struct_inst = new_zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
2518 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
2520 const old_main_struct_inst = old_zir.getMainStruct();
2521 const new_main_struct_inst = new_zir.getMainStruct();
25192522
25202523 try match_stack.append(gpa, .{
25212524 .old_inst = old_main_struct_inst,
......@@ -2579,7 +2582,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
25792582 .complete => return,
25802583
25812584 .outdated => blk: {
2582 log.debug("re-analyzing {s}", .{decl.name});
2585 log.debug("re-analyzing {*} ({s})", .{ decl, decl.name });
25832586
25842587 // The exports this Decl performs will be re-discovered, so we remove them here
25852588 // prior to re-analysis.
......@@ -2667,8 +2670,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
26672670 }
26682671
26692672 assert(file.zir_loaded);
2670 const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
2671 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
2673 const main_struct_inst = file.zir.getMainStruct();
26722674
26732675 const gpa = mod.gpa;
26742676 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
......@@ -2745,14 +2747,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
27452747 defer tracy.end();
27462748
27472749 const gpa = mod.gpa;
2750 const zir = decl.namespace.file_scope.zir;
2751 const zir_datas = zir.instructions.items(.data);
27482752
27492753 decl.analysis = .in_progress;
27502754
27512755 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
27522756 defer analysis_arena.deinit();
27532757
2754 const zir = decl.namespace.file_scope.zir;
2755
27562758 var sema: Sema = .{
27572759 .mod = mod,
27582760 .gpa = gpa,
......@@ -2765,6 +2767,19 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
27652767 .owner_func = null,
27662768 .param_inst_list = &.{},
27672769 };
2770
2771 if (decl.isRoot()) {
2772 log.debug("semaDecl root {*} ({s})", .{decl, decl.name});
2773 const main_struct_inst = zir.getMainStruct();
2774 const struct_obj = decl.getStruct().?;
2775 try sema.analyzeStructDecl(decl, main_struct_inst, struct_obj);
2776 assert(decl.namespace.file_scope.status == .success_zir);
2777 decl.namespace.file_scope.status = .success_air;
2778 decl.analysis = .complete;
2779 decl.generation = mod.generation;
2780 return false;
2781 }
2782
27682783 var block_scope: Scope.Block = .{
27692784 .parent = null,
27702785 .sema = &sema,
......@@ -2775,25 +2790,23 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
27752790 };
27762791 defer block_scope.instructions.deinit(gpa);
27772792
2778 const zir_datas = zir.instructions.items(.data);
2779 const zir_tags = zir.instructions.items(.tag);
2780
27812793 const zir_block_index = decl.zirBlockIndex();
27822794 const inst_data = zir_datas[zir_block_index].pl_node;
27832795 const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index);
27842796 const body = zir.extra[extra.end..][0..extra.data.body_len];
27852797 const break_index = try sema.analyzeBody(&block_scope, body);
27862798 const result_ref = zir_datas[break_index].@"break".operand;
2787 const decl_tv = try sema.resolveInstConst(&block_scope, inst_data.src(), result_ref);
2799 const src = inst_data.src();
2800 const decl_tv = try sema.resolveInstConst(&block_scope, src, result_ref);
27882801 const align_val = blk: {
27892802 const align_ref = decl.zirAlignRef();
27902803 if (align_ref == .none) break :blk Value.initTag(.null_value);
2791 break :blk (try sema.resolveInstConst(&block_scope, inst_data.src(), align_ref)).val;
2804 break :blk (try sema.resolveInstConst(&block_scope, src, align_ref)).val;
27922805 };
27932806 const linksection_val = blk: {
27942807 const linksection_ref = decl.zirLinksectionRef();
27952808 if (linksection_ref == .none) break :blk Value.initTag(.null_value);
2796 break :blk (try sema.resolveInstConst(&block_scope, inst_data.src(), linksection_ref)).val;
2809 break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;
27972810 };
27982811
27992812 // We need the memory for the Type to go into the arena for the Decl
......@@ -2842,7 +2855,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
28422855 }
28432856
28442857 if (decl.is_exported) {
2845 const export_src = inst_data.src(); // TODO make this point at `export` token
2858 const export_src = src; // TODO make this point at `export` token
28462859 if (is_inline) {
28472860 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
28482861 }
......@@ -2859,7 +2872,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
28592872 if (is_mutable and !decl_tv.ty.isValidVarType(is_extern)) {
28602873 return mod.fail(
28612874 &block_scope.base,
2862 inst_data.src(), // TODO point at the mut token
2875 src, // TODO point at the mut token
28632876 "variable of type '{}' must be const",
28642877 .{decl_tv.ty},
28652878 );
......@@ -2891,7 +2904,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
28912904 decl.generation = mod.generation;
28922905
28932906 if (decl.is_exported) {
2894 const export_src = inst_data.src(); // TODO point to the export token
2907 const export_src = src; // TODO point to the export token
28952908 // The scope needs to have the decl in it.
28962909 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
28972910 }
......@@ -3047,26 +3060,6 @@ pub fn scanNamespace(
30473060 try mod.comp.work_queue.ensureUnusedCapacity(decls_len);
30483061 try namespace.decls.ensureCapacity(gpa, decls_len);
30493062
3050 // Keep track of the decls that we expect to see in this namespace so that
3051 // we know which ones have been deleted.
3052 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(gpa);
3053 defer deleted_decls.deinit();
3054 {
3055 const namespace_decls = namespace.decls.items();
3056 try deleted_decls.ensureCapacity(namespace_decls.len);
3057 for (namespace_decls) |entry| {
3058 deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
3059 }
3060 }
3061
3062 // Keep track of decls that are invalidated from the update. Ultimately,
3063 // the goal is to queue up `analyze_decl` tasks in the work queue for
3064 // the outdated decls, but we cannot queue up the tasks until after
3065 // we find out which ones have been deleted, otherwise there would be
3066 // deleted Decl pointers in the work queue.
3067 var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(gpa);
3068 defer outdated_decls.deinit();
3069
30703063 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
30713064 var extra_index = extra_start + bit_bags_count;
30723065 var bit_bag_index: usize = extra_start;
......@@ -3075,8 +3068,6 @@ pub fn scanNamespace(
30753068 var scan_decl_iter: ScanDeclIter = .{
30763069 .module = mod,
30773070 .namespace = namespace,
3078 .deleted_decls = &deleted_decls,
3079 .outdated_decls = &outdated_decls,
30803071 .parent_decl = parent_decl,
30813072 };
30823073 while (decl_i < decls_len) : (decl_i += 1) {
......@@ -3094,36 +3085,12 @@ pub fn scanNamespace(
30943085
30953086 try scanDecl(&scan_decl_iter, decl_sub_index, flags);
30963087 }
3097 // Handle explicitly deleted decls from the source code. This is one of two
3098 // places that Decl deletions happen. The other is in `Compilation`, after
3099 // `performAllTheWork`, where we iterate over `Module.deletion_set` and
3100 // delete Decls which are no longer referenced.
3101 // If a Decl is explicitly deleted from source, and also no longer referenced,
3102 // it may be both in this `deleted_decls` set, as well as in the
3103 // `Module.deletion_set`. To avoid deleting it twice, we remove it from the
3104 // deletion set at this time.
3105 for (deleted_decls.items()) |entry| {
3106 const decl = entry.key;
3107 log.debug("'{s}' deleted from source", .{decl.name});
3108 if (decl.deletion_flag) {
3109 log.debug("'{s}' redundantly in deletion set; removing", .{decl.name});
3110 mod.deletion_set.removeAssertDiscard(decl);
3111 }
3112 try mod.deleteDecl(decl, &outdated_decls);
3113 }
3114 // Finally we can queue up re-analysis tasks after we have processed
3115 // the deleted decls.
3116 for (outdated_decls.items()) |entry| {
3117 try mod.markOutdatedDecl(entry.key);
3118 }
31193088 return extra_index;
31203089}
31213090
31223091const ScanDeclIter = struct {
31233092 module: *Module,
31243093 namespace: *Scope.Namespace,
3125 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
3126 outdated_decls: *std.AutoArrayHashMap(*Decl, void),
31273094 parent_decl: *Decl,
31283095 usingnamespace_index: usize = 0,
31293096 comptime_index: usize = 0,
......@@ -3211,37 +3178,16 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
32113178 decl.src_node = decl_node;
32123179 decl.src_line = line;
32133180
3181 decl.clearName(gpa);
3182 decl.name = decl_name;
3183
32143184 decl.is_pub = is_pub;
32153185 decl.is_exported = is_exported;
32163186 decl.has_align = has_align;
32173187 decl.has_linksection = has_linksection;
32183188 decl.zir_decl_index = @intCast(u32, decl_sub_index);
3219 if (iter.deleted_decls.swapRemove(decl) == null) {
3220 if (true) {
3221 @panic("TODO I think this code path is unreachable; should be caught by AstGen.");
3222 }
3223 decl.analysis = .sema_failure;
3224 const msg = try ErrorMsg.create(gpa, .{
3225 .file_scope = namespace.file_scope,
3226 .parent_decl_node = 0,
3227 .lazy = .{ .token_abs = name_token },
3228 }, "redeclaration of '{s}'", .{decl.name});
3229 errdefer msg.destroy(gpa);
3230 const other_src_loc: SrcLoc = .{
3231 .file_scope = namespace.file_scope,
3232 .parent_decl_node = 0,
3233 .lazy = .{ .node_abs = prev_src_node },
3234 };
3235 try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{});
3236 try mod.failed_decls.putNoClobber(gpa, decl, msg);
3237 } else {
3238 if (true) {
3239 @panic("TODO reimplement scanDecl with regards to incremental compilation.");
3240 }
3241 if (!std.zig.srcHashEql(decl.contents_hash, contents_hash)) {
3242 try iter.outdated_decls.put(decl, {});
3243 decl.contents_hash = contents_hash;
3244 } else if (try decl.isFunction()) switch (mod.comp.bin_file.tag) {
3189 if (decl.getFunction()) |func| {
3190 switch (mod.comp.bin_file.tag) {
32453191 .coff => {
32463192 // TODO Implement for COFF
32473193 },
......@@ -3256,7 +3202,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
32563202 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
32573203 },
32583204 .c, .wasm, .spirv => {},
3259 };
3205 }
32603206 }
32613207}
32623208
......@@ -3277,8 +3223,7 @@ pub fn deleteDecl(
32773223 try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() +
32783224 decl.dependencies.count());
32793225
3280 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
3281 // not be present in the set, and this does nothing.
3226 // Remove from the namespace it resides in.
32823227 decl.namespace.removeDecl(decl);
32833228
32843229 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
......@@ -3430,7 +3375,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
34303375}
34313376
34323377fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
3433 log.debug("mark {s} outdated", .{decl.name});
3378 log.debug("mark outdated {*} ({s})", .{ decl, decl.name });
34343379 try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl });
34353380 if (mod.failed_decls.swapRemove(decl)) |entry| {
34363381 entry.value.destroy(mod.gpa);
......@@ -4471,3 +4416,43 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!
44714416 }
44724417 }
44734418}
4419
4420/// Called from `performAllTheWork`, after all AstGen workers have finished,
4421/// and before the main semantic analysis loop begins.
4422pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
4423 // Ultimately, the goal is to queue up `analyze_decl` tasks in the work queue
4424 // for the outdated decls, but we cannot queue up the tasks until after
4425 // we find out which ones have been deleted, otherwise there would be
4426 // deleted Decl pointers in the work queue.
4427 var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);
4428 defer outdated_decls.deinit();
4429 for (mod.import_table.items()) |import_table_entry| {
4430 const file = import_table_entry.value;
4431
4432 try outdated_decls.ensureUnusedCapacity(file.outdated_decls.items.len);
4433 for (file.outdated_decls.items) |decl| {
4434 outdated_decls.putAssumeCapacity(decl, {});
4435 }
4436 // Handle explicitly deleted decls from the source code. This is one of two
4437 // places that Decl deletions happen. The other is in `Compilation`, after
4438 // `performAllTheWork`, where we iterate over `Module.deletion_set` and
4439 // delete Decls which are no longer referenced.
4440 // If a Decl is explicitly deleted from source, and also no longer referenced,
4441 // it may be both in this `deleted_decls` set, as well as in the
4442 // `Module.deletion_set`. To avoid deleting it twice, we remove it from the
4443 // deletion set at this time.
4444 for (file.deleted_decls.items) |decl| {
4445 log.debug("deleted from source: {*} ({s})", .{ decl, decl.name });
4446 if (decl.deletion_flag) {
4447 log.debug("{*} ({s}) redundantly in deletion set; removing", .{ decl, decl.name });
4448 mod.deletion_set.removeAssertDiscard(decl);
4449 }
4450 try mod.deleteDecl(decl, &outdated_decls);
4451 }
4452 }
4453 // Finally we can queue up re-analysis tasks after we have processed
4454 // the deleted decls.
4455 for (outdated_decls.items()) |entry| {
4456 try mod.markOutdatedDecl(entry.key);
4457 }
4458}
src/Zir.zig+6-2
......@@ -62,6 +62,11 @@ pub const ExtraIndex = enum(u32) {
6262 _,
6363};
6464
65pub fn getMainStruct(zir: Zir) Zir.Inst.Index {
66 return zir.extra[@enumToInt(ExtraIndex.main_struct)] -
67 @intCast(u32, Inst.Ref.typed_value_map.len);
68}
69
6570/// Returns the requested data, as well as the new index which is at the start of the
6671/// trailers for the object.
6772pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } {
......@@ -126,8 +131,7 @@ pub fn renderAsTextToFile(
126131 .parent_decl_node = 0,
127132 };
128133
129 const main_struct_inst = scope_file.zir.extra[@enumToInt(ExtraIndex.main_struct)] -
130 @intCast(u32, Inst.Ref.typed_value_map.len);
134 const main_struct_inst = scope_file.zir.getMainStruct();
131135 try fs_file.writer().print("%{d} ", .{main_struct_inst});
132136 try writer.writeInstToStream(fs_file.writer(), main_struct_inst);
133137 try fs_file.writeAll("\n");
src/type.zig+22-3
......@@ -485,14 +485,33 @@ pub const Type = extern union {
485485 var buf_b: Payload.ElemType = undefined;
486486 return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
487487 },
488 .Struct => {
489 if (a.castTag(.@"struct")) |a_payload| {
490 if (b.castTag(.@"struct")) |b_payload| {
491 return a_payload.data == b_payload.data;
492 }
493 }
494 return a.tag() == b.tag();
495 },
496 .Enum => {
497 if (a.cast(Payload.EnumFull)) |a_payload| {
498 if (b.cast(Payload.EnumFull)) |b_payload| {
499 return a_payload.data == b_payload.data;
500 }
501 }
502 if (a.cast(Payload.EnumSimple)) |a_payload| {
503 if (b.cast(Payload.EnumSimple)) |b_payload| {
504 return a_payload.data == b_payload.data;
505 }
506 }
507 return a.tag() == b.tag();
508 },
509 .Opaque,
488510 .Float,
489 .Struct,
490511 .ErrorUnion,
491512 .ErrorSet,
492 .Enum,
493513 .Union,
494514 .BoundFn,
495 .Opaque,
496515 .Frame,
497516 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
498517 }