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,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
9 * namespace decls table can't reference ZIR memory because it can get modified on updates1 * namespace decls table can't reference ZIR memory because it can get modified on updates
10 - change it for astgen worker to compare old and new ZIR, updating existing2 - change it for astgen worker to compare old and new ZIR, updating existing
11 namespaces & decls, and creating a changelist.3 namespaces & decls, and creating a changelist.
src/Compilation.zig+5
...@@ -1878,6 +1878,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1878,6 +1878,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1878 }1878 }
1879 }1879 }
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
1881 while (self.work_queue.readItem()) |work_item| switch (work_item) {1886 while (self.work_queue.readItem()) |work_item| switch (work_item) {
1882 .codegen_decl => |decl| switch (decl.analysis) {1887 .codegen_decl => |decl| switch (decl.analysis) {
1883 .unreferenced => unreachable,1888 .unreferenced => unreachable,
src/Module.zig+118-133
...@@ -285,8 +285,7 @@ pub const Decl = struct {...@@ -285,8 +285,7 @@ pub const Decl = struct {
285 log.debug("destroy Decl {*} ({s})", .{ decl, decl.name });285 log.debug("destroy Decl {*} ({s})", .{ decl, decl.name });
286 decl.clearName(gpa);286 decl.clearName(gpa);
287 if (decl.has_tv) {287 if (decl.has_tv) {
288 if (decl.val.castTag(.function)) |payload| {288 if (decl.getFunction()) |func| {
289 const func = payload.data;
290 func.deinit(gpa);289 func.deinit(gpa);
291 gpa.destroy(func);290 gpa.destroy(func);
292 } else if (decl.val.getTypeNamespace()) |namespace| {291 } else if (decl.val.getTypeNamespace()) |namespace| {
...@@ -308,6 +307,10 @@ pub const Decl = struct {...@@ -308,6 +307,10 @@ pub const Decl = struct {
308 }307 }
309308
310 pub fn clearValues(decl: *Decl, gpa: *Allocator) void {309 pub fn clearValues(decl: *Decl, gpa: *Allocator) void {
310 if (decl.getFunction()) |func| {
311 func.deinit(gpa);
312 gpa.destroy(func);
313 }
311 if (decl.value_arena) |arena_state| {314 if (decl.value_arena) |arena_state| {
312 arena_state.promote(gpa).deinit();315 arena_state.promote(gpa).deinit();
313 decl.value_arena = null;316 decl.value_arena = null;
...@@ -348,7 +351,7 @@ pub const Decl = struct {...@@ -348,7 +351,7 @@ pub const Decl = struct {
348 return contents_hash;351 return contents_hash;
349 }352 }
350353
351 pub fn zirBlockIndex(decl: Decl) Zir.Inst.Index {354 pub fn zirBlockIndex(decl: *const Decl) Zir.Inst.Index {
352 assert(decl.zir_decl_index != 0);355 assert(decl.zir_decl_index != 0);
353 const zir = decl.namespace.file_scope.zir;356 const zir = decl.namespace.file_scope.zir;
354 return zir.extra[decl.zir_decl_index + 6];357 return zir.extra[decl.zir_decl_index + 6];
...@@ -369,6 +372,13 @@ pub const Decl = struct {...@@ -369,6 +372,13 @@ pub const Decl = struct {
369 return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);372 return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
370 }373 }
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
372 pub fn relativeToLine(decl: Decl, offset: u32) u32 {382 pub fn relativeToLine(decl: Decl, offset: u32) u32 {
373 return decl.src_line + offset;383 return decl.src_line + offset;
374 }384 }
...@@ -832,6 +842,14 @@ pub const Scope = struct {...@@ -832,6 +842,14 @@ pub const Scope = struct {
832 /// Owned by its owner Decl Value.842 /// Owned by its owner Decl Value.
833 namespace: *Namespace,843 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
835 pub fn unload(file: *File, gpa: *Allocator) void {853 pub fn unload(file: *File, gpa: *Allocator) void {
836 file.unloadTree(gpa);854 file.unloadTree(gpa);
837 file.unloadSource(gpa);855 file.unloadSource(gpa);
...@@ -862,6 +880,8 @@ pub const Scope = struct {...@@ -862,6 +880,8 @@ pub const Scope = struct {
862 pub fn deinit(file: *File, mod: *Module) void {880 pub fn deinit(file: *File, mod: *Module) void {
863 const gpa = mod.gpa;881 const gpa = mod.gpa;
864 log.debug("deinit File {s}", .{file.sub_file_path});882 log.debug("deinit File {s}", .{file.sub_file_path});
883 file.deleted_decls.deinit(gpa);
884 file.outdated_decls.deinit(gpa);
865 if (file.status == .success_air) {885 if (file.status == .success_air) {
866 file.namespace.getDecl().destroy(mod);886 file.namespace.getDecl().destroy(mod);
867 }887 }
...@@ -2361,8 +2381,10 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node...@@ -2361,8 +2381,10 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
2361 // We do not need to hold any locks at this time because all the Decl and Namespace2381 // We do not need to hold any locks at this time because all the Decl and Namespace
2362 // objects being touched are specific to this File, and the only other concurrent2382 // objects being touched are specific to this File, and the only other concurrent
2363 // tasks are touching other File objects.2383 // tasks are touching other File objects.
2364 const change_list = try updateZirRefs(gpa, file, prev_zir);2384 try updateZirRefs(gpa, file, prev_zir);
2365 @panic("TODO do something with change_list");2385
2386 // At this point, `file.outdated_decls` and `file.deleted_decls` are populated,
2387 // and semantic analysis will deal with them properly.
2366 }2388 }
23672389
2368 // TODO don't report compile errors until Sema @importFile2390 // 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...@@ -2377,23 +2399,13 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
2377 }2399 }
2378}2400}
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
2390/// Patch ups:2402/// Patch ups:
2391/// * Struct.zir_index2403/// * Struct.zir_index
2392/// * Fn.zir_body_inst2404/// * Fn.zir_body_inst
2393/// * Decl.zir_decl_index2405/// * Decl.zir_decl_index
2394/// * Decl.name2406/// * Decl.name
2395/// * Namespace.decl keys2407/// * 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 {
2397 const new_zir = file.zir;2409 const new_zir = file.zir;
23982410
2399 // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which2411 // 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...@@ -2419,7 +2431,8 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange
2419 }2431 }
2420 }2432 }
24212433
2422 // Walk the Decl graph.2434 // Walk the Decl graph, updating ZIR indexes, strings, and populating
2435 // the deleted and outdated lists.
24232436
2424 var decl_stack: std.ArrayListUnmanaged(*Decl) = .{};2437 var decl_stack: std.ArrayListUnmanaged(*Decl) = .{};
2425 defer decl_stack.deinit(gpa);2438 defer decl_stack.deinit(gpa);
...@@ -2427,48 +2440,48 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange...@@ -2427,48 +2440,48 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange
2427 const root_decl = file.namespace.getDecl();2440 const root_decl = file.namespace.getDecl();
2428 try decl_stack.append(gpa, root_decl);2441 try decl_stack.append(gpa, root_decl);
24292442
2430 var deleted_decls: std.ArrayListUnmanaged(*Decl) = .{};2443 file.deleted_decls.clearRetainingCapacity();
2431 defer deleted_decls.deinit(gpa);2444 file.outdated_decls.clearRetainingCapacity();
2432 var outdated_decls: std.ArrayListUnmanaged(*Decl) = .{};2445
2433 defer outdated_decls.deinit(gpa);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
2435 while (decl_stack.popOrNull()) |decl| {2450 while (decl_stack.popOrNull()) |decl| {
2436 // Anonymous decls and the root decl have this set to 0. We still need2451 // Anonymous decls and the root decl have this set to 0. We still need
2437 // to walk them but we do not need to modify this value.2452 // 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.
2438 if (decl.zir_decl_index != 0) {2455 if (decl.zir_decl_index != 0) {
2439 const old_hash = decl.contentsHashZir(old_zir);2456 const old_hash = decl.contentsHashZir(old_zir);
2440 decl.zir_decl_index = extra_map.get(decl.zir_decl_index) orelse {2457 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);
2442 continue;2459 continue;
2443 };2460 };
2444 const new_name_index = string_table.get(mem.spanZ(decl.name)) orelse {2461 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);
2446 continue;2463 continue;
2447 };2464 };
2448 decl.name = new_zir.nullTerminatedString(new_name_index).ptr;2465 decl.name = new_zir.nullTerminatedString(new_name_index).ptr;
24492466
2450 const new_hash = decl.contentsHashZir(new_zir);2467 const new_hash = decl.contentsHashZir(new_zir);
2451 if (!std.zig.srcHashEql(old_hash, new_hash)) {2468 if (!std.zig.srcHashEql(old_hash, new_hash)) {
2452 try outdated_decls.append(gpa, decl);2469 try file.outdated_decls.append(gpa, decl);
2453 }2470 }
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);
2458 }2471 }
24592472
2460 if (!decl.has_tv) continue;2473 if (!decl.has_tv) continue;
24612474
2462 if (decl.getStruct()) |struct_obj| {2475 if (decl.getStruct()) |struct_obj| {
2463 struct_obj.zir_index = inst_map.get(struct_obj.zir_index) orelse {2476 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);
2465 continue;2478 continue;
2466 };2479 };
2467 }2480 }
24682481
2469 if (decl.getFunction()) |func| {2482 if (decl.getFunction()) |func| {
2470 func.zir_body_inst = inst_map.get(func.zir_body_inst) orelse {2483 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);
2472 continue;2485 continue;
2473 };2486 };
2474 }2487 }
...@@ -2478,7 +2491,7 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange...@@ -2478,7 +2491,7 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange
2478 const sub_decl = entry.value;2491 const sub_decl = entry.value;
2479 if (sub_decl.zir_decl_index != 0) {2492 if (sub_decl.zir_decl_index != 0) {
2480 const new_key_index = string_table.get(entry.key) orelse {2493 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);
2482 continue;2495 continue;
2483 };2496 };
2484 entry.key = new_zir.nullTerminatedString(new_key_index);2497 entry.key = new_zir.nullTerminatedString(new_key_index);
...@@ -2487,14 +2500,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange...@@ -2487,14 +2500,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange
2487 }2500 }
2488 }2501 }
2489 }2502 }
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 };
2498}2503}
24992504
2500pub fn mapOldZirToNew(2505pub fn mapOldZirToNew(
...@@ -2512,10 +2517,8 @@ pub fn mapOldZirToNew(...@@ -2512,10 +2517,8 @@ pub fn mapOldZirToNew(
2512 var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{};2517 var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{};
2513 defer match_stack.deinit(gpa);2518 defer match_stack.deinit(gpa);
25142519
2515 const old_main_struct_inst = old_zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -2520 const old_main_struct_inst = old_zir.getMainStruct();
2516 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);2521 const new_main_struct_inst = new_zir.getMainStruct();
2517 const new_main_struct_inst = new_zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
2518 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
25192522
2520 try match_stack.append(gpa, .{2523 try match_stack.append(gpa, .{
2521 .old_inst = old_main_struct_inst,2524 .old_inst = old_main_struct_inst,
...@@ -2579,7 +2582,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {...@@ -2579,7 +2582,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
2579 .complete => return,2582 .complete => return,
25802583
2581 .outdated => blk: {2584 .outdated => blk: {
2582 log.debug("re-analyzing {s}", .{decl.name});2585 log.debug("re-analyzing {*} ({s})", .{ decl, decl.name });
25832586
2584 // The exports this Decl performs will be re-discovered, so we remove them here2587 // The exports this Decl performs will be re-discovered, so we remove them here
2585 // prior to re-analysis.2588 // prior to re-analysis.
...@@ -2667,8 +2670,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {...@@ -2667,8 +2670,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
2667 }2670 }
26682671
2669 assert(file.zir_loaded);2672 assert(file.zir_loaded);
2670 const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -2673 const main_struct_inst = file.zir.getMainStruct();
2671 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
26722674
2673 const gpa = mod.gpa;2675 const gpa = mod.gpa;
2674 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);2676 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
...@@ -2745,14 +2747,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2745,14 +2747,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2745 defer tracy.end();2747 defer tracy.end();
27462748
2747 const gpa = mod.gpa;2749 const gpa = mod.gpa;
2750 const zir = decl.namespace.file_scope.zir;
2751 const zir_datas = zir.instructions.items(.data);
27482752
2749 decl.analysis = .in_progress;2753 decl.analysis = .in_progress;
27502754
2751 var analysis_arena = std.heap.ArenaAllocator.init(gpa);2755 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
2752 defer analysis_arena.deinit();2756 defer analysis_arena.deinit();
27532757
2754 const zir = decl.namespace.file_scope.zir;
2755
2756 var sema: Sema = .{2758 var sema: Sema = .{
2757 .mod = mod,2759 .mod = mod,
2758 .gpa = gpa,2760 .gpa = gpa,
...@@ -2765,6 +2767,19 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2765,6 +2767,19 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2765 .owner_func = null,2767 .owner_func = null,
2766 .param_inst_list = &.{},2768 .param_inst_list = &.{},
2767 };2769 };
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
2768 var block_scope: Scope.Block = .{2783 var block_scope: Scope.Block = .{
2769 .parent = null,2784 .parent = null,
2770 .sema = &sema,2785 .sema = &sema,
...@@ -2775,25 +2790,23 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2775,25 +2790,23 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2775 };2790 };
2776 defer block_scope.instructions.deinit(gpa);2791 defer block_scope.instructions.deinit(gpa);
27772792
2778 const zir_datas = zir.instructions.items(.data);
2779 const zir_tags = zir.instructions.items(.tag);
2780
2781 const zir_block_index = decl.zirBlockIndex();2793 const zir_block_index = decl.zirBlockIndex();
2782 const inst_data = zir_datas[zir_block_index].pl_node;2794 const inst_data = zir_datas[zir_block_index].pl_node;
2783 const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index);2795 const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index);
2784 const body = zir.extra[extra.end..][0..extra.data.body_len];2796 const body = zir.extra[extra.end..][0..extra.data.body_len];
2785 const break_index = try sema.analyzeBody(&block_scope, body);2797 const break_index = try sema.analyzeBody(&block_scope, body);
2786 const result_ref = zir_datas[break_index].@"break".operand;2798 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);
2788 const align_val = blk: {2801 const align_val = blk: {
2789 const align_ref = decl.zirAlignRef();2802 const align_ref = decl.zirAlignRef();
2790 if (align_ref == .none) break :blk Value.initTag(.null_value);2803 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;
2792 };2805 };
2793 const linksection_val = blk: {2806 const linksection_val = blk: {
2794 const linksection_ref = decl.zirLinksectionRef();2807 const linksection_ref = decl.zirLinksectionRef();
2795 if (linksection_ref == .none) break :blk Value.initTag(.null_value);2808 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;
2797 };2810 };
27982811
2799 // We need the memory for the Type to go into the arena for the Decl2812 // 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 {...@@ -2842,7 +2855,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2842 }2855 }
28432856
2844 if (decl.is_exported) {2857 if (decl.is_exported) {
2845 const export_src = inst_data.src(); // TODO make this point at `export` token2858 const export_src = src; // TODO make this point at `export` token
2846 if (is_inline) {2859 if (is_inline) {
2847 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});2860 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
2848 }2861 }
...@@ -2859,7 +2872,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2859,7 +2872,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2859 if (is_mutable and !decl_tv.ty.isValidVarType(is_extern)) {2872 if (is_mutable and !decl_tv.ty.isValidVarType(is_extern)) {
2860 return mod.fail(2873 return mod.fail(
2861 &block_scope.base,2874 &block_scope.base,
2862 inst_data.src(), // TODO point at the mut token2875 src, // TODO point at the mut token
2863 "variable of type '{}' must be const",2876 "variable of type '{}' must be const",
2864 .{decl_tv.ty},2877 .{decl_tv.ty},
2865 );2878 );
...@@ -2891,7 +2904,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2891,7 +2904,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2891 decl.generation = mod.generation;2904 decl.generation = mod.generation;
28922905
2893 if (decl.is_exported) {2906 if (decl.is_exported) {
2894 const export_src = inst_data.src(); // TODO point to the export token2907 const export_src = src; // TODO point to the export token
2895 // The scope needs to have the decl in it.2908 // The scope needs to have the decl in it.
2896 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);2909 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
2897 }2910 }
...@@ -3047,26 +3060,6 @@ pub fn scanNamespace(...@@ -3047,26 +3060,6 @@ pub fn scanNamespace(
3047 try mod.comp.work_queue.ensureUnusedCapacity(decls_len);3060 try mod.comp.work_queue.ensureUnusedCapacity(decls_len);
3048 try namespace.decls.ensureCapacity(gpa, decls_len);3061 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
3070 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;3063 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
3071 var extra_index = extra_start + bit_bags_count;3064 var extra_index = extra_start + bit_bags_count;
3072 var bit_bag_index: usize = extra_start;3065 var bit_bag_index: usize = extra_start;
...@@ -3075,8 +3068,6 @@ pub fn scanNamespace(...@@ -3075,8 +3068,6 @@ pub fn scanNamespace(
3075 var scan_decl_iter: ScanDeclIter = .{3068 var scan_decl_iter: ScanDeclIter = .{
3076 .module = mod,3069 .module = mod,
3077 .namespace = namespace,3070 .namespace = namespace,
3078 .deleted_decls = &deleted_decls,
3079 .outdated_decls = &outdated_decls,
3080 .parent_decl = parent_decl,3071 .parent_decl = parent_decl,
3081 };3072 };
3082 while (decl_i < decls_len) : (decl_i += 1) {3073 while (decl_i < decls_len) : (decl_i += 1) {
...@@ -3094,36 +3085,12 @@ pub fn scanNamespace(...@@ -3094,36 +3085,12 @@ pub fn scanNamespace(
30943085
3095 try scanDecl(&scan_decl_iter, decl_sub_index, flags);3086 try scanDecl(&scan_decl_iter, decl_sub_index, flags);
3096 }3087 }
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 }
3119 return extra_index;3088 return extra_index;
3120}3089}
31213090
3122const ScanDeclIter = struct {3091const ScanDeclIter = struct {
3123 module: *Module,3092 module: *Module,
3124 namespace: *Scope.Namespace,3093 namespace: *Scope.Namespace,
3125 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
3126 outdated_decls: *std.AutoArrayHashMap(*Decl, void),
3127 parent_decl: *Decl,3094 parent_decl: *Decl,
3128 usingnamespace_index: usize = 0,3095 usingnamespace_index: usize = 0,
3129 comptime_index: usize = 0,3096 comptime_index: usize = 0,
...@@ -3211,37 +3178,16 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo...@@ -3211,37 +3178,16 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
3211 decl.src_node = decl_node;3178 decl.src_node = decl_node;
3212 decl.src_line = line;3179 decl.src_line = line;
32133180
3181 decl.clearName(gpa);
3182 decl.name = decl_name;
3183
3214 decl.is_pub = is_pub;3184 decl.is_pub = is_pub;
3215 decl.is_exported = is_exported;3185 decl.is_exported = is_exported;
3216 decl.has_align = has_align;3186 decl.has_align = has_align;
3217 decl.has_linksection = has_linksection;3187 decl.has_linksection = has_linksection;
3218 decl.zir_decl_index = @intCast(u32, decl_sub_index);3188 decl.zir_decl_index = @intCast(u32, decl_sub_index);
3219 if (iter.deleted_decls.swapRemove(decl) == null) {3189 if (decl.getFunction()) |func| {
3220 if (true) {3190 switch (mod.comp.bin_file.tag) {
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) {
3245 .coff => {3191 .coff => {
3246 // TODO Implement for COFF3192 // TODO Implement for COFF
3247 },3193 },
...@@ -3256,7 +3202,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo...@@ -3256,7 +3202,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
3256 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });3202 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3257 },3203 },
3258 .c, .wasm, .spirv => {},3204 .c, .wasm, .spirv => {},
3259 };3205 }
3260 }3206 }
3261}3207}
32623208
...@@ -3277,8 +3223,7 @@ pub fn deleteDecl(...@@ -3277,8 +3223,7 @@ pub fn deleteDecl(
3277 try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() +3223 try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() +
3278 decl.dependencies.count());3224 decl.dependencies.count());
32793225
3280 // Remove from the namespace it resides in. In the case of an anonymous Decl it will3226 // Remove from the namespace it resides in.
3281 // not be present in the set, and this does nothing.
3282 decl.namespace.removeDecl(decl);3227 decl.namespace.removeDecl(decl);
32833228
3284 // Remove itself from its dependencies, because we are about to destroy the decl pointer.3229 // 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 {...@@ -3430,7 +3375,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
3430}3375}
34313376
3432fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {3377fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
3433 log.debug("mark {s} outdated", .{decl.name});3378 log.debug("mark outdated {*} ({s})", .{ decl, decl.name });
3434 try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl });3379 try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl });
3435 if (mod.failed_decls.swapRemove(decl)) |entry| {3380 if (mod.failed_decls.swapRemove(decl)) |entry| {
3436 entry.value.destroy(mod.gpa);3381 entry.value.destroy(mod.gpa);
...@@ -4471,3 +4416,43 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!...@@ -4471,3 +4416,43 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!
4471 }4416 }
4472 }4417 }
4473}4418}
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) {...@@ -62,6 +62,11 @@ pub const ExtraIndex = enum(u32) {
62 _,62 _,
63};63};
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
65/// Returns the requested data, as well as the new index which is at the start of the70/// Returns the requested data, as well as the new index which is at the start of the
66/// trailers for the object.71/// trailers for the object.
67pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } {72pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } {
...@@ -126,8 +131,7 @@ pub fn renderAsTextToFile(...@@ -126,8 +131,7 @@ pub fn renderAsTextToFile(
126 .parent_decl_node = 0,131 .parent_decl_node = 0,
127 };132 };
128133
129 const main_struct_inst = scope_file.zir.extra[@enumToInt(ExtraIndex.main_struct)] -134 const main_struct_inst = scope_file.zir.getMainStruct();
130 @intCast(u32, Inst.Ref.typed_value_map.len);
131 try fs_file.writer().print("%{d} ", .{main_struct_inst});135 try fs_file.writer().print("%{d} ", .{main_struct_inst});
132 try writer.writeInstToStream(fs_file.writer(), main_struct_inst);136 try writer.writeInstToStream(fs_file.writer(), main_struct_inst);
133 try fs_file.writeAll("\n");137 try fs_file.writeAll("\n");
src/type.zig+22-3
...@@ -485,14 +485,33 @@ pub const Type = extern union {...@@ -485,14 +485,33 @@ pub const Type = extern union {
485 var buf_b: Payload.ElemType = undefined;485 var buf_b: Payload.ElemType = undefined;
486 return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));486 return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
487 },487 },
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,
488 .Float,510 .Float,
489 .Struct,
490 .ErrorUnion,511 .ErrorUnion,
491 .ErrorSet,512 .ErrorSet,
492 .Enum,
493 .Union,513 .Union,
494 .BoundFn,514 .BoundFn,
495 .Opaque,
496 .Frame,515 .Frame,
497 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),516 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
498 }517 }