| ... | ... | @@ -199,8 +199,12 @@ pub const Decl = struct { |
| 199 | 199 | /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore |
| 200 | 200 | /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced. |
| 201 | 201 | unreferenced, |
| 202 | | /// Semantic analysis for this Decl is running right now. This state detects dependency loops. |
| 202 | /// Semantic analysis for this Decl is running right now. |
| 203 | /// This state detects dependency loops. |
| 203 | 204 | in_progress, |
| 205 | /// The file corresponding to this Decl had a parse error or ZIR error. |
| 206 | /// There will be a corresponding ErrorMsg in Module.failed_files. |
| 207 | file_failure, |
| 204 | 208 | /// This Decl might be OK but it depends on another one which did not successfully complete |
| 205 | 209 | /// semantic analysis. |
| 206 | 210 | dependency_failure, |
| ... | ... | @@ -548,6 +552,7 @@ pub const Decl = struct { |
| 548 | 552 | .unreferenced, |
| 549 | 553 | .in_progress, |
| 550 | 554 | .dependency_failure, |
| 555 | .file_failure, |
| 551 | 556 | .sema_failure, |
| 552 | 557 | .sema_failure_retryable, |
| 553 | 558 | .codegen_failure, |
| ... | ... | @@ -836,7 +841,7 @@ pub const Scope = struct { |
| 836 | 841 | pub fn namespace(scope: *Scope) *Namespace { |
| 837 | 842 | switch (scope.tag) { |
| 838 | 843 | .block => return scope.cast(Block).?.sema.owner_decl.namespace, |
| 839 | | .file => return scope.cast(File).?.namespace, |
| 844 | .file => return scope.cast(File).?.namespace.?, |
| 840 | 845 | .namespace => return scope.cast(Namespace).?, |
| 841 | 846 | .decl_ref => return scope.cast(DeclRef).?.decl.namespace, |
| 842 | 847 | } |
| ... | ... | @@ -965,7 +970,6 @@ pub const Scope = struct { |
| 965 | 970 | parse_failure, |
| 966 | 971 | astgen_failure, |
| 967 | 972 | success_zir, |
| 968 | | success_air, |
| 969 | 973 | }, |
| 970 | 974 | source_loaded: bool, |
| 971 | 975 | tree_loaded: bool, |
| ... | ... | @@ -988,9 +992,9 @@ pub const Scope = struct { |
| 988 | 992 | /// Package that this file is a part of, managed externally. |
| 989 | 993 | pkg: *Package, |
| 990 | 994 | /// The namespace of the struct that represents this file. |
| 991 | | /// Populated only when status is `success_air`. |
| 995 | /// Populated only when `have_decl` is true. |
| 992 | 996 | /// Owned by its owner Decl Value. |
| 993 | | namespace: *Namespace, |
| 997 | namespace: ?*Namespace, |
| 994 | 998 | |
| 995 | 999 | /// Used by change detection algorithm, after astgen, contains the |
| 996 | 1000 | /// set of decls that existed in the previous ZIR but not in the new one. |
| ... | ... | @@ -1000,6 +1004,12 @@ pub const Scope = struct { |
| 1000 | 1004 | /// but their source code has been modified. |
| 1001 | 1005 | outdated_decls: std.ArrayListUnmanaged(*Decl) = .{}, |
| 1002 | 1006 | |
| 1007 | /// The most recent successful ZIR for this file, with no errors. |
| 1008 | /// This is only populated when a previously successful ZIR |
| 1009 | /// newly introduces compile errors during an update. When ZIR is |
| 1010 | /// successful, this field is unloaded. |
| 1011 | prev_zir: ?*Zir = null, |
| 1012 | |
| 1003 | 1013 | pub fn unload(file: *File, gpa: *Allocator) void { |
| 1004 | 1014 | file.unloadTree(gpa); |
| 1005 | 1015 | file.unloadSource(gpa); |
| ... | ... | @@ -1032,11 +1042,15 @@ pub const Scope = struct { |
| 1032 | 1042 | log.debug("deinit File {s}", .{file.sub_file_path}); |
| 1033 | 1043 | file.deleted_decls.deinit(gpa); |
| 1034 | 1044 | file.outdated_decls.deinit(gpa); |
| 1035 | | if (file.status == .success_air) { |
| 1036 | | file.namespace.getDecl().destroy(mod); |
| 1045 | if (file.namespace) |ns| { |
| 1046 | ns.getDecl().destroy(mod); |
| 1037 | 1047 | } |
| 1038 | 1048 | gpa.free(file.sub_file_path); |
| 1039 | 1049 | file.unload(gpa); |
| 1050 | if (file.prev_zir) |prev_zir| { |
| 1051 | prev_zir.deinit(gpa); |
| 1052 | gpa.destroy(prev_zir); |
| 1053 | } |
| 1040 | 1054 | file.* = undefined; |
| 1041 | 1055 | } |
| 1042 | 1056 | |
| ... | ... | @@ -2370,7 +2384,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node |
| 2370 | 2384 | } |
| 2371 | 2385 | return; |
| 2372 | 2386 | }, |
| 2373 | | .parse_failure, .astgen_failure, .success_zir, .success_air => { |
| 2387 | .parse_failure, .astgen_failure, .success_zir => { |
| 2374 | 2388 | const unchanged_metadata = |
| 2375 | 2389 | stat.size == file.stat_size and |
| 2376 | 2390 | stat.mtime == file.stat_mtime and |
| ... | ... | @@ -2411,7 +2425,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node |
| 2411 | 2425 | |
| 2412 | 2426 | // Move previous ZIR to a local variable so we can compare it with the new one. |
| 2413 | 2427 | var prev_zir = file.zir; |
| 2414 | | const prev_zir_loaded = file.zir_loaded; |
| 2428 | var prev_zir_loaded = file.zir_loaded; |
| 2415 | 2429 | file.zir_loaded = false; |
| 2416 | 2430 | file.zir = undefined; |
| 2417 | 2431 | defer if (prev_zir_loaded) prev_zir.deinit(gpa); |
| ... | ... | @@ -2536,8 +2550,34 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node |
| 2536 | 2550 | // We do not need to hold any locks at this time because all the Decl and Namespace |
| 2537 | 2551 | // objects being touched are specific to this File, and the only other concurrent |
| 2538 | 2552 | // tasks are touching other File objects. |
| 2539 | | try updateZirRefs(gpa, file, prev_zir); |
| 2540 | | |
| 2553 | if (file.zir.hasCompileErrors()) { |
| 2554 | // In this case, we keep the previous ZIR loaded so that we can use it |
| 2555 | // for the update next time it does not have any compile errors. This avoids |
| 2556 | // needlessly tossing out semantic analysis work when a ZIR error is |
| 2557 | // temporarily introduced. |
| 2558 | if (!prev_zir.hasCompileErrors()) { |
| 2559 | assert(file.prev_zir == null); |
| 2560 | const prev_zir_ptr = try gpa.create(Zir); |
| 2561 | file.prev_zir = prev_zir_ptr; |
| 2562 | prev_zir_ptr.* = prev_zir; |
| 2563 | prev_zir_loaded = false; |
| 2564 | } |
| 2565 | } else if (prev_zir.hasCompileErrors()) { |
| 2566 | if (file.prev_zir) |file_prev_zir| { |
| 2567 | prev_zir.deinit(gpa); |
| 2568 | prev_zir = file_prev_zir.*; |
| 2569 | gpa.destroy(file_prev_zir); |
| 2570 | file.prev_zir = null; |
| 2571 | try updateZirRefs(gpa, file, prev_zir); |
| 2572 | } else if (file.namespace) |ns| { |
| 2573 | // First time the File has succeeded ZIR. We must mark it outdated since |
| 2574 | // we have already tried to semantically analyze it. |
| 2575 | try file.outdated_decls.resize(gpa, 1); |
| 2576 | file.outdated_decls.items[0] = ns.getDecl(); |
| 2577 | } |
| 2578 | } else { |
| 2579 | try updateZirRefs(gpa, file, prev_zir); |
| 2580 | } |
| 2541 | 2581 | // At this point, `file.outdated_decls` and `file.deleted_decls` are populated, |
| 2542 | 2582 | // and semantic analysis will deal with them properly. |
| 2543 | 2583 | } |
| ... | ... | @@ -2579,7 +2619,7 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void { |
| 2579 | 2619 | var decl_stack: std.ArrayListUnmanaged(*Decl) = .{}; |
| 2580 | 2620 | defer decl_stack.deinit(gpa); |
| 2581 | 2621 | |
| 2582 | | const root_decl = file.namespace.getDecl(); |
| 2622 | const root_decl = file.namespace.?.getDecl(); |
| 2583 | 2623 | try decl_stack.append(gpa, root_decl); |
| 2584 | 2624 | |
| 2585 | 2625 | file.deleted_decls.clearRetainingCapacity(); |
| ... | ... | @@ -2712,6 +2752,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2712 | 2752 | const subsequent_analysis = switch (decl.analysis) { |
| 2713 | 2753 | .in_progress => unreachable, |
| 2714 | 2754 | |
| 2755 | .file_failure, |
| 2715 | 2756 | .sema_failure, |
| 2716 | 2757 | .sema_failure_retryable, |
| 2717 | 2758 | .codegen_failure, |
| ... | ... | @@ -2773,6 +2814,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2773 | 2814 | .in_progress => unreachable, |
| 2774 | 2815 | .outdated => continue, // already queued for update |
| 2775 | 2816 | |
| 2817 | .file_failure, |
| 2776 | 2818 | .dependency_failure, |
| 2777 | 2819 | .sema_failure, |
| 2778 | 2820 | .sema_failure_retryable, |
| ... | ... | @@ -2793,24 +2835,13 @@ pub fn semaPkg(mod: *Module, pkg: *Package) !void { |
| 2793 | 2835 | return mod.semaFile(file); |
| 2794 | 2836 | } |
| 2795 | 2837 | |
| 2838 | /// Regardless of the file status, will create a `Decl` so that we |
| 2839 | /// can track dependencies and re-analyze when the file becomes outdated. |
| 2796 | 2840 | pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { |
| 2797 | 2841 | const tracy = trace(@src()); |
| 2798 | 2842 | defer tracy.end(); |
| 2799 | 2843 | |
| 2800 | | switch (file.status) { |
| 2801 | | .never_loaded => unreachable, |
| 2802 | | |
| 2803 | | .retryable_failure, |
| 2804 | | .parse_failure, |
| 2805 | | .astgen_failure, |
| 2806 | | => return error.AnalysisFail, |
| 2807 | | |
| 2808 | | .success_zir => {}, |
| 2809 | | .success_air => return, |
| 2810 | | } |
| 2811 | | |
| 2812 | | assert(file.zir_loaded); |
| 2813 | | const main_struct_inst = file.zir.getMainStruct(); |
| 2844 | if (file.namespace != null) return; |
| 2814 | 2845 | |
| 2815 | 2846 | const gpa = mod.gpa; |
| 2816 | 2847 | var new_decl_arena = std.heap.ArenaAllocator.init(gpa); |
| ... | ... | @@ -2823,7 +2854,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { |
| 2823 | 2854 | .owner_decl = undefined, // set below |
| 2824 | 2855 | .fields = .{}, |
| 2825 | 2856 | .node_offset = 0, // it's the struct for the root file |
| 2826 | | .zir_index = main_struct_inst, |
| 2857 | .zir_index = undefined, // set below |
| 2827 | 2858 | .layout = .Auto, |
| 2828 | 2859 | .status = .none, |
| 2829 | 2860 | .namespace = .{ |
| ... | ... | @@ -2845,39 +2876,48 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { |
| 2845 | 2876 | new_decl.val = struct_val; |
| 2846 | 2877 | new_decl.has_tv = true; |
| 2847 | 2878 | new_decl.owns_tv = true; |
| 2848 | | new_decl.analysis = .complete; |
| 2879 | new_decl.analysis = .in_progress; |
| 2849 | 2880 | new_decl.generation = mod.generation; |
| 2850 | 2881 | |
| 2851 | | var sema_arena = std.heap.ArenaAllocator.init(gpa); |
| 2852 | | defer sema_arena.deinit(); |
| 2882 | if (file.status == .success_zir) { |
| 2883 | assert(file.zir_loaded); |
| 2884 | const main_struct_inst = file.zir.getMainStruct(); |
| 2885 | struct_obj.zir_index = main_struct_inst; |
| 2886 | |
| 2887 | var sema_arena = std.heap.ArenaAllocator.init(gpa); |
| 2888 | defer sema_arena.deinit(); |
| 2889 | |
| 2890 | var sema: Sema = .{ |
| 2891 | .mod = mod, |
| 2892 | .gpa = gpa, |
| 2893 | .arena = &sema_arena.allocator, |
| 2894 | .code = file.zir, |
| 2895 | // TODO use a map because this array is too big |
| 2896 | .inst_map = try sema_arena.allocator.alloc(*ir.Inst, file.zir.instructions.len), |
| 2897 | .owner_decl = new_decl, |
| 2898 | .namespace = &struct_obj.namespace, |
| 2899 | .func = null, |
| 2900 | .owner_func = null, |
| 2901 | .param_inst_list = &.{}, |
| 2902 | }; |
| 2903 | var block_scope: Scope.Block = .{ |
| 2904 | .parent = null, |
| 2905 | .sema = &sema, |
| 2906 | .src_decl = new_decl, |
| 2907 | .instructions = .{}, |
| 2908 | .inlining = null, |
| 2909 | .is_comptime = true, |
| 2910 | }; |
| 2911 | defer block_scope.instructions.deinit(gpa); |
| 2853 | 2912 | |
| 2854 | | var sema: Sema = .{ |
| 2855 | | .mod = mod, |
| 2856 | | .gpa = gpa, |
| 2857 | | .arena = &sema_arena.allocator, |
| 2858 | | .code = file.zir, |
| 2859 | | // TODO use a map because this array is too big |
| 2860 | | .inst_map = try sema_arena.allocator.alloc(*ir.Inst, file.zir.instructions.len), |
| 2861 | | .owner_decl = new_decl, |
| 2862 | | .namespace = &struct_obj.namespace, |
| 2863 | | .func = null, |
| 2864 | | .owner_func = null, |
| 2865 | | .param_inst_list = &.{}, |
| 2866 | | }; |
| 2867 | | var block_scope: Scope.Block = .{ |
| 2868 | | .parent = null, |
| 2869 | | .sema = &sema, |
| 2870 | | .src_decl = new_decl, |
| 2871 | | .instructions = .{}, |
| 2872 | | .inlining = null, |
| 2873 | | .is_comptime = true, |
| 2874 | | }; |
| 2875 | | defer block_scope.instructions.deinit(gpa); |
| 2913 | try sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj); |
| 2876 | 2914 | |
| 2877 | | try sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj); |
| 2878 | | try new_decl.finalizeNewArena(&new_decl_arena); |
| 2915 | new_decl.analysis = .complete; |
| 2916 | } else { |
| 2917 | new_decl.analysis = .file_failure; |
| 2918 | } |
| 2879 | 2919 | |
| 2880 | | file.status = .success_air; |
| 2920 | try new_decl.finalizeNewArena(&new_decl_arena); |
| 2881 | 2921 | } |
| 2882 | 2922 | |
| 2883 | 2923 | /// Returns `true` if the Decl type changed. |
| ... | ... | @@ -2887,6 +2927,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2887 | 2927 | const tracy = trace(@src()); |
| 2888 | 2928 | defer tracy.end(); |
| 2889 | 2929 | |
| 2930 | if (decl.namespace.file_scope.status != .success_zir) { |
| 2931 | return error.AnalysisFail; |
| 2932 | } |
| 2933 | |
| 2890 | 2934 | const gpa = mod.gpa; |
| 2891 | 2935 | const zir = decl.namespace.file_scope.zir; |
| 2892 | 2936 | const zir_datas = zir.instructions.items(.data); |
| ... | ... | @@ -2914,8 +2958,6 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2914 | 2958 | const main_struct_inst = zir.getMainStruct(); |
| 2915 | 2959 | const struct_obj = decl.getStruct().?; |
| 2916 | 2960 | try sema.analyzeStructDecl(decl, main_struct_inst, struct_obj); |
| 2917 | | assert(decl.namespace.file_scope.status == .success_zir); |
| 2918 | | decl.namespace.file_scope.status = .success_air; |
| 2919 | 2961 | decl.analysis = .complete; |
| 2920 | 2962 | decl.generation = mod.generation; |
| 2921 | 2963 | return false; |
| ... | ... | @@ -3117,7 +3159,7 @@ pub fn importPkg(mod: *Module, cur_pkg: *Package, pkg: *Package) !ImportFileResu |
| 3117 | 3159 | .zir = undefined, |
| 3118 | 3160 | .status = .never_loaded, |
| 3119 | 3161 | .pkg = pkg, |
| 3120 | | .namespace = undefined, |
| 3162 | .namespace = null, |
| 3121 | 3163 | }; |
| 3122 | 3164 | return ImportFileResult{ |
| 3123 | 3165 | .file = new_file, |
| ... | ... | @@ -3183,7 +3225,7 @@ pub fn importFile( |
| 3183 | 3225 | .zir = undefined, |
| 3184 | 3226 | .status = .never_loaded, |
| 3185 | 3227 | .pkg = cur_file.pkg, |
| 3186 | | .namespace = undefined, |
| 3228 | .namespace = null, |
| 3187 | 3229 | }; |
| 3188 | 3230 | return ImportFileResult{ |
| 3189 | 3231 | .file = new_file, |
| ... | ... | @@ -4337,7 +4379,7 @@ pub fn optimizeMode(mod: Module) std.builtin.Mode { |
| 4337 | 4379 | |
| 4338 | 4380 | fn lockAndClearFileCompileError(mod: *Module, file: *Scope.File) void { |
| 4339 | 4381 | switch (file.status) { |
| 4340 | | .success_zir, .success_air, .retryable_failure => {}, |
| 4382 | .success_zir, .retryable_failure => {}, |
| 4341 | 4383 | .never_loaded, .parse_failure, .astgen_failure => { |
| 4342 | 4384 | const lock = mod.comp.mutex.acquire(); |
| 4343 | 4385 | defer lock.release(); |