src/typechecker/TypeCheckerImplicit.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | #include "TypeChecker.h" | ||
| 4 | |||
| 5 | #include <unordered_set> | ||
| 6 | |||
| 7 | #include <SourceFile.h> | ||
| 8 | #include <ast/ASTBuilder.h> | ||
| 9 | #include <ast/ASTNodes.h> | ||
| 10 | #include <global/GlobalResourceManager.h> | ||
| 11 | #include <model/GenericType.h> | ||
| 12 | #include <model/Struct.h> | ||
| 13 | #include <symboltablebuilder/Scope.h> | ||
| 14 | #include <symboltablebuilder/SymbolTableBuilder.h> | ||
| 15 | #include <typechecker/FunctionManager.h> | ||
| 16 | #include <typechecker/TypeMatcher.h> | ||
| 17 | |||
| 18 | namespace spice::compiler { | ||
| 19 | |||
| 20 | static const char *const FCT_NAME_DEALLOC = "sDealloc"; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Check whether the given struct and all of its by-value struct fields are manifested, transitively, and have already | ||
| 24 | * decided their own implicit default members. While a circular import is still being prepared, a by-value struct field | ||
| 25 | * may (transitively) reference a struct that is not manifested yet; generating implicit special members for such a | ||
| 26 | * struct recurses through isTriviallyConstructible and would dereference a null struct lookup. A genuine infinite-size | ||
| 27 | * containment cycle among these structs is reported separately by the infinite-size check during struct preparation, | ||
| 28 | * so a containment cycle here is treated as manifested. | ||
| 29 | * | ||
| 30 | * A field struct can also be manifested (the Struct object exists) but not decided yet: a generic struct like | ||
| 31 | * Result<T> can be substantiated for T = Url while checking a function body during Url's own file's one-shot sweep | ||
| 32 | * (TypeChecker::visitEntry), before that sweep has reached Url's struct declaration and decided that Url needs a | ||
| 33 | * dtor/copy ctor. Deciding Result<Url> right then would see Url as trivial (no dtor found yet) and permanently skip | ||
| 34 | * generating Result<Url>'s dtor, since createImplicitDefaultMembers only ever runs once per manifestation. Requiring | ||
| 35 | * the field struct to have decided its own members first defers Result<Url> the same way as the circular-import case | ||
| 36 | * above, to be picked up again by the fallback sweep in visitStructDefCheck once Url has decided. | ||
| 37 | */ | ||
| 38 | 25759 | static bool structFullyManifested(const Struct *spiceStruct, const ASTNode *node, std::unordered_set<const Scope *> &visited) { | |
| 39 |
2/2✓ Branch 30 → 4 taken 62207 times.
✓ Branch 30 → 31 taken 25519 times.
|
113485 | for (const QualType &fieldType : spiceStruct->fieldTypes) { |
| 40 |
3/4✓ Branch 6 → 7 taken 62207 times.
✗ Branch 6 → 34 not taken.
✓ Branch 7 → 8 taken 48324 times.
✓ Branch 7 → 9 taken 13883 times.
|
62207 | if (!fieldType.is(TY_STRUCT)) |
| 41 | 48324 | continue; | |
| 42 |
1/2✓ Branch 9 → 10 taken 13883 times.
✗ Branch 9 → 34 not taken.
|
13883 | const Struct *fieldStruct = fieldType.getStruct(node); |
| 43 |
4/4✓ Branch 10 → 11 taken 13881 times.
✓ Branch 10 → 12 taken 2 times.
✓ Branch 11 → 12 taken 238 times.
✓ Branch 11 → 13 taken 13643 times.
|
13883 | if (fieldStruct == nullptr || !fieldStruct->implicitDefaultMembersDecided) |
| 44 | 240 | return false; | |
| 45 | // Recurse into each field struct once. A containment cycle (already visited) is fine here - it is reported | ||
| 46 | // separately as an infinite-size error. Leaf structs never touch the set, so the common case stays allocation-free. | ||
| 47 |
6/10✓ Branch 13 → 14 taken 13643 times.
✗ Branch 13 → 34 not taken.
✓ Branch 14 → 15 taken 13336 times.
✓ Branch 14 → 18 taken 307 times.
✓ Branch 15 → 16 taken 13336 times.
✗ Branch 15 → 34 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 13336 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 13643 times.
|
13643 | if (visited.insert(fieldStruct->scope).second && !structFullyManifested(fieldStruct, node, visited)) |
| 48 | ✗ | return false; | |
| 49 | } | ||
| 50 | 25519 | return true; | |
| 51 | } | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Decide which compiler-generated default members (ctor, copy ctor, move ctor, dtor) the given struct manifestation | ||
| 55 | * needs and create them. | ||
| 56 | * | ||
| 57 | * This decision depends on the concrete field types, so it has to be taken once per manifestation and not once per | ||
| 58 | * struct declaration: whether e.g. HashEntry<K, V> needs a dtor is unanswerable while K and V are still generic, but | ||
| 59 | * well-defined for HashEntry<int, String>. It is therefore taken as soon as a manifestation comes into existence - | ||
| 60 | * either by the one-shot sweep over the manifestations that already exist when a source file is prepared | ||
| 61 | * (TypeChecker::visitEntry), or, for manifestations that are substantiated later on, right at the point where | ||
| 62 | * StructManager::match creates them. Deciding any later would be too late for all the reactive lookups that ask a | ||
| 63 | * struct for its default members (FunctionManager::match/::lookup, doScopeCleanup, ...), since a function body is | ||
| 64 | * type-checked exactly once and would not pick up a dtor that appears afterward. | ||
| 65 | * | ||
| 66 | * The default move ctor is decided about only when the caller asks for it. A manifestation that is substantiated after | ||
| 67 | * its file was prepared does not get one, which keeps it at the behavior it had before this decision was taken per | ||
| 68 | * manifestation at all. Synthesizing one would require the move ctor of each struct-typed field to be resolvable for | ||
| 69 | * the concrete manifestation, which the raw manifestation scan behind it (FunctionManager::findMoveCtor) cannot do for | ||
| 70 | * a generic field type - it returns the generic preset that the manifestation scope inherited. Without a move ctor, | ||
| 71 | * moving such a struct falls back to copying it, which is correct, just less efficient. | ||
| 72 | * | ||
| 73 | * @param spiceStruct Struct manifestation to decide the default members for | ||
| 74 | * @param node AST node for error messages | ||
| 75 | * @param withMoveCtor Also decide about the default move ctor | ||
| 76 | */ | ||
| 77 | 32611 | void TypeChecker::createImplicitDefaultMembers(Struct &spiceStruct, const ASTNode *node, bool withMoveCtor) { | |
| 78 | // Take the decision only once per manifestation | ||
| 79 |
2/2✓ Branch 2 → 3 taken 20426 times.
✓ Branch 2 → 4 taken 12185 times.
|
32611 | if (spiceStruct.implicitDefaultMembersDecided) |
| 80 | 20478 | return; | |
| 81 | |||
| 82 | 12185 | GlobalResourceManager &resourceManager = spiceStruct.scope->sourceFile->resourceManager; | |
| 83 | |||
| 84 | // Skip while a by-value struct field is (transitively) not manifested yet, or has not decided its own implicit | ||
| 85 | // default members yet (circular import still in progress, or a sibling struct's one-shot sweep has not reached it | ||
| 86 | // yet - see structFullyManifested). Relying solely on the fallback sweep in visitStructDefCheck to pick this back up | ||
| 87 | // can be too late: that sweep only runs during the type-checker POST pass, by which point some other function body | ||
| 88 | // elsewhere may already have looked up (and missed) this manifestation's dtor for good, since a function body is | ||
| 89 | // type-checked exactly once. Queue the manifestation instead, so it gets retried the moment something else's | ||
| 90 | // decision completes (see drainPendingImplicitDefaultMemberDecisions below) - typically still within the same PRE | ||
| 91 | // pass that first substantiated it. | ||
| 92 | 12185 | std::unordered_set<const Scope *> visitedScopes; | |
| 93 |
3/4✓ Branch 5 → 6 taken 12185 times.
✗ Branch 5 → 33 not taken.
✓ Branch 6 → 7 taken 52 times.
✓ Branch 6 → 22 taken 12133 times.
|
12185 | if (!structFullyManifested(&spiceStruct, node, visitedScopes)) { |
| 94 | 52 | std::vector<std::pair<Struct *, bool>> &pending = resourceManager.pendingStructDefaultMemberDecisions; | |
| 95 | 12 | const auto pred = [&](const auto &entry) { return entry.first == &spiceStruct; }; | |
| 96 |
1/2✓ Branch 7 → 8 taken 52 times.
✗ Branch 7 → 32 not taken.
|
52 | const auto it = std::ranges::find_if(pending, pred); |
| 97 |
1/2✓ Branch 15 → 16 taken 52 times.
✗ Branch 15 → 18 not taken.
|
104 | if (it == pending.end()) |
| 98 |
1/2✓ Branch 16 → 17 taken 52 times.
✗ Branch 16 → 31 not taken.
|
52 | pending.emplace_back(&spiceStruct, withMoveCtor); |
| 99 | else | ||
| 100 | ✗ | it->second |= withMoveCtor; // Upgrade to deciding the move ctor too if any deferred caller asked for it | |
| 101 | 52 | return; | |
| 102 | } | ||
| 103 | |||
| 104 |
1/2✓ Branch 22 → 23 taken 12133 times.
✗ Branch 22 → 33 not taken.
|
12133 | decideDefaultMembers(spiceStruct, withMoveCtor); |
| 105 | |||
| 106 | // This struct just became decided - drain the pending list so it can pick up anything that was blocked on it, directly or | ||
| 107 | // transitively (a chain of Result<Outer<Inner>>-shaped dependencies). | ||
| 108 |
1/2✓ Branch 23 → 24 taken 12133 times.
✗ Branch 23 → 33 not taken.
|
12133 | drainPendingImplicitDefaultMemberDecisions(resourceManager); |
| 109 |
2/2✓ Branch 26 → 27 taken 12133 times.
✓ Branch 26 → 29 taken 52 times.
|
12185 | } |
| 110 | |||
| 111 | /** | ||
| 112 | * Set a struct manifestation's default members as decided and create whichever of them (ctor, copy ctor, move ctor, | ||
| 113 | * dtor) it actually needs. Only called once structFullyManifested has confirmed the struct is ready. | ||
| 114 | * | ||
| 115 | * @param spiceStruct Struct manifestation to decide the default members for | ||
| 116 | * @param withMoveCtor Also decide about the default move ctor | ||
| 117 | */ | ||
| 118 | 12183 | void TypeChecker::decideDefaultMembers(Struct &spiceStruct, bool withMoveCtor) { | |
| 119 | 12183 | spiceStruct.implicitDefaultMembersDecided = true; | |
| 120 | |||
| 121 | 12183 | createDefaultCtorIfRequired(spiceStruct, spiceStruct.scope); | |
| 122 | 12183 | createDefaultCopyCtorIfRequired(spiceStruct, spiceStruct.scope); | |
| 123 |
2/2✓ Branch 4 → 5 taken 7511 times.
✓ Branch 4 → 6 taken 4672 times.
|
12183 | if (withMoveCtor) |
| 124 | 7511 | createDefaultMoveCtorIfRequired(spiceStruct, spiceStruct.scope); | |
| 125 | 12183 | createDefaultDtorIfRequired(spiceStruct, spiceStruct.scope); | |
| 126 | 12183 | } | |
| 127 | |||
| 128 | /** | ||
| 129 | * Drain every struct manifestation that createImplicitDefaultMembers previously had to defer because a by-value | ||
| 130 | * field struct had not decided its own default members yet. Called whenever a struct's decision completes, since | ||
| 131 | * that can unblock others - directly (a struct that was waiting on exactly this one) or transitively (a chain of | ||
| 132 | * Result<Outer<Inner>>-shaped dependencies). | ||
| 133 | * | ||
| 134 | * This calls decideDefaultMembers directly rather than createImplicitDefaultMembers, so draining one entry can never | ||
| 135 | * recurse back into this function - no reentrancy guard is needed, the loop below just keeps going until a full pass | ||
| 136 | * makes no further progress. | ||
| 137 | * | ||
| 138 | * @param resourceManager Resource manager that owns the pending list for this compilation | ||
| 139 | */ | ||
| 140 | 12133 | void TypeChecker::drainPendingImplicitDefaultMemberDecisions(GlobalResourceManager &resourceManager) { | |
| 141 | bool progressed; | ||
| 142 |
2/2✓ Branch 34 → 3 taken 50 times.
✓ Branch 34 → 35 taken 12133 times.
|
12183 | do { |
| 143 | 12183 | progressed = false; | |
| 144 | // Snapshot before iterating: deciding one pending manifestation can append newly-deferred entries to the very | ||
| 145 | // list being iterated (a struct that unblocks can itself be a field of another, still-blocked struct). | ||
| 146 |
1/2✓ Branch 3 → 4 taken 12183 times.
✗ Branch 3 → 43 not taken.
|
12183 | const std::vector<std::pair<Struct *, bool>> snapshot = resourceManager.pendingStructDefaultMemberDecisions; |
| 147 |
2/2✓ Branch 32 → 6 taken 288 times.
✓ Branch 32 → 33 taken 12183 times.
|
24942 | for (const auto &[pendingStruct, pendingWithMoveCtor] : snapshot) { |
| 148 |
2/2✓ Branch 10 → 11 taken 50 times.
✓ Branch 10 → 12 taken 238 times.
|
288 | if (pendingStruct->implicitDefaultMembersDecided) |
| 149 | 238 | continue; // Already resolved by an earlier entry in this same pass | |
| 150 | |||
| 151 | 238 | std::unordered_set<const Scope *> visitedScopes; | |
| 152 |
3/4✓ Branch 13 → 14 taken 238 times.
✗ Branch 13 → 37 not taken.
✓ Branch 14 → 15 taken 188 times.
✓ Branch 14 → 16 taken 50 times.
|
238 | if (!structFullyManifested(pendingStruct, pendingStruct->declNode, visitedScopes)) |
| 153 | 188 | continue; // Still blocked on something else; leave it queued | |
| 154 | |||
| 155 |
1/2✓ Branch 16 → 17 taken 50 times.
✗ Branch 16 → 37 not taken.
|
50 | decideDefaultMembers(*pendingStruct, pendingWithMoveCtor); |
| 156 | 50 | progressed = true; | |
| 157 |
2/2✓ Branch 19 → 20 taken 50 times.
✓ Branch 19 → 22 taken 188 times.
|
238 | } |
| 158 | 12183 | } while (progressed); | |
| 159 | |||
| 160 | // Drop everything that got resolved. Whatever remains is still genuinely blocked (e.g. a circular import still in | ||
| 161 | // progress) and stays queued to be retried again the next time some other struct's decision completes. | ||
| 162 | 226 | const auto pred = [](const std::pair<Struct *, bool> &entry) { return entry.first->implicitDefaultMembersDecided; }; | |
| 163 |
1/2✓ Branch 35 → 36 taken 12133 times.
✗ Branch 35 → 44 not taken.
|
12133 | std::erase_if(resourceManager.pendingStructDefaultMemberDecisions, pred); |
| 164 | 12133 | } | |
| 165 | |||
| 166 | /** | ||
| 167 | * Create a default struct method | ||
| 168 | * Checks if the given struct scope already has a user-defined constructor and creates a default one if not. | ||
| 169 | * | ||
| 170 | * @param spiceStruct Struct instance | ||
| 171 | * @param entryName Name of the symbol table entry | ||
| 172 | * @param name Name of the method to create | ||
| 173 | * @param params Parameter types of the method | ||
| 174 | */ | ||
| 175 | 8082 | void TypeChecker::createDefaultStructMethod(const Struct &spiceStruct, const std::string &entryName, const std::string &name, | |
| 176 | const ParamList ¶ms) { | ||
| 177 | 8082 | Scope *structScope = spiceStruct.scope; | |
| 178 | 8082 | SourceFile *sourceFile = structScope->sourceFile; | |
| 179 | 8082 | ASTNode *node = spiceStruct.declNode; | |
| 180 | 8082 | const SymbolTableEntry *structEntry = spiceStruct.entry; | |
| 181 |
1/2✓ Branch 2 → 3 taken 8082 times.
✗ Branch 2 → 99 not taken.
|
8082 | const QualType &structType = structEntry->getQualType(); |
| 182 |
3/6✓ Branch 3 → 4 taken 8082 times.
✗ Branch 3 → 70 not taken.
✓ Branch 4 → 5 taken 8082 times.
✗ Branch 4 → 70 not taken.
✓ Branch 5 → 6 taken 8082 times.
✗ Branch 5 → 68 not taken.
|
8082 | const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + name; |
| 183 | |||
| 184 | // Procedure type | ||
| 185 |
1/2✓ Branch 7 → 8 taken 8082 times.
✗ Branch 7 → 97 not taken.
|
8082 | QualType procedureType(TY_PROCEDURE); |
| 186 |
1/2✓ Branch 8 → 9 taken 8082 times.
✗ Branch 8 → 97 not taken.
|
8082 | procedureType.makePublic(); // Always public |
| 187 | |||
| 188 | // Insert symbol for function into the symbol table | ||
| 189 |
1/2✓ Branch 9 → 10 taken 8082 times.
✗ Branch 9 → 97 not taken.
|
8082 | SymbolTableEntry *procEntry = structScope->insert(entryName, structEntry->declNode); |
| 190 |
1/2✓ Branch 12 → 13 taken 8082 times.
✗ Branch 12 → 97 not taken.
|
8082 | procEntry->updateType(procedureType, true); |
| 191 | |||
| 192 | // Add to external name registry. Only the generic preset does this: the registry is keyed by the plain struct name, | ||
| 193 | // so an entry added for one manifestation would be indistinguishable from a user-defined member and would wrongly | ||
| 194 | // suppress the default member of every sibling manifestation (e.g. HashEntry<int, String> vs. HashEntry<int, int>). | ||
| 195 | // Manifestation-level default members are only ever looked up through their struct scope anyway. | ||
| 196 |
3/4✓ Branch 13 → 14 taken 8082 times.
✗ Branch 13 → 97 not taken.
✓ Branch 14 → 15 taken 7138 times.
✓ Branch 14 → 16 taken 944 times.
|
8082 | if (!spiceStruct.isGenericSubstantiation()) |
| 197 |
1/2✓ Branch 15 → 16 taken 7138 times.
✗ Branch 15 → 97 not taken.
|
7138 | sourceFile->addNameRegistryEntry(fqFctName, TY_PROCEDURE, procEntry, structScope, true); |
| 198 | |||
| 199 | // Create the default method | ||
| 200 |
1/2✓ Branch 16 → 17 taken 8082 times.
✗ Branch 16 → 97 not taken.
|
8082 | const std::vector<GenericType> templateTypes = spiceStruct.templateTypes; |
| 201 |
1/2✓ Branch 17 → 18 taken 8082 times.
✗ Branch 17 → 95 not taken.
|
8082 | const QualType returnType(TY_DYN); |
| 202 |
4/8✓ Branch 18 → 19 taken 8082 times.
✗ Branch 18 → 79 not taken.
✓ Branch 19 → 20 taken 8082 times.
✗ Branch 19 → 76 not taken.
✓ Branch 20 → 21 taken 8082 times.
✗ Branch 20 → 73 not taken.
✓ Branch 21 → 22 taken 8082 times.
✗ Branch 21 → 71 not taken.
|
8082 | Function defaultMethod(name, procEntry, structType, returnType, params, templateTypes, structEntry->declNode); |
| 203 | 8082 | defaultMethod.implicitDefault = true; | |
| 204 | |||
| 205 | // Fill type mapping for the case, that the template type list contains non-generic types. Only struct, interface | ||
| 206 | // and enum types carry a sub type usable as a key here; a non-generic primitive template argument (e.g. the 'int' | ||
| 207 | // in BlockAllocator<int>) has no sub type, so getSubType() would assert on it - skip those. | ||
| 208 |
2/2✓ Branch 48 → 27 taken 2258 times.
✓ Branch 48 → 49 taken 8082 times.
|
18422 | for (const GenericType &templateType : templateTypes) |
| 209 |
8/10✓ Branch 29 → 30 taken 2258 times.
✗ Branch 29 → 80 not taken.
✓ Branch 30 → 31 taken 1256 times.
✓ Branch 30 → 34 taken 1002 times.
✓ Branch 31 → 32 taken 1256 times.
✗ Branch 31 → 80 not taken.
✓ Branch 32 → 33 taken 608 times.
✓ Branch 32 → 34 taken 648 times.
✓ Branch 35 → 36 taken 608 times.
✓ Branch 35 → 39 taken 1650 times.
|
2258 | if (!templateType.is(TY_GENERIC) && templateType.isOneOf({TY_STRUCT, TY_INTERFACE, TY_ENUM})) |
| 210 |
2/4✓ Branch 36 → 37 taken 608 times.
✗ Branch 36 → 81 not taken.
✓ Branch 37 → 38 taken 608 times.
✗ Branch 37 → 81 not taken.
|
608 | defaultMethod.typeMapping[templateType.getSubType()] = static_cast<QualType>(templateType); |
| 211 | |||
| 212 | // Create function scope | ||
| 213 |
2/4✓ Branch 49 → 50 taken 8082 times.
✗ Branch 49 → 85 not taken.
✓ Branch 50 → 51 taken 8082 times.
✗ Branch 50 → 83 not taken.
|
8082 | Scope *procScope = structScope->createChildScope(defaultMethod.getScopeName(), ScopeType::FUNC_PROC_BODY, &node->codeLoc); |
| 214 | 8082 | defaultMethod.bodyScope = procScope; | |
| 215 | |||
| 216 | // Create 'this' symbol in the function scope | ||
| 217 |
1/2✓ Branch 54 → 55 taken 8082 times.
✗ Branch 54 → 88 not taken.
|
24246 | SymbolTableEntry *thisEntry = procScope->insert(THIS_VARIABLE_NAME, node); |
| 218 |
2/4✓ Branch 60 → 61 taken 8082 times.
✗ Branch 60 → 92 not taken.
✓ Branch 61 → 62 taken 8082 times.
✗ Branch 61 → 92 not taken.
|
8082 | thisEntry->updateType(structType.toPtr(node), true); |
| 219 | 8082 | thisEntry->used = true; // Always set to used to not print warnings for non-existing code | |
| 220 | |||
| 221 | // Hand it off to the function manager to register the function | ||
| 222 |
2/4✓ Branch 62 → 63 taken 8082 times.
✗ Branch 62 → 93 not taken.
✓ Branch 63 → 64 taken 8082 times.
✗ Branch 63 → 93 not taken.
|
8082 | FunctionManager::insert(structScope, defaultMethod, structEntry->declNode->getFctManifestations(name)); |
| 223 | |||
| 224 | // The body of a default member is not prepared here, but in visitStructDefCheck, which runs over all manifestations | ||
| 225 | // of the struct. For a manifestation that was substantiated after its file was already checked, that run is long | ||
| 226 | // over, so ask for another one - otherwise the member would end up without a body preamble, and the IR generator | ||
| 227 | // would e.g. not find the copy ctor of a field it is supposed to copy. During the prepare stage the flag is set | ||
| 228 | // anyway, so this is a no-op there. | ||
| 229 | 8082 | sourceFile->reVisitRequested = true; | |
| 230 | 8082 | } | |
| 231 | |||
| 232 | /** | ||
| 233 | * Checks if the given struct scope already has a user-defined constructor and creates a default one if not. | ||
| 234 | * | ||
| 235 | * For generating a default ctor, the following conditions need to be met: | ||
| 236 | * - No user-defined constructors (incl. copy/move ctors) | ||
| 237 | * | ||
| 238 | * @param spiceStruct Struct instance | ||
| 239 | * @param structScope Scope of the struct | ||
| 240 | */ | ||
| 241 | 12183 | void TypeChecker::createDefaultCtorIfRequired(const Struct &spiceStruct, Scope *structScope) { | |
| 242 |
1/2✓ Branch 2 → 3 taken 12183 times.
✗ Branch 2 → 4 not taken.
|
12183 | const auto node = spice_pointer_cast<StructDefNode *>(spiceStruct.declNode); |
| 243 |
2/4✓ Branch 11 → 12 taken 12183 times.
✗ Branch 11 → 14 not taken.
✓ Branch 12 → 13 taken 12183 times.
✗ Branch 12 → 14 not taken.
|
12183 | assert(structScope != nullptr && structScope->type == ScopeType::STRUCT); |
| 244 | 12183 | const SourceFile *sourceFile = structScope->sourceFile; | |
| 245 | |||
| 246 | // Abort if the struct already has a constructor. The name registry is the authoritative source here: it is filled by | ||
| 247 | // the symbol table builder, so it already knows about every user-defined ctor, even one that is declared further down | ||
| 248 | // in the file and therefore has not been registered in the struct scope yet. It also covers a ctor that the generic | ||
| 249 | // preset received, which every manifestation inherits through its copied scope. The scope scan on top of it only | ||
| 250 | // guards against creating a second ctor into a scope that visibly has one already. | ||
| 251 | 12183 | const SymbolTableEntry *structEntry = spiceStruct.entry; | |
| 252 |
1/2✓ Branch 15 → 16 taken 12183 times.
✗ Branch 15 → 142 not taken.
|
12183 | const QualType &structType = structEntry->getQualType(); |
| 253 |
3/6✓ Branch 16 → 17 taken 12183 times.
✗ Branch 16 → 115 not taken.
✓ Branch 17 → 18 taken 12183 times.
✗ Branch 17 → 115 not taken.
✓ Branch 18 → 19 taken 12183 times.
✗ Branch 18 → 113 not taken.
|
12183 | const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + CTOR_FUNCTION_NAME; |
| 254 |
7/10✓ Branch 20 → 21 taken 12183 times.
✗ Branch 20 → 140 not taken.
✓ Branch 21 → 22 taken 1973 times.
✓ Branch 21 → 24 taken 10210 times.
✓ Branch 22 → 23 taken 1973 times.
✗ Branch 22 → 140 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 1973 times.
✓ Branch 26 → 27 taken 10210 times.
✓ Branch 26 → 28 taken 1973 times.
|
12183 | if (sourceFile->getNameRegistryEntry(fqFctName) || FunctionManager::hasAnyCtor(structScope)) |
| 255 | 10210 | return; | |
| 256 | |||
| 257 | // Check if we have fields, that require us to do anything in the ctor | ||
| 258 |
1/2✓ Branch 28 → 29 taken 1973 times.
✗ Branch 28 → 140 not taken.
|
1973 | const size_t fieldCount = structScope->getFieldCount(); |
| 259 | 1973 | bool hasFieldsWithDefaultValue = false; | |
| 260 | 1973 | bool hasFieldsToConstruct = false; | |
| 261 |
2/2✓ Branch 91 → 30 taken 5021 times.
✓ Branch 91 → 92 taken 1895 times.
|
6916 | for (size_t i = 0; i < fieldCount; i++) { |
| 262 |
1/2✗ Branch 30 → 31 not taken.
✓ Branch 30 → 33 taken 5021 times.
|
5021 | const SymbolTableEntry *fieldSymbol = structScope->lookupField(i); |
| 263 |
2/4✓ Branch 36 → 37 taken 5021 times.
✗ Branch 36 → 39 not taken.
✓ Branch 37 → 38 taken 5021 times.
✗ Branch 37 → 39 not taken.
|
5021 | assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr); |
| 264 | |||
| 265 |
1/2✓ Branch 40 → 41 taken 5021 times.
✗ Branch 40 → 128 not taken.
|
5021 | QualType fieldType = fieldSymbol->getQualType(); |
| 266 |
7/8✓ Branch 41 → 42 taken 5021 times.
✗ Branch 41 → 128 not taken.
✓ Branch 42 → 43 taken 335 times.
✓ Branch 42 → 46 taken 4686 times.
✓ Branch 44 → 45 taken 26 times.
✓ Branch 44 → 46 taken 309 times.
✓ Branch 47 → 48 taken 26 times.
✓ Branch 47 → 49 taken 4995 times.
|
5021 | if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty()) |
| 267 |
1/2✓ Branch 48 → 49 taken 26 times.
✗ Branch 48 → 128 not taken.
|
26 | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode); |
| 268 | |||
| 269 | // Abort if we have a field, that is a reference | ||
| 270 |
3/4✓ Branch 49 → 50 taken 5021 times.
✗ Branch 49 → 128 not taken.
✓ Branch 50 → 51 taken 16 times.
✓ Branch 50 → 52 taken 5005 times.
|
5021 | if (fieldType.isRef()) |
| 271 | 78 | return; | |
| 272 | |||
| 273 |
3/4✓ Branch 52 → 53 taken 5005 times.
✗ Branch 52 → 54 not taken.
✓ Branch 55 → 56 taken 4897 times.
✓ Branch 55 → 57 taken 108 times.
|
5005 | if (const auto fieldNode = dynamic_cast<FieldNode *>(fieldSymbol->declNode)) { |
| 274 | 4897 | hasFieldsWithDefaultValue |= fieldNode->defaultValue != nullptr; | |
| 275 | } else { | ||
| 276 |
2/4✓ Branch 57 → 58 taken 108 times.
✗ Branch 57 → 59 not taken.
✗ Branch 62 → 63 not taken.
✓ Branch 62 → 64 taken 108 times.
|
216 | assert(is<DataTypeNode *>(fieldSymbol->declNode)); |
| 277 | } | ||
| 278 | |||
| 279 |
3/4✓ Branch 64 → 65 taken 5005 times.
✗ Branch 64 → 128 not taken.
✓ Branch 65 → 66 taken 295 times.
✓ Branch 65 → 89 taken 4710 times.
|
5005 | if (fieldType.is(TY_STRUCT)) { |
| 280 |
1/2✓ Branch 66 → 67 taken 295 times.
✗ Branch 66 → 128 not taken.
|
295 | Scope *bodyScope = fieldType.getBodyScope(); |
| 281 | // Check if we are required to call a ctor | ||
| 282 |
1/2✓ Branch 67 → 68 taken 295 times.
✗ Branch 67 → 128 not taken.
|
295 | const bool isCtorCallRequired = !fieldType.isTriviallyConstructible(node); |
| 283 | // While the outer struct is still a generic preset, a field type that is itself generic (e.g. Inner<T, bool>, | ||
| 284 | // the internal field of Outer<T>) cannot be matched to a concrete ctor yet. Since a default ctor takes no | ||
| 285 | // arguments, matching it against the still-generic 'this' type would partially substantiate a bogus | ||
| 286 | // manifestation (e.g. Inner<K, bool>, with K left generic) that is wrongly treated as fully substantiated | ||
| 287 | // later on and crashes name mangling during IR generation (see #1255). In that case we only record that the | ||
| 288 | // field has a default ctor via a direct scan; the concrete ctor is matched later, per manifestation, in | ||
| 289 | // createCtorBodyPreamble. | ||
| 290 |
3/4✓ Branch 68 → 69 taken 295 times.
✗ Branch 68 → 128 not taken.
✓ Branch 69 → 70 taken 22 times.
✓ Branch 69 → 75 taken 273 times.
|
295 | if (fieldType.hasAnyGenericParts()) { |
| 291 |
1/2✓ Branch 70 → 71 taken 22 times.
✗ Branch 70 → 128 not taken.
|
22 | const bool hasDefaultCtor = FunctionManager::hasDefaultCtor(bodyScope); |
| 292 |
1/4✗ Branch 71 → 72 not taken.
✓ Branch 71 → 74 taken 22 times.
✗ Branch 72 → 73 not taken.
✗ Branch 72 → 74 not taken.
|
22 | if (!hasDefaultCtor && isCtorCallRequired) |
| 293 | ✗ | return; | |
| 294 | 22 | hasFieldsToConstruct |= hasDefaultCtor; | |
| 295 | } else { | ||
| 296 | // Lookup ctor function | ||
| 297 |
2/4✓ Branch 79 → 80 taken 273 times.
✗ Branch 79 → 118 not taken.
✓ Branch 80 → 81 taken 273 times.
✗ Branch 80 → 116 not taken.
|
819 | const Function *ctorFct = FunctionManager::match(bodyScope, CTOR_FUNCTION_NAME, fieldType, {}, {}, true, node); |
| 298 | // If we are required to construct, but no constructor is found, we can't generate a default ctor for the outer struct | ||
| 299 |
4/4✓ Branch 85 → 86 taken 94 times.
✓ Branch 85 → 88 taken 179 times.
✓ Branch 86 → 87 taken 62 times.
✓ Branch 86 → 88 taken 32 times.
|
273 | if (!ctorFct && isCtorCallRequired) |
| 300 | 62 | return; | |
| 301 | 211 | hasFieldsToConstruct |= ctorFct != nullptr; | |
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | // If we don't have any fields, that require us to do anything in the ctor, we can skip it | ||
| 307 |
6/6✓ Branch 92 → 93 taken 1757 times.
✓ Branch 92 → 96 taken 138 times.
✓ Branch 93 → 94 taken 1629 times.
✓ Branch 93 → 96 taken 128 times.
✓ Branch 94 → 95 taken 1563 times.
✓ Branch 94 → 96 taken 66 times.
|
1895 | if (!hasFieldsWithDefaultValue && !hasFieldsToConstruct && !node->emitVTable) |
| 308 | 1563 | return; | |
| 309 | |||
| 310 | // Create the default ctor function | ||
| 311 |
1/2✓ Branch 96 → 97 taken 332 times.
✗ Branch 96 → 140 not taken.
|
332 | const std::string entryName = Function::getSymbolTableEntryNameDefaultCtor(node->codeLoc); |
| 312 |
2/4✓ Branch 100 → 101 taken 332 times.
✗ Branch 100 → 131 not taken.
✓ Branch 101 → 102 taken 332 times.
✗ Branch 101 → 129 not taken.
|
996 | createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, {}); |
| 313 |
2/2✓ Branch 108 → 109 taken 332 times.
✓ Branch 108 → 111 taken 11851 times.
|
12183 | } |
| 314 | |||
| 315 | /** | ||
| 316 | * Checks if the given struct scope already has a user-defined constructor and creates a default one if not. | ||
| 317 | * | ||
| 318 | * For generating a default copy ctor, the following conditions need to be met: | ||
| 319 | * - No user-defined copy ctor | ||
| 320 | * | ||
| 321 | * @param spiceStruct Struct instance | ||
| 322 | * @param structScope Scope of the struct | ||
| 323 | */ | ||
| 324 | 12183 | void TypeChecker::createDefaultCopyCtorIfRequired(const Struct &spiceStruct, Scope *structScope) { | |
| 325 |
1/2✓ Branch 2 → 3 taken 12183 times.
✗ Branch 2 → 4 not taken.
|
12183 | const auto node = spice_pointer_cast<const StructDefNode *>(spiceStruct.declNode); |
| 326 |
2/4✓ Branch 11 → 12 taken 12183 times.
✗ Branch 11 → 14 not taken.
✓ Branch 12 → 13 taken 12183 times.
✗ Branch 12 → 14 not taken.
|
12183 | assert(structScope != nullptr && structScope->type == ScopeType::STRUCT); |
| 327 | |||
| 328 | // Abort if the struct already has a user-defined copy constructor | ||
| 329 |
1/2✓ Branch 15 → 16 taken 12183 times.
✗ Branch 15 → 131 not taken.
|
12183 | const QualType structType = spiceStruct.entry->getQualType(); |
| 330 |
3/4✓ Branch 16 → 17 taken 12183 times.
✗ Branch 16 → 131 not taken.
✓ Branch 17 → 18 taken 3084 times.
✓ Branch 17 → 19 taken 9099 times.
|
12183 | if (FunctionManager::hasCopyCtor(structScope)) |
| 331 | 7982 | return; | |
| 332 | |||
| 333 | // Check if we have fields, that require us to do anything in the ctor | ||
| 334 |
1/2✓ Branch 19 → 20 taken 9099 times.
✗ Branch 19 → 131 not taken.
|
9099 | const size_t fieldCount = structScope->getFieldCount(); |
| 335 | 9099 | bool copyCtorRequired = false; | |
| 336 |
2/2✓ Branch 77 → 21 taken 17898 times.
✓ Branch 77 → 78 taken 9099 times.
|
26997 | for (size_t i = 0; i < fieldCount; i++) { |
| 337 |
1/2✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 17898 times.
|
17898 | const SymbolTableEntry *fieldSymbol = structScope->lookupField(i); |
| 338 |
2/4✓ Branch 27 → 28 taken 17898 times.
✗ Branch 27 → 30 not taken.
✓ Branch 28 → 29 taken 17898 times.
✗ Branch 28 → 30 not taken.
|
17898 | assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr); |
| 339 | |||
| 340 |
1/2✓ Branch 31 → 32 taken 17898 times.
✗ Branch 31 → 116 not taken.
|
17898 | QualType fieldType = fieldSymbol->getQualType(); |
| 341 |
7/8✓ Branch 32 → 33 taken 17898 times.
✗ Branch 32 → 116 not taken.
✓ Branch 33 → 34 taken 2327 times.
✓ Branch 33 → 37 taken 15571 times.
✓ Branch 35 → 36 taken 262 times.
✓ Branch 35 → 37 taken 2065 times.
✓ Branch 38 → 39 taken 262 times.
✓ Branch 38 → 40 taken 17636 times.
|
17898 | if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty()) |
| 342 |
1/2✓ Branch 39 → 40 taken 262 times.
✗ Branch 39 → 116 not taken.
|
262 | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode); |
| 343 | |||
| 344 | // If the field is of type struct, check if this struct has a copy ctor that has to be called | ||
| 345 |
3/4✓ Branch 40 → 41 taken 17898 times.
✗ Branch 40 → 116 not taken.
✓ Branch 41 → 42 taken 5212 times.
✓ Branch 41 → 70 taken 12686 times.
|
17898 | if (fieldType.is(TY_STRUCT)) { |
| 346 | // A field whose copy is non-trivial forces the outer struct to have a copy ctor as well. | ||
| 347 |
1/2✓ Branch 42 → 43 taken 5212 times.
✗ Branch 42 → 116 not taken.
|
5212 | const bool fieldRequiresCopyCtor = !fieldType.isTriviallyCopyable(node); |
| 348 | // While the outer struct is still a generic preset, a field type that is itself generic (e.g. Inner<T, bool>, | ||
| 349 | // the internal field of Outer<T>) cannot be matched to a concrete copy ctor yet. Matching it via the | ||
| 350 | // substantiating FunctionManager::match() below can partially substantiate a bogus manifestation (e.g. | ||
| 351 | // Inner<K, bool>, with K left generic) that is wrongly treated as fully substantiated later on and crashes | ||
| 352 | // name mangling during IR generation (see #1255). This can happen even though the copy ctor call carries an | ||
| 353 | // argument of the same (still-generic) field type, whenever the field struct's own generic type name | ||
| 354 | // collides with the outer struct's generic type name (e.g. both named 'V', as in Set<V> wrapping | ||
| 355 | // RedBlackTree<K,V>). The concrete copy ctor is matched later, per manifestation, in | ||
| 356 | // createCopyCtorBodyPreamble, and its result here is not otherwise consulted while the field type is still | ||
| 357 | // generic (fieldRequiresCopyCtor already reflects copy-ctor existence via the non-substantiating | ||
| 358 | // isTriviallyCopyable check above) - so skip the substantiating match entirely in that case. | ||
| 359 |
3/4✓ Branch 43 → 44 taken 5212 times.
✗ Branch 43 → 116 not taken.
✓ Branch 44 → 45 taken 4938 times.
✓ Branch 44 → 69 taken 274 times.
|
5212 | if (!fieldType.hasAnyGenericParts()) { |
| 360 |
1/2✓ Branch 45 → 46 taken 4938 times.
✗ Branch 45 → 115 not taken.
|
4938 | Scope *bodyScope = fieldType.getBodyScope(); |
| 361 |
2/4✓ Branch 46 → 47 taken 4938 times.
✗ Branch 46 → 102 not taken.
✓ Branch 50 → 51 taken 4938 times.
✗ Branch 50 → 98 not taken.
|
9876 | const ArgList args = {{fieldType.toConstRef(node), false /* we always have the field as storage */}}; |
| 362 |
2/4✓ Branch 55 → 56 taken 4938 times.
✗ Branch 55 → 106 not taken.
✓ Branch 56 → 57 taken 4938 times.
✗ Branch 56 → 104 not taken.
|
14814 | const Function *ctorFct = FunctionManager::match(bodyScope, CTOR_FUNCTION_NAME, fieldType, args, {}, true, node); |
| 363 | // If the field requires a copy ctor, but we proved none exists, we cannot synthesize one for the outer struct. | ||
| 364 |
3/4✓ Branch 60 → 61 taken 1225 times.
✓ Branch 60 → 63 taken 3713 times.
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 63 taken 1225 times.
|
4938 | if (!ctorFct && fieldRequiresCopyCtor) |
| 365 | ✗ | return; | |
| 366 |
1/2✓ Branch 65 → 66 taken 4938 times.
✗ Branch 65 → 68 not taken.
|
4938 | } |
| 367 | 5212 | copyCtorRequired |= fieldRequiresCopyCtor; | |
| 368 | } | ||
| 369 | |||
| 370 | // If we have an owning heap pointer, we need to do a memcpy of the heap storage and therefore need a default copy ctor | ||
| 371 |
3/4✓ Branch 70 → 71 taken 17898 times.
✗ Branch 70 → 116 not taken.
✓ Branch 71 → 72 taken 315 times.
✓ Branch 71 → 76 taken 17583 times.
|
17898 | if (fieldType.isHeap()) { |
| 372 |
2/4✓ Branch 72 → 73 taken 315 times.
✗ Branch 72 → 116 not taken.
✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 315 times.
|
315 | assert(fieldType.isPtr()); |
| 373 | 315 | copyCtorRequired = true; | |
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | // If we don't have any fields, that require us to do anything in the copy ctor, we can skip it | ||
| 378 |
4/4✓ Branch 78 → 79 taken 5485 times.
✓ Branch 78 → 81 taken 3614 times.
✓ Branch 79 → 80 taken 4898 times.
✓ Branch 79 → 81 taken 587 times.
|
9099 | if (!copyCtorRequired && !node->emitVTable) |
| 379 | 4898 | return; | |
| 380 | |||
| 381 | // Create the default copy ctor function | ||
| 382 |
1/2✓ Branch 81 → 82 taken 4201 times.
✗ Branch 81 → 131 not taken.
|
4201 | const std::string entryName = Function::getSymbolTableEntryNameDefaultCopyCtor(node->codeLoc); |
| 383 |
2/4✓ Branch 82 → 83 taken 4201 times.
✗ Branch 82 → 120 not taken.
✓ Branch 85 → 86 taken 4201 times.
✗ Branch 85 → 117 not taken.
|
12603 | const ParamList paramTypes = {{structType.toConstRef(node), false}}; |
| 384 |
2/4✓ Branch 89 → 90 taken 4201 times.
✗ Branch 89 → 123 not taken.
✓ Branch 90 → 91 taken 4201 times.
✗ Branch 90 → 121 not taken.
|
4201 | createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, paramTypes); |
| 385 | 4201 | } | |
| 386 | |||
| 387 | /** | ||
| 388 | * Checks if the given struct scope already has a user-defined move constructor and creates a default one if not. | ||
| 389 | * | ||
| 390 | * For generating a default move ctor, the following conditions need to be met: | ||
| 391 | * - No user-defined move ctor | ||
| 392 | * - At least one field requires non-trivial moving (heap pointer or struct field with move ctor) | ||
| 393 | * | ||
| 394 | * @param spiceStruct Struct instance | ||
| 395 | * @param structScope Scope of the struct | ||
| 396 | */ | ||
| 397 | 7511 | void TypeChecker::createDefaultMoveCtorIfRequired(const Struct &spiceStruct, Scope *structScope) { | |
| 398 |
1/2✓ Branch 2 → 3 taken 7511 times.
✗ Branch 2 → 4 not taken.
|
7511 | const auto node = spice_pointer_cast<const StructDefNode *>(spiceStruct.declNode); |
| 399 |
2/4✓ Branch 11 → 12 taken 7511 times.
✗ Branch 11 → 14 not taken.
✓ Branch 12 → 13 taken 7511 times.
✗ Branch 12 → 14 not taken.
|
7511 | assert(structScope != nullptr && structScope->type == ScopeType::STRUCT); |
| 400 | |||
| 401 | // Skip generic struct presets - we only synthesize a default move ctor for fully substantiated structs. | ||
| 402 | // Reason: each manifestation gets its own scope (deep-copied from the generic), and the implicit move ctor | ||
| 403 | // body's preamble writes to field lifecycle state via bodyScope->parent. If we created the move ctor on the | ||
| 404 | // generic struct, the deep-copied Function in the manifestation's scope would still carry a bodyScope pointer | ||
| 405 | // into the generic struct's scope, causing the preamble to update fields in the wrong scope. | ||
| 406 |
3/4✓ Branch 15 → 16 taken 7511 times.
✗ Branch 15 → 95 not taken.
✓ Branch 16 → 17 taken 1474 times.
✓ Branch 16 → 18 taken 6037 times.
|
7511 | if (!spiceStruct.isFullySubstantiated()) |
| 407 | 7491 | return; | |
| 408 | |||
| 409 | // Abort if the struct already has a user-defined move constructor. | ||
| 410 | // We can't just call FunctionManager::lookup with a non-const ref arg here, since the lookup permits | ||
| 411 | // const-param-to-non-const-arg matching ("constify") and would return the copy ctor (if one exists) as | ||
| 412 | // a false positive. Instead, check the function manifestations directly for a single-self-non-const-ref ctor. | ||
| 413 |
3/4✓ Branch 18 → 19 taken 6037 times.
✗ Branch 18 → 95 not taken.
✓ Branch 19 → 20 taken 8 times.
✓ Branch 19 → 21 taken 6029 times.
|
6037 | if (FunctionManager::hasMoveCtor(structScope)) |
| 414 | 8 | return; | |
| 415 | |||
| 416 | // Abort if the struct has a user-defined copy ctor. Reason: in Spice the move-vs-copy tie-breaker picks the | ||
| 417 | // non-const-ref (move) candidate for non-const lvalue arguments, so silently auto-generating a move ctor on | ||
| 418 | // top of a user-defined copy ctor would change the behavior of existing `T b = T(a)` call sites (a's heap | ||
| 419 | // contents would be stolen instead of deep-copied). Users that want a default move ctor in addition to their | ||
| 420 | // own copy ctor can write an empty `p T.ctor(T& other) {}` to opt in. We still synthesize the default move | ||
| 421 | // ctor when only the auto-generated copy ctor exists (heap-owning struct with no user ctors) - the user did | ||
| 422 | // not write any binding semantics in that case, so picking move for non-const lvalues is fine. | ||
| 423 |
3/4✓ Branch 21 → 22 taken 6029 times.
✗ Branch 21 → 95 not taken.
✓ Branch 22 → 23 taken 512 times.
✓ Branch 22 → 24 taken 5517 times.
|
6029 | if (FunctionManager::hasUserCopyCtor(structScope)) |
| 424 | 512 | return; | |
| 425 |
1/2✓ Branch 24 → 25 taken 5517 times.
✗ Branch 24 → 95 not taken.
|
5517 | const QualType structType = spiceStruct.entry->getQualType(); |
| 426 | |||
| 427 | // Check if we have fields, that require us to do anything in the move ctor | ||
| 428 |
1/2✓ Branch 25 → 26 taken 5517 times.
✗ Branch 25 → 95 not taken.
|
5517 | const size_t fieldCount = structScope->getFieldCount(); |
| 429 | 5517 | bool moveCtorRequired = false; | |
| 430 |
2/2✓ Branch 58 → 27 taken 10157 times.
✓ Branch 58 → 59 taken 5517 times.
|
15674 | for (size_t i = 0; i < fieldCount; i++) { |
| 431 |
1/2✗ Branch 27 → 28 not taken.
✓ Branch 27 → 30 taken 10157 times.
|
10157 | const SymbolTableEntry *fieldSymbol = structScope->lookupField(i); |
| 432 |
2/4✓ Branch 33 → 34 taken 10157 times.
✗ Branch 33 → 36 not taken.
✓ Branch 34 → 35 taken 10157 times.
✗ Branch 34 → 36 not taken.
|
10157 | assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr); |
| 433 | |||
| 434 |
1/2✓ Branch 37 → 38 taken 10157 times.
✗ Branch 37 → 79 not taken.
|
10157 | QualType fieldType = fieldSymbol->getQualType(); |
| 435 |
3/8✓ Branch 38 → 39 taken 10157 times.
✗ Branch 38 → 79 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 43 taken 10157 times.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 46 taken 10157 times.
|
10157 | if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty()) |
| 436 | ✗ | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode); | |
| 437 | |||
| 438 | // If the field is of type struct, check whether the field's struct has its own move ctor. We use | ||
| 439 | // findMoveCtor (a direct scan of the manifestations) rather than FunctionManager::match here for two | ||
| 440 | // reasons: (1) match permits const-param-to-non-const-arg "constify" matching and would return the | ||
| 441 | // field's copy ctor as a false positive, and (2) match populates the lookup cache, which would inflate | ||
| 442 | // the per-struct cache-miss counts even for structs that have nothing to do with move ctors. | ||
| 443 |
3/4✓ Branch 46 → 47 taken 10157 times.
✗ Branch 46 → 79 not taken.
✓ Branch 47 → 48 taken 3525 times.
✓ Branch 47 → 51 taken 6632 times.
|
10157 | if (fieldType.is(TY_STRUCT)) { |
| 444 |
1/2✓ Branch 48 → 49 taken 3525 times.
✗ Branch 48 → 79 not taken.
|
3525 | Scope *bodyScope = fieldType.getBodyScope(); |
| 445 |
1/2✓ Branch 49 → 50 taken 3525 times.
✗ Branch 49 → 79 not taken.
|
3525 | moveCtorRequired |= FunctionManager::findMoveCtor(bodyScope) != nullptr; |
| 446 | } | ||
| 447 | |||
| 448 | // If we have an owning heap pointer, we transfer ownership of the heap storage | ||
| 449 |
3/4✓ Branch 51 → 52 taken 10157 times.
✗ Branch 51 → 79 not taken.
✓ Branch 52 → 53 taken 14 times.
✓ Branch 52 → 57 taken 10143 times.
|
10157 | if (fieldType.isHeap()) { |
| 450 |
2/4✓ Branch 53 → 54 taken 14 times.
✗ Branch 53 → 79 not taken.
✗ Branch 54 → 55 not taken.
✓ Branch 54 → 56 taken 14 times.
|
14 | assert(fieldType.isPtr()); |
| 451 | 14 | moveCtorRequired = true; | |
| 452 | } | ||
| 453 | } | ||
| 454 | |||
| 455 | // If we don't have any fields that require us to do anything special in the move ctor, we can skip it. | ||
| 456 | // Unlike the copy ctor we do NOT generate a default move ctor just because the struct emits a vtable - the | ||
| 457 | // vtable pointer is part of the struct layout and gets shallow-copied along with everything else, no | ||
| 458 | // special move handling needed. Users that want to move a vtable-bearing struct fall back to the default | ||
| 459 | // copy ctor. | ||
| 460 |
2/2✓ Branch 59 → 60 taken 5497 times.
✓ Branch 59 → 61 taken 20 times.
|
5517 | if (!moveCtorRequired) |
| 461 | 5497 | return; | |
| 462 | |||
| 463 | // Create the default move ctor function | ||
| 464 |
1/2✓ Branch 61 → 62 taken 20 times.
✗ Branch 61 → 95 not taken.
|
20 | const std::string entryName = Function::getSymbolTableEntryNameDefaultMoveCtor(node->codeLoc); |
| 465 |
3/6✓ Branch 62 → 63 taken 20 times.
✗ Branch 62 → 83 not taken.
✓ Branch 63 → 64 taken 20 times.
✗ Branch 63 → 83 not taken.
✓ Branch 66 → 67 taken 20 times.
✗ Branch 66 → 80 not taken.
|
60 | const ParamList paramTypes = {{structType.toNonConst().toRef(node), false}}; |
| 466 |
2/4✓ Branch 70 → 71 taken 20 times.
✗ Branch 70 → 87 not taken.
✓ Branch 71 → 72 taken 20 times.
✗ Branch 71 → 85 not taken.
|
20 | createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, paramTypes); |
| 467 | 20 | } | |
| 468 | |||
| 469 | /** | ||
| 470 | * Checks if the given struct scope already has a user-defined destructor and creates a default one if not. | ||
| 471 | * | ||
| 472 | * For generating a default dtor, the following conditions need to be met: | ||
| 473 | * - No user-defined dtor | ||
| 474 | * | ||
| 475 | * @param spiceStruct Struct instance | ||
| 476 | * @param structScope Scope of the struct | ||
| 477 | */ | ||
| 478 | 12183 | void TypeChecker::createDefaultDtorIfRequired(const Struct &spiceStruct, Scope *structScope) { | |
| 479 | 12183 | const ASTNode *node = spiceStruct.declNode; | |
| 480 | 12183 | const SourceFile *sourceFile = structScope->sourceFile; | |
| 481 |
2/4✓ Branch 2 → 3 taken 12183 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 12183 times.
✗ Branch 3 → 5 not taken.
|
12183 | assert(structScope != nullptr && structScope->type == ScopeType::STRUCT); |
| 482 | |||
| 483 | // Abort if the struct already has a destructor. See createDefaultCtorIfRequired for why the name registry is asked | ||
| 484 | // first and the struct scope only on top of it. | ||
| 485 | 12183 | const SymbolTableEntry *structEntry = spiceStruct.entry; | |
| 486 |
1/2✓ Branch 6 → 7 taken 12183 times.
✗ Branch 6 → 110 not taken.
|
12183 | const QualType &structType = structEntry->getQualType(); |
| 487 |
3/6✓ Branch 7 → 8 taken 12183 times.
✗ Branch 7 → 83 not taken.
✓ Branch 8 → 9 taken 12183 times.
✗ Branch 8 → 83 not taken.
✓ Branch 9 → 10 taken 12183 times.
✗ Branch 9 → 81 not taken.
|
12183 | const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + DTOR_FUNCTION_NAME; |
| 488 |
7/10✓ Branch 11 → 12 taken 12183 times.
✗ Branch 11 → 108 not taken.
✓ Branch 12 → 13 taken 9908 times.
✓ Branch 12 → 15 taken 2275 times.
✓ Branch 13 → 14 taken 9908 times.
✗ Branch 13 → 108 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 9908 times.
✓ Branch 17 → 18 taken 2275 times.
✓ Branch 17 → 19 taken 9908 times.
|
12183 | if (sourceFile->getNameRegistryEntry(fqFctName) || FunctionManager::hasDtor(structScope)) |
| 489 | 2275 | return; | |
| 490 | |||
| 491 | // Check we have field types, that require use to do anything in the destructor | ||
| 492 |
1/2✓ Branch 19 → 20 taken 9908 times.
✗ Branch 19 → 108 not taken.
|
9908 | const size_t fieldCount = structScope->getFieldCount(); |
| 493 | 9908 | bool hasFieldsToDeAllocate = false; | |
| 494 | 9908 | bool hasFieldsToDestruct = false; | |
| 495 |
2/2✓ Branch 60 → 21 taken 20889 times.
✓ Branch 60 → 61 taken 9908 times.
|
30797 | for (size_t i = 0; i < fieldCount; i++) { |
| 496 |
1/2✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 20889 times.
|
20889 | const SymbolTableEntry *fieldSymbol = structScope->lookupField(i); |
| 497 |
2/4✓ Branch 27 → 28 taken 20889 times.
✗ Branch 27 → 30 not taken.
✓ Branch 28 → 29 taken 20889 times.
✗ Branch 28 → 30 not taken.
|
20889 | assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr); |
| 498 | |||
| 499 |
1/2✓ Branch 31 → 32 taken 20889 times.
✗ Branch 31 → 96 not taken.
|
20889 | QualType fieldType = fieldSymbol->getQualType(); |
| 500 |
7/8✓ Branch 32 → 33 taken 20889 times.
✗ Branch 32 → 96 not taken.
✓ Branch 33 → 34 taken 2490 times.
✓ Branch 33 → 37 taken 18399 times.
✓ Branch 35 → 36 taken 321 times.
✓ Branch 35 → 37 taken 2169 times.
✓ Branch 38 → 39 taken 321 times.
✓ Branch 38 → 40 taken 20568 times.
|
20889 | if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty()) |
| 501 |
1/2✓ Branch 39 → 40 taken 321 times.
✗ Branch 39 → 96 not taken.
|
321 | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode); |
| 502 | |||
| 503 |
1/2✓ Branch 40 → 41 taken 20889 times.
✗ Branch 40 → 96 not taken.
|
20889 | hasFieldsToDeAllocate |= fieldType.needsDeAllocation(); |
| 504 |
3/4✓ Branch 41 → 42 taken 20889 times.
✗ Branch 41 → 96 not taken.
✓ Branch 42 → 43 taken 5354 times.
✓ Branch 42 → 59 taken 15535 times.
|
20889 | if (fieldType.is(TY_STRUCT)) { |
| 505 |
1/2✓ Branch 43 → 44 taken 5354 times.
✗ Branch 43 → 96 not taken.
|
5354 | Scope *fieldScope = fieldType.getBodyScope(); |
| 506 | // While the outer struct is still a generic preset, a field type that is itself generic (e.g. | ||
| 507 | // RedBlackTree<V, bool>, the internal field of Set<V>) cannot be matched to a concrete dtor yet. Since a | ||
| 508 | // dtor takes no arguments, matching it against the still-generic 'this' type would partially substantiate a | ||
| 509 | // bogus manifestation (e.g. RedBlackTree<K, bool>, with K left generic) that is wrongly treated as fully | ||
| 510 | // substantiated later on and crashes name mangling during IR generation. In that case we only record that | ||
| 511 | // the field has a dtor via a direct scan; the concrete dtor is matched later, per manifestation, in | ||
| 512 | // createDtorBodyPreamble. | ||
| 513 |
3/4✓ Branch 44 → 45 taken 5354 times.
✗ Branch 44 → 96 not taken.
✓ Branch 45 → 46 taken 271 times.
✓ Branch 45 → 48 taken 5083 times.
|
5354 | if (fieldType.hasAnyGenericParts()) { |
| 514 |
1/2✓ Branch 46 → 47 taken 271 times.
✗ Branch 46 → 96 not taken.
|
271 | hasFieldsToDestruct |= FunctionManager::hasDtor(fieldScope); |
| 515 | } else { | ||
| 516 | // Lookup dtor function | ||
| 517 |
2/4✓ Branch 52 → 53 taken 5083 times.
✗ Branch 52 → 86 not taken.
✓ Branch 53 → 54 taken 5083 times.
✗ Branch 53 → 84 not taken.
|
15249 | const Function *dtorFct = FunctionManager::match(fieldScope, DTOR_FUNCTION_NAME, fieldType, {}, {}, true, node); |
| 518 | 5083 | hasFieldsToDestruct |= dtorFct != nullptr; | |
| 519 |
1/2✓ Branch 58 → 59 taken 5083 times.
✗ Branch 58 → 96 not taken.
|
5083 | requestRevisitIfRequired(dtorFct); |
| 520 | } | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | // If we don't have any fields, that require us to do anything in the dtor, we can skip it | ||
| 525 |
4/4✓ Branch 61 → 62 taken 9541 times.
✓ Branch 61 → 64 taken 367 times.
✓ Branch 62 → 63 taken 6379 times.
✓ Branch 62 → 64 taken 3162 times.
|
9908 | if (!hasFieldsToDeAllocate && !hasFieldsToDestruct) |
| 526 | 6379 | return; | |
| 527 | |||
| 528 | // Create the default dtor function. The memory runtime that the generated body needs for its de-allocations is not | ||
| 529 | // requested here, but only when that body is prepared (see createDtorBodyPreamble) - see the comment there. | ||
| 530 |
1/2✓ Branch 64 → 65 taken 3529 times.
✗ Branch 64 → 108 not taken.
|
3529 | const std::string entryName = Function::getSymbolTableEntryNameDefaultDtor(node->codeLoc); |
| 531 |
2/4✓ Branch 68 → 69 taken 3529 times.
✗ Branch 68 → 99 not taken.
✓ Branch 69 → 70 taken 3529 times.
✗ Branch 69 → 97 not taken.
|
10587 | createDefaultStructMethod(spiceStruct, entryName, DTOR_FUNCTION_NAME, {}); |
| 532 |
2/2✓ Branch 76 → 77 taken 3529 times.
✓ Branch 76 → 79 taken 8654 times.
|
12183 | } |
| 533 | |||
| 534 | /** | ||
| 535 | * Prepare the generation of the ctor body preamble. This preamble is used to initialize the VTable, construct or initialize | ||
| 536 | * fields. | ||
| 537 | */ | ||
| 538 | 13534 | void TypeChecker::createCtorBodyPreamble(const Scope *bodyScope) const { | |
| 539 | // Retrieve struct scope | ||
| 540 | 13534 | Scope *structScope = bodyScope->parent; | |
| 541 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 13534 times.
|
13534 | assert(structScope != nullptr); |
| 542 | |||
| 543 | 13534 | const size_t fieldCount = structScope->getFieldCount(); | |
| 544 |
2/2✓ Branch 60 → 6 taken 33014 times.
✓ Branch 60 → 61 taken 13534 times.
|
46548 | for (size_t i = 0; i < fieldCount; i++) { |
| 545 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 33014 times.
|
33014 | SymbolTableEntry *fieldSymbol = structScope->lookupField(i); |
| 546 |
3/6✓ Branch 12 → 13 taken 33014 times.
✗ Branch 12 → 16 not taken.
✓ Branch 13 → 14 taken 33014 times.
✗ Branch 13 → 76 not taken.
✓ Branch 14 → 15 taken 33014 times.
✗ Branch 14 → 16 not taken.
|
33014 | assert(fieldSymbol != nullptr && fieldSymbol->isField()); |
| 547 |
2/2✓ Branch 17 → 18 taken 2794 times.
✓ Branch 17 → 19 taken 30220 times.
|
33014 | if (fieldSymbol->isImplicitField) |
| 548 | 7738 | continue; | |
| 549 | |||
| 550 |
1/2✓ Branch 19 → 20 taken 30220 times.
✗ Branch 19 → 76 not taken.
|
30220 | QualType fieldType = fieldSymbol->getQualType(); |
| 551 |
2/4✓ Branch 20 → 21 taken 30220 times.
✗ Branch 20 → 76 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 30220 times.
|
30220 | if (fieldType.hasAnyGenericParts()) |
| 552 | ✗ | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode); | |
| 553 | |||
| 554 | // A reference field has no default value to construct - it can only be bound by an explicit assignment in the | ||
| 555 | // ctor body (e.g. Pair<K, V&>'s 'this.second = second;'), and that assignment is what should mark the field's | ||
| 556 | // lifecycle as initialized (see the isDecl / initial-field-ref-assign logic in TypeChecker::visitAssignExpr and | ||
| 557 | // IRGenerator::doAssignment). Marking it initialized here, before that binding assignment runs, would make the | ||
| 558 | // compiler treat the binding as a reassignment through the reference instead of the initial binding - illegal | ||
| 559 | // for a const reference field, and wrong even for a non-const one. | ||
| 560 |
3/4✓ Branch 23 → 24 taken 30220 times.
✗ Branch 23 → 76 not taken.
✓ Branch 24 → 25 taken 2023 times.
✓ Branch 24 → 26 taken 28197 times.
|
30220 | if (fieldType.isRef()) |
| 561 | 2023 | continue; | |
| 562 | |||
| 563 |
3/4✓ Branch 26 → 27 taken 28197 times.
✗ Branch 26 → 76 not taken.
✓ Branch 27 → 28 taken 7218 times.
✓ Branch 27 → 56 taken 20979 times.
|
28197 | if (fieldType.is(TY_STRUCT)) { |
| 564 |
1/2✓ Branch 28 → 29 taken 7218 times.
✗ Branch 28 → 30 not taken.
|
7218 | const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode); |
| 565 | // Match ctor function, create the concrete manifestation and set it to used | ||
| 566 |
1/2✓ Branch 37 → 38 taken 7218 times.
✗ Branch 37 → 76 not taken.
|
7218 | Scope *matchScope = fieldType.getBodyScope(); |
| 567 |
2/4✓ Branch 42 → 43 taken 7218 times.
✗ Branch 42 → 64 not taken.
✓ Branch 43 → 44 taken 7218 times.
✗ Branch 43 → 62 not taken.
|
21654 | const Function *spiceFunc = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode); |
| 568 |
2/2✓ Branch 48 → 49 taken 4177 times.
✓ Branch 48 → 53 taken 3041 times.
|
7218 | if (spiceFunc != nullptr) |
| 569 |
3/6✓ Branch 49 → 50 taken 4177 times.
✗ Branch 49 → 74 not taken.
✓ Branch 50 → 51 taken 4177 times.
✗ Branch 50 → 74 not taken.
✓ Branch 51 → 52 taken 4177 times.
✗ Branch 51 → 74 not taken.
|
4177 | fieldSymbol->updateType(fieldType.getWithBodyScope(spiceFunc->thisType.getBodyScope()), true); |
| 570 |
3/4✓ Branch 53 → 54 taken 3041 times.
✗ Branch 53 → 76 not taken.
✓ Branch 54 → 55 taken 2921 times.
✓ Branch 54 → 56 taken 120 times.
|
3041 | else if (!fieldType.isTriviallyConstructible(fieldNode)) |
| 571 | 2921 | continue; | |
| 572 | } | ||
| 573 | |||
| 574 |
1/2✓ Branch 56 → 57 taken 25276 times.
✗ Branch 56 → 75 not taken.
|
25276 | fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode); |
| 575 | } | ||
| 576 | 13534 | } | |
| 577 | |||
| 578 | /** | ||
| 579 | * Prepare the generation of the copy ctor body preamble. This preamble is used to initialize the VTable, construct or initialize | ||
| 580 | * fields. | ||
| 581 | */ | ||
| 582 | 7406 | void TypeChecker::createCopyCtorBodyPreamble(const Scope *bodyScope) const { | |
| 583 | // Retrieve struct scope | ||
| 584 | 7406 | Scope *structScope = bodyScope->parent; | |
| 585 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 7406 times.
|
7406 | assert(structScope != nullptr); |
| 586 | |||
| 587 | 7406 | const size_t fieldCount = structScope->getFieldCount(); | |
| 588 |
2/2✓ Branch 67 → 6 taken 11830 times.
✓ Branch 67 → 68 taken 7406 times.
|
19236 | for (size_t i = 0; i < fieldCount; i++) { |
| 589 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 11830 times.
|
11830 | SymbolTableEntry *fieldSymbol = structScope->lookupField(i); |
| 590 |
3/6✓ Branch 12 → 13 taken 11830 times.
✗ Branch 12 → 16 not taken.
✓ Branch 13 → 14 taken 11830 times.
✗ Branch 13 → 89 not taken.
✓ Branch 14 → 15 taken 11830 times.
✗ Branch 14 → 16 not taken.
|
11830 | assert(fieldSymbol != nullptr && fieldSymbol->isField()); |
| 591 |
2/2✓ Branch 17 → 18 taken 352 times.
✓ Branch 17 → 19 taken 11478 times.
|
11830 | if (fieldSymbol->isImplicitField) |
| 592 | 352 | continue; | |
| 593 | |||
| 594 |
1/2✓ Branch 19 → 20 taken 11478 times.
✗ Branch 19 → 89 not taken.
|
11478 | QualType fieldType = fieldSymbol->getQualType(); |
| 595 |
2/4✓ Branch 20 → 21 taken 11478 times.
✗ Branch 20 → 89 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 11478 times.
|
11478 | if (fieldType.hasAnyGenericParts()) |
| 596 | ✗ | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode); | |
| 597 | |||
| 598 |
3/4✓ Branch 23 → 24 taken 11478 times.
✗ Branch 23 → 89 not taken.
✓ Branch 24 → 25 taken 8542 times.
✓ Branch 24 → 63 taken 2936 times.
|
11478 | if (fieldType.is(TY_STRUCT)) { |
| 599 |
1/2✓ Branch 25 → 26 taken 8542 times.
✗ Branch 25 → 27 not taken.
|
8542 | const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode); |
| 600 | // Match ctor function, create the concrete manifestation and set it to used | ||
| 601 |
1/2✓ Branch 34 → 35 taken 8542 times.
✗ Branch 34 → 87 not taken.
|
8542 | Scope *matchScope = fieldType.getBodyScope(); |
| 602 |
2/4✓ Branch 35 → 36 taken 8542 times.
✗ Branch 35 → 73 not taken.
✓ Branch 39 → 40 taken 8542 times.
✗ Branch 39 → 69 not taken.
|
17084 | const ArgList args = {{fieldType.toConstRef(fieldNode), false /* we always have the field as storage */}}; |
| 603 |
2/4✓ Branch 44 → 45 taken 8542 times.
✗ Branch 44 → 77 not taken.
✓ Branch 45 → 46 taken 8542 times.
✗ Branch 45 → 75 not taken.
|
25626 | const Function *copyCtorFct = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, fieldType, args, {}, false, fieldNode); |
| 604 |
2/2✓ Branch 49 → 50 taken 7728 times.
✓ Branch 49 → 54 taken 814 times.
|
8542 | if (copyCtorFct != nullptr) |
| 605 |
3/6✓ Branch 50 → 51 taken 7728 times.
✗ Branch 50 → 84 not taken.
✓ Branch 51 → 52 taken 7728 times.
✗ Branch 51 → 84 not taken.
✓ Branch 52 → 53 taken 7728 times.
✗ Branch 52 → 84 not taken.
|
7728 | fieldSymbol->updateType(fieldType.getWithBodyScope(copyCtorFct->thisType.getBodyScope()), true); |
| 606 |
2/4✓ Branch 54 → 55 taken 814 times.
✗ Branch 54 → 85 not taken.
✗ Branch 55 → 56 not taken.
✓ Branch 55 → 57 taken 814 times.
|
814 | else if (!fieldType.isTriviallyCopyable(fieldNode)) |
| 607 | ✗ | continue; | |
| 608 |
1/2✓ Branch 59 → 60 taken 8542 times.
✗ Branch 59 → 62 not taken.
|
8542 | } |
| 609 | |||
| 610 |
1/2✓ Branch 63 → 64 taken 11478 times.
✗ Branch 63 → 88 not taken.
|
11478 | fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode); |
| 611 | } | ||
| 612 | 7406 | } | |
| 613 | |||
| 614 | /** | ||
| 615 | * Prepare the generation of the move ctor body preamble. This preamble is used to initialize the VTable, move-construct or | ||
| 616 | * shallow-copy fields and transfer ownership of heap allocations. | ||
| 617 | */ | ||
| 618 | 34 | void TypeChecker::createMoveCtorBodyPreamble(const Scope *bodyScope) const { | |
| 619 | // Retrieve struct scope | ||
| 620 | 34 | Scope *structScope = bodyScope->parent; | |
| 621 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 34 times.
|
34 | assert(structScope != nullptr); |
| 622 | |||
| 623 | 34 | const size_t fieldCount = structScope->getFieldCount(); | |
| 624 |
2/2✓ Branch 38 → 6 taken 42 times.
✓ Branch 38 → 39 taken 34 times.
|
76 | for (size_t i = 0; i < fieldCount; i++) { |
| 625 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 42 times.
|
42 | SymbolTableEntry *fieldSymbol = structScope->lookupField(i); |
| 626 |
3/6✓ Branch 12 → 13 taken 42 times.
✗ Branch 12 → 16 not taken.
✓ Branch 13 → 14 taken 42 times.
✗ Branch 13 → 42 not taken.
✓ Branch 14 → 15 taken 42 times.
✗ Branch 14 → 16 not taken.
|
42 | assert(fieldSymbol != nullptr && fieldSymbol->isField()); |
| 627 |
1/2✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 42 times.
|
42 | if (fieldSymbol->isImplicitField) |
| 628 | ✗ | continue; | |
| 629 | |||
| 630 |
1/2✓ Branch 19 → 20 taken 42 times.
✗ Branch 19 → 42 not taken.
|
42 | QualType fieldType = fieldSymbol->getQualType(); |
| 631 |
2/4✓ Branch 20 → 21 taken 42 times.
✗ Branch 20 → 42 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 42 times.
|
42 | if (fieldType.hasAnyGenericParts()) |
| 632 | ✗ | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode); | |
| 633 | |||
| 634 |
3/4✓ Branch 23 → 24 taken 42 times.
✗ Branch 23 → 42 not taken.
✓ Branch 24 → 25 taken 12 times.
✓ Branch 24 → 34 taken 30 times.
|
42 | if (fieldType.is(TY_STRUCT)) { |
| 635 | // Find a move ctor on the field's struct. We use findMoveCtor rather than FunctionManager::match to | ||
| 636 | // avoid a constify-based false positive returning the copy ctor. findMoveCtor doesn't mark the | ||
| 637 | // function as used, so do that explicitly to ensure the inner move ctor's body is actually emitted. | ||
| 638 |
1/2✓ Branch 25 → 26 taken 12 times.
✗ Branch 25 → 42 not taken.
|
12 | Scope *matchScope = fieldType.getBodyScope(); |
| 639 |
2/4✓ Branch 26 → 27 taken 12 times.
✗ Branch 26 → 42 not taken.
✓ Branch 27 → 28 taken 12 times.
✗ Branch 27 → 34 not taken.
|
12 | if (Function *moveCtorFct = FunctionManager::findMoveCtor(matchScope)) { |
| 640 | 12 | moveCtorFct->used = true; | |
| 641 |
1/2✓ Branch 28 → 29 taken 12 times.
✗ Branch 28 → 30 not taken.
|
12 | if (moveCtorFct->entry) |
| 642 | 12 | moveCtorFct->entry->used = true; | |
| 643 |
3/6✓ Branch 30 → 31 taken 12 times.
✗ Branch 30 → 40 not taken.
✓ Branch 31 → 32 taken 12 times.
✗ Branch 31 → 40 not taken.
✓ Branch 32 → 33 taken 12 times.
✗ Branch 32 → 40 not taken.
|
12 | fieldSymbol->updateType(fieldType.getWithBodyScope(moveCtorFct->thisType.getBodyScope()), true); |
| 644 | } | ||
| 645 | } | ||
| 646 | |||
| 647 |
1/2✓ Branch 34 → 35 taken 42 times.
✗ Branch 34 → 41 not taken.
|
42 | fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode); |
| 648 | } | ||
| 649 | 34 | } | |
| 650 | |||
| 651 | /** | ||
| 652 | * Prepare the generation of the dtor body preamble. This preamble is used to destruct all fields and to free all heap fields. | ||
| 653 | * | ||
| 654 | * @param bodyScope Body scope of the dtor | ||
| 655 | * @param node Struct definition node for error messages | ||
| 656 | */ | ||
| 657 | 7261 | void TypeChecker::createDtorBodyPreamble(const Scope *bodyScope, const ASTNode *node) const { | |
| 658 | // Retrieve struct scope | ||
| 659 | 7261 | Scope *structScope = bodyScope->parent; | |
| 660 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 7261 times.
|
7261 | assert(structScope != nullptr); |
| 661 | |||
| 662 | 7261 | const size_t fieldCount = structScope->getFieldCount(); | |
| 663 | 7261 | bool hasFieldsToDeAllocate = false; | |
| 664 |
2/2✓ Branch 50 → 6 taken 12304 times.
✓ Branch 50 → 51 taken 7261 times.
|
19565 | for (size_t i = 0; i < fieldCount; i++) { |
| 665 | 12304 | const size_t fieldIdx = fieldCount - 1 - i; // Destruct fields in reverse order | |
| 666 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 12304 times.
|
12304 | const SymbolTableEntry *fieldSymbol = structScope->lookupField(fieldIdx); |
| 667 |
3/6✓ Branch 12 → 13 taken 12304 times.
✗ Branch 12 → 16 not taken.
✓ Branch 13 → 14 taken 12304 times.
✗ Branch 13 → 98 not taken.
✓ Branch 14 → 15 taken 12304 times.
✗ Branch 14 → 16 not taken.
|
12304 | assert(fieldSymbol != nullptr && fieldSymbol->isField()); |
| 668 |
2/2✓ Branch 17 → 18 taken 362 times.
✓ Branch 17 → 19 taken 11942 times.
|
12304 | if (fieldSymbol->isImplicitField) |
| 669 | 362 | continue; | |
| 670 | |||
| 671 |
1/2✓ Branch 19 → 20 taken 11942 times.
✗ Branch 19 → 98 not taken.
|
11942 | QualType fieldType = fieldSymbol->getQualType(); |
| 672 |
2/4✓ Branch 20 → 21 taken 11942 times.
✗ Branch 20 → 98 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 11942 times.
|
11942 | if (fieldType.hasAnyGenericParts()) |
| 673 | ✗ | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode); | |
| 674 | |||
| 675 |
1/2✓ Branch 23 → 24 taken 11942 times.
✗ Branch 23 → 98 not taken.
|
11942 | hasFieldsToDeAllocate |= fieldType.needsDeAllocation(); |
| 676 | |||
| 677 |
3/4✓ Branch 24 → 25 taken 11942 times.
✗ Branch 24 → 98 not taken.
✓ Branch 25 → 26 taken 8648 times.
✓ Branch 25 → 47 taken 3294 times.
|
11942 | if (fieldType.is(TY_STRUCT)) { |
| 678 |
1/2✓ Branch 26 → 27 taken 8648 times.
✗ Branch 26 → 28 not taken.
|
8648 | const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode); |
| 679 | // Match ctor function, create the concrete manifestation and set it to used | ||
| 680 |
1/2✓ Branch 35 → 36 taken 8648 times.
✗ Branch 35 → 98 not taken.
|
8648 | Scope *matchScope = fieldType.getBodyScope(); |
| 681 |
2/4✓ Branch 40 → 41 taken 8648 times.
✗ Branch 40 → 88 not taken.
✓ Branch 41 → 42 taken 8648 times.
✗ Branch 41 → 86 not taken.
|
25944 | FunctionManager::match(matchScope, DTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode); |
| 682 | } | ||
| 683 | } | ||
| 684 | |||
| 685 | // Request the memory runtime for the de-allocations that the generated body will contain, and mark its sDealloc as | ||
| 686 | // used so that a definition is emitted for it. This deliberately happens here and not where the dtor is created | ||
| 687 | // (createDefaultDtorIfRequired): a manifestation can come into existence while memory_rt.spice is itself still being | ||
| 688 | // pre-checked - sAlloc() returns Result<heap byte*>, so the very first Result manifestation is substantiated from | ||
| 689 | // memory_rt's own signature list. Asking that half-prepared file for sDealloc, which is declared further down in it, | ||
| 690 | // would come up empty. By the time a default member's body is prepared, every runtime module is fully pre-checked. | ||
| 691 | // The string runtime does not use sDealloc, but frees manually to avoid a circular dependency. | ||
| 692 | 7261 | SourceFile *structSourceFile = structScope->sourceFile; | |
| 693 |
5/6✓ Branch 51 → 52 taken 765 times.
✓ Branch 51 → 57 taken 6496 times.
✓ Branch 55 → 56 taken 765 times.
✗ Branch 55 → 57 not taken.
✓ Branch 58 → 59 taken 765 times.
✓ Branch 58 → 85 taken 6496 times.
|
8026 | if (hasFieldsToDeAllocate && !structSourceFile->isStringRT()) { |
| 694 |
1/2✓ Branch 59 → 60 taken 765 times.
✗ Branch 59 → 117 not taken.
|
765 | const SourceFile *memoryRT = structSourceFile->requestRuntimeModule(MEMORY_RT); |
| 695 |
1/2✗ Branch 60 → 61 not taken.
✓ Branch 60 → 62 taken 765 times.
|
765 | assert(memoryRT != nullptr); |
| 696 | 765 | Scope *matchScope = memoryRT->globalScope.get(); | |
| 697 | // Set dealloc function to used | ||
| 698 |
1/2✓ Branch 63 → 64 taken 765 times.
✗ Branch 63 → 117 not taken.
|
765 | const QualType thisType(TY_DYN); |
| 699 |
3/6✓ Branch 64 → 65 taken 765 times.
✗ Branch 64 → 99 not taken.
✓ Branch 65 → 66 taken 765 times.
✗ Branch 65 → 99 not taken.
✓ Branch 66 → 67 taken 765 times.
✗ Branch 66 → 99 not taken.
|
765 | QualType bytePtrRefType = QualType(TY_BYTE).toPtr(node).toRef(node); |
| 700 |
1/2✓ Branch 67 → 68 taken 765 times.
✗ Branch 67 → 117 not taken.
|
765 | bytePtrRefType.makeHeap(); |
| 701 |
1/2✓ Branch 71 → 72 taken 765 times.
✗ Branch 71 → 101 not taken.
|
1530 | const ArgList args = {{bytePtrRefType, false /* we always have the field as storage */}}; |
| 702 |
2/4✓ Branch 76 → 77 taken 765 times.
✗ Branch 76 → 108 not taken.
✓ Branch 77 → 78 taken 765 times.
✗ Branch 77 → 106 not taken.
|
2295 | Function *deallocFct = FunctionManager::match(matchScope, FCT_NAME_DEALLOC, thisType, args, {}, true, node); |
| 703 |
1/2✗ Branch 81 → 82 not taken.
✓ Branch 81 → 83 taken 765 times.
|
765 | assert(deallocFct != nullptr); |
| 704 | 765 | deallocFct->used = true; | |
| 705 | 765 | } | |
| 706 | 7261 | } | |
| 707 | |||
| 708 | /** | ||
| 709 | * Prepare the generation of a call to a method of a given struct | ||
| 710 | * | ||
| 711 | * @param entry Symbol entry to use as 'this' pointer for the method call | ||
| 712 | * @param methodName Name of the method to call | ||
| 713 | * @param args Provided arguments by the caller | ||
| 714 | * @param node AST node | ||
| 715 | */ | ||
| 716 | 13431 | Function *TypeChecker::implicitlyCallStructMethod(const SymbolTableEntry *entry, const std::string &methodName, | |
| 717 | const ArgList &args, const ASTNode *node) const { | ||
| 718 |
3/6✓ Branch 2 → 3 taken 13431 times.
✗ Branch 2 → 9 not taken.
✓ Branch 3 → 4 taken 13431 times.
✗ Branch 3 → 9 not taken.
✓ Branch 4 → 5 taken 13431 times.
✗ Branch 4 → 9 not taken.
|
13431 | const QualType thisType = entry->getQualType().removeReferenceWrapper().toNonConst(); |
| 719 |
1/2✓ Branch 5 → 6 taken 13431 times.
✗ Branch 5 → 10 not taken.
|
26862 | return implicitlyCallStructMethod(thisType, methodName, args, node); |
| 720 | } | ||
| 721 | |||
| 722 | /** | ||
| 723 | * Prepare the generation of a call to a method of a given struct | ||
| 724 | * | ||
| 725 | * @param thisType Struct type to call the method on | ||
| 726 | * @param methodName Name of the method to call | ||
| 727 | * @param args Provided arguments by the caller | ||
| 728 | * @param node AST node | ||
| 729 | */ | ||
| 730 | 15801 | Function *TypeChecker::implicitlyCallStructMethod(QualType thisType, const std::string &methodName, const ArgList &args, | |
| 731 | const ASTNode *node) const { | ||
| 732 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 15801 times.
|
15801 | assert(thisType.is(TY_STRUCT)); |
| 733 | 15801 | Scope *matchScope = thisType.getBodyScope(); | |
| 734 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 15801 times.
|
15801 | assert(matchScope->type == ScopeType::STRUCT); |
| 735 | |||
| 736 | // Search for dtor | ||
| 737 |
2/2✓ Branch 9 → 10 taken 13562 times.
✓ Branch 9 → 12 taken 2239 times.
|
15801 | if (matchScope->isImportedBy(rootScope)) |
| 738 |
1/2✓ Branch 10 → 11 taken 13562 times.
✗ Branch 10 → 18 not taken.
|
13562 | thisType = mapLocalTypeToImportedScopeType(matchScope, thisType); |
| 739 |
1/2✓ Branch 13 → 14 taken 15801 times.
✗ Branch 13 → 19 not taken.
|
15801 | return FunctionManager::match(matchScope, methodName, thisType, args, {}, true, node); |
| 740 | } | ||
| 741 | |||
| 742 | /** | ||
| 743 | * Prepare the generation of a call to the copy ctor of a given struct | ||
| 744 | * | ||
| 745 | * @param entry Symbol entry to use as 'this' pointer for the copy ctor call | ||
| 746 | * @param node Current AST node | ||
| 747 | */ | ||
| 748 | ✗ | Function *TypeChecker::implicitlyCallStructCopyCtor(const SymbolTableEntry *entry, const ASTNode *node) const { | |
| 749 | ✗ | assert(entry != nullptr && entry->getQualType().is(TY_STRUCT)); | |
| 750 | ✗ | return implicitlyCallStructCopyCtor(entry->getQualType(), node); | |
| 751 | } | ||
| 752 | |||
| 753 | /** | ||
| 754 | * Prepare the generation of a call to the copy ctor of a given struct | ||
| 755 | * | ||
| 756 | * @param thisType Struct type to call the copy ctor on | ||
| 757 | * @param node Current AST node | ||
| 758 | */ | ||
| 759 | 2318 | Function *TypeChecker::implicitlyCallStructCopyCtor(const QualType &thisType, const ASTNode *node) const { | |
| 760 |
2/4✓ Branch 2 → 3 taken 2318 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 2318 times.
✗ Branch 3 → 19 not taken.
|
2318 | const QualType argType = thisType.removeReferenceWrapper().toConstRef(node); |
| 761 |
1/2✓ Branch 7 → 8 taken 2318 times.
✗ Branch 7 → 20 not taken.
|
6954 | const ArgList args = {{argType, false /* we always have an entry here */}}; |
| 762 |
2/4✓ Branch 11 → 12 taken 2318 times.
✗ Branch 11 → 27 not taken.
✓ Branch 12 → 13 taken 2318 times.
✗ Branch 12 → 25 not taken.
|
4636 | return implicitlyCallStructMethod(thisType, CTOR_FUNCTION_NAME, args, node); |
| 763 | 2318 | } | |
| 764 | |||
| 765 | /** | ||
| 766 | * Prepare the generation of a call to the move ctor of a given struct | ||
| 767 | * | ||
| 768 | * @param entry Symbol entry to use as 'this' pointer for the move ctor call | ||
| 769 | * @param node Current AST node | ||
| 770 | */ | ||
| 771 | ✗ | Function *TypeChecker::implicitlyCallStructMoveCtor(const SymbolTableEntry *entry, const ASTNode *node) const { | |
| 772 | ✗ | assert(entry != nullptr && entry->getQualType().is(TY_STRUCT)); | |
| 773 | ✗ | return implicitlyCallStructMoveCtor(entry->getQualType(), node); | |
| 774 | } | ||
| 775 | |||
| 776 | /** | ||
| 777 | * Prepare the generation of a call to the move ctor of a given struct | ||
| 778 | * | ||
| 779 | * @param thisType Struct type to call the move ctor on | ||
| 780 | * @param node Current AST node | ||
| 781 | */ | ||
| 782 | ✗ | Function *TypeChecker::implicitlyCallStructMoveCtor(const QualType &thisType, const ASTNode *node) const { | |
| 783 | ✗ | const QualType argType = thisType.removeReferenceWrapper().toNonConst().toRef(node); | |
| 784 | ✗ | const ArgList args = {{argType, false /* we always have an entry here */}}; | |
| 785 | ✗ | return implicitlyCallStructMethod(thisType, CTOR_FUNCTION_NAME, args, node); | |
| 786 | ✗ | } | |
| 787 | |||
| 788 | /** | ||
| 789 | * Prepare the generation of a call to the dtor of a given struct | ||
| 790 | * | ||
| 791 | * @param entry Symbol entry to use as 'this' pointer for the dtor call | ||
| 792 | * @param node StmtLstNode for the current scope | ||
| 793 | */ | ||
| 794 | 13431 | void TypeChecker::implicitlyCallStructDtor(SymbolTableEntry *entry, StmtLstNode *node) const { | |
| 795 | // Add the dtor to the stmt list node to call it later in codegen | ||
| 796 |
4/6✓ Branch 5 → 6 taken 13431 times.
✗ Branch 5 → 16 not taken.
✓ Branch 6 → 7 taken 13431 times.
✗ Branch 6 → 14 not taken.
✓ Branch 10 → 11 taken 11138 times.
✓ Branch 10 → 13 taken 2293 times.
|
40293 | if (Function *dtor = implicitlyCallStructMethod(entry, DTOR_FUNCTION_NAME, {}, node)) |
| 797 |
2/4✓ Branch 11 → 12 taken 11138 times.
✗ Branch 11 → 23 not taken.
✓ Branch 12 → 13 taken 11138 times.
✗ Branch 12 → 23 not taken.
|
11138 | node->resourcesToCleanup.at(manIdx).dtorFunctionsToCall.emplace_back(entry, dtor); |
| 798 | 13431 | } | |
| 799 | |||
| 800 | /** | ||
| 801 | * Prepare the generation of a call to the deallocate function for a heap-allocated variable | ||
| 802 | * | ||
| 803 | * @param node Current AST node for error messages | ||
| 804 | */ | ||
| 805 | 917 | void TypeChecker::implicitlyCallDeallocate(const ASTNode *node) const { | |
| 806 |
1/2✓ Branch 2 → 3 taken 917 times.
✗ Branch 2 → 46 not taken.
|
917 | const SourceFile *memoryRT = sourceFile->requestRuntimeModule(MEMORY_RT); |
| 807 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 917 times.
|
917 | assert(memoryRT != nullptr); |
| 808 | 917 | Scope *matchScope = memoryRT->globalScope.get(); | |
| 809 | // Set dealloc function to used | ||
| 810 |
1/2✓ Branch 6 → 7 taken 917 times.
✗ Branch 6 → 46 not taken.
|
917 | const QualType thisType(TY_DYN); |
| 811 |
3/6✓ Branch 7 → 8 taken 917 times.
✗ Branch 7 → 28 not taken.
✓ Branch 8 → 9 taken 917 times.
✗ Branch 8 → 28 not taken.
✓ Branch 9 → 10 taken 917 times.
✗ Branch 9 → 28 not taken.
|
917 | QualType bytePtrRefType = QualType(TY_BYTE).toPtr(node).toRef(node); |
| 812 |
1/2✓ Branch 10 → 11 taken 917 times.
✗ Branch 10 → 46 not taken.
|
917 | bytePtrRefType.makeHeap(); |
| 813 |
1/2✓ Branch 14 → 15 taken 917 times.
✗ Branch 14 → 30 not taken.
|
1834 | const ArgList args = {{bytePtrRefType, false /* we always have the field as storage */}}; |
| 814 |
2/4✓ Branch 19 → 20 taken 917 times.
✗ Branch 19 → 37 not taken.
✓ Branch 20 → 21 taken 917 times.
✗ Branch 20 → 35 not taken.
|
2751 | Function *deallocFct = FunctionManager::match(matchScope, FCT_NAME_DEALLOC, thisType, args, {}, true, node); |
| 815 |
1/2✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 917 times.
|
917 | assert(deallocFct != nullptr); |
| 816 | 917 | deallocFct->used = true; | |
| 817 | 917 | } | |
| 818 | |||
| 819 | /** | ||
| 820 | * Consider calls to destructors for the given scope | ||
| 821 | * | ||
| 822 | * @param node StmtLstNode for the current scope | ||
| 823 | */ | ||
| 824 | 168405 | void TypeChecker::doScopeCleanup(StmtLstNode *node) const { | |
| 825 | // Get all variables, that are approved for de-allocation | ||
| 826 |
1/2✓ Branch 2 → 3 taken 168405 times.
✗ Branch 2 → 69 not taken.
|
168405 | std::vector<SymbolTableEntry *> vars = currentScope->getVarsGoingOutOfScope(); |
| 827 | // Sort by reverse declaration order | ||
| 828 | 231025 | const auto comp = [this](const SymbolTableEntry *a, const SymbolTableEntry *b) { | |
| 829 | 62620 | const ASTNode *aDeclNode = a->declNode; | |
| 830 | 62620 | const ASTNode *bDeclNode = b->declNode; | |
| 831 | // Primary sort criteria is the code location | ||
| 832 |
3/4✓ Branch 2 → 3 taken 62620 times.
✗ Branch 2 → 15 not taken.
✓ Branch 3 → 4 taken 62050 times.
✓ Branch 3 → 10 taken 570 times.
|
62620 | if (aDeclNode->codeLoc != bDeclNode->codeLoc) |
| 833 |
2/2✓ Branch 4 → 5 taken 2112 times.
✓ Branch 4 → 6 taken 59938 times.
|
124100 | return aDeclNode->codeLoc > bDeclNode->codeLoc; |
| 834 | // Secondary sort criteria is the node id | ||
| 835 |
2/4✓ Branch 10 → 11 taken 570 times.
✗ Branch 10 → 15 not taken.
✓ Branch 11 → 12 taken 570 times.
✗ Branch 11 → 15 not taken.
|
570 | return resourceManager.nodeToNodeId[aDeclNode] > resourceManager.nodeToNodeId[bDeclNode]; |
| 836 | 168405 | }; | |
| 837 |
1/2✓ Branch 3 → 4 taken 168405 times.
✗ Branch 3 → 67 not taken.
|
168405 | std::ranges::stable_sort(vars, comp); |
| 838 | // Call the dtor of each variable. We call the dtor in reverse declaration order | ||
| 839 |
2/2✓ Branch 62 → 6 taken 69940 times.
✓ Branch 62 → 63 taken 168405 times.
|
406750 | for (SymbolTableEntry *var : vars) { |
| 840 | // Check if we have a heap-allocated pointer | ||
| 841 |
10/14✓ Branch 8 → 9 taken 69940 times.
✗ Branch 8 → 65 not taken.
✓ Branch 9 → 10 taken 69940 times.
✗ Branch 9 → 65 not taken.
✓ Branch 10 → 11 taken 7079 times.
✓ Branch 10 → 15 taken 62861 times.
✓ Branch 11 → 12 taken 7079 times.
✗ Branch 11 → 65 not taken.
✓ Branch 12 → 13 taken 7079 times.
✗ Branch 12 → 65 not taken.
✓ Branch 13 → 14 taken 5718 times.
✓ Branch 13 → 15 taken 1361 times.
✓ Branch 16 → 17 taken 5718 times.
✓ Branch 16 → 36 taken 64222 times.
|
69940 | if (var->getQualType().isHeap() && var->getQualType().isOneOf({TY_PTR, TY_STRING, TY_FUNCTION, TY_PROCEDURE})) { |
| 842 | // The memory runtime is ignored, because it manually allocates to avoid circular dependencies. | ||
| 843 | // Same goes for the string runtime. | ||
| 844 |
8/10✓ Branch 17 → 18 taken 5718 times.
✗ Branch 17 → 66 not taken.
✓ Branch 20 → 21 taken 4738 times.
✓ Branch 20 → 25 taken 980 times.
✓ Branch 21 → 22 taken 4738 times.
✗ Branch 21 → 66 not taken.
✓ Branch 24 → 25 taken 3674 times.
✓ Branch 24 → 26 taken 1064 times.
✓ Branch 27 → 28 taken 4654 times.
✓ Branch 27 → 29 taken 1064 times.
|
16174 | if (sourceFile->isMemoryRT() || sourceFile->isStringRT()) |
| 845 | 4654 | continue; | |
| 846 | // If the local variable currently does not have the ownership, we must not deallocate its memory | ||
| 847 |
3/4✓ Branch 30 → 31 taken 1064 times.
✗ Branch 30 → 66 not taken.
✓ Branch 31 → 32 taken 147 times.
✓ Branch 31 → 33 taken 917 times.
|
1064 | if (!var->getLifecycle().isInOwningState()) |
| 848 | 147 | continue; | |
| 849 | |||
| 850 |
1/2✓ Branch 33 → 34 taken 917 times.
✗ Branch 33 → 66 not taken.
|
917 | implicitlyCallDeallocate(node); // Required to request the memory runtime |
| 851 |
2/4✓ Branch 34 → 35 taken 917 times.
✗ Branch 34 → 66 not taken.
✓ Branch 35 → 36 taken 917 times.
✗ Branch 35 → 66 not taken.
|
917 | node->resourcesToCleanup.at(manIdx).heapVarsToFree.push_back(var); |
| 852 | } | ||
| 853 | // Only generate dtor call for structs and if not omitted | ||
| 854 |
8/10✓ Branch 36 → 37 taken 65139 times.
✗ Branch 36 → 66 not taken.
✓ Branch 37 → 38 taken 65139 times.
✗ Branch 37 → 66 not taken.
✓ Branch 38 → 39 taken 14227 times.
✓ Branch 38 → 40 taken 50912 times.
✓ Branch 39 → 40 taken 796 times.
✓ Branch 39 → 41 taken 13431 times.
✓ Branch 42 → 43 taken 51708 times.
✓ Branch 42 → 44 taken 13431 times.
|
65139 | if (!var->getQualType().is(TY_STRUCT) || var->omitDtorCall) |
| 855 | 51708 | continue; | |
| 856 | // Variable must be either initialized or a struct field | ||
| 857 |
5/8✓ Branch 45 → 46 taken 13431 times.
✗ Branch 45 → 66 not taken.
✓ Branch 46 → 47 taken 2 times.
✓ Branch 46 → 49 taken 13429 times.
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 49 taken 2 times.
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 52 taken 13431 times.
|
13431 | if (!var->getLifecycle().isInitialized() && var->scope->type != ScopeType::STRUCT) |
| 858 | ✗ | continue; | |
| 859 | // Call dtor | ||
| 860 |
1/2✓ Branch 52 → 53 taken 13431 times.
✗ Branch 52 → 66 not taken.
|
13431 | implicitlyCallStructDtor(var, node); |
| 861 | } | ||
| 862 | 168405 | } | |
| 863 | |||
| 864 | } // namespace spice::compiler | ||
| 865 |