| ... | @@ -362,7 +362,7 @@ pub const Decl = struct { | ... | @@ -362,7 +362,7 @@ pub const Decl = struct { |
| 362 | src_line: u32, | 362 | src_line: u32, |
| 363 | /// Index of the ZIR `declaration` instruction from which this `Decl` was created. | 363 | /// Index of the ZIR `declaration` instruction from which this `Decl` was created. |
| 364 | /// For the root `Decl` of a `File` and legacy anonymous decls, this is `.none`. | 364 | /// For the root `Decl` of a `File` and legacy anonymous decls, this is `.none`. |
| 365 | zir_decl_index: Zir.Inst.OptionalIndex, | 365 | zir_decl_index: InternPool.TrackedInst.Index.Optional, |
| 366 | | 366 | |
| 367 | /// Represents the "shallow" analysis status. For example, for decls that are functions, | 367 | /// Represents the "shallow" analysis status. For example, for decls that are functions, |
| 368 | /// the function type is analyzed with this set to `in_progress`, however, the semantic | 368 | /// the function type is analyzed with this set to `in_progress`, however, the semantic |
| ... | @@ -428,16 +428,9 @@ pub const Decl = struct { | ... | @@ -428,16 +428,9 @@ pub const Decl = struct { |
| 428 | const Index = InternPool.DeclIndex; | 428 | const Index = InternPool.DeclIndex; |
| 429 | const OptionalIndex = InternPool.OptionalDeclIndex; | 429 | const OptionalIndex = InternPool.OptionalDeclIndex; |
| 430 | | 430 | |
| 431 | /// Asserts that `zir_decl_index` is not `.none`. | | |
| 432 | fn getDeclaration(decl: Decl, zir: Zir) Zir.Inst.Declaration { | | |
| 433 | const zir_index = decl.zir_decl_index.unwrap().?; | | |
| 434 | const pl_node = zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node; | | |
| 435 | return zir.extraData(Zir.Inst.Declaration, pl_node.payload_index).data; | | |
| 436 | } | | |
| 437 | | | |
| 438 | pub fn zirBodies(decl: Decl, zcu: *Zcu) Zir.Inst.Declaration.Bodies { | 431 | pub fn zirBodies(decl: Decl, zcu: *Zcu) Zir.Inst.Declaration.Bodies { |
| 439 | const zir = decl.getFileScope(zcu).zir; | 432 | const zir = decl.getFileScope(zcu).zir; |
| 440 | const zir_index = decl.zir_decl_index.unwrap().?; | 433 | const zir_index = decl.zir_decl_index.unwrap().?.resolve(&zcu.intern_pool); |
| 441 | const pl_node = zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node; | 434 | const pl_node = zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node; |
| 442 | const extra = zir.extraData(Zir.Inst.Declaration, pl_node.payload_index); | 435 | const extra = zir.extraData(Zir.Inst.Declaration, pl_node.payload_index); |
| 443 | return extra.data.getBodies(@intCast(extra.end), zir); | 436 | return extra.data.getBodies(@intCast(extra.end), zir); |
| ... | @@ -769,14 +762,14 @@ pub const Namespace = struct { | ... | @@ -769,14 +762,14 @@ pub const Namespace = struct { |
| 769 | zcu: *Zcu, | 762 | zcu: *Zcu, |
| 770 | | 763 | |
| 771 | pub fn hash(ctx: @This(), decl_index: Decl.Index) u32 { | 764 | pub fn hash(ctx: @This(), decl_index: Decl.Index) u32 { |
| 772 | const decl = ctx.module.declPtr(decl_index); | 765 | const decl = ctx.zcu.declPtr(decl_index); |
| 773 | return std.hash.uint32(@intFromEnum(decl.name)); | 766 | return std.hash.uint32(@intFromEnum(decl.name)); |
| 774 | } | 767 | } |
| 775 | | 768 | |
| 776 | pub fn eql(ctx: @This(), a_decl_index: Decl.Index, b_decl_index: Decl.Index, b_index: usize) bool { | 769 | pub fn eql(ctx: @This(), a_decl_index: Decl.Index, b_decl_index: Decl.Index, b_index: usize) bool { |
| 777 | _ = b_index; | 770 | _ = b_index; |
| 778 | const a_decl = ctx.module.declPtr(a_decl_index); | 771 | const a_decl = ctx.zcu.declPtr(a_decl_index); |
| 779 | const b_decl = ctx.module.declPtr(b_decl_index); | 772 | const b_decl = ctx.zcu.declPtr(b_decl_index); |
| 780 | return a_decl.name == b_decl.name; | 773 | return a_decl.name == b_decl.name; |
| 781 | } | 774 | } |
| 782 | }; | 775 | }; |
| ... | @@ -2662,16 +2655,15 @@ pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void { | ... | @@ -2662,16 +2655,15 @@ pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 2662 | if (opt_po_entry) |e| e.value else 0, | 2655 | if (opt_po_entry) |e| e.value else 0, |
| 2663 | ); | 2656 | ); |
| 2664 | log.debug("outdated: {}", .{depender}); | 2657 | log.debug("outdated: {}", .{depender}); |
| 2665 | if (opt_po_entry != null) { | 2658 | if (opt_po_entry == null) { |
| 2666 | // This is a new entry with no PO dependencies. | 2659 | // This is a new entry with no PO dependencies. |
| 2667 | try zcu.outdated_ready.put(zcu.gpa, depender, {}); | 2660 | try zcu.outdated_ready.put(zcu.gpa, depender, {}); |
| 2668 | } | 2661 | } |
| 2669 | // If this is a Decl and was not previously PO, we must recursively | 2662 | // If this is a Decl and was not previously PO, we must recursively |
| 2670 | // mark dependencies on its tyval as PO. | 2663 | // mark dependencies on its tyval as PO. |
| 2671 | if (opt_po_entry == null) switch (depender.unwrap()) { | 2664 | if (opt_po_entry == null) { |
| 2672 | .decl => |decl_index| try zcu.markDeclDependenciesPotentiallyOutdated(decl_index), | 2665 | try zcu.markTransitiveDependersPotentiallyOutdated(depender); |
| 2673 | .func => {}, | 2666 | } |
| 2674 | }; | | |
| 2675 | } | 2667 | } |
| 2676 | } | 2668 | } |
| 2677 | | 2669 | |
| ... | @@ -2701,15 +2693,19 @@ fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { | ... | @@ -2701,15 +2693,19 @@ fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 2701 | // as no longer PO. | 2693 | // as no longer PO. |
| 2702 | switch (depender.unwrap()) { | 2694 | switch (depender.unwrap()) { |
| 2703 | .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }), | 2695 | .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }), |
| 2704 | .func => {}, | 2696 | .func => |func_index| try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }), |
| 2705 | } | 2697 | } |
| 2706 | } | 2698 | } |
| 2707 | } | 2699 | } |
| 2708 | | 2700 | |
| 2709 | /// Given a Decl which is newly outdated or PO, mark all dependers which depend | 2701 | /// Given a Depender which is newly outdated or PO, mark all Dependers which may |
| 2710 | /// on its tyval as PO. | 2702 | /// in turn be PO, due to a dependency on the original Depender's tyval or IES. |
| 2711 | fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !void { | 2703 | fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: InternPool.Depender) !void { |
| 2712 | var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); | 2704 | var it = zcu.intern_pool.dependencyIterator(switch (maybe_outdated.unwrap()) { |
| | 2705 | .decl => |decl_index| .{ .decl_val = decl_index }, // TODO: also `decl_ref` deps when introduced |
| | 2706 | .func => |func_index| .{ .func_ies = func_index }, |
| | 2707 | }); |
| | 2708 | |
| 2713 | while (it.next()) |po| { | 2709 | while (it.next()) |po| { |
| 2714 | if (zcu.outdated.getPtr(po)) |po_dep_count| { | 2710 | if (zcu.outdated.getPtr(po)) |po_dep_count| { |
| 2715 | // This dependency is already outdated, but it now has one more PO | 2711 | // This dependency is already outdated, but it now has one more PO |
| ... | @@ -2726,14 +2722,9 @@ fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !v | ... | @@ -2726,14 +2722,9 @@ fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !v |
| 2726 | continue; | 2722 | continue; |
| 2727 | } | 2723 | } |
| 2728 | try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1); | 2724 | try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1); |
| 2729 | // If this ia a Decl, we must recursively mark dependencies | 2725 | // This Depender was not already PO, so we must recursively mark its dependers as also PO. |
| 2730 | // on its tyval as PO. | 2726 | try zcu.markTransitiveDependersPotentiallyOutdated(po); |
| 2731 | switch (po.unwrap()) { | | |
| 2732 | .decl => |po_decl| try zcu.markDeclDependenciesPotentiallyOutdated(po_decl), | | |
| 2733 | .func => {}, | | |
| 2734 | } | | |
| 2735 | } | 2727 | } |
| 2736 | // TODO: repeat the above for `decl_ty` dependencies when they are introduced | | |
| 2737 | } | 2728 | } |
| 2738 | | 2729 | |
| 2739 | pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender { | 2730 | pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender { |
| ... | @@ -2859,10 +2850,7 @@ pub fn flushRetryableFailures(zcu: *Zcu) !void { | ... | @@ -2859,10 +2850,7 @@ pub fn flushRetryableFailures(zcu: *Zcu) !void { |
| 2859 | // This Depender was not marked PO, but is now outdated. Mark it as | 2850 | // This Depender was not marked PO, but is now outdated. Mark it as |
| 2860 | // such, then recursively mark transitive dependencies as PO. | 2851 | // such, then recursively mark transitive dependencies as PO. |
| 2861 | try zcu.outdated.put(gpa, depender, 0); | 2852 | try zcu.outdated.put(gpa, depender, 0); |
| 2862 | switch (depender.unwrap()) { | 2853 | try zcu.markTransitiveDependersPotentiallyOutdated(depender); |
| 2863 | .decl => |decl| try zcu.markDeclDependenciesPotentiallyOutdated(decl), | | |
| 2864 | .func => {}, | | |
| 2865 | } | | |
| 2866 | } | 2854 | } |
| 2867 | zcu.retryable_failures.clearRetainingCapacity(); | 2855 | zcu.retryable_failures.clearRetainingCapacity(); |
| 2868 | } | 2856 | } |
| ... | @@ -2994,6 +2982,15 @@ pub fn mapOldZirToNew( | ... | @@ -2994,6 +2982,15 @@ pub fn mapOldZirToNew( |
| 2994 | } | 2982 | } |
| 2995 | } | 2983 | } |
| 2996 | | 2984 | |
| | 2985 | /// Like `ensureDeclAnalyzed`, but the Decl is a file's root Decl. |
| | 2986 | pub fn ensureFileAnalyzed(zcu: *Zcu, file: *File) SemaError!void { |
| | 2987 | if (file.root_decl.unwrap()) |existing_root| { |
| | 2988 | return zcu.ensureDeclAnalyzed(existing_root); |
| | 2989 | } else { |
| | 2990 | return zcu.semaFile(file); |
| | 2991 | } |
| | 2992 | } |
| | 2993 | |
| 2997 | /// This ensures that the Decl will have an up-to-date Type and Value populated. | 2994 | /// This ensures that the Decl will have an up-to-date Type and Value populated. |
| 2998 | /// However the resolution status of the Type may not be fully resolved. | 2995 | /// However the resolution status of the Type may not be fully resolved. |
| 2999 | /// For example an inferred error set is not resolved until after `analyzeFnBody`. | 2996 | /// For example an inferred error set is not resolved until after `analyzeFnBody`. |
| ... | @@ -3004,6 +3001,11 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { | ... | @@ -3004,6 +3001,11 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 3004 | | 3001 | |
| 3005 | const decl = mod.declPtr(decl_index); | 3002 | const decl = mod.declPtr(decl_index); |
| 3006 | | 3003 | |
| | 3004 | log.debug("ensureDeclAnalyzed '{d}' (name '{}')", .{ |
| | 3005 | @intFromEnum(decl_index), |
| | 3006 | decl.name.fmt(&mod.intern_pool), |
| | 3007 | }); |
| | 3008 | |
| 3007 | // Determine whether or not this Decl is outdated, i.e. requires re-analysis | 3009 | // Determine whether or not this Decl is outdated, i.e. requires re-analysis |
| 3008 | // even if `complete`. If a Decl is PO, we pessismistically assume that it | 3010 | // even if `complete`. If a Decl is PO, we pessismistically assume that it |
| 3009 | // *does* require re-analysis, to ensure that the Decl is definitely | 3011 | // *does* require re-analysis, to ensure that the Decl is definitely |
| ... | @@ -3015,13 +3017,15 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { | ... | @@ -3015,13 +3017,15 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 3015 | // dependencies are all up-to-date. | 3017 | // dependencies are all up-to-date. |
| 3016 | | 3018 | |
| 3017 | const decl_as_depender = InternPool.Depender.wrap(.{ .decl = decl_index }); | 3019 | const decl_as_depender = InternPool.Depender.wrap(.{ .decl = decl_index }); |
| 3018 | const was_outdated = mod.outdated.swapRemove(decl_as_depender) or | 3020 | const decl_was_outdated = mod.outdated.swapRemove(decl_as_depender) or |
| 3019 | mod.potentially_outdated.swapRemove(decl_as_depender); | 3021 | mod.potentially_outdated.swapRemove(decl_as_depender); |
| 3020 | | 3022 | |
| 3021 | if (was_outdated) { | 3023 | if (decl_was_outdated) { |
| 3022 | _ = mod.outdated_ready.swapRemove(decl_as_depender); | 3024 | _ = mod.outdated_ready.swapRemove(decl_as_depender); |
| 3023 | } | 3025 | } |
| 3024 | | 3026 | |
| | 3027 | const was_outdated = mod.outdated_file_root.swapRemove(decl_index) or decl_was_outdated; |
| | 3028 | |
| 3025 | switch (decl.analysis) { | 3029 | switch (decl.analysis) { |
| 3026 | .in_progress => unreachable, | 3030 | .in_progress => unreachable, |
| 3027 | | 3031 | |
| ... | @@ -3057,6 +3061,14 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { | ... | @@ -3057,6 +3061,14 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 3057 | }; | 3061 | }; |
| 3058 | } | 3062 | } |
| 3059 | | 3063 | |
| | 3064 | if (mod.declIsRoot(decl_index)) { |
| | 3065 | const changed = try mod.semaFileUpdate(decl.getFileScope(mod), decl_was_outdated); |
| | 3066 | break :blk .{ |
| | 3067 | .invalidate_decl_val = changed, |
| | 3068 | .invalidate_decl_ref = changed, |
| | 3069 | }; |
| | 3070 | } |
| | 3071 | |
| 3060 | break :blk mod.semaDecl(decl_index) catch |err| switch (err) { | 3072 | break :blk mod.semaDecl(decl_index) catch |err| switch (err) { |
| 3061 | error.AnalysisFail => { | 3073 | error.AnalysisFail => { |
| 3062 | if (decl.analysis == .in_progress) { | 3074 | if (decl.analysis == .in_progress) { |
| ... | @@ -3085,13 +3097,15 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { | ... | @@ -3085,13 +3097,15 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 3085 | }; | 3097 | }; |
| 3086 | | 3098 | |
| 3087 | // TODO: we do not yet have separate dependencies for decl values vs types. | 3099 | // TODO: we do not yet have separate dependencies for decl values vs types. |
| 3088 | if (was_outdated) { | 3100 | if (decl_was_outdated) { |
| 3089 | if (sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref) { | 3101 | if (sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref) { |
| | 3102 | log.debug("Decl tv invalidated ('{d}')", .{@intFromEnum(decl_index)}); |
| 3090 | // This dependency was marked as PO, meaning dependees were waiting | 3103 | // This dependency was marked as PO, meaning dependees were waiting |
| 3091 | // on its analysis result, and it has turned out to be outdated. | 3104 | // on its analysis result, and it has turned out to be outdated. |
| 3092 | // Update dependees accordingly. | 3105 | // Update dependees accordingly. |
| 3093 | try mod.markDependeeOutdated(.{ .decl_val = decl_index }); | 3106 | try mod.markDependeeOutdated(.{ .decl_val = decl_index }); |
| 3094 | } else { | 3107 | } else { |
| | 3108 | log.debug("Decl tv up-to-date ('{d}')", .{@intFromEnum(decl_index)}); |
| 3095 | // This dependency was previously PO, but turned out to be up-to-date. | 3109 | // This dependency was previously PO, but turned out to be up-to-date. |
| 3096 | // We do not need to queue successive analysis. | 3110 | // We do not need to queue successive analysis. |
| 3097 | try mod.markPoDependeeUpToDate(.{ .decl_val = decl_index }); | 3111 | try mod.markPoDependeeUpToDate(.{ .decl_val = decl_index }); |
| ... | @@ -3099,15 +3113,48 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { | ... | @@ -3099,15 +3113,48 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 3099 | } | 3113 | } |
| 3100 | } | 3114 | } |
| 3101 | | 3115 | |
| 3102 | pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError!void { | 3116 | pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.Index) SemaError!void { |
| 3103 | const tracy = trace(@src()); | 3117 | const tracy = trace(@src()); |
| 3104 | defer tracy.end(); | 3118 | defer tracy.end(); |
| 3105 | | 3119 | |
| | 3120 | const gpa = zcu.gpa; |
| 3106 | const ip = &zcu.intern_pool; | 3121 | const ip = &zcu.intern_pool; |
| 3107 | const func = zcu.funcInfo(func_index); | 3122 | |
| | 3123 | // We only care about the uncoerced function. |
| | 3124 | // We need to do this for the "orphaned function" check below to be valid. |
| | 3125 | const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index); |
| | 3126 | |
| | 3127 | const func = zcu.funcInfo(maybe_coerced_func_index); |
| 3108 | const decl_index = func.owner_decl; | 3128 | const decl_index = func.owner_decl; |
| 3109 | const decl = zcu.declPtr(decl_index); | 3129 | const decl = zcu.declPtr(decl_index); |
| 3110 | | 3130 | |
| | 3131 | log.debug("ensureFuncBodyAnalyzed '{d}' (instance of '{}')", .{ |
| | 3132 | @intFromEnum(func_index), |
| | 3133 | decl.name.fmt(ip), |
| | 3134 | }); |
| | 3135 | |
| | 3136 | // First, our owner decl must be up-to-date. This will always be the case |
| | 3137 | // during the first update, but may not on successive updates if we happen |
| | 3138 | // to get analyzed before our parent decl. |
| | 3139 | try zcu.ensureDeclAnalyzed(decl_index); |
| | 3140 | |
| | 3141 | // On an update, it's possible this function changed such that our owner |
| | 3142 | // decl now refers to a different function, making this one orphaned. If |
| | 3143 | // that's the case, we should remove this function from the binary. |
| | 3144 | if (decl.val.ip_index != func_index) { |
| | 3145 | try zcu.markDependeeOutdated(.{ .func_ies = func_index }); |
| | 3146 | ip.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index })); |
| | 3147 | ip.remove(func_index); |
| | 3148 | @panic("TODO: remove orphaned function from binary"); |
| | 3149 | } |
| | 3150 | |
| | 3151 | // We'll want to remember what the IES used to be before the update for |
| | 3152 | // dependency invalidation purposes. |
| | 3153 | const old_resolved_ies = if (func.analysis(ip).inferred_error_set) |
| | 3154 | func.resolvedErrorSet(ip).* |
| | 3155 | else |
| | 3156 | .none; |
| | 3157 | |
| 3111 | switch (decl.analysis) { | 3158 | switch (decl.analysis) { |
| 3112 | .unreferenced => unreachable, | 3159 | .unreferenced => unreachable, |
| 3113 | .in_progress => unreachable, | 3160 | .in_progress => unreachable, |
| ... | @@ -3131,7 +3178,7 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError | ... | @@ -3131,7 +3178,7 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError |
| 3131 | } | 3178 | } |
| 3132 | | 3179 | |
| 3133 | switch (func.analysis(ip).state) { | 3180 | switch (func.analysis(ip).state) { |
| 3134 | .success, | 3181 | .success => if (!was_outdated) return, |
| 3135 | .sema_failure, | 3182 | .sema_failure, |
| 3136 | .dependency_failure, | 3183 | .dependency_failure, |
| 3137 | .codegen_failure, | 3184 | .codegen_failure, |
| ... | @@ -3141,7 +3188,10 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError | ... | @@ -3141,7 +3188,10 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError |
| 3141 | .inline_only => unreachable, // don't queue work for this | 3188 | .inline_only => unreachable, // don't queue work for this |
| 3142 | } | 3189 | } |
| 3143 | | 3190 | |
| 3144 | const gpa = zcu.gpa; | 3191 | log.debug("analyze and generate fn body '{d}'; reason='{s}'", .{ |
| | 3192 | @intFromEnum(func_index), |
| | 3193 | if (was_outdated) "outdated" else "never analyzed", |
| | 3194 | }); |
| 3145 | | 3195 | |
| 3146 | var tmp_arena = std.heap.ArenaAllocator.init(gpa); | 3196 | var tmp_arena = std.heap.ArenaAllocator.init(gpa); |
| 3147 | defer tmp_arena.deinit(); | 3197 | defer tmp_arena.deinit(); |
| ... | @@ -3161,6 +3211,20 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError | ... | @@ -3161,6 +3211,20 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError |
| 3161 | }; | 3211 | }; |
| 3162 | defer air.deinit(gpa); | 3212 | defer air.deinit(gpa); |
| 3163 | | 3213 | |
| | 3214 | const invalidate_ies_deps = i: { |
| | 3215 | if (!was_outdated) break :i false; |
| | 3216 | if (!func.analysis(ip).inferred_error_set) break :i true; |
| | 3217 | const new_resolved_ies = func.resolvedErrorSet(ip).*; |
| | 3218 | break :i new_resolved_ies != old_resolved_ies; |
| | 3219 | }; |
| | 3220 | if (invalidate_ies_deps) { |
| | 3221 | log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)}); |
| | 3222 | try zcu.markDependeeOutdated(.{ .func_ies = func_index }); |
| | 3223 | } else if (was_outdated) { |
| | 3224 | log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)}); |
| | 3225 | try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }); |
| | 3226 | } |
| | 3227 | |
| 3164 | const comp = zcu.comp; | 3228 | const comp = zcu.comp; |
| 3165 | | 3229 | |
| 3166 | const dump_air = build_options.enable_debug_extensions and comp.verbose_air; | 3230 | const dump_air = build_options.enable_debug_extensions and comp.verbose_air; |
| ... | @@ -3299,7 +3363,9 @@ pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index) | ... | @@ -3299,7 +3363,9 @@ pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index) |
| 3299 | /// https://github.com/ziglang/zig/issues/14307 | 3363 | /// https://github.com/ziglang/zig/issues/14307 |
| 3300 | pub fn semaPkg(mod: *Module, pkg: *Package.Module) !void { | 3364 | pub fn semaPkg(mod: *Module, pkg: *Package.Module) !void { |
| 3301 | const file = (try mod.importPkg(pkg)).file; | 3365 | const file = (try mod.importPkg(pkg)).file; |
| 3302 | return mod.semaFile(file); | 3366 | if (file.root_decl == .none) { |
| | 3367 | return mod.semaFile(file); |
| | 3368 | } |
| 3303 | } | 3369 | } |
| 3304 | | 3370 | |
| 3305 | fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespace.Index, file: *File) Allocator.Error!InternPool.Index { | 3371 | fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespace.Index, file: *File) Allocator.Error!InternPool.Index { |
| ... | @@ -3366,13 +3432,75 @@ fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespa | ... | @@ -3366,13 +3432,75 @@ fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespa |
| 3366 | return wip_ty.finish(ip, decl_index, namespace_index.toOptional()); | 3432 | return wip_ty.finish(ip, decl_index, namespace_index.toOptional()); |
| 3367 | } | 3433 | } |
| 3368 | | 3434 | |
| 3369 | /// Regardless of the file status, will create a `Decl` so that we | 3435 | /// Re-analyze the root Decl of a file on an incremental update. |
| 3370 | /// can track dependencies and re-analyze when the file becomes outdated. | 3436 | /// If `type_outdated`, the struct type itself is considered outdated and is |
| 3371 | pub fn semaFile(mod: *Module, file: *File) SemaError!void { | 3437 | /// reconstructed at a new InternPool index. Otherwise, the namespace is just |
| | 3438 | /// re-analyzed. Returns whether the decl's tyval was invalidated. |
| | 3439 | fn semaFileUpdate(zcu: *Zcu, file: *File, type_outdated: bool) SemaError!bool { |
| | 3440 | const decl = zcu.declPtr(file.root_decl.unwrap().?); |
| | 3441 | |
| | 3442 | log.debug("semaFileUpdate mod={s} sub_file_path={s} type_outdated={}", .{ |
| | 3443 | file.mod.fully_qualified_name, |
| | 3444 | file.sub_file_path, |
| | 3445 | type_outdated, |
| | 3446 | }); |
| | 3447 | |
| | 3448 | if (file.status != .success_zir) { |
| | 3449 | if (decl.analysis == .file_failure) { |
| | 3450 | return false; |
| | 3451 | } else { |
| | 3452 | decl.analysis = .file_failure; |
| | 3453 | return true; |
| | 3454 | } |
| | 3455 | } |
| | 3456 | |
| | 3457 | if (decl.analysis == .file_failure) { |
| | 3458 | // No struct type currently exists. Create one! |
| | 3459 | _ = try zcu.getFileRootStruct(file.root_decl.unwrap().?, decl.src_namespace, file); |
| | 3460 | return true; |
| | 3461 | } |
| | 3462 | |
| | 3463 | assert(decl.has_tv); |
| | 3464 | assert(decl.owns_tv); |
| | 3465 | |
| | 3466 | if (type_outdated) { |
| | 3467 | // Invalidate the existing type, reusing the decl and namespace. |
| | 3468 | zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.Depender.wrap(.{ .decl = file.root_decl.unwrap().? })); |
| | 3469 | zcu.intern_pool.remove(decl.val.toIntern()); |
| | 3470 | decl.val = undefined; |
| | 3471 | _ = try zcu.getFileRootStruct(file.root_decl.unwrap().?, decl.src_namespace, file); |
| | 3472 | return true; |
| | 3473 | } |
| | 3474 | |
| | 3475 | // Only the struct's namespace is outdated. |
| | 3476 | // Preserve the type - just scan the namespace again. |
| | 3477 | |
| | 3478 | const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; |
| | 3479 | const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); |
| | 3480 | |
| | 3481 | var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; |
| | 3482 | extra_index += @intFromBool(small.has_fields_len); |
| | 3483 | const decls_len = if (small.has_decls_len) blk: { |
| | 3484 | const decls_len = file.zir.extra[extra_index]; |
| | 3485 | extra_index += 1; |
| | 3486 | break :blk decls_len; |
| | 3487 | } else 0; |
| | 3488 | const decls = file.zir.bodySlice(extra_index, decls_len); |
| | 3489 | |
| | 3490 | if (!type_outdated) { |
| | 3491 | try zcu.scanNamespace(decl.src_namespace, decls, decl); |
| | 3492 | } |
| | 3493 | |
| | 3494 | return false; |
| | 3495 | } |
| | 3496 | |
| | 3497 | /// Regardless of the file status, will create a `Decl` if none exists so that we can track |
| | 3498 | /// dependencies and re-analyze when the file becomes outdated. |
| | 3499 | fn semaFile(mod: *Module, file: *File) SemaError!void { |
| 3372 | const tracy = trace(@src()); | 3500 | const tracy = trace(@src()); |
| 3373 | defer tracy.end(); | 3501 | defer tracy.end(); |
| 3374 | | 3502 | |
| 3375 | if (file.root_decl != .none) return; | 3503 | assert(file.root_decl == .none); |
| 3376 | | 3504 | |
| 3377 | const gpa = mod.gpa; | 3505 | const gpa = mod.gpa; |
| 3378 | log.debug("semaFile mod={s} sub_file_path={s}", .{ | 3506 | log.debug("semaFile mod={s} sub_file_path={s}", .{ |
| ... | @@ -3439,9 +3567,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { | ... | @@ -3439,9 +3567,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { |
| 3439 | }, | 3567 | }, |
| 3440 | .incremental => {}, | 3568 | .incremental => {}, |
| 3441 | } | 3569 | } |
| 3442 | | | |
| 3443 | // Since this is our first time analyzing this file, there can be no dependencies on | | |
| 3444 | // its root Decl. Thus, we do not need to invalidate any dependencies. | | |
| 3445 | } | 3570 | } |
| 3446 | | 3571 | |
| 3447 | const SemaDeclResult = packed struct { | 3572 | const SemaDeclResult = packed struct { |
| ... | @@ -3462,16 +3587,16 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { | ... | @@ -3462,16 +3587,16 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { |
| 3462 | return error.AnalysisFail; | 3587 | return error.AnalysisFail; |
| 3463 | } | 3588 | } |
| 3464 | | 3589 | |
| 3465 | if (mod.declIsRoot(decl_index)) { | 3590 | assert(!mod.declIsRoot(decl_index)); |
| 3466 | // This comes from an `analyze_decl` job on an incremental update where | 3591 | |
| 3467 | // this file changed. | 3592 | if (decl.zir_decl_index == .none and decl.owns_tv) { |
| 3468 | @panic("TODO: update root Decl of modified file"); | 3593 | // We are re-analyzing an anonymous owner Decl (for a function or a namespace type). |
| 3469 | } else if (decl.owns_tv) { | 3594 | return mod.semaAnonOwnerDecl(decl_index); |
| 3470 | // We are re-analyzing an owner Decl (for a function or a namespace type). | | |
| 3471 | @panic("TODO: update owner Decl"); | | |
| 3472 | } | 3595 | } |
| 3473 | | 3596 | |
| 3474 | const decl_inst = decl.zir_decl_index.unwrap().?; | 3597 | log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)}); |
| | 3598 | |
| | 3599 | const decl_inst = decl.zir_decl_index.unwrap().?.resolve(ip); |
| 3475 | | 3600 | |
| 3476 | const gpa = mod.gpa; | 3601 | const gpa = mod.gpa; |
| 3477 | const zir = decl.getFileScope(mod).zir; | 3602 | const zir = decl.getFileScope(mod).zir; |
| ... | @@ -3763,6 +3888,42 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { | ... | @@ -3763,6 +3888,42 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { |
| 3763 | return result; | 3888 | return result; |
| 3764 | } | 3889 | } |
| 3765 | | 3890 | |
| | 3891 | fn semaAnonOwnerDecl(zcu: *Zcu, decl_index: Decl.Index) !SemaDeclResult { |
| | 3892 | const decl = zcu.declPtr(decl_index); |
| | 3893 | |
| | 3894 | assert(decl.has_tv); |
| | 3895 | assert(decl.owns_tv); |
| | 3896 | |
| | 3897 | log.debug("semaAnonOwnerDecl '{d}'", .{@intFromEnum(decl_index)}); |
| | 3898 | |
| | 3899 | switch (decl.ty.zigTypeTag(zcu)) { |
| | 3900 | .Fn => @panic("TODO: update fn instance"), |
| | 3901 | .Type => {}, |
| | 3902 | else => unreachable, |
| | 3903 | } |
| | 3904 | |
| | 3905 | // We are the owner Decl of a type, and we were marked as outdated. That means the *structure* |
| | 3906 | // of this type changed; not just its namespace. Therefore, we need a new InternPool index. |
| | 3907 | // |
| | 3908 | // However, as soon as we make that, the context that created us will require re-analysis anyway |
| | 3909 | // (as it depends on this Decl's value), meaning the `struct_decl` (or equivalent) instruction |
| | 3910 | // will be analyzed again. Since Sema already needs to be able to reconstruct types like this, |
| | 3911 | // why should we bother implementing it here too when the Sema logic will be hit right after? |
| | 3912 | // |
| | 3913 | // So instead, let's just mark this Decl as failed - so that any remaining Decls which genuinely |
| | 3914 | // reference it (via `@This`) end up silently erroring too - and we'll let Sema make a new type |
| | 3915 | // with a new Decl. |
| | 3916 | // |
| | 3917 | // Yes, this does mean that any type owner Decl has a constant value for its entire lifetime. |
| | 3918 | zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.Depender.wrap(.{ .decl = decl_index })); |
| | 3919 | zcu.intern_pool.remove(decl.val.toIntern()); |
| | 3920 | decl.analysis = .dependency_failure; |
| | 3921 | return .{ |
| | 3922 | .invalidate_decl_val = true, |
| | 3923 | .invalidate_decl_ref = true, |
| | 3924 | }; |
| | 3925 | } |
| | 3926 | |
| 3766 | pub const ImportFileResult = struct { | 3927 | pub const ImportFileResult = struct { |
| 3767 | file: *File, | 3928 | file: *File, |
| 3768 | is_new: bool, | 3929 | is_new: bool, |
| ... | @@ -4083,26 +4244,87 @@ pub fn scanNamespace( | ... | @@ -4083,26 +4244,87 @@ pub fn scanNamespace( |
| 4083 | const gpa = zcu.gpa; | 4244 | const gpa = zcu.gpa; |
| 4084 | const namespace = zcu.namespacePtr(namespace_index); | 4245 | const namespace = zcu.namespacePtr(namespace_index); |
| 4085 | | 4246 | |
| | 4247 | // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather |
| | 4248 | // than their name. We'll build an efficient mapping now, then discard the current `decls`. |
| | 4249 | var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, Decl.Index) = .{}; |
| | 4250 | defer existing_by_inst.deinit(gpa); |
| | 4251 | |
| | 4252 | try existing_by_inst.ensureTotalCapacity(gpa, @intCast(namespace.decls.count())); |
| | 4253 | |
| | 4254 | for (namespace.decls.keys()) |decl_index| { |
| | 4255 | const decl = zcu.declPtr(decl_index); |
| | 4256 | existing_by_inst.putAssumeCapacityNoClobber(decl.zir_decl_index.unwrap().?, decl_index); |
| | 4257 | } |
| | 4258 | |
| | 4259 | var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; |
| | 4260 | defer seen_decls.deinit(gpa); |
| | 4261 | |
| 4086 | try zcu.comp.work_queue.ensureUnusedCapacity(decls.len); | 4262 | try zcu.comp.work_queue.ensureUnusedCapacity(decls.len); |
| | 4263 | |
| | 4264 | namespace.decls.clearRetainingCapacity(); |
| 4087 | try namespace.decls.ensureTotalCapacity(gpa, decls.len); | 4265 | try namespace.decls.ensureTotalCapacity(gpa, decls.len); |
| 4088 | | 4266 | |
| | 4267 | namespace.usingnamespace_set.clearRetainingCapacity(); |
| | 4268 | |
| 4089 | var scan_decl_iter: ScanDeclIter = .{ | 4269 | var scan_decl_iter: ScanDeclIter = .{ |
| 4090 | .zcu = zcu, | 4270 | .zcu = zcu, |
| 4091 | .namespace_index = namespace_index, | 4271 | .namespace_index = namespace_index, |
| 4092 | .parent_decl = parent_decl, | 4272 | .parent_decl = parent_decl, |
| | 4273 | .seen_decls = &seen_decls, |
| | 4274 | .existing_by_inst = &existing_by_inst, |
| | 4275 | .pass = .named, |
| 4093 | }; | 4276 | }; |
| 4094 | for (decls) |decl_inst| { | 4277 | for (decls) |decl_inst| { |
| 4095 | try scanDecl(&scan_decl_iter, decl_inst); | 4278 | try scanDecl(&scan_decl_iter, decl_inst); |
| 4096 | } | 4279 | } |
| | 4280 | scan_decl_iter.pass = .unnamed; |
| | 4281 | for (decls) |decl_inst| { |
| | 4282 | try scanDecl(&scan_decl_iter, decl_inst); |
| | 4283 | } |
| | 4284 | |
| | 4285 | if (seen_decls.count() != namespace.decls.count()) { |
| | 4286 | // Do a pass over the namespace contents and remove any decls from the last update |
| | 4287 | // which were removed in this one. |
| | 4288 | var i: usize = 0; |
| | 4289 | while (i < namespace.decls.count()) { |
| | 4290 | const decl_index = namespace.decls.keys()[i]; |
| | 4291 | const decl = zcu.declPtr(decl_index); |
| | 4292 | if (!seen_decls.contains(decl.name)) { |
| | 4293 | // We must preserve namespace ordering for @typeInfo. |
| | 4294 | namespace.decls.orderedRemoveAt(i); |
| | 4295 | i -= 1; |
| | 4296 | } |
| | 4297 | } |
| | 4298 | } |
| 4097 | } | 4299 | } |
| 4098 | | 4300 | |
| 4099 | const ScanDeclIter = struct { | 4301 | const ScanDeclIter = struct { |
| 4100 | zcu: *Zcu, | 4302 | zcu: *Zcu, |
| 4101 | namespace_index: Namespace.Index, | 4303 | namespace_index: Namespace.Index, |
| 4102 | parent_decl: *Decl, | 4304 | parent_decl: *Decl, |
| | 4305 | seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void), |
| | 4306 | existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, Decl.Index), |
| | 4307 | /// Decl scanning is run in two passes, so that we can detect when a generated |
| | 4308 | /// name would clash with an explicit name and use a different one. |
| | 4309 | pass: enum { named, unnamed }, |
| 4103 | usingnamespace_index: usize = 0, | 4310 | usingnamespace_index: usize = 0, |
| 4104 | comptime_index: usize = 0, | 4311 | comptime_index: usize = 0, |
| 4105 | unnamed_test_index: usize = 0, | 4312 | unnamed_test_index: usize = 0, |
| | 4313 | |
| | 4314 | fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString { |
| | 4315 | const zcu = iter.zcu; |
| | 4316 | const gpa = zcu.gpa; |
| | 4317 | const ip = &zcu.intern_pool; |
| | 4318 | var name = try ip.getOrPutStringFmt(gpa, fmt, args); |
| | 4319 | var gop = try iter.seen_decls.getOrPut(gpa, name); |
| | 4320 | var next_suffix: u32 = 0; |
| | 4321 | while (gop.found_existing) { |
| | 4322 | name = try ip.getOrPutStringFmt(gpa, fmt ++ "_{d}", args ++ .{next_suffix}); |
| | 4323 | gop = try iter.seen_decls.getOrPut(gpa, name); |
| | 4324 | next_suffix += 1; |
| | 4325 | } |
| | 4326 | return name; |
| | 4327 | } |
| 4106 | }; | 4328 | }; |
| 4107 | | 4329 | |
| 4108 | fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void { | 4330 | fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void { |
| ... | @@ -4126,134 +4348,148 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void | ... | @@ -4126,134 +4348,148 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void |
| 4126 | // Every Decl needs a name. | 4348 | // Every Decl needs a name. |
| 4127 | const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) { | 4349 | const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) { |
| 4128 | .@"comptime" => info: { | 4350 | .@"comptime" => info: { |
| | 4351 | if (iter.pass != .unnamed) return; |
| 4129 | const i = iter.comptime_index; | 4352 | const i = iter.comptime_index; |
| 4130 | iter.comptime_index += 1; | 4353 | iter.comptime_index += 1; |
| 4131 | break :info .{ | 4354 | break :info .{ |
| 4132 | try ip.getOrPutStringFmt(gpa, "comptime_{d}", .{i}), | 4355 | try iter.avoidNameConflict("comptime_{d}", .{i}), |
| 4133 | .@"comptime", | 4356 | .@"comptime", |
| 4134 | false, | 4357 | false, |
| 4135 | }; | 4358 | }; |
| 4136 | }, | 4359 | }, |
| 4137 | .@"usingnamespace" => info: { | 4360 | .@"usingnamespace" => info: { |
| | 4361 | // TODO: this isn't right! These should be considered unnamed. Name conflicts can happen here. |
| | 4362 | // The problem is, we need to preserve the decl ordering for `@typeInfo`. |
| | 4363 | // I'm not bothering to fix this now, since some upcoming changes will change this code significantly anyway. |
| | 4364 | if (iter.pass != .named) return; |
| 4138 | const i = iter.usingnamespace_index; | 4365 | const i = iter.usingnamespace_index; |
| 4139 | iter.usingnamespace_index += 1; | 4366 | iter.usingnamespace_index += 1; |
| 4140 | break :info .{ | 4367 | break :info .{ |
| 4141 | try ip.getOrPutStringFmt(gpa, "usingnamespace_{d}", .{i}), | 4368 | try iter.avoidNameConflict("usingnamespace_{d}", .{i}), |
| 4142 | .@"usingnamespace", | 4369 | .@"usingnamespace", |
| 4143 | false, | 4370 | false, |
| 4144 | }; | 4371 | }; |
| 4145 | }, | 4372 | }, |
| 4146 | .unnamed_test => info: { | 4373 | .unnamed_test => info: { |
| | 4374 | if (iter.pass != .unnamed) return; |
| 4147 | const i = iter.unnamed_test_index; | 4375 | const i = iter.unnamed_test_index; |
| 4148 | iter.unnamed_test_index += 1; | 4376 | iter.unnamed_test_index += 1; |
| 4149 | break :info .{ | 4377 | break :info .{ |
| 4150 | try ip.getOrPutStringFmt(gpa, "test_{d}", .{i}), | 4378 | try iter.avoidNameConflict("test_{d}", .{i}), |
| 4151 | .@"test", | 4379 | .@"test", |
| 4152 | false, | 4380 | false, |
| 4153 | }; | 4381 | }; |
| 4154 | }, | 4382 | }, |
| 4155 | .decltest => info: { | 4383 | .decltest => info: { |
| | 4384 | // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary. |
| | 4385 | if (iter.pass != .unnamed) return; |
| 4156 | assert(declaration.flags.has_doc_comment); | 4386 | assert(declaration.flags.has_doc_comment); |
| 4157 | const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end])); | 4387 | const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end])); |
| 4158 | break :info .{ | 4388 | break :info .{ |
| 4159 | try ip.getOrPutStringFmt(gpa, "decltest.{s}", .{name}), | 4389 | try iter.avoidNameConflict("decltest.{s}", .{name}), |
| 4160 | .@"test", | 4390 | .@"test", |
| 4161 | true, | 4391 | true, |
| 4162 | }; | 4392 | }; |
| 4163 | }, | 4393 | }, |
| 4164 | _ => if (declaration.name.isNamedTest(zir)) .{ | 4394 | _ => if (declaration.name.isNamedTest(zir)) info: { |
| 4165 | try ip.getOrPutStringFmt(gpa, "test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}), | 4395 | // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary. |
| 4166 | .@"test", | 4396 | if (iter.pass != .unnamed) return; |
| 4167 | true, | 4397 | break :info .{ |
| 4168 | } else .{ | 4398 | try iter.avoidNameConflict("test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}), |
| 4169 | try ip.getOrPutString(gpa, zir.nullTerminatedString(declaration.name.toString(zir).?)), | 4399 | .@"test", |
| 4170 | .named, | 4400 | true, |
| 4171 | false, | 4401 | }; |
| | 4402 | } else info: { |
| | 4403 | if (iter.pass != .named) return; |
| | 4404 | const name = try ip.getOrPutString(gpa, zir.nullTerminatedString(declaration.name.toString(zir).?)); |
| | 4405 | try iter.seen_decls.putNoClobber(gpa, name, {}); |
| | 4406 | break :info .{ |
| | 4407 | name, |
| | 4408 | .named, |
| | 4409 | false, |
| | 4410 | }; |
| 4172 | }, | 4411 | }, |
| 4173 | }; | 4412 | }; |
| 4174 | | 4413 | |
| 4175 | if (kind == .@"usingnamespace") try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); | 4414 | switch (kind) { |
| | 4415 | .@"usingnamespace" => try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1), |
| | 4416 | .@"test" => try zcu.test_functions.ensureUnusedCapacity(gpa, 1), |
| | 4417 | else => {}, |
| | 4418 | } |
| | 4419 | |
| | 4420 | const tracked_inst = try ip.trackZir(gpa, iter.parent_decl.getFileScope(zcu), decl_inst); |
| 4176 | | 4421 | |
| 4177 | // We create a Decl for it regardless of analysis status. | 4422 | // We create a Decl for it regardless of analysis status. |
| 4178 | const gop = try namespace.decls.getOrPutContextAdapted( | 4423 | |
| 4179 | gpa, | 4424 | const prev_exported, const decl_index = if (iter.existing_by_inst.get(tracked_inst)) |decl_index| decl_index: { |
| 4180 | decl_name, | 4425 | // We need only update this existing Decl. |
| 4181 | DeclAdapter{ .zcu = zcu }, | 4426 | const decl = zcu.declPtr(decl_index); |
| 4182 | Namespace.DeclContext{ .zcu = zcu }, | 4427 | const was_exported = decl.is_exported; |
| 4183 | ); | 4428 | assert(decl.kind == kind); // ZIR tracking should preserve this |
| 4184 | const comp = zcu.comp; | 4429 | assert(decl.alive); |
| 4185 | if (!gop.found_existing) { | 4430 | decl.name = decl_name; |
| | 4431 | decl.src_node = decl_node; |
| | 4432 | decl.src_line = line; |
| | 4433 | decl.is_pub = declaration.flags.is_pub; |
| | 4434 | decl.is_exported = declaration.flags.is_export; |
| | 4435 | break :decl_index .{ was_exported, decl_index }; |
| | 4436 | } else decl_index: { |
| | 4437 | // Create and set up a new Decl. |
| 4186 | const new_decl_index = try zcu.allocateNewDecl(namespace_index, decl_node); | 4438 | const new_decl_index = try zcu.allocateNewDecl(namespace_index, decl_node); |
| 4187 | const new_decl = zcu.declPtr(new_decl_index); | 4439 | const new_decl = zcu.declPtr(new_decl_index); |
| 4188 | new_decl.kind = kind; | 4440 | new_decl.kind = kind; |
| 4189 | new_decl.name = decl_name; | 4441 | new_decl.name = decl_name; |
| 4190 | if (kind == .@"usingnamespace") { | | |
| 4191 | namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, declaration.flags.is_pub); | | |
| 4192 | } | | |
| 4193 | new_decl.src_line = line; | 4442 | new_decl.src_line = line; |
| 4194 | gop.key_ptr.* = new_decl_index; | | |
| 4195 | // Exported decls, comptime decls, usingnamespace decls, and | | |
| 4196 | // test decls if in test mode, get analyzed. | | |
| 4197 | const decl_mod = namespace.file_scope.mod; | | |
| 4198 | const want_analysis = declaration.flags.is_export or switch (kind) { | | |
| 4199 | .anon => unreachable, | | |
| 4200 | .@"comptime", .@"usingnamespace" => true, | | |
| 4201 | .named => false, | | |
| 4202 | .@"test" => a: { | | |
| 4203 | if (!comp.config.is_test) break :a false; | | |
| 4204 | if (decl_mod != zcu.main_mod) break :a false; | | |
| 4205 | if (is_named_test and comp.test_filters.len > 0) { | | |
| 4206 | const decl_fqn = ip.stringToSlice(try namespace.fullyQualifiedName(zcu, decl_name)); | | |
| 4207 | for (comp.test_filters) |test_filter| { | | |
| 4208 | if (mem.indexOf(u8, decl_fqn, test_filter)) |_| break; | | |
| 4209 | } else break :a false; | | |
| 4210 | } | | |
| 4211 | try zcu.test_functions.put(gpa, new_decl_index, {}); | | |
| 4212 | break :a true; | | |
| 4213 | }, | | |
| 4214 | }; | | |
| 4215 | if (want_analysis) { | | |
| 4216 | log.debug("scanDecl queue analyze_decl file='{s}' decl_name='{s}' decl_index={d}", .{ | | |
| 4217 | namespace.file_scope.sub_file_path, ip.stringToSlice(decl_name), new_decl_index, | | |
| 4218 | }); | | |
| 4219 | comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index }); | | |
| 4220 | } | | |
| 4221 | new_decl.is_pub = declaration.flags.is_pub; | 4443 | new_decl.is_pub = declaration.flags.is_pub; |
| 4222 | new_decl.is_exported = declaration.flags.is_export; | 4444 | new_decl.is_exported = declaration.flags.is_export; |
| 4223 | new_decl.zir_decl_index = decl_inst.toOptional(); | 4445 | new_decl.zir_decl_index = tracked_inst.toOptional(); |
| 4224 | new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive. | 4446 | new_decl.alive = true; // This Decl corresponds to an AST node and is therefore always alive. |
| 4225 | return; | 4447 | break :decl_index .{ false, new_decl_index }; |
| 4226 | } | 4448 | }; |
| 4227 | const decl_index = gop.key_ptr.*; | 4449 | |
| 4228 | const decl = zcu.declPtr(decl_index); | 4450 | const decl = zcu.declPtr(decl_index); |
| 4229 | if (kind == .@"test") { | 4451 | |
| 4230 | const src_loc = SrcLoc{ | 4452 | namespace.decls.putAssumeCapacityNoClobberContext(decl_index, {}, .{ .zcu = zcu }); |
| 4231 | .file_scope = decl.getFileScope(zcu), | 4453 | |
| 4232 | .parent_decl_node = decl.src_node, | 4454 | const comp = zcu.comp; |
| 4233 | .lazy = .{ .token_offset = 1 }, | 4455 | const decl_mod = namespace.file_scope.mod; |
| 4234 | }; | 4456 | const want_analysis = declaration.flags.is_export or switch (kind) { |
| 4235 | const msg = try ErrorMsg.create(gpa, src_loc, "duplicate test name: {}", .{ | 4457 | .anon => unreachable, |
| 4236 | decl_name.fmt(ip), | 4458 | .@"comptime" => true, |
| 4237 | }); | 4459 | .@"usingnamespace" => a: { |
| 4238 | errdefer msg.destroy(gpa); | 4460 | namespace.usingnamespace_set.putAssumeCapacityNoClobber(decl_index, declaration.flags.is_pub); |
| 4239 | try zcu.failed_decls.putNoClobber(gpa, decl_index, msg); | 4461 | break :a true; |
| 4240 | const other_src_loc = SrcLoc{ | 4462 | }, |
| 4241 | .file_scope = namespace.file_scope, | 4463 | .named => false, |
| 4242 | .parent_decl_node = decl_node, | 4464 | .@"test" => a: { |
| 4243 | .lazy = .{ .token_offset = 1 }, | 4465 | if (!comp.config.is_test) break :a false; |
| 4244 | }; | 4466 | if (decl_mod != zcu.main_mod) break :a false; |
| 4245 | try zcu.errNoteNonLazy(other_src_loc, msg, "other test here", .{}); | 4467 | if (is_named_test and comp.test_filters.len > 0) { |
| | 4468 | const decl_fqn = ip.stringToSlice(try namespace.fullyQualifiedName(zcu, decl_name)); |
| | 4469 | for (comp.test_filters) |test_filter| { |
| | 4470 | if (mem.indexOf(u8, decl_fqn, test_filter)) |_| break; |
| | 4471 | } else break :a false; |
| | 4472 | } |
| | 4473 | zcu.test_functions.putAssumeCapacity(decl_index, {}); // may clobber on incremental update |
| | 4474 | break :a true; |
| | 4475 | }, |
| | 4476 | }; |
| | 4477 | |
| | 4478 | if (want_analysis) { |
| | 4479 | // We will not queue analysis if the decl has been analyzed on a previous update and |
| | 4480 | // `is_export` is unchanged. In this case, the incremental update mechanism will handle |
| | 4481 | // re-analysis for us if necessary. |
| | 4482 | if (prev_exported != declaration.flags.is_export or decl.analysis == .unreferenced) { |
| | 4483 | log.debug("scanDecl queue analyze_decl file='{s}' decl_name='{s}' decl_index={d}", .{ |
| | 4484 | namespace.file_scope.sub_file_path, ip.stringToSlice(decl_name), decl_index, |
| | 4485 | }); |
| | 4486 | comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = decl_index }); |
| | 4487 | } |
| 4246 | } | 4488 | } |
| 4247 | // Update the AST node of the decl; even if its contents are unchanged, it may | | |
| 4248 | // have been re-ordered. | | |
| 4249 | decl.src_node = decl_node; | | |
| 4250 | decl.src_line = line; | | |
| 4251 | | 4489 | |
| 4252 | decl.is_pub = declaration.flags.is_pub; | | |
| 4253 | decl.is_exported = declaration.flags.is_export; | | |
| 4254 | decl.kind = kind; | | |
| 4255 | decl.zir_decl_index = decl_inst.toOptional(); | | |
| 4256 | if (decl.getOwnedFunction(zcu) != null) { | 4490 | if (decl.getOwnedFunction(zcu) != null) { |
| | 4491 | // TODO this logic is insufficient; namespaces we don't re-scan may still require |
| | 4492 | // updated line numbers. Look into this! |
| 4257 | // TODO Look into detecting when this would be unnecessary by storing enough state | 4493 | // TODO Look into detecting when this would be unnecessary by storing enough state |
| 4258 | // in `Decl` to notice that the line number did not change. | 4494 | // in `Decl` to notice that the line number did not change. |
| 4259 | comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); | 4495 | comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); |
| ... | @@ -4397,6 +4633,11 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato | ... | @@ -4397,6 +4633,11 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato |
| 4397 | }; | 4633 | }; |
| 4398 | defer sema.deinit(); | 4634 | defer sema.deinit(); |
| 4399 | | 4635 | |
| | 4636 | // Every runtime function has a dependency on the source of the Decl it originates from. |
| | 4637 | // It also depends on the value of its owner Decl. |
| | 4638 | try sema.declareDependency(.{ .src_hash = decl.zir_decl_index.unwrap().? }); |
| | 4639 | try sema.declareDependency(.{ .decl_val = decl_index }); |
| | 4640 | |
| 4400 | if (func.analysis(ip).inferred_error_set) { | 4641 | if (func.analysis(ip).inferred_error_set) { |
| 4401 | const ies = try arena.create(Sema.InferredErrorSet); | 4642 | const ies = try arena.create(Sema.InferredErrorSet); |
| 4402 | ies.* = .{ .func = func_index }; | 4643 | ies.* = .{ .func = func_index }; |