| author | |
| committer | |
| log | 46388d338a93a35d139866411f80115a03b30a6a |
| tree | 57cd962b1fec235a99b808d2a2877bc4bcbd5f05 |
| parent | 978fe68a65be2b5a1551ab5eafdcdbfa467ba891 |
When a type becomes outdated, there will still be lingering references
to the old index -- for instance, any declaration whose value was that
type holds a reference to that index. These references may live for an
arbitrarily long time in some cases. So, we can't just remove the type
from the pool -- the old `Index` must remain valid!
Instead, we want to preserve the old `Index`, but avoid it from ever
appearing in lookups. (It's okay if analysis of something referencing
the old `Index` does weird stuff -- such analysis are guaranteed by the
incremental compilation model to always be unreferenced.) So, we use the
new `InternPool.putKeyReplace` to replace the shard entry for this index
with the newly-created index.3 files changed, 87 insertions(+), 32 deletions(-)
src/InternPool.zig+65-6| ... | ... | @@ -7077,6 +7077,7 @@ fn getOrPutKeyEnsuringAdditionalCapacity( |
| 7077 | 7077 | const index = entry.acquire(); |
| 7078 | 7078 | if (index == .none) break; |
| 7079 | 7079 | if (entry.hash != hash) continue; |
| 7080 | if (ip.isRemoved(index)) continue; | |
| 7080 | 7081 | if (ip.indexToKey(index).eql(key, ip)) return .{ .existing = index }; |
| 7081 | 7082 | } |
| 7082 | 7083 | shard.mutate.map.mutex.lock(); |
| ... | ... | @@ -7151,6 +7152,43 @@ fn getOrPutKeyEnsuringAdditionalCapacity( |
| 7151 | 7152 | .map_index = map_index, |
| 7152 | 7153 | } }; |
| 7153 | 7154 | } |
| 7155 | /// Like `getOrPutKey`, but asserts that the key already exists, and prepares to replace | |
| 7156 | /// its shard entry with a new `Index` anyway. After finalizing this, the old index remains | |
| 7157 | /// valid (in that `indexToKey` and similar queries will behave as before), but it will | |
| 7158 | /// never be returned from a lookup (`getOrPutKey` etc). | |
| 7159 | /// This is used by incremental compilation when an existing container type is outdated. In | |
| 7160 | /// this case, the type must be recreated at a new `InternPool.Index`, but the old index must | |
| 7161 | /// remain valid since now-unreferenced `AnalUnit`s may retain references to it. The old index | |
| 7162 | /// will be cleaned up when the `Zcu` undergoes garbage collection. | |
| 7163 | fn putKeyReplace( | |
| 7164 | ip: *InternPool, | |
| 7165 | tid: Zcu.PerThread.Id, | |
| 7166 | key: Key, | |
| 7167 | ) GetOrPutKey { | |
| 7168 | const full_hash = key.hash64(ip); | |
| 7169 | const hash: u32 = @truncate(full_hash >> 32); | |
| 7170 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; | |
| 7171 | shard.mutate.map.mutex.lock(); | |
| 7172 | errdefer shard.mutate.map.mutex.unlock(); | |
| 7173 | const map = shard.shared.map; | |
| 7174 | const map_mask = map.header().mask(); | |
| 7175 | var map_index = hash; | |
| 7176 | while (true) : (map_index += 1) { | |
| 7177 | map_index &= map_mask; | |
| 7178 | const entry = &map.entries[map_index]; | |
| 7179 | const index = entry.value; | |
| 7180 | assert(index != .none); // key not present | |
| 7181 | if (entry.hash == hash and ip.indexToKey(index).eql(key, ip)) { | |
| 7182 | break; // we found the entry to replace | |
| 7183 | } | |
| 7184 | } | |
| 7185 | return .{ .new = .{ | |
| 7186 | .ip = ip, | |
| 7187 | .tid = tid, | |
| 7188 | .shard = shard, | |
| 7189 | .map_index = map_index, | |
| 7190 | } }; | |
| 7191 | } | |
| 7154 | 7192 | |
| 7155 | 7193 | pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) Allocator.Error!Index { |
| 7156 | 7194 | var gop = try ip.getOrPutKey(gpa, tid, key); |
| ... | ... | @@ -7990,8 +8028,11 @@ pub fn getUnionType( |
| 7990 | 8028 | gpa: Allocator, |
| 7991 | 8029 | tid: Zcu.PerThread.Id, |
| 7992 | 8030 | ini: UnionTypeInit, |
| 8031 | /// If it is known that there is an existing type with this key which is outdated, | |
| 8032 | /// this is passed as `true`, and the type is replaced with one at a fresh index. | |
| 8033 | replace_existing: bool, | |
| 7993 | 8034 | ) Allocator.Error!WipNamespaceType.Result { |
| 7994 | var gop = try ip.getOrPutKey(gpa, tid, .{ .union_type = switch (ini.key) { | |
| 8035 | const key: Key = .{ .union_type = switch (ini.key) { | |
| 7995 | 8036 | .declared => |d| .{ .declared = .{ |
| 7996 | 8037 | .zir_index = d.zir_index, |
| 7997 | 8038 | .captures = .{ .external = d.captures }, |
| ... | ... | @@ -8000,7 +8041,11 @@ pub fn getUnionType( |
| 8000 | 8041 | .zir_index = r.zir_index, |
| 8001 | 8042 | .type_hash = r.type_hash, |
| 8002 | 8043 | } }, |
| 8003 | } }); | |
| 8044 | } }; | |
| 8045 | var gop = if (replace_existing) | |
| 8046 | ip.putKeyReplace(tid, key) | |
| 8047 | else | |
| 8048 | try ip.getOrPutKey(gpa, tid, key); | |
| 8004 | 8049 | defer gop.deinit(); |
| 8005 | 8050 | if (gop == .existing) return .{ .existing = gop.existing }; |
| 8006 | 8051 | |
| ... | ... | @@ -8166,8 +8211,11 @@ pub fn getStructType( |
| 8166 | 8211 | gpa: Allocator, |
| 8167 | 8212 | tid: Zcu.PerThread.Id, |
| 8168 | 8213 | ini: StructTypeInit, |
| 8214 | /// If it is known that there is an existing type with this key which is outdated, | |
| 8215 | /// this is passed as `true`, and the type is replaced with one at a fresh index. | |
| 8216 | replace_existing: bool, | |
| 8169 | 8217 | ) Allocator.Error!WipNamespaceType.Result { |
| 8170 | var gop = try ip.getOrPutKey(gpa, tid, .{ .struct_type = switch (ini.key) { | |
| 8218 | const key: Key = .{ .struct_type = switch (ini.key) { | |
| 8171 | 8219 | .declared => |d| .{ .declared = .{ |
| 8172 | 8220 | .zir_index = d.zir_index, |
| 8173 | 8221 | .captures = .{ .external = d.captures }, |
| ... | ... | @@ -8176,7 +8224,11 @@ pub fn getStructType( |
| 8176 | 8224 | .zir_index = r.zir_index, |
| 8177 | 8225 | .type_hash = r.type_hash, |
| 8178 | 8226 | } }, |
| 8179 | } }); | |
| 8227 | } }; | |
| 8228 | var gop = if (replace_existing) | |
| 8229 | ip.putKeyReplace(tid, key) | |
| 8230 | else | |
| 8231 | try ip.getOrPutKey(gpa, tid, key); | |
| 8180 | 8232 | defer gop.deinit(); |
| 8181 | 8233 | if (gop == .existing) return .{ .existing = gop.existing }; |
| 8182 | 8234 | |
| ... | ... | @@ -9200,8 +9252,11 @@ pub fn getEnumType( |
| 9200 | 9252 | gpa: Allocator, |
| 9201 | 9253 | tid: Zcu.PerThread.Id, |
| 9202 | 9254 | ini: EnumTypeInit, |
| 9255 | /// If it is known that there is an existing type with this key which is outdated, | |
| 9256 | /// this is passed as `true`, and the type is replaced with one at a fresh index. | |
| 9257 | replace_existing: bool, | |
| 9203 | 9258 | ) Allocator.Error!WipEnumType.Result { |
| 9204 | var gop = try ip.getOrPutKey(gpa, tid, .{ .enum_type = switch (ini.key) { | |
| 9259 | const key: Key = .{ .enum_type = switch (ini.key) { | |
| 9205 | 9260 | .declared => |d| .{ .declared = .{ |
| 9206 | 9261 | .zir_index = d.zir_index, |
| 9207 | 9262 | .captures = .{ .external = d.captures }, |
| ... | ... | @@ -9210,7 +9265,11 @@ pub fn getEnumType( |
| 9210 | 9265 | .zir_index = r.zir_index, |
| 9211 | 9266 | .type_hash = r.type_hash, |
| 9212 | 9267 | } }, |
| 9213 | } }); | |
| 9268 | } }; | |
| 9269 | var gop = if (replace_existing) | |
| 9270 | ip.putKeyReplace(tid, key) | |
| 9271 | else | |
| 9272 | try ip.getOrPutKey(gpa, tid, key); | |
| 9214 | 9273 | defer gop.deinit(); |
| 9215 | 9274 | if (gop == .existing) return .{ .existing = gop.existing }; |
| 9216 | 9275 |
src/Sema.zig+16-17| ... | ... | @@ -2724,9 +2724,9 @@ fn wrapWipTy(sema: *Sema, wip_ty: anytype) @TypeOf(wip_ty) { |
| 2724 | 2724 | } |
| 2725 | 2725 | |
| 2726 | 2726 | /// Given a type just looked up in the `InternPool`, check whether it is |
| 2727 | /// considered outdated on this update. If so, remove it from the pool | |
| 2728 | /// and return `true`. | |
| 2729 | fn maybeRemoveOutdatedType(sema: *Sema, ty: InternPool.Index) !bool { | |
| 2727 | /// considered outdated on this update. If so, returns `true`, and the | |
| 2728 | /// caller must replace the outdated type with a fresh one. | |
| 2729 | fn checkOutdatedType(sema: *Sema, ty: InternPool.Index) !bool { | |
| 2730 | 2730 | const pt = sema.pt; |
| 2731 | 2731 | const zcu = pt.zcu; |
| 2732 | 2732 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -2745,7 +2745,6 @@ fn maybeRemoveOutdatedType(sema: *Sema, ty: InternPool.Index) !bool { |
| 2745 | 2745 | if (!was_outdated) return false; |
| 2746 | 2746 | _ = zcu.outdated_ready.swapRemove(cau_unit); |
| 2747 | 2747 | zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, cau_unit); |
| 2748 | zcu.intern_pool.remove(pt.tid, ty); | |
| 2749 | 2748 | try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty }); |
| 2750 | 2749 | return true; |
| 2751 | 2750 | } |
| ... | ... | @@ -2815,14 +2814,14 @@ fn zirStructDecl( |
| 2815 | 2814 | .captures = captures, |
| 2816 | 2815 | } }, |
| 2817 | 2816 | }; |
| 2818 | const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init)) { | |
| 2817 | const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) { | |
| 2819 | 2818 | .existing => |ty| wip: { |
| 2820 | if (!try sema.maybeRemoveOutdatedType(ty)) { | |
| 2819 | if (!try sema.checkOutdatedType(ty)) { | |
| 2821 | 2820 | try sema.declareDependency(.{ .interned = ty }); |
| 2822 | 2821 | try sema.addTypeReferenceEntry(src, ty); |
| 2823 | 2822 | return Air.internedToRef(ty); |
| 2824 | 2823 | } |
| 2825 | break :wip (try ip.getStructType(gpa, pt.tid, struct_init)).wip; | |
| 2824 | break :wip (try ip.getStructType(gpa, pt.tid, struct_init, true)).wip; | |
| 2826 | 2825 | }, |
| 2827 | 2826 | .wip => |wip| wip, |
| 2828 | 2827 | }); |
| ... | ... | @@ -3041,14 +3040,14 @@ fn zirEnumDecl( |
| 3041 | 3040 | .captures = captures, |
| 3042 | 3041 | } }, |
| 3043 | 3042 | }; |
| 3044 | const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init)) { | |
| 3043 | const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) { | |
| 3045 | 3044 | .existing => |ty| wip: { |
| 3046 | if (!try sema.maybeRemoveOutdatedType(ty)) { | |
| 3045 | if (!try sema.checkOutdatedType(ty)) { | |
| 3047 | 3046 | try sema.declareDependency(.{ .interned = ty }); |
| 3048 | 3047 | try sema.addTypeReferenceEntry(src, ty); |
| 3049 | 3048 | return Air.internedToRef(ty); |
| 3050 | 3049 | } |
| 3051 | break :wip (try ip.getEnumType(gpa, pt.tid, enum_init)).wip; | |
| 3050 | break :wip (try ip.getEnumType(gpa, pt.tid, enum_init, true)).wip; | |
| 3052 | 3051 | }, |
| 3053 | 3052 | .wip => |wip| wip, |
| 3054 | 3053 | }); |
| ... | ... | @@ -3311,14 +3310,14 @@ fn zirUnionDecl( |
| 3311 | 3310 | .captures = captures, |
| 3312 | 3311 | } }, |
| 3313 | 3312 | }; |
| 3314 | const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init)) { | |
| 3313 | const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) { | |
| 3315 | 3314 | .existing => |ty| wip: { |
| 3316 | if (!try sema.maybeRemoveOutdatedType(ty)) { | |
| 3315 | if (!try sema.checkOutdatedType(ty)) { | |
| 3317 | 3316 | try sema.declareDependency(.{ .interned = ty }); |
| 3318 | 3317 | try sema.addTypeReferenceEntry(src, ty); |
| 3319 | 3318 | return Air.internedToRef(ty); |
| 3320 | 3319 | } |
| 3321 | break :wip (try ip.getUnionType(gpa, pt.tid, union_init)).wip; | |
| 3320 | break :wip (try ip.getUnionType(gpa, pt.tid, union_init, true)).wip; | |
| 3322 | 3321 | }, |
| 3323 | 3322 | .wip => |wip| wip, |
| 3324 | 3323 | }); |
| ... | ... | @@ -3407,7 +3406,7 @@ fn zirOpaqueDecl( |
| 3407 | 3406 | }; |
| 3408 | 3407 | // No `wrapWipTy` needed as no std.builtin types are opaque. |
| 3409 | 3408 | const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) { |
| 3410 | // No `maybeRemoveOutdatedType` as opaque types are never outdated. | |
| 3409 | // No `checkOutdatedType` as opaque types are never outdated. | |
| 3411 | 3410 | .existing => |ty| { |
| 3412 | 3411 | try sema.addTypeReferenceEntry(src, ty); |
| 3413 | 3412 | return Air.internedToRef(ty); |
| ... | ... | @@ -22054,7 +22053,7 @@ fn reifyEnum( |
| 22054 | 22053 | .zir_index = tracked_inst, |
| 22055 | 22054 | .type_hash = hasher.final(), |
| 22056 | 22055 | } }, |
| 22057 | })) { | |
| 22056 | }, false)) { | |
| 22058 | 22057 | .wip => |wip| wip, |
| 22059 | 22058 | .existing => |ty| { |
| 22060 | 22059 | try sema.declareDependency(.{ .interned = ty }); |
| ... | ... | @@ -22224,7 +22223,7 @@ fn reifyUnion( |
| 22224 | 22223 | .zir_index = tracked_inst, |
| 22225 | 22224 | .type_hash = hasher.final(), |
| 22226 | 22225 | } }, |
| 22227 | })) { | |
| 22226 | }, false)) { | |
| 22228 | 22227 | .wip => |wip| wip, |
| 22229 | 22228 | .existing => |ty| { |
| 22230 | 22229 | try sema.declareDependency(.{ .interned = ty }); |
| ... | ... | @@ -22494,7 +22493,7 @@ fn reifyStruct( |
| 22494 | 22493 | .zir_index = tracked_inst, |
| 22495 | 22494 | .type_hash = hasher.final(), |
| 22496 | 22495 | } }, |
| 22497 | })) { | |
| 22496 | }, false)) { | |
| 22498 | 22497 | .wip => |wip| wip, |
| 22499 | 22498 | .existing => |ty| { |
| 22500 | 22499 | try sema.declareDependency(.{ .interned = ty }); |
src/Zcu/PerThread.zig+6-9| ... | ... | @@ -925,6 +925,7 @@ fn createFileRootStruct( |
| 925 | 925 | pt: Zcu.PerThread, |
| 926 | 926 | file_index: Zcu.File.Index, |
| 927 | 927 | namespace_index: Zcu.Namespace.Index, |
| 928 | replace_existing: bool, | |
| 928 | 929 | ) Allocator.Error!InternPool.Index { |
| 929 | 930 | const zcu = pt.zcu; |
| 930 | 931 | const gpa = zcu.gpa; |
| ... | ... | @@ -968,7 +969,7 @@ fn createFileRootStruct( |
| 968 | 969 | .zir_index = tracked_inst, |
| 969 | 970 | .captures = &.{}, |
| 970 | 971 | } }, |
| 971 | })) { | |
| 972 | }, replace_existing)) { | |
| 972 | 973 | .existing => unreachable, // we wouldn't be analysing the file root if this type existed |
| 973 | 974 | .wip => |wip| wip, |
| 974 | 975 | }; |
| ... | ... | @@ -1023,8 +1024,7 @@ fn recreateFileRoot(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError |
| 1023 | 1024 | zcu.gpa, |
| 1024 | 1025 | InternPool.AnalUnit.wrap(.{ .cau = file_root_type_cau }), |
| 1025 | 1026 | ); |
| 1026 | ip.remove(pt.tid, file_root_type); | |
| 1027 | _ = try pt.createFileRootStruct(file_index, namespace_index); | |
| 1027 | _ = try pt.createFileRootStruct(file_index, namespace_index, true); | |
| 1028 | 1028 | } |
| 1029 | 1029 | |
| 1030 | 1030 | /// Re-scan the namespace of a file's root struct type on an incremental update. |
| ... | ... | @@ -1062,8 +1062,6 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator. |
| 1062 | 1062 | try pt.scanNamespace(namespace_index, decls); |
| 1063 | 1063 | } |
| 1064 | 1064 | |
| 1065 | /// Regardless of the file status, will create a `Decl` if none exists so that we can track | |
| 1066 | /// dependencies and re-analyze when the file becomes outdated. | |
| 1067 | 1065 | fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { |
| 1068 | 1066 | const tracy = trace(@src()); |
| 1069 | 1067 | defer tracy.end(); |
| ... | ... | @@ -1083,7 +1081,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { |
| 1083 | 1081 | .owner_type = undefined, // set in `createFileRootStruct` |
| 1084 | 1082 | .file_scope = file_index, |
| 1085 | 1083 | }); |
| 1086 | const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index); | |
| 1084 | const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false); | |
| 1087 | 1085 | errdefer zcu.intern_pool.remove(pt.tid, struct_ty); |
| 1088 | 1086 | |
| 1089 | 1087 | switch (zcu.comp.cache_use) { |
| ... | ... | @@ -1153,11 +1151,10 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult { |
| 1153 | 1151 | // This declaration has no value so is definitely not a std.builtin type. |
| 1154 | 1152 | break :ip_index .none; |
| 1155 | 1153 | }, |
| 1156 | .type => |ty| { | |
| 1154 | .type => { | |
| 1157 | 1155 | // This is an incremental update, and this type is being re-analyzed because it is outdated. |
| 1158 | 1156 | // The type must be recreated at a new `InternPool.Index`. |
| 1159 | // Remove it from the InternPool and mark it outdated so that creation sites are re-analyzed. | |
| 1160 | ip.remove(pt.tid, ty); | |
| 1157 | // Mark it outdated so that creation sites are re-analyzed. | |
| 1161 | 1158 | return .{ |
| 1162 | 1159 | .invalidate_decl_val = true, |
| 1163 | 1160 | .invalidate_decl_ref = true, |