GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 92.9% 290 / 0 / 312
Functions: 84.2% 16 / 0 / 19
Branches: 57.4% 413 / 0 / 720

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 <SourceFile.h>
6 #include <ast/ASTBuilder.h>
7 #include <ast/ASTNodes.h>
8 #include <global/GlobalResourceManager.h>
9 #include <model/GenericType.h>
10 #include <model/Struct.h>
11 #include <symboltablebuilder/Scope.h>
12 #include <symboltablebuilder/SymbolTableBuilder.h>
13 #include <typechecker/FunctionManager.h>
14 #include <typechecker/TypeMatcher.h>
15
16 namespace spice::compiler {
17
18 static const char *const FCT_NAME_DEALLOC = "sDealloc";
19
20 /**
21 * Create a default struct method
22 * Checks if the given struct scope already has a user-defined constructor and creates a default one if not.
23 *
24 * @param spiceStruct Struct instance
25 * @param entryName Name of the symbol table entry
26 * @param name Name of the method to create
27 * @param params Parameter types of the method
28 */
29 3339 void TypeChecker::createDefaultStructMethod(const Struct &spiceStruct, const std::string &entryName, const std::string &name,
30 const ParamList &params) const {
31 3339 Scope *structScope = spiceStruct.scope;
32 3339 ASTNode *node = spiceStruct.declNode;
33 3339 const SymbolTableEntry *structEntry = spiceStruct.entry;
34
1/2
✓ Branch 2 → 3 taken 3339 times.
✗ Branch 2 → 97 not taken.
3339 const QualType &structType = structEntry->getQualType();
35
3/6
✓ Branch 3 → 4 taken 3339 times.
✗ Branch 3 → 68 not taken.
✓ Branch 4 → 5 taken 3339 times.
✗ Branch 4 → 68 not taken.
✓ Branch 5 → 6 taken 3339 times.
✗ Branch 5 → 66 not taken.
3339 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + name;
36
37 // Procedure type
38
1/2
✓ Branch 7 → 8 taken 3339 times.
✗ Branch 7 → 95 not taken.
3339 QualType procedureType(TY_PROCEDURE);
39
1/2
✓ Branch 8 → 9 taken 3339 times.
✗ Branch 8 → 95 not taken.
3339 procedureType.makePublic(); // Always public
40
41 // Insert symbol for function into the symbol table
42
1/2
✓ Branch 9 → 10 taken 3339 times.
✗ Branch 9 → 95 not taken.
3339 SymbolTableEntry *procEntry = structScope->insert(entryName, structEntry->declNode);
43
1/2
✓ Branch 12 → 13 taken 3339 times.
✗ Branch 12 → 95 not taken.
3339 procEntry->updateType(procedureType, true);
44
45 // Add to external name registry
46
1/2
✓ Branch 13 → 14 taken 3339 times.
✗ Branch 13 → 95 not taken.
3339 sourceFile->addNameRegistryEntry(fqFctName, TY_PROCEDURE, procEntry, structScope, true);
47
48 // Create the default method
49
1/2
✓ Branch 14 → 15 taken 3339 times.
✗ Branch 14 → 95 not taken.
3339 const std::vector<GenericType> templateTypes = spiceStruct.templateTypes;
50
1/2
✓ Branch 15 → 16 taken 3339 times.
✗ Branch 15 → 93 not taken.
3339 const QualType returnType(TY_DYN);
51
4/8
✓ Branch 16 → 17 taken 3339 times.
✗ Branch 16 → 77 not taken.
✓ Branch 17 → 18 taken 3339 times.
✗ Branch 17 → 74 not taken.
✓ Branch 18 → 19 taken 3339 times.
✗ Branch 18 → 71 not taken.
✓ Branch 19 → 20 taken 3339 times.
✗ Branch 19 → 69 not taken.
3339 Function defaultMethod(name, procEntry, structType, returnType, params, templateTypes, structEntry->declNode);
52 3339 defaultMethod.implicitDefault = true;
53
54 // Fill type mapping for the case, that the template type list contains non-generic types. Only struct, interface
55 // and enum types carry a sub type usable as a key here; a non-generic primitive template argument (e.g. the 'int'
56 // in BlockAllocator<int>) has no sub type, so getSubType() would assert on it - skip those.
57
2/2
✓ Branch 46 → 25 taken 787 times.
✓ Branch 46 → 47 taken 3339 times.
7465 for (const GenericType &templateType : templateTypes)
58
7/10
✓ Branch 27 → 28 taken 787 times.
✗ Branch 27 → 78 not taken.
✓ Branch 28 → 29 taken 4 times.
✓ Branch 28 → 32 taken 783 times.
✓ Branch 29 → 30 taken 4 times.
✗ Branch 29 → 78 not taken.
✓ Branch 30 → 31 taken 4 times.
✗ Branch 30 → 32 not taken.
✓ Branch 33 → 34 taken 4 times.
✓ Branch 33 → 37 taken 783 times.
787 if (!templateType.is(TY_GENERIC) && templateType.isOneOf({TY_STRUCT, TY_INTERFACE, TY_ENUM}))
59
2/4
✓ Branch 34 → 35 taken 4 times.
✗ Branch 34 → 79 not taken.
✓ Branch 35 → 36 taken 4 times.
✗ Branch 35 → 79 not taken.
4 defaultMethod.typeMapping[templateType.getSubType()] = static_cast<QualType>(templateType);
60
61 // Create function scope
62
2/4
✓ Branch 47 → 48 taken 3339 times.
✗ Branch 47 → 83 not taken.
✓ Branch 48 → 49 taken 3339 times.
✗ Branch 48 → 81 not taken.
3339 Scope *procScope = structScope->createChildScope(defaultMethod.getScopeName(), ScopeType::FUNC_PROC_BODY, &node->codeLoc);
63 3339 defaultMethod.bodyScope = procScope;
64
65 // Create 'this' symbol in the function scope
66
1/2
✓ Branch 52 → 53 taken 3339 times.
✗ Branch 52 → 86 not taken.
10017 SymbolTableEntry *thisEntry = procScope->insert(THIS_VARIABLE_NAME, node);
67
2/4
✓ Branch 58 → 59 taken 3339 times.
✗ Branch 58 → 90 not taken.
✓ Branch 59 → 60 taken 3339 times.
✗ Branch 59 → 90 not taken.
3339 thisEntry->updateType(structType.toPtr(node), true);
68 3339 thisEntry->used = true; // Always set to used to not print warnings for non-existing code
69
70 // Hand it off to the function manager to register the function
71
2/4
✓ Branch 60 → 61 taken 3339 times.
✗ Branch 60 → 91 not taken.
✓ Branch 61 → 62 taken 3339 times.
✗ Branch 61 → 91 not taken.
3339 FunctionManager::insert(structScope, defaultMethod, structEntry->declNode->getFctManifestations(name));
72 3339 }
73
74 /**
75 * Checks if the given struct scope already has a user-defined constructor and creates a default one if not.
76 *
77 * For generating a default ctor, the following conditions need to be met:
78 * - No user-defined constructors (incl. copy/move ctors)
79 *
80 * @param spiceStruct Struct instance
81 * @param structScope Scope of the struct
82 */
83 2805 void TypeChecker::createDefaultCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
84
1/2
✓ Branch 2 → 3 taken 2805 times.
✗ Branch 2 → 4 not taken.
2805 const auto node = spice_pointer_cast<StructDefNode *>(spiceStruct.declNode);
85
2/4
✓ Branch 9 → 10 taken 2805 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 2805 times.
✗ Branch 10 → 12 not taken.
2805 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
86
87 // Abort if the struct already has a user-defined constructor
88 2805 const SymbolTableEntry *structEntry = spiceStruct.entry;
89
1/2
✓ Branch 13 → 14 taken 2805 times.
✗ Branch 13 → 125 not taken.
2805 const QualType &structType = structEntry->getQualType();
90
3/6
✓ Branch 14 → 15 taken 2805 times.
✗ Branch 14 → 98 not taken.
✓ Branch 15 → 16 taken 2805 times.
✗ Branch 15 → 98 not taken.
✓ Branch 16 → 17 taken 2805 times.
✗ Branch 16 → 96 not taken.
2805 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + CTOR_FUNCTION_NAME;
91
3/4
✓ Branch 18 → 19 taken 2805 times.
✗ Branch 18 → 123 not taken.
✓ Branch 19 → 20 taken 2449 times.
✓ Branch 19 → 21 taken 356 times.
2805 if (sourceFile->getNameRegistryEntry(fqFctName))
92 2449 return;
93
94 // Check if we have fields, that require us to do anything in the ctor
95
1/2
✓ Branch 21 → 22 taken 356 times.
✗ Branch 21 → 123 not taken.
356 const size_t fieldCount = structScope->getFieldCount();
96 356 bool hasFieldsWithDefaultValue = false;
97 356 bool hasFieldsToConstruct = false;
98
2/2
✓ Branch 74 → 23 taken 921 times.
✓ Branch 74 → 75 taken 337 times.
1258 for (size_t i = 0; i < fieldCount; i++) {
99
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 921 times.
921 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
100
2/4
✓ Branch 28 → 29 taken 921 times.
✗ Branch 28 → 31 not taken.
✓ Branch 29 → 30 taken 921 times.
✗ Branch 29 → 31 not taken.
921 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
101
102
1/2
✓ Branch 32 → 33 taken 921 times.
✗ Branch 32 → 111 not taken.
921 QualType fieldType = fieldSymbol->getQualType();
103
5/8
✓ Branch 33 → 34 taken 921 times.
✗ Branch 33 → 111 not taken.
✓ Branch 34 → 35 taken 195 times.
✓ Branch 34 → 38 taken 726 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 195 times.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 921 times.
921 if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty())
104 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode);
105
106 // Abort if we have a field, that is a reference
107
3/4
✓ Branch 41 → 42 taken 921 times.
✗ Branch 41 → 111 not taken.
✓ Branch 42 → 43 taken 8 times.
✓ Branch 42 → 44 taken 913 times.
921 if (fieldType.isRef())
108 19 return;
109
110
3/4
✓ Branch 44 → 45 taken 913 times.
✗ Branch 44 → 46 not taken.
✓ Branch 47 → 48 taken 838 times.
✓ Branch 47 → 49 taken 75 times.
913 if (const auto fieldNode = dynamic_cast<FieldNode *>(fieldSymbol->declNode)) {
111 838 hasFieldsWithDefaultValue |= fieldNode->defaultValue != nullptr;
112 } else {
113
2/4
✓ Branch 49 → 50 taken 75 times.
✗ Branch 49 → 51 not taken.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 75 times.
75 assert(dynamic_cast<DataTypeNode *>(fieldSymbol->declNode) != nullptr);
114 }
115
116
3/4
✓ Branch 54 → 55 taken 913 times.
✗ Branch 54 → 111 not taken.
✓ Branch 55 → 56 taken 72 times.
✓ Branch 55 → 72 taken 841 times.
913 if (fieldType.is(TY_STRUCT)) {
117
1/2
✓ Branch 56 → 57 taken 72 times.
✗ Branch 56 → 111 not taken.
72 Scope *bodyScope = fieldType.getBodyScope();
118 // Check if we are required to call a ctor
119
1/2
✓ Branch 57 → 58 taken 72 times.
✗ Branch 57 → 111 not taken.
72 const bool isCtorCallRequired = !fieldType.isTriviallyConstructible(node);
120 // Lookup ctor function
121
2/4
✓ Branch 62 → 63 taken 72 times.
✗ Branch 62 → 101 not taken.
✓ Branch 63 → 64 taken 72 times.
✗ Branch 63 → 99 not taken.
216 const Function *ctorFct = FunctionManager::match(bodyScope, CTOR_FUNCTION_NAME, fieldType, {}, {}, true, node);
122 // If we are required to construct, but no constructor is found, we can't generate a default ctor for the outer struct
123
4/4
✓ Branch 68 → 69 taken 25 times.
✓ Branch 68 → 71 taken 47 times.
✓ Branch 69 → 70 taken 11 times.
✓ Branch 69 → 71 taken 14 times.
72 if (!ctorFct && isCtorCallRequired)
124 11 return;
125 61 hasFieldsToConstruct |= ctorFct != nullptr;
126 }
127 }
128
129 // If we don't have any fields, that require us to do anything in the ctor, we can skip it
130
6/6
✓ Branch 75 → 76 taken 212 times.
✓ Branch 75 → 79 taken 125 times.
✓ Branch 76 → 77 taken 180 times.
✓ Branch 76 → 79 taken 32 times.
✓ Branch 77 → 78 taken 159 times.
✓ Branch 77 → 79 taken 21 times.
337 if (!hasFieldsWithDefaultValue && !hasFieldsToConstruct && !node->emitVTable)
131 159 return;
132
133 // Create the default ctor function
134
1/2
✓ Branch 79 → 80 taken 178 times.
✗ Branch 79 → 123 not taken.
178 const std::string entryName = Function::getSymbolTableEntryNameDefaultCtor(node->codeLoc);
135
2/4
✓ Branch 83 → 84 taken 178 times.
✗ Branch 83 → 114 not taken.
✓ Branch 84 → 85 taken 178 times.
✗ Branch 84 → 112 not taken.
534 createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, {});
136
2/2
✓ Branch 91 → 92 taken 178 times.
✓ Branch 91 → 94 taken 2627 times.
2805 }
137
138 /**
139 * Checks if the given struct scope already has a user-defined constructor and creates a default one if not.
140 *
141 * For generating a default copy ctor, the following conditions need to be met:
142 * - No user-defined copy ctor
143 *
144 * @param spiceStruct Struct instance
145 * @param structScope Scope of the struct
146 */
147 2805 void TypeChecker::createDefaultCopyCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
148
1/2
✓ Branch 2 → 3 taken 2805 times.
✗ Branch 2 → 4 not taken.
2805 const auto node = spice_pointer_cast<const StructDefNode *>(spiceStruct.declNode);
149
2/4
✓ Branch 9 → 10 taken 2805 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 2805 times.
✗ Branch 10 → 12 not taken.
2805 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
150
151 // Abort if the struct already has a user-defined copy constructor
152
1/2
✓ Branch 13 → 14 taken 2805 times.
✗ Branch 13 → 130 not taken.
2805 const QualType structType = spiceStruct.entry->getQualType();
153
3/4
✓ Branch 14 → 15 taken 2805 times.
✗ Branch 14 → 130 not taken.
✓ Branch 15 → 16 taken 333 times.
✓ Branch 15 → 17 taken 2472 times.
2805 if (FunctionManager::hasCopyCtor(structScope))
154 1110 return;
155
156 // Check if we have fields, that require us to do anything in the ctor
157
1/2
✓ Branch 17 → 18 taken 2472 times.
✗ Branch 17 → 130 not taken.
2472 const size_t fieldCount = structScope->getFieldCount();
158 2472 bool copyCtorRequired = false;
159
2/2
✓ Branch 76 → 19 taken 4566 times.
✓ Branch 76 → 77 taken 2472 times.
7038 for (size_t i = 0; i < fieldCount; i++) {
160
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 4566 times.
4566 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
161
2/4
✓ Branch 24 → 25 taken 4566 times.
✗ Branch 24 → 27 not taken.
✓ Branch 25 → 26 taken 4566 times.
✗ Branch 25 → 27 not taken.
4566 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
162
163
1/2
✓ Branch 28 → 29 taken 4566 times.
✗ Branch 28 → 115 not taken.
4566 QualType fieldType = fieldSymbol->getQualType();
164
5/8
✓ Branch 29 → 30 taken 4566 times.
✗ Branch 29 → 115 not taken.
✓ Branch 30 → 31 taken 985 times.
✓ Branch 30 → 34 taken 3581 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 985 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 4566 times.
4566 if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty())
165 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode);
166
167 // If the field is of type struct, check if this struct has a copy ctor that has to be called
168
3/4
✓ Branch 37 → 38 taken 4566 times.
✗ Branch 37 → 115 not taken.
✓ Branch 38 → 39 taken 1783 times.
✓ Branch 38 → 69 taken 2783 times.
4566 if (fieldType.is(TY_STRUCT)) {
169
1/2
✓ Branch 39 → 40 taken 1783 times.
✗ Branch 39 → 114 not taken.
1783 Scope *bodyScope = fieldType.getBodyScope();
170 // A field whose copy is non-trivial forces the outer struct to have a copy ctor as well.
171
1/2
✓ Branch 40 → 41 taken 1783 times.
✗ Branch 40 → 114 not taken.
1783 const bool fieldRequiresCopyCtor = !fieldType.isTriviallyCopyable(node);
172 // Try to resolve (and substantiate) the field's copy ctor. While the outer struct is still a generic preset,
173 // a field type that is itself generic (e.g. Lambda<p(const T&)>) cannot be matched to a concrete copy ctor
174 // yet; it is matched later, per manifestation, in generateCopyCtorBodyPreamble.
175
2/4
✓ Branch 41 → 42 taken 1783 times.
✗ Branch 41 → 101 not taken.
✓ Branch 45 → 46 taken 1783 times.
✗ Branch 45 → 97 not taken.
3566 const ArgList args = {{fieldType.toConstRef(node), false /* we always have the field as storage */}};
176
2/4
✓ Branch 50 → 51 taken 1783 times.
✗ Branch 50 → 105 not taken.
✓ Branch 51 → 52 taken 1783 times.
✗ Branch 51 → 103 not taken.
5349 const Function *ctorFct = FunctionManager::match(bodyScope, CTOR_FUNCTION_NAME, fieldType, args, {}, true, node);
177 // If the field requires a copy ctor, but we proved none exists, we cannot synthesize one for the outer struct.
178 // A null match only proves absence for a concrete field type - for a still-generic field it merely means the
179 // ctor is not resolvable yet, which must not abort copy ctor synthesis.
180
7/10
✓ Branch 55 → 56 taken 279 times.
✓ Branch 55 → 60 taken 1504 times.
✓ Branch 56 → 57 taken 61 times.
✓ Branch 56 → 60 taken 218 times.
✓ Branch 57 → 58 taken 61 times.
✗ Branch 57 → 112 not taken.
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 60 taken 61 times.
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 63 taken 1783 times.
1783 if (!ctorFct && fieldRequiresCopyCtor && !fieldType.hasAnyGenericParts())
181 return;
182 1783 copyCtorRequired |= fieldRequiresCopyCtor;
183
1/2
✓ Branch 65 → 66 taken 1783 times.
✗ Branch 65 → 68 not taken.
1783 }
184
185 // If we have an owning heap pointer, we need to do a memcpy of the heap storage and therefore need a default copy ctor
186
3/4
✓ Branch 69 → 70 taken 4566 times.
✗ Branch 69 → 115 not taken.
✓ Branch 70 → 71 taken 82 times.
✓ Branch 70 → 75 taken 4484 times.
4566 if (fieldType.isHeap()) {
187
2/4
✓ Branch 71 → 72 taken 82 times.
✗ Branch 71 → 115 not taken.
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 74 taken 82 times.
82 assert(fieldType.isPtr());
188 82 copyCtorRequired = true;
189 }
190 }
191
192 // If we don't have any fields, that require us to do anything in the copy ctor, we can skip it
193
4/4
✓ Branch 77 → 78 taken 1056 times.
✓ Branch 77 → 80 taken 1416 times.
✓ Branch 78 → 79 taken 777 times.
✓ Branch 78 → 80 taken 279 times.
2472 if (!copyCtorRequired && !node->emitVTable)
194 777 return;
195
196 // Create the default copy ctor function
197
1/2
✓ Branch 80 → 81 taken 1695 times.
✗ Branch 80 → 130 not taken.
1695 const std::string entryName = Function::getSymbolTableEntryNameDefaultCopyCtor(node->codeLoc);
198
2/4
✓ Branch 81 → 82 taken 1695 times.
✗ Branch 81 → 119 not taken.
✓ Branch 84 → 85 taken 1695 times.
✗ Branch 84 → 116 not taken.
5085 const ParamList paramTypes = {{structType.toConstRef(node), false}};
199
2/4
✓ Branch 88 → 89 taken 1695 times.
✗ Branch 88 → 122 not taken.
✓ Branch 89 → 90 taken 1695 times.
✗ Branch 89 → 120 not taken.
1695 createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, paramTypes);
200 1695 }
201
202 /**
203 * Checks if the given struct scope already has a user-defined move constructor and creates a default one if not.
204 *
205 * For generating a default move ctor, the following conditions need to be met:
206 * - No user-defined move ctor
207 * - At least one field requires non-trivial moving (heap pointer or struct field with move ctor)
208 *
209 * @param spiceStruct Struct instance
210 * @param structScope Scope of the struct
211 */
212 2805 void TypeChecker::createDefaultMoveCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
213
1/2
✓ Branch 2 → 3 taken 2805 times.
✗ Branch 2 → 4 not taken.
2805 const auto node = spice_pointer_cast<const StructDefNode *>(spiceStruct.declNode);
214
2/4
✓ Branch 9 → 10 taken 2805 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 2805 times.
✗ Branch 10 → 12 not taken.
2805 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
215
216 // Skip generic struct presets - we only synthesize a default move ctor for fully substantiated structs.
217 // Reason: each manifestation gets its own scope (deep-copied from the generic), and the implicit move ctor
218 // body's preamble writes to field lifecycle state via bodyScope->parent. If we created the move ctor on the
219 // generic struct, the deep-copied Function in the manifestation's scope would still carry a bodyScope pointer
220 // into the generic struct's scope, causing the preamble to update fields in the wrong scope.
221
3/4
✓ Branch 13 → 14 taken 2805 times.
✗ Branch 13 → 92 not taken.
✓ Branch 14 → 15 taken 631 times.
✓ Branch 14 → 16 taken 2174 times.
2805 if (!spiceStruct.isFullySubstantiated())
222 2795 return;
223
224 // Abort if the struct already has a user-defined move constructor.
225 // We can't just call FunctionManager::lookup with a non-const ref arg here, since the lookup permits
226 // const-param-to-non-const-arg matching ("constify") and would return the copy ctor (if one exists) as
227 // a false positive. Instead, check the function manifestations directly for a single-self-non-const-ref ctor.
228
3/4
✓ Branch 16 → 17 taken 2174 times.
✗ Branch 16 → 92 not taken.
✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 19 taken 2170 times.
2174 if (FunctionManager::hasMoveCtor(structScope))
229 4 return;
230
231 // Abort if the struct has a user-defined copy ctor. Reason: in Spice the move-vs-copy tie-breaker picks the
232 // non-const-ref (move) candidate for non-const lvalue arguments, so silently auto-generating a move ctor on
233 // top of a user-defined copy ctor would change the behavior of existing `T b = T(a)` call sites (a's heap
234 // contents would be stolen instead of deep-copied). Users that want a default move ctor in addition to their
235 // own copy ctor can write an empty `p T.ctor(T& other) {}` to opt in. We still synthesize the default move
236 // ctor when only the auto-generated copy ctor exists (heap-owning struct with no user ctors) - the user did
237 // not write any binding semantics in that case, so picking move for non-const lvalues is fine.
238
3/4
✓ Branch 19 → 20 taken 2170 times.
✗ Branch 19 → 92 not taken.
✓ Branch 20 → 21 taken 214 times.
✓ Branch 20 → 22 taken 1956 times.
2170 if (FunctionManager::hasUserCopyCtor(structScope))
239 214 return;
240
1/2
✓ Branch 22 → 23 taken 1956 times.
✗ Branch 22 → 92 not taken.
1956 const QualType structType = spiceStruct.entry->getQualType();
241
242 // Check if we have fields, that require us to do anything in the move ctor
243
1/2
✓ Branch 23 → 24 taken 1956 times.
✗ Branch 23 → 92 not taken.
1956 const size_t fieldCount = structScope->getFieldCount();
244 1956 bool moveCtorRequired = false;
245
2/2
✓ Branch 55 → 25 taken 3172 times.
✓ Branch 55 → 56 taken 1956 times.
5128 for (size_t i = 0; i < fieldCount; i++) {
246
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 3172 times.
3172 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
247
2/4
✓ Branch 30 → 31 taken 3172 times.
✗ Branch 30 → 33 not taken.
✓ Branch 31 → 32 taken 3172 times.
✗ Branch 31 → 33 not taken.
3172 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
248
249
1/2
✓ Branch 34 → 35 taken 3172 times.
✗ Branch 34 → 76 not taken.
3172 QualType fieldType = fieldSymbol->getQualType();
250
3/8
✓ Branch 35 → 36 taken 3172 times.
✗ Branch 35 → 76 not taken.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 40 taken 3172 times.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 3172 times.
3172 if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty())
251 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode);
252
253 // If the field is of type struct, check whether the field's struct has its own move ctor. We use
254 // findMoveCtor (a direct scan of the manifestations) rather than FunctionManager::match here for two
255 // reasons: (1) match permits const-param-to-non-const-arg "constify" matching and would return the
256 // field's copy ctor as a false positive, and (2) match populates the lookup cache, which would inflate
257 // the per-struct cache-miss counts even for structs that have nothing to do with move ctors.
258
3/4
✓ Branch 43 → 44 taken 3172 times.
✗ Branch 43 → 76 not taken.
✓ Branch 44 → 45 taken 1517 times.
✓ Branch 44 → 48 taken 1655 times.
3172 if (fieldType.is(TY_STRUCT)) {
259
1/2
✓ Branch 45 → 46 taken 1517 times.
✗ Branch 45 → 76 not taken.
1517 Scope *bodyScope = fieldType.getBodyScope();
260
1/2
✓ Branch 46 → 47 taken 1517 times.
✗ Branch 46 → 76 not taken.
1517 moveCtorRequired |= FunctionManager::findMoveCtor(bodyScope) != nullptr;
261 }
262
263 // If we have an owning heap pointer, we transfer ownership of the heap storage
264
3/4
✓ Branch 48 → 49 taken 3172 times.
✗ Branch 48 → 76 not taken.
✓ Branch 49 → 50 taken 7 times.
✓ Branch 49 → 54 taken 3165 times.
3172 if (fieldType.isHeap()) {
265
2/4
✓ Branch 50 → 51 taken 7 times.
✗ Branch 50 → 76 not taken.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 53 taken 7 times.
7 assert(fieldType.isPtr());
266 7 moveCtorRequired = true;
267 }
268 }
269
270 // If we don't have any fields that require us to do anything special in the move ctor, we can skip it.
271 // Unlike the copy ctor we do NOT generate a default move ctor just because the struct emits a vtable - the
272 // vtable pointer is part of the struct layout and gets shallow-copied along with everything else, no
273 // special move handling needed. Users that want to move a vtable-bearing struct fall back to the default
274 // copy ctor.
275
2/2
✓ Branch 56 → 57 taken 1946 times.
✓ Branch 56 → 58 taken 10 times.
1956 if (!moveCtorRequired)
276 1946 return;
277
278 // Create the default move ctor function
279
1/2
✓ Branch 58 → 59 taken 10 times.
✗ Branch 58 → 92 not taken.
10 const std::string entryName = Function::getSymbolTableEntryNameDefaultMoveCtor(node->codeLoc);
280
3/6
✓ Branch 59 → 60 taken 10 times.
✗ Branch 59 → 80 not taken.
✓ Branch 60 → 61 taken 10 times.
✗ Branch 60 → 80 not taken.
✓ Branch 63 → 64 taken 10 times.
✗ Branch 63 → 77 not taken.
30 const ParamList paramTypes = {{structType.toNonConst().toRef(node), false}};
281
2/4
✓ Branch 67 → 68 taken 10 times.
✗ Branch 67 → 84 not taken.
✓ Branch 68 → 69 taken 10 times.
✗ Branch 68 → 82 not taken.
10 createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, paramTypes);
282 10 }
283
284 /**
285 * Checks if the given struct scope already has a user-defined destructor and creates a default one if not.
286 *
287 * For generating a default dtor, the following conditions need to be met:
288 * - No user-defined dtor
289 *
290 * @param spiceStruct Struct instance
291 * @param structScope Scope of the struct
292 */
293 2805 void TypeChecker::createDefaultDtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
294 2805 const ASTNode *node = spiceStruct.declNode;
295
2/4
✓ Branch 2 → 3 taken 2805 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 2805 times.
✗ Branch 3 → 5 not taken.
2805 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
296
297 // Abort if the struct already has a user-defined destructor
298 2805 const SymbolTableEntry *structEntry = spiceStruct.entry;
299
1/2
✓ Branch 6 → 7 taken 2805 times.
✗ Branch 6 → 153 not taken.
2805 const QualType &structType = structEntry->getQualType();
300
3/6
✓ Branch 7 → 8 taken 2805 times.
✗ Branch 7 → 107 not taken.
✓ Branch 8 → 9 taken 2805 times.
✗ Branch 8 → 107 not taken.
✓ Branch 9 → 10 taken 2805 times.
✗ Branch 9 → 105 not taken.
2805 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + DTOR_FUNCTION_NAME;
301
3/4
✓ Branch 11 → 12 taken 2805 times.
✗ Branch 11 → 151 not taken.
✓ Branch 12 → 13 taken 310 times.
✓ Branch 12 → 14 taken 2495 times.
2805 if (sourceFile->getNameRegistryEntry(fqFctName))
302 310 return;
303
304 // Check we have field types, that require use to do anything in the destructor
305
1/2
✓ Branch 14 → 15 taken 2495 times.
✗ Branch 14 → 151 not taken.
2495 const size_t fieldCount = structScope->getFieldCount();
306 2495 bool hasFieldsToDeAllocate = false;
307 2495 bool hasFieldsToDestruct = false;
308
2/2
✓ Branch 50 → 16 taken 4866 times.
✓ Branch 50 → 51 taken 2495 times.
7361 for (size_t i = 0; i < fieldCount; i++) {
309
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 4866 times.
4866 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
310
2/4
✓ Branch 21 → 22 taken 4866 times.
✗ Branch 21 → 24 not taken.
✓ Branch 22 → 23 taken 4866 times.
✗ Branch 22 → 24 not taken.
4866 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
311
312
1/2
✓ Branch 25 → 26 taken 4866 times.
✗ Branch 25 → 120 not taken.
4866 QualType fieldType = fieldSymbol->getQualType();
313
5/8
✓ Branch 26 → 27 taken 4866 times.
✗ Branch 26 → 120 not taken.
✓ Branch 27 → 28 taken 1194 times.
✓ Branch 27 → 31 taken 3672 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 1194 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 4866 times.
4866 if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty())
314 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode);
315
316
1/2
✓ Branch 34 → 35 taken 4866 times.
✗ Branch 34 → 120 not taken.
4866 hasFieldsToDeAllocate |= fieldType.needsDeAllocation();
317
3/4
✓ Branch 35 → 36 taken 4866 times.
✗ Branch 35 → 120 not taken.
✓ Branch 36 → 37 taken 1795 times.
✓ Branch 36 → 49 taken 3071 times.
4866 if (fieldType.is(TY_STRUCT)) {
318
1/2
✓ Branch 37 → 38 taken 1795 times.
✗ Branch 37 → 120 not taken.
1795 Scope *fieldScope = fieldType.getBodyScope();
319 // Lookup dtor function
320
2/4
✓ Branch 42 → 43 taken 1795 times.
✗ Branch 42 → 110 not taken.
✓ Branch 43 → 44 taken 1795 times.
✗ Branch 43 → 108 not taken.
5385 const Function *dtorFct = FunctionManager::match(fieldScope, DTOR_FUNCTION_NAME, fieldType, {}, {}, true, node);
321 1795 hasFieldsToDestruct |= dtorFct != nullptr;
322
1/2
✓ Branch 48 → 49 taken 1795 times.
✗ Branch 48 → 120 not taken.
1795 requestRevisitIfRequired(dtorFct);
323 }
324 }
325
326 // If we don't have any fields, that require us to do anything in the dtor, we can skip it
327
4/4
✓ Branch 51 → 52 taken 2328 times.
✓ Branch 51 → 54 taken 167 times.
✓ Branch 52 → 53 taken 1039 times.
✓ Branch 52 → 54 taken 1289 times.
2495 if (!hasFieldsToDeAllocate && !hasFieldsToDestruct)
328 1039 return;
329
330 // Create the default dtor function
331
1/2
✓ Branch 54 → 55 taken 1456 times.
✗ Branch 54 → 151 not taken.
1456 const std::string entryName = Function::getSymbolTableEntryNameDefaultDtor(node->codeLoc);
332
2/4
✓ Branch 58 → 59 taken 1456 times.
✗ Branch 58 → 123 not taken.
✓ Branch 59 → 60 taken 1456 times.
✗ Branch 59 → 121 not taken.
4368 createDefaultStructMethod(spiceStruct, entryName, DTOR_FUNCTION_NAME, {});
333
334 // Request memory runtime if we have fields, that are allocated on the heap
335 // The string runtime does not use it, but allocates manually to avoid circular dependencies
336
6/8
✓ Branch 63 → 64 taken 167 times.
✓ Branch 63 → 69 taken 1289 times.
✓ Branch 64 → 65 taken 167 times.
✗ Branch 64 → 149 not taken.
✓ Branch 67 → 68 taken 167 times.
✗ Branch 67 → 69 not taken.
✓ Branch 70 → 71 taken 167 times.
✓ Branch 70 → 97 taken 1289 times.
1623 if (hasFieldsToDeAllocate && !sourceFile->isStringRT()) {
337
1/2
✓ Branch 71 → 72 taken 167 times.
✗ Branch 71 → 148 not taken.
167 const SourceFile *memoryRT = sourceFile->requestRuntimeModule(MEMORY_RT);
338
1/2
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 74 taken 167 times.
167 assert(memoryRT != nullptr);
339 167 Scope *matchScope = memoryRT->globalScope.get();
340 // Set dealloc function to used
341
1/2
✓ Branch 75 → 76 taken 167 times.
✗ Branch 75 → 148 not taken.
167 const QualType thisType(TY_DYN);
342
3/6
✓ Branch 76 → 77 taken 167 times.
✗ Branch 76 → 130 not taken.
✓ Branch 77 → 78 taken 167 times.
✗ Branch 77 → 130 not taken.
✓ Branch 78 → 79 taken 167 times.
✗ Branch 78 → 130 not taken.
167 QualType bytePtrRefType = QualType(TY_BYTE).toPtr(node).toRef(node);
343
1/2
✓ Branch 79 → 80 taken 167 times.
✗ Branch 79 → 148 not taken.
167 bytePtrRefType.makeHeap();
344
1/2
✓ Branch 83 → 84 taken 167 times.
✗ Branch 83 → 132 not taken.
334 const ArgList args = {{bytePtrRefType, false /* we always have the field as storage */}};
345
2/4
✓ Branch 88 → 89 taken 167 times.
✗ Branch 88 → 139 not taken.
✓ Branch 89 → 90 taken 167 times.
✗ Branch 89 → 137 not taken.
501 Function *deallocFct = FunctionManager::match(matchScope, FCT_NAME_DEALLOC, thisType, args, {}, true, node);
346
1/2
✗ Branch 93 → 94 not taken.
✓ Branch 93 → 95 taken 167 times.
167 assert(deallocFct != nullptr);
347 167 deallocFct->used = true;
348 167 }
349
2/2
✓ Branch 100 → 101 taken 1456 times.
✓ Branch 100 → 103 taken 1349 times.
2805 }
350
351 /**
352 * Prepare the generation of the ctor body preamble. This preamble is used to initialize the VTable, construct or initialize
353 * fields.
354 */
355 5616 void TypeChecker::createCtorBodyPreamble(const Scope *bodyScope) const {
356 // Retrieve struct scope
357 5616 Scope *structScope = bodyScope->parent;
358
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 5616 times.
5616 assert(structScope != nullptr);
359
360 5616 const size_t fieldCount = structScope->getFieldCount();
361
2/2
✓ Branch 54 → 6 taken 13839 times.
✓ Branch 54 → 55 taken 5616 times.
19455 for (size_t i = 0; i < fieldCount; i++) {
362
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 13839 times.
13839 SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
363
3/6
✓ Branch 11 → 12 taken 13839 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 13839 times.
✗ Branch 12 → 70 not taken.
✓ Branch 13 → 14 taken 13839 times.
✗ Branch 13 → 15 not taken.
13839 assert(fieldSymbol != nullptr && fieldSymbol->isField());
364
2/2
✓ Branch 16 → 17 taken 1356 times.
✓ Branch 16 → 18 taken 12483 times.
13839 if (fieldSymbol->isImplicitField)
365 2633 continue;
366
367
1/2
✓ Branch 18 → 19 taken 12483 times.
✗ Branch 18 → 70 not taken.
12483 QualType fieldType = fieldSymbol->getQualType();
368
3/4
✓ Branch 19 → 20 taken 12483 times.
✗ Branch 19 → 70 not taken.
✓ Branch 20 → 21 taken 2625 times.
✓ Branch 20 → 22 taken 9858 times.
12483 if (fieldType.hasAnyGenericParts())
369
1/2
✓ Branch 21 → 22 taken 2625 times.
✗ Branch 21 → 70 not taken.
2625 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
370
371
3/4
✓ Branch 22 → 23 taken 12483 times.
✗ Branch 22 → 70 not taken.
✓ Branch 23 → 24 taken 2889 times.
✓ Branch 23 → 50 taken 9594 times.
12483 if (fieldType.is(TY_STRUCT)) {
372
1/2
✓ Branch 24 → 25 taken 2889 times.
✗ Branch 24 → 26 not taken.
2889 const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode);
373 // Match ctor function, create the concrete manifestation and set it to used
374
1/2
✓ Branch 31 → 32 taken 2889 times.
✗ Branch 31 → 70 not taken.
2889 Scope *matchScope = fieldType.getBodyScope();
375
2/4
✓ Branch 36 → 37 taken 2889 times.
✗ Branch 36 → 58 not taken.
✓ Branch 37 → 38 taken 2889 times.
✗ Branch 37 → 56 not taken.
8667 const Function *spiceFunc = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode);
376
2/2
✓ Branch 42 → 43 taken 1570 times.
✓ Branch 42 → 47 taken 1319 times.
2889 if (spiceFunc != nullptr)
377
3/6
✓ Branch 43 → 44 taken 1570 times.
✗ Branch 43 → 68 not taken.
✓ Branch 44 → 45 taken 1570 times.
✗ Branch 44 → 68 not taken.
✓ Branch 45 → 46 taken 1570 times.
✗ Branch 45 → 68 not taken.
1570 fieldSymbol->updateType(fieldType.getWithBodyScope(spiceFunc->thisType.getBodyScope()), true);
378
3/4
✓ Branch 47 → 48 taken 1319 times.
✗ Branch 47 → 70 not taken.
✓ Branch 48 → 49 taken 1277 times.
✓ Branch 48 → 50 taken 42 times.
1319 else if (!fieldType.isTriviallyConstructible(fieldNode))
379 1277 continue;
380 }
381
382
1/2
✓ Branch 50 → 51 taken 11206 times.
✗ Branch 50 → 69 not taken.
11206 fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode);
383 }
384 5616 }
385
386 /**
387 * Prepare the generation of the copy ctor body preamble. This preamble is used to initialize the VTable, construct or initialize
388 * fields.
389 */
390 2790 void TypeChecker::createCopyCtorBodyPreamble(const Scope *bodyScope) const {
391 // Retrieve struct scope
392 2790 Scope *structScope = bodyScope->parent;
393
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2790 times.
2790 assert(structScope != nullptr);
394
395 2790 const size_t fieldCount = structScope->getFieldCount();
396
2/2
✓ Branch 64 → 6 taken 4218 times.
✓ Branch 64 → 65 taken 2790 times.
7008 for (size_t i = 0; i < fieldCount; i++) {
397
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 4218 times.
4218 SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
398
3/6
✓ Branch 11 → 12 taken 4218 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 4218 times.
✗ Branch 12 → 86 not taken.
✓ Branch 13 → 14 taken 4218 times.
✗ Branch 13 → 15 not taken.
4218 assert(fieldSymbol != nullptr && fieldSymbol->isField());
399
2/2
✓ Branch 16 → 17 taken 242 times.
✓ Branch 16 → 18 taken 3976 times.
4218 if (fieldSymbol->isImplicitField)
400 242 continue;
401
402
1/2
✓ Branch 18 → 19 taken 3976 times.
✗ Branch 18 → 86 not taken.
3976 QualType fieldType = fieldSymbol->getQualType();
403
2/4
✓ Branch 19 → 20 taken 3976 times.
✗ Branch 19 → 86 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 3976 times.
3976 if (fieldType.hasAnyGenericParts())
404 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
405
406
3/4
✓ Branch 22 → 23 taken 3976 times.
✗ Branch 22 → 86 not taken.
✓ Branch 23 → 24 taken 3089 times.
✓ Branch 23 → 60 taken 887 times.
3976 if (fieldType.is(TY_STRUCT)) {
407
1/2
✓ Branch 24 → 25 taken 3089 times.
✗ Branch 24 → 26 not taken.
3089 const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode);
408 // Match ctor function, create the concrete manifestation and set it to used
409
1/2
✓ Branch 31 → 32 taken 3089 times.
✗ Branch 31 → 84 not taken.
3089 Scope *matchScope = fieldType.getBodyScope();
410
2/4
✓ Branch 32 → 33 taken 3089 times.
✗ Branch 32 → 70 not taken.
✓ Branch 36 → 37 taken 3089 times.
✗ Branch 36 → 66 not taken.
6178 const ArgList args = {{fieldType.toConstRef(fieldNode), false /* we always have the field as storage */}};
411
2/4
✓ Branch 41 → 42 taken 3089 times.
✗ Branch 41 → 74 not taken.
✓ Branch 42 → 43 taken 3089 times.
✗ Branch 42 → 72 not taken.
9267 const Function *copyCtorFct = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, fieldType, args, {}, false, fieldNode);
412
2/2
✓ Branch 46 → 47 taken 3000 times.
✓ Branch 46 → 51 taken 89 times.
3089 if (copyCtorFct != nullptr)
413
3/6
✓ Branch 47 → 48 taken 3000 times.
✗ Branch 47 → 81 not taken.
✓ Branch 48 → 49 taken 3000 times.
✗ Branch 48 → 81 not taken.
✓ Branch 49 → 50 taken 3000 times.
✗ Branch 49 → 81 not taken.
3000 fieldSymbol->updateType(fieldType.getWithBodyScope(copyCtorFct->thisType.getBodyScope()), true);
414
2/4
✓ Branch 51 → 52 taken 89 times.
✗ Branch 51 → 82 not taken.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 89 times.
89 else if (!fieldType.isTriviallyCopyable(fieldNode))
415 continue;
416
1/2
✓ Branch 56 → 57 taken 3089 times.
✗ Branch 56 → 59 not taken.
3089 }
417
418
1/2
✓ Branch 60 → 61 taken 3976 times.
✗ Branch 60 → 85 not taken.
3976 fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode);
419 }
420 2790 }
421
422 /**
423 * Prepare the generation of the move ctor body preamble. This preamble is used to initialize the VTable, move-construct or
424 * shallow-copy fields and transfer ownership of heap allocations.
425 */
426 17 void TypeChecker::createMoveCtorBodyPreamble(const Scope *bodyScope) const {
427 // Retrieve struct scope
428 17 Scope *structScope = bodyScope->parent;
429
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 17 times.
17 assert(structScope != nullptr);
430
431 17 const size_t fieldCount = structScope->getFieldCount();
432
2/2
✓ Branch 37 → 6 taken 21 times.
✓ Branch 37 → 38 taken 17 times.
38 for (size_t i = 0; i < fieldCount; i++) {
433
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 21 times.
21 SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
434
3/6
✓ Branch 11 → 12 taken 21 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 21 times.
✗ Branch 12 → 41 not taken.
✓ Branch 13 → 14 taken 21 times.
✗ Branch 13 → 15 not taken.
21 assert(fieldSymbol != nullptr && fieldSymbol->isField());
435
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 21 times.
21 if (fieldSymbol->isImplicitField)
436 continue;
437
438
1/2
✓ Branch 18 → 19 taken 21 times.
✗ Branch 18 → 41 not taken.
21 QualType fieldType = fieldSymbol->getQualType();
439
2/4
✓ Branch 19 → 20 taken 21 times.
✗ Branch 19 → 41 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 21 times.
21 if (fieldType.hasAnyGenericParts())
440 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
441
442
3/4
✓ Branch 22 → 23 taken 21 times.
✗ Branch 22 → 41 not taken.
✓ Branch 23 → 24 taken 6 times.
✓ Branch 23 → 33 taken 15 times.
21 if (fieldType.is(TY_STRUCT)) {
443 // Find a move ctor on the field's struct. We use findMoveCtor rather than FunctionManager::match to
444 // avoid a constify-based false positive returning the copy ctor. findMoveCtor doesn't mark the
445 // function as used, so do that explicitly to ensure the inner move ctor's body is actually emitted.
446
1/2
✓ Branch 24 → 25 taken 6 times.
✗ Branch 24 → 41 not taken.
6 Scope *matchScope = fieldType.getBodyScope();
447
2/4
✓ Branch 25 → 26 taken 6 times.
✗ Branch 25 → 41 not taken.
✓ Branch 26 → 27 taken 6 times.
✗ Branch 26 → 33 not taken.
6 if (Function *moveCtorFct = FunctionManager::findMoveCtor(matchScope)) {
448 6 moveCtorFct->used = true;
449
1/2
✓ Branch 27 → 28 taken 6 times.
✗ Branch 27 → 29 not taken.
6 if (moveCtorFct->entry)
450 6 moveCtorFct->entry->used = true;
451
3/6
✓ Branch 29 → 30 taken 6 times.
✗ Branch 29 → 39 not taken.
✓ Branch 30 → 31 taken 6 times.
✗ Branch 30 → 39 not taken.
✓ Branch 31 → 32 taken 6 times.
✗ Branch 31 → 39 not taken.
6 fieldSymbol->updateType(fieldType.getWithBodyScope(moveCtorFct->thisType.getBodyScope()), true);
452 }
453 }
454
455
1/2
✓ Branch 33 → 34 taken 21 times.
✗ Branch 33 → 40 not taken.
21 fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode);
456 }
457 17 }
458
459 /**
460 * Prepare the generation of the dtor body preamble. This preamble is used to destruct all fields and to free all heap fields.
461 */
462 3327 void TypeChecker::createDtorBodyPreamble(const Scope *bodyScope) const {
463 // Retrieve struct scope
464 3327 Scope *structScope = bodyScope->parent;
465
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3327 times.
3327 assert(structScope != nullptr);
466
467 3327 const size_t fieldCount = structScope->getFieldCount();
468
2/2
✓ Branch 46 → 6 taken 6760 times.
✓ Branch 46 → 47 taken 3327 times.
10087 for (size_t i = 0; i < fieldCount; i++) {
469 6760 const size_t fieldIdx = fieldCount - 1 - i; // Destruct fields in reverse order
470
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 6760 times.
6760 const SymbolTableEntry *fieldSymbol = structScope->lookupField(fieldIdx);
471
3/6
✓ Branch 11 → 12 taken 6760 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 6760 times.
✗ Branch 12 → 60 not taken.
✓ Branch 13 → 14 taken 6760 times.
✗ Branch 13 → 15 not taken.
6760 assert(fieldSymbol != nullptr && fieldSymbol->isField());
472
2/2
✓ Branch 16 → 17 taken 826 times.
✓ Branch 16 → 18 taken 5934 times.
6760 if (fieldSymbol->isImplicitField)
473 826 continue;
474
475
1/2
✓ Branch 18 → 19 taken 5934 times.
✗ Branch 18 → 60 not taken.
5934 QualType fieldType = fieldSymbol->getQualType();
476
2/4
✓ Branch 19 → 20 taken 5934 times.
✗ Branch 19 → 60 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 5934 times.
5934 if (fieldType.hasAnyGenericParts())
477 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
478
479
3/4
✓ Branch 22 → 23 taken 5934 times.
✗ Branch 22 → 60 not taken.
✓ Branch 23 → 24 taken 3236 times.
✓ Branch 23 → 43 taken 2698 times.
5934 if (fieldType.is(TY_STRUCT)) {
480
1/2
✓ Branch 24 → 25 taken 3236 times.
✗ Branch 24 → 26 not taken.
3236 const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode);
481 // Match ctor function, create the concrete manifestation and set it to used
482
1/2
✓ Branch 31 → 32 taken 3236 times.
✗ Branch 31 → 60 not taken.
3236 Scope *matchScope = fieldType.getBodyScope();
483
2/4
✓ Branch 36 → 37 taken 3236 times.
✗ Branch 36 → 50 not taken.
✓ Branch 37 → 38 taken 3236 times.
✗ Branch 37 → 48 not taken.
9708 FunctionManager::match(matchScope, DTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode);
484 }
485 }
486 3327 }
487
488 /**
489 * Prepare the generation of a call to a method of a given struct
490 *
491 * @param entry Symbol entry to use as 'this' pointer for the method call
492 * @param methodName Name of the method to call
493 * @param args Provided arguments by the caller
494 * @param node AST node
495 */
496 6467 Function *TypeChecker::implicitlyCallStructMethod(const SymbolTableEntry *entry, const std::string &methodName,
497 const ArgList &args, const ASTNode *node) const {
498
3/6
✓ Branch 2 → 3 taken 6467 times.
✗ Branch 2 → 9 not taken.
✓ Branch 3 → 4 taken 6467 times.
✗ Branch 3 → 9 not taken.
✓ Branch 4 → 5 taken 6467 times.
✗ Branch 4 → 9 not taken.
6467 const QualType thisType = entry->getQualType().removeReferenceWrapper().toNonConst();
499
1/2
✓ Branch 5 → 6 taken 6467 times.
✗ Branch 5 → 10 not taken.
12934 return implicitlyCallStructMethod(thisType, methodName, args, node);
500 }
501
502 /**
503 * Prepare the generation of a call to a method of a given struct
504 *
505 * @param thisType Struct type to call the method on
506 * @param methodName Name of the method to call
507 * @param args Provided arguments by the caller
508 * @param node AST node
509 */
510 7434 Function *TypeChecker::implicitlyCallStructMethod(QualType thisType, const std::string &methodName, const ArgList &args,
511 const ASTNode *node) const {
512
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 7434 times.
7434 assert(thisType.is(TY_STRUCT));
513 7434 Scope *matchScope = thisType.getBodyScope();
514
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 7434 times.
7434 assert(matchScope->type == ScopeType::STRUCT);
515
516 // Search for dtor
517
2/2
✓ Branch 9 → 10 taken 5443 times.
✓ Branch 9 → 12 taken 1991 times.
7434 if (matchScope->isImportedBy(rootScope))
518
1/2
✓ Branch 10 → 11 taken 5443 times.
✗ Branch 10 → 18 not taken.
5443 thisType = mapLocalTypeToImportedScopeType(matchScope, thisType);
519
1/2
✓ Branch 13 → 14 taken 7434 times.
✗ Branch 13 → 19 not taken.
7434 return FunctionManager::match(matchScope, methodName, thisType, args, {}, true, node);
520 }
521
522 /**
523 * Prepare the generation of a call to the copy ctor of a given struct
524 *
525 * @param entry Symbol entry to use as 'this' pointer for the copy ctor call
526 * @param node Current AST node
527 */
528 Function *TypeChecker::implicitlyCallStructCopyCtor(const SymbolTableEntry *entry, const ASTNode *node) const {
529 assert(entry != nullptr && entry->getQualType().is(TY_STRUCT));
530 return implicitlyCallStructCopyCtor(entry->getQualType(), node);
531 }
532
533 /**
534 * Prepare the generation of a call to the copy ctor of a given struct
535 *
536 * @param thisType Struct type to call the copy ctor on
537 * @param node Current AST node
538 */
539 957 Function *TypeChecker::implicitlyCallStructCopyCtor(const QualType &thisType, const ASTNode *node) const {
540
2/4
✓ Branch 2 → 3 taken 957 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 957 times.
✗ Branch 3 → 19 not taken.
957 const QualType argType = thisType.removeReferenceWrapper().toConstRef(node);
541
1/2
✓ Branch 7 → 8 taken 957 times.
✗ Branch 7 → 20 not taken.
2871 const ArgList args = {{argType, false /* we always have an entry here */}};
542
2/4
✓ Branch 11 → 12 taken 957 times.
✗ Branch 11 → 27 not taken.
✓ Branch 12 → 13 taken 957 times.
✗ Branch 12 → 25 not taken.
1914 return implicitlyCallStructMethod(thisType, CTOR_FUNCTION_NAME, args, node);
543 957 }
544
545 /**
546 * Prepare the generation of a call to the move ctor of a given struct
547 *
548 * @param entry Symbol entry to use as 'this' pointer for the move ctor call
549 * @param node Current AST node
550 */
551 Function *TypeChecker::implicitlyCallStructMoveCtor(const SymbolTableEntry *entry, const ASTNode *node) const {
552 assert(entry != nullptr && entry->getQualType().is(TY_STRUCT));
553 return implicitlyCallStructMoveCtor(entry->getQualType(), node);
554 }
555
556 /**
557 * Prepare the generation of a call to the move ctor of a given struct
558 *
559 * @param thisType Struct type to call the move ctor on
560 * @param node Current AST node
561 */
562 Function *TypeChecker::implicitlyCallStructMoveCtor(const QualType &thisType, const ASTNode *node) const {
563 const QualType argType = thisType.removeReferenceWrapper().toNonConst().toRef(node);
564 const ArgList args = {{argType, false /* we always have an entry here */}};
565 return implicitlyCallStructMethod(thisType, CTOR_FUNCTION_NAME, args, node);
566 }
567
568 /**
569 * Prepare the generation of a call to the dtor of a given struct
570 *
571 * @param entry Symbol entry to use as 'this' pointer for the dtor call
572 * @param node StmtLstNode for the current scope
573 */
574 6467 void TypeChecker::implicitlyCallStructDtor(SymbolTableEntry *entry, StmtLstNode *node) const {
575 // Add the dtor to the stmt list node to call it later in codegen
576
4/6
✓ Branch 5 → 6 taken 6467 times.
✗ Branch 5 → 16 not taken.
✓ Branch 6 → 7 taken 6467 times.
✗ Branch 6 → 14 not taken.
✓ Branch 10 → 11 taken 4626 times.
✓ Branch 10 → 13 taken 1841 times.
19401 if (Function *dtor = implicitlyCallStructMethod(entry, DTOR_FUNCTION_NAME, {}, node))
577
2/4
✓ Branch 11 → 12 taken 4626 times.
✗ Branch 11 → 23 not taken.
✓ Branch 12 → 13 taken 4626 times.
✗ Branch 12 → 23 not taken.
4626 node->resourcesToCleanup.at(manIdx).dtorFunctionsToCall.emplace_back(entry, dtor);
578 6467 }
579
580 /**
581 * Prepare the generation of a call to the deallocate function for a heap-allocated variable
582 *
583 * @param node Current AST node for error messages
584 */
585 69 void TypeChecker::implicitlyCallDeallocate(const ASTNode *node) const {
586
1/2
✓ Branch 2 → 3 taken 69 times.
✗ Branch 2 → 46 not taken.
69 const SourceFile *memoryRT = sourceFile->requestRuntimeModule(MEMORY_RT);
587
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 69 times.
69 assert(memoryRT != nullptr);
588 69 Scope *matchScope = memoryRT->globalScope.get();
589 // Set dealloc function to used
590
1/2
✓ Branch 6 → 7 taken 69 times.
✗ Branch 6 → 46 not taken.
69 const QualType thisType(TY_DYN);
591
3/6
✓ Branch 7 → 8 taken 69 times.
✗ Branch 7 → 28 not taken.
✓ Branch 8 → 9 taken 69 times.
✗ Branch 8 → 28 not taken.
✓ Branch 9 → 10 taken 69 times.
✗ Branch 9 → 28 not taken.
69 QualType bytePtrRefType = QualType(TY_BYTE).toPtr(node).toRef(node);
592
1/2
✓ Branch 10 → 11 taken 69 times.
✗ Branch 10 → 46 not taken.
69 bytePtrRefType.makeHeap();
593
1/2
✓ Branch 14 → 15 taken 69 times.
✗ Branch 14 → 30 not taken.
138 const ArgList args = {{bytePtrRefType, false /* we always have the field as storage */}};
594
2/4
✓ Branch 19 → 20 taken 69 times.
✗ Branch 19 → 37 not taken.
✓ Branch 20 → 21 taken 69 times.
✗ Branch 20 → 35 not taken.
207 Function *deallocFct = FunctionManager::match(matchScope, FCT_NAME_DEALLOC, thisType, args, {}, true, node);
595
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 69 times.
69 assert(deallocFct != nullptr);
596 69 deallocFct->used = true;
597 69 }
598
599 /**
600 * Consider calls to destructors for the given scope
601 *
602 * @param node StmtLstNode for the current scope
603 */
604 58618 void TypeChecker::doScopeCleanup(StmtLstNode *node) const {
605 // Get all variables, that are approved for de-allocation
606
1/2
✓ Branch 2 → 3 taken 58618 times.
✗ Branch 2 → 69 not taken.
58618 std::vector<SymbolTableEntry *> vars = currentScope->getVarsGoingOutOfScope();
607 // Sort by reverse declaration order
608 78387 const auto comp = [this](const SymbolTableEntry *a, const SymbolTableEntry *b) {
609 19769 const ASTNode *aDeclNode = a->declNode;
610 19769 const ASTNode *bDeclNode = b->declNode;
611 // Primary sort criteria is the code location
612
3/4
✓ Branch 2 → 3 taken 19769 times.
✗ Branch 2 → 15 not taken.
✓ Branch 3 → 4 taken 19478 times.
✓ Branch 3 → 10 taken 291 times.
19769 if (aDeclNode->codeLoc != bDeclNode->codeLoc)
613
2/2
✓ Branch 4 → 5 taken 809 times.
✓ Branch 4 → 6 taken 18669 times.
38956 return aDeclNode->codeLoc > bDeclNode->codeLoc;
614 // Secondary sort criteria is the node id
615
2/4
✓ Branch 10 → 11 taken 291 times.
✗ Branch 10 → 15 not taken.
✓ Branch 11 → 12 taken 291 times.
✗ Branch 11 → 15 not taken.
291 return resourceManager.nodeToNodeId[aDeclNode] > resourceManager.nodeToNodeId[bDeclNode];
616 58618 };
617
1/2
✓ Branch 3 → 4 taken 58618 times.
✗ Branch 3 → 67 not taken.
58618 std::ranges::stable_sort(vars, comp);
618 // Call the dtor of each variable. We call the dtor in reverse declaration order
619
2/2
✓ Branch 62 → 6 taken 25787 times.
✓ Branch 62 → 63 taken 58618 times.
143023 for (SymbolTableEntry *var : vars) {
620 // Check if we have a heap-allocated pointer
621
10/14
✓ Branch 8 → 9 taken 25787 times.
✗ Branch 8 → 65 not taken.
✓ Branch 9 → 10 taken 25787 times.
✗ Branch 9 → 65 not taken.
✓ Branch 10 → 11 taken 2270 times.
✓ Branch 10 → 15 taken 23517 times.
✓ Branch 11 → 12 taken 2270 times.
✗ Branch 11 → 65 not taken.
✓ Branch 12 → 13 taken 2270 times.
✗ Branch 12 → 65 not taken.
✓ Branch 13 → 14 taken 2079 times.
✓ Branch 13 → 15 taken 191 times.
✓ Branch 16 → 17 taken 2079 times.
✓ Branch 16 → 36 taken 23708 times.
25787 if (var->getQualType().isHeap() && var->getQualType().isOneOf({TY_PTR, TY_STRING, TY_FUNCTION, TY_PROCEDURE})) {
622 // The memory runtime is ignored, because it manually allocates to avoid circular dependencies.
623 // Same goes for the string runtime.
624
8/10
✓ Branch 17 → 18 taken 2079 times.
✗ Branch 17 → 66 not taken.
✓ Branch 20 → 21 taken 1679 times.
✓ Branch 20 → 25 taken 400 times.
✓ Branch 21 → 22 taken 1679 times.
✗ Branch 21 → 66 not taken.
✓ Branch 24 → 25 taken 1510 times.
✓ Branch 24 → 26 taken 169 times.
✓ Branch 27 → 28 taken 1910 times.
✓ Branch 27 → 29 taken 169 times.
5837 if (sourceFile->isMemoryRT() || sourceFile->isStringRT())
625 1910 continue;
626 // If the local variable currently does not have the ownership, we must not deallocate its memory
627
3/4
✓ Branch 30 → 31 taken 169 times.
✗ Branch 30 → 66 not taken.
✓ Branch 31 → 32 taken 100 times.
✓ Branch 31 → 33 taken 69 times.
169 if (!var->getLifecycle().isInOwningState())
628 100 continue;
629
630
1/2
✓ Branch 33 → 34 taken 69 times.
✗ Branch 33 → 66 not taken.
69 implicitlyCallDeallocate(node); // Required to request the memory runtime
631
2/4
✓ Branch 34 → 35 taken 69 times.
✗ Branch 34 → 66 not taken.
✓ Branch 35 → 36 taken 69 times.
✗ Branch 35 → 66 not taken.
69 node->resourcesToCleanup.at(manIdx).heapVarsToFree.push_back(var);
632 }
633 // Only generate dtor call for structs and if not omitted
634
8/10
✓ Branch 36 → 37 taken 23777 times.
✗ Branch 36 → 66 not taken.
✓ Branch 37 → 38 taken 23777 times.
✗ Branch 37 → 66 not taken.
✓ Branch 38 → 39 taken 6774 times.
✓ Branch 38 → 40 taken 17003 times.
✓ Branch 39 → 40 taken 307 times.
✓ Branch 39 → 41 taken 6467 times.
✓ Branch 42 → 43 taken 17310 times.
✓ Branch 42 → 44 taken 6467 times.
23777 if (!var->getQualType().is(TY_STRUCT) || var->omitDtorCall)
635 17310 continue;
636 // Variable must be either initialized or a struct field
637
5/8
✓ Branch 45 → 46 taken 6467 times.
✗ Branch 45 → 66 not taken.
✓ Branch 46 → 47 taken 22 times.
✓ Branch 46 → 49 taken 6445 times.
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 49 taken 22 times.
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 52 taken 6467 times.
6467 if (!var->getLifecycle().isInitialized() && var->scope->type != ScopeType::STRUCT)
638 continue;
639 // Call dtor
640
1/2
✓ Branch 52 → 53 taken 6467 times.
✗ Branch 52 → 66 not taken.
6467 implicitlyCallStructDtor(var, node);
641 }
642 58618 }
643
644 } // namespace spice::compiler
645