global/type-name-disambiguator.spice
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | // Std imports | ||
| 4 | import "std/data/vector"; | ||
| 5 | import "std/data/unordered-map"; | ||
| 6 | import "std/type/type-conversion"; | ||
| 7 | import "std/os/recursive-mutex"; | ||
| 8 | |||
| 9 | // Own imports | ||
| 10 | import "@/util/concurrency"; | ||
| 11 | |||
| 12 | /** | ||
| 13 | * Two structs or interfaces that share a simple name but stem from independent declarations (e.g. a package-private | ||
| 14 | * `Node<T>` declared separately in two source files, see issue #1253) are distinct types, but render to the same | ||
| 15 | * name. That collision surfaces both as a type-checker conflation (same manifestation signature) and as a linker/codegen | ||
| 16 | * clash (same mangled name). To keep them apart without affecting the common, non-colliding case, this registry hands | ||
| 17 | * out a deterministic disambiguation suffix for the second and any further distinct type sharing a given name. The | ||
| 18 | * first type to claim a name keeps it unchanged, so nothing changes unless a name is actually reused. | ||
| 19 | * | ||
| 20 | * Spice only supports globals of primitive type, so like the TypeRegistry, the disambiguator is a normal struct owned by the GlobalResourceManager, which registers it as the active one. | ||
| 21 | * Since the GlobalResourceManager is created per compilation, the state is automatically cleared between compilations. | ||
| 22 | */ | ||
| 23 | 1821 | public type TypeNameDisambiguator struct { | |
| 24 | // Maps a simple type name to the list of distinct type ids claiming it, in first-seen order | ||
| 25 | UnorderedMap<String, Vector<unsigned long>> claimedTypeIds | ||
| 26 | // Name mangling claims names while the back end runs in parallel | ||
| 27 | RecursiveMutex claimedTypeIdsMutex | ||
| 28 | } | ||
| 29 | |||
| 30 | // Active type name disambiguator. It is owned by the global resource manager. | ||
| 31 | TypeNameDisambiguator* ACTIVE_TYPE_NAME_DISAMBIGUATOR; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Make the given type name disambiguator the active one. getDisambiguationSuffix() operates on it. | ||
| 35 | * | ||
| 36 | * @param disambiguator Type name disambiguator | ||
| 37 | */ | ||
| 38 | 1055 | public p setActiveTypeNameDisambiguator(TypeNameDisambiguator* disambiguator) { | |
| 39 | 1055 | ACTIVE_TYPE_NAME_DISAMBIGUATOR = disambiguator; | |
| 40 | 1055 | } | |
| 41 | |||
| 42 | /** | ||
| 43 | * Get the active type name disambiguator | ||
| 44 | * | ||
| 45 | * @return Active type name disambiguator | ||
| 46 | */ | ||
| 47 | 569534 | f<TypeNameDisambiguator*> getActiveTypeNameDisambiguator() { | |
| 48 | 569534 | assert ACTIVE_TYPE_NAME_DISAMBIGUATOR != nil<TypeNameDisambiguator*>; | |
| 49 | 569534 | return ACTIVE_TYPE_NAME_DISAMBIGUATOR; | |
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Get the disambiguation suffix for a struct/interface type with the given name and type id. | ||
| 54 | * The first type id to claim a name keeps it unchanged (empty suffix); every further distinct type id sharing the name | ||
| 55 | * receives a deterministic, dot-separated suffix (e.g. ".1", ".2"). A dot is used on purpose: it is not a valid Spice | ||
| 56 | * identifier character, so the disambiguated name can never collide with a genuine, user-declared type name. | ||
| 57 | * | ||
| 58 | * @param name Simple type name | ||
| 59 | * @param typeId Custom type id of the type | ||
| 60 | * @return Disambiguation suffix (empty for the first claimer of the name) | ||
| 61 | */ | ||
| 62 | 569534 | public f<String> TypeNameDisambiguator.getDisambiguationSuffix(const String& name, unsigned long typeId) { | |
| 63 | 569534 | ConditionalLock _lock = ConditionalLock(&this.claimedTypeIdsMutex); | |
| 64 | |||
| 65 | 569534 | if !this.claimedTypeIds.contains(name) { | |
| 66 | 3401 | this.claimedTypeIds.upsert(name, Vector<unsigned long>()); | |
| 67 | 3401 | } | |
| 68 | 569534 | Vector<unsigned long>& ids = this.claimedTypeIds.get(name); | |
| 69 | |||
| 70 | // Find the index of the type id among the ones already claiming this name | ||
| 71 | 569534 | unsigned long index = ids.getSize(); | |
| 72 | 575390 | for unsigned long i = 0l; i < ids.getSize(); i++ { | |
| 73 | 571897 | if ids.get(i) == typeId { | |
| 74 | 566041 | index = i; | |
| 75 | 566041 | break; | |
| 76 | } | ||
| 77 | 5856 | } | |
| 78 | // If not seen yet, claim the name | ||
| 79 | 569534 | if index == ids.getSize() { | |
| 80 | 3493 | ids.pushBack(typeId); | |
| 81 | 3493 | } | |
| 82 | |||
| 83 | // The first claimer keeps the plain name to avoid churn for the common, non-colliding case | ||
| 84 | 569534 | if index == 0l { | |
| 85 | 1131876 | return String(); | |
| 86 | } | ||
| 87 | 7192 | return String(".") + toString(index); | |
| 88 | 569534 | } | |
| 89 | |||
| 90 | /** | ||
| 91 | * Get the disambiguation suffix for a struct/interface type with the given name and type id from the active type name | ||
| 92 | * disambiguator. | ||
| 93 | * | ||
| 94 | * @param name Simple type name | ||
| 95 | * @param typeId Custom type id of the type | ||
| 96 | * @return Disambiguation suffix (empty for the first claimer of the name) | ||
| 97 | */ | ||
| 98 | 569534 | public f<String> getDisambiguationSuffix(const String& name, unsigned long typeId) { | |
| 99 | 569534 | TypeNameDisambiguator* disambiguator = getActiveTypeNameDisambiguator(); | |
| 100 | 569534 | return disambiguator.getDisambiguationSuffix(name, typeId); | |
| 101 | } | ||
| 102 | |||
| 103 | /** | ||
| 104 | * Clear the disambiguation registry | ||
| 105 | */ | ||
| 106 | 766 | public p TypeNameDisambiguator.clear() { | |
| 107 | 766 | this.claimedTypeIds.clear(); | |
| 108 | 766 | } | |
| 109 |