| ... | ... | @@ -285,8 +285,7 @@ pub const Decl = struct { |
| 285 | 285 | log.debug("destroy Decl {*} ({s})", .{ decl, decl.name }); |
| 286 | 286 | decl.clearName(gpa); |
| 287 | 287 | if (decl.has_tv) { |
| 288 | | if (decl.val.castTag(.function)) |payload| { |
| 289 | | const func = payload.data; |
| 288 | if (decl.getFunction()) |func| { |
| 290 | 289 | func.deinit(gpa); |
| 291 | 290 | gpa.destroy(func); |
| 292 | 291 | } else if (decl.val.getTypeNamespace()) |namespace| { |
| ... | ... | @@ -308,6 +307,10 @@ pub const Decl = struct { |
| 308 | 307 | } |
| 309 | 308 | |
| 310 | 309 | pub fn clearValues(decl: *Decl, gpa: *Allocator) void { |
| 310 | if (decl.getFunction()) |func| { |
| 311 | func.deinit(gpa); |
| 312 | gpa.destroy(func); |
| 313 | } |
| 311 | 314 | if (decl.value_arena) |arena_state| { |
| 312 | 315 | arena_state.promote(gpa).deinit(); |
| 313 | 316 | decl.value_arena = null; |
| ... | ... | @@ -348,7 +351,7 @@ pub const Decl = struct { |
| 348 | 351 | return contents_hash; |
| 349 | 352 | } |
| 350 | 353 | |
| 351 | | pub fn zirBlockIndex(decl: Decl) Zir.Inst.Index { |
| 354 | pub fn zirBlockIndex(decl: *const Decl) Zir.Inst.Index { |
| 352 | 355 | assert(decl.zir_decl_index != 0); |
| 353 | 356 | const zir = decl.namespace.file_scope.zir; |
| 354 | 357 | return zir.extra[decl.zir_decl_index + 6]; |
| ... | ... | @@ -369,6 +372,13 @@ pub const Decl = struct { |
| 369 | 372 | return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); |
| 370 | 373 | } |
| 371 | 374 | |
| 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 | 382 | pub fn relativeToLine(decl: Decl, offset: u32) u32 { |
| 373 | 383 | return decl.src_line + offset; |
| 374 | 384 | } |
| ... | ... | @@ -832,6 +842,14 @@ pub const Scope = struct { |
| 832 | 842 | /// Owned by its owner Decl Value. |
| 833 | 843 | namespace: *Namespace, |
| 834 | 844 | |
| 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 | 853 | pub fn unload(file: *File, gpa: *Allocator) void { |
| 836 | 854 | file.unloadTree(gpa); |
| 837 | 855 | file.unloadSource(gpa); |
| ... | ... | @@ -862,6 +880,8 @@ pub const Scope = struct { |
| 862 | 880 | pub fn deinit(file: *File, mod: *Module) void { |
| 863 | 881 | const gpa = mod.gpa; |
| 864 | 882 | log.debug("deinit File {s}", .{file.sub_file_path}); |
| 883 | file.deleted_decls.deinit(gpa); |
| 884 | file.outdated_decls.deinit(gpa); |
| 865 | 885 | if (file.status == .success_air) { |
| 866 | 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 | 2381 | // We do not need to hold any locks at this time because all the Decl and Namespace |
| 2362 | 2382 | // objects being touched are specific to this File, and the only other concurrent |
| 2363 | 2383 | // 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. |
| 2366 | 2388 | } |
| 2367 | 2389 | |
| 2368 | 2390 | // 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 | 2399 | } |
| 2378 | 2400 | } |
| 2379 | 2401 | |
| 2380 | | const 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 | 2402 | /// Patch ups: |
| 2391 | 2403 | /// * Struct.zir_index |
| 2392 | 2404 | /// * Fn.zir_body_inst |
| 2393 | 2405 | /// * Decl.zir_decl_index |
| 2394 | 2406 | /// * Decl.name |
| 2395 | 2407 | /// * Namespace.decl keys |
| 2396 | | fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChangeList { |
| 2408 | fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void { |
| 2397 | 2409 | const new_zir = file.zir; |
| 2398 | 2410 | |
| 2399 | 2411 | // 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 | 2431 | } |
| 2420 | 2432 | } |
| 2421 | 2433 | |
| 2422 | | // Walk the Decl graph. |
| 2434 | // Walk the Decl graph, updating ZIR indexes, strings, and populating |
| 2435 | // the deleted and outdated lists. |
| 2423 | 2436 | |
| 2424 | 2437 | var decl_stack: std.ArrayListUnmanaged(*Decl) = .{}; |
| 2425 | 2438 | defer decl_stack.deinit(gpa); |
| ... | ... | @@ -2427,48 +2440,48 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange |
| 2427 | 2440 | const root_decl = file.namespace.getDecl(); |
| 2428 | 2441 | try decl_stack.append(gpa, root_decl); |
| 2429 | 2442 | |
| 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); |
| 2434 | 2449 | |
| 2435 | 2450 | while (decl_stack.popOrNull()) |decl| { |
| 2436 | 2451 | // Anonymous decls and the root decl have this set to 0. We still need |
| 2437 | 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 | 2455 | if (decl.zir_decl_index != 0) { |
| 2439 | 2456 | const old_hash = decl.contentsHashZir(old_zir); |
| 2440 | 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 | 2459 | continue; |
| 2443 | 2460 | }; |
| 2444 | 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 | 2463 | continue; |
| 2447 | 2464 | }; |
| 2448 | 2465 | decl.name = new_zir.nullTerminatedString(new_name_index).ptr; |
| 2449 | 2466 | |
| 2450 | 2467 | const new_hash = decl.contentsHashZir(new_zir); |
| 2451 | 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 | } |
| 2459 | 2472 | |
| 2460 | 2473 | if (!decl.has_tv) continue; |
| 2461 | 2474 | |
| 2462 | 2475 | if (decl.getStruct()) |struct_obj| { |
| 2463 | 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 | 2478 | continue; |
| 2466 | 2479 | }; |
| 2467 | 2480 | } |
| 2468 | 2481 | |
| 2469 | 2482 | if (decl.getFunction()) |func| { |
| 2470 | 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 | 2485 | continue; |
| 2473 | 2486 | }; |
| 2474 | 2487 | } |
| ... | ... | @@ -2478,7 +2491,7 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChange |
| 2478 | 2491 | const sub_decl = entry.value; |
| 2479 | 2492 | if (sub_decl.zir_decl_index != 0) { |
| 2480 | 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 | 2495 | continue; |
| 2483 | 2496 | }; |
| 2484 | 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 | 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 | } |
| 2499 | 2504 | |
| 2500 | 2505 | pub fn mapOldZirToNew( |
| ... | ... | @@ -2512,10 +2517,8 @@ pub fn mapOldZirToNew( |
| 2512 | 2517 | var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{}; |
| 2513 | 2518 | defer match_stack.deinit(gpa); |
| 2514 | 2519 | |
| 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(); |
| 2519 | 2522 | |
| 2520 | 2523 | try match_stack.append(gpa, .{ |
| 2521 | 2524 | .old_inst = old_main_struct_inst, |
| ... | ... | @@ -2579,7 +2582,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2579 | 2582 | .complete => return, |
| 2580 | 2583 | |
| 2581 | 2584 | .outdated => blk: { |
| 2582 | | log.debug("re-analyzing {s}", .{decl.name}); |
| 2585 | log.debug("re-analyzing {*} ({s})", .{ decl, decl.name }); |
| 2583 | 2586 | |
| 2584 | 2587 | // The exports this Decl performs will be re-discovered, so we remove them here |
| 2585 | 2588 | // prior to re-analysis. |
| ... | ... | @@ -2667,8 +2670,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { |
| 2667 | 2670 | } |
| 2668 | 2671 | |
| 2669 | 2672 | 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(); |
| 2672 | 2674 | |
| 2673 | 2675 | const gpa = mod.gpa; |
| 2674 | 2676 | var new_decl_arena = std.heap.ArenaAllocator.init(gpa); |
| ... | ... | @@ -2745,14 +2747,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2745 | 2747 | defer tracy.end(); |
| 2746 | 2748 | |
| 2747 | 2749 | const gpa = mod.gpa; |
| 2750 | const zir = decl.namespace.file_scope.zir; |
| 2751 | const zir_datas = zir.instructions.items(.data); |
| 2748 | 2752 | |
| 2749 | 2753 | decl.analysis = .in_progress; |
| 2750 | 2754 | |
| 2751 | 2755 | var analysis_arena = std.heap.ArenaAllocator.init(gpa); |
| 2752 | 2756 | defer analysis_arena.deinit(); |
| 2753 | 2757 | |
| 2754 | | const zir = decl.namespace.file_scope.zir; |
| 2755 | | |
| 2756 | 2758 | var sema: Sema = .{ |
| 2757 | 2759 | .mod = mod, |
| 2758 | 2760 | .gpa = gpa, |
| ... | ... | @@ -2765,6 +2767,19 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2765 | 2767 | .owner_func = null, |
| 2766 | 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 | 2783 | var block_scope: Scope.Block = .{ |
| 2769 | 2784 | .parent = null, |
| 2770 | 2785 | .sema = &sema, |
| ... | ... | @@ -2775,25 +2790,23 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2775 | 2790 | }; |
| 2776 | 2791 | defer block_scope.instructions.deinit(gpa); |
| 2777 | 2792 | |
| 2778 | | const zir_datas = zir.instructions.items(.data); |
| 2779 | | const zir_tags = zir.instructions.items(.tag); |
| 2780 | | |
| 2781 | 2793 | const zir_block_index = decl.zirBlockIndex(); |
| 2782 | 2794 | const inst_data = zir_datas[zir_block_index].pl_node; |
| 2783 | 2795 | const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index); |
| 2784 | 2796 | const body = zir.extra[extra.end..][0..extra.data.body_len]; |
| 2785 | 2797 | const break_index = try sema.analyzeBody(&block_scope, body); |
| 2786 | 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 | 2801 | const align_val = blk: { |
| 2789 | 2802 | const align_ref = decl.zirAlignRef(); |
| 2790 | 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 | 2806 | const linksection_val = blk: { |
| 2794 | 2807 | const linksection_ref = decl.zirLinksectionRef(); |
| 2795 | 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 | }; |
| 2798 | 2811 | |
| 2799 | 2812 | // 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 | 2855 | } |
| 2843 | 2856 | |
| 2844 | 2857 | 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 |
| 2846 | 2859 | if (is_inline) { |
| 2847 | 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 | 2872 | if (is_mutable and !decl_tv.ty.isValidVarType(is_extern)) { |
| 2860 | 2873 | return mod.fail( |
| 2861 | 2874 | &block_scope.base, |
| 2862 | | inst_data.src(), // TODO point at the mut token |
| 2875 | src, // TODO point at the mut token |
| 2863 | 2876 | "variable of type '{}' must be const", |
| 2864 | 2877 | .{decl_tv.ty}, |
| 2865 | 2878 | ); |
| ... | ... | @@ -2891,7 +2904,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2891 | 2904 | decl.generation = mod.generation; |
| 2892 | 2905 | |
| 2893 | 2906 | 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 |
| 2895 | 2908 | // The scope needs to have the decl in it. |
| 2896 | 2909 | try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl); |
| 2897 | 2910 | } |
| ... | ... | @@ -3047,26 +3060,6 @@ pub fn scanNamespace( |
| 3047 | 3060 | try mod.comp.work_queue.ensureUnusedCapacity(decls_len); |
| 3048 | 3061 | try namespace.decls.ensureCapacity(gpa, decls_len); |
| 3049 | 3062 | |
| 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 | 3063 | const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; |
| 3071 | 3064 | var extra_index = extra_start + bit_bags_count; |
| 3072 | 3065 | var bit_bag_index: usize = extra_start; |
| ... | ... | @@ -3075,8 +3068,6 @@ pub fn scanNamespace( |
| 3075 | 3068 | var scan_decl_iter: ScanDeclIter = .{ |
| 3076 | 3069 | .module = mod, |
| 3077 | 3070 | .namespace = namespace, |
| 3078 | | .deleted_decls = &deleted_decls, |
| 3079 | | .outdated_decls = &outdated_decls, |
| 3080 | 3071 | .parent_decl = parent_decl, |
| 3081 | 3072 | }; |
| 3082 | 3073 | while (decl_i < decls_len) : (decl_i += 1) { |
| ... | ... | @@ -3094,36 +3085,12 @@ pub fn scanNamespace( |
| 3094 | 3085 | |
| 3095 | 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 | 3088 | return extra_index; |
| 3120 | 3089 | } |
| 3121 | 3090 | |
| 3122 | 3091 | const ScanDeclIter = struct { |
| 3123 | 3092 | module: *Module, |
| 3124 | 3093 | namespace: *Scope.Namespace, |
| 3125 | | deleted_decls: *std.AutoArrayHashMap(*Decl, void), |
| 3126 | | outdated_decls: *std.AutoArrayHashMap(*Decl, void), |
| 3127 | 3094 | parent_decl: *Decl, |
| 3128 | 3095 | usingnamespace_index: usize = 0, |
| 3129 | 3096 | comptime_index: usize = 0, |
| ... | ... | @@ -3211,37 +3178,16 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo |
| 3211 | 3178 | decl.src_node = decl_node; |
| 3212 | 3179 | decl.src_line = line; |
| 3213 | 3180 | |
| 3181 | decl.clearName(gpa); |
| 3182 | decl.name = decl_name; |
| 3183 | |
| 3214 | 3184 | decl.is_pub = is_pub; |
| 3215 | 3185 | decl.is_exported = is_exported; |
| 3216 | 3186 | decl.has_align = has_align; |
| 3217 | 3187 | decl.has_linksection = has_linksection; |
| 3218 | 3188 | 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) { |
| 3245 | 3191 | .coff => { |
| 3246 | 3192 | // TODO Implement for COFF |
| 3247 | 3193 | }, |
| ... | ... | @@ -3256,7 +3202,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo |
| 3256 | 3202 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); |
| 3257 | 3203 | }, |
| 3258 | 3204 | .c, .wasm, .spirv => {}, |
| 3259 | | }; |
| 3205 | } |
| 3260 | 3206 | } |
| 3261 | 3207 | } |
| 3262 | 3208 | |
| ... | ... | @@ -3277,8 +3223,7 @@ pub fn deleteDecl( |
| 3277 | 3223 | try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() + |
| 3278 | 3224 | decl.dependencies.count()); |
| 3279 | 3225 | |
| 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. |
| 3282 | 3227 | decl.namespace.removeDecl(decl); |
| 3283 | 3228 | |
| 3284 | 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 | 3375 | } |
| 3431 | 3376 | |
| 3432 | 3377 | fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { |
| 3433 | | log.debug("mark {s} outdated", .{decl.name}); |
| 3378 | log.debug("mark outdated {*} ({s})", .{ decl, decl.name }); |
| 3434 | 3379 | try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl }); |
| 3435 | 3380 | if (mod.failed_decls.swapRemove(decl)) |entry| { |
| 3436 | 3381 | entry.value.destroy(mod.gpa); |
| ... | ... | @@ -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. |
| 4422 | pub 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 | } |