GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 93.4% 299 / 0 / 320
Functions: 84.2% 16 / 0 / 19
Branches: 58.2% 427 / 0 / 734

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 3550 void TypeChecker::createDefaultStructMethod(const Struct &spiceStruct, const std::string &entryName, const std::string &name,
30 const ParamList &params) const {
31 3550 Scope *structScope = spiceStruct.scope;
32 3550 ASTNode *node = spiceStruct.declNode;
33 3550 const SymbolTableEntry *structEntry = spiceStruct.entry;
34
1/2
✓ Branch 2 → 3 taken 3550 times.
✗ Branch 2 → 97 not taken.
3550 const QualType &structType = structEntry->getQualType();
35
3/6
✓ Branch 3 → 4 taken 3550 times.
✗ Branch 3 → 68 not taken.
✓ Branch 4 → 5 taken 3550 times.
✗ Branch 4 → 68 not taken.
✓ Branch 5 → 6 taken 3550 times.
✗ Branch 5 → 66 not taken.
3550 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + name;
36
37 // Procedure type
38
1/2
✓ Branch 7 → 8 taken 3550 times.
✗ Branch 7 → 95 not taken.
3550 QualType procedureType(TY_PROCEDURE);
39
1/2
✓ Branch 8 → 9 taken 3550 times.
✗ Branch 8 → 95 not taken.
3550 procedureType.makePublic(); // Always public
40
41 // Insert symbol for function into the symbol table
42
1/2
✓ Branch 9 → 10 taken 3550 times.
✗ Branch 9 → 95 not taken.
3550 SymbolTableEntry *procEntry = structScope->insert(entryName, structEntry->declNode);
43
1/2
✓ Branch 12 → 13 taken 3550 times.
✗ Branch 12 → 95 not taken.
3550 procEntry->updateType(procedureType, true);
44
45 // Add to external name registry
46
1/2
✓ Branch 13 → 14 taken 3550 times.
✗ Branch 13 → 95 not taken.
3550 sourceFile->addNameRegistryEntry(fqFctName, TY_PROCEDURE, procEntry, structScope, true);
47
48 // Create the default method
49
1/2
✓ Branch 14 → 15 taken 3550 times.
✗ Branch 14 → 95 not taken.
3550 const std::vector<GenericType> templateTypes = spiceStruct.templateTypes;
50
1/2
✓ Branch 15 → 16 taken 3550 times.
✗ Branch 15 → 93 not taken.
3550 const QualType returnType(TY_DYN);
51
4/8
✓ Branch 16 → 17 taken 3550 times.
✗ Branch 16 → 77 not taken.
✓ Branch 17 → 18 taken 3550 times.
✗ Branch 17 → 74 not taken.
✓ Branch 18 → 19 taken 3550 times.
✗ Branch 18 → 71 not taken.
✓ Branch 19 → 20 taken 3550 times.
✗ Branch 19 → 69 not taken.
3550 Function defaultMethod(name, procEntry, structType, returnType, params, templateTypes, structEntry->declNode);
52 3550 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 628 times.
✓ Branch 46 → 47 taken 3550 times.
7728 for (const GenericType &templateType : templateTypes)
58
7/10
✓ Branch 27 → 28 taken 628 times.
✗ Branch 27 → 78 not taken.
✓ Branch 28 → 29 taken 4 times.
✓ Branch 28 → 32 taken 624 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 624 times.
628 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 3550 times.
✗ Branch 47 → 83 not taken.
✓ Branch 48 → 49 taken 3550 times.
✗ Branch 48 → 81 not taken.
3550 Scope *procScope = structScope->createChildScope(defaultMethod.getScopeName(), ScopeType::FUNC_PROC_BODY, &node->codeLoc);
63 3550 defaultMethod.bodyScope = procScope;
64
65 // Create 'this' symbol in the function scope
66
1/2
✓ Branch 52 → 53 taken 3550 times.
✗ Branch 52 → 86 not taken.
10650 SymbolTableEntry *thisEntry = procScope->insert(THIS_VARIABLE_NAME, node);
67
2/4
✓ Branch 58 → 59 taken 3550 times.
✗ Branch 58 → 90 not taken.
✓ Branch 59 → 60 taken 3550 times.
✗ Branch 59 → 90 not taken.
3550 thisEntry->updateType(structType.toPtr(node), true);
68 3550 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 3550 times.
✗ Branch 60 → 91 not taken.
✓ Branch 61 → 62 taken 3550 times.
✗ Branch 61 → 91 not taken.
3550 FunctionManager::insert(structScope, defaultMethod, structEntry->declNode->getFctManifestations(name));
72 3550 }
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 3067 void TypeChecker::createDefaultCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
84
1/2
✓ Branch 2 → 3 taken 3067 times.
✗ Branch 2 → 4 not taken.
3067 const auto node = spice_pointer_cast<StructDefNode *>(spiceStruct.declNode);
85
2/4
✓ Branch 9 → 10 taken 3067 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 3067 times.
✗ Branch 10 → 12 not taken.
3067 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
86
87 // Abort if the struct already has a user-defined constructor
88 3067 const SymbolTableEntry *structEntry = spiceStruct.entry;
89
1/2
✓ Branch 13 → 14 taken 3067 times.
✗ Branch 13 → 132 not taken.
3067 const QualType &structType = structEntry->getQualType();
90
3/6
✓ Branch 14 → 15 taken 3067 times.
✗ Branch 14 → 105 not taken.
✓ Branch 15 → 16 taken 3067 times.
✗ Branch 15 → 105 not taken.
✓ Branch 16 → 17 taken 3067 times.
✗ Branch 16 → 103 not taken.
3067 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + CTOR_FUNCTION_NAME;
91
3/4
✓ Branch 18 → 19 taken 3067 times.
✗ Branch 18 → 130 not taken.
✓ Branch 19 → 20 taken 2723 times.
✓ Branch 19 → 21 taken 344 times.
3067 if (sourceFile->getNameRegistryEntry(fqFctName))
92 2723 return;
93
94 // Check if we have fields, that require us to do anything in the ctor
95
1/2
✓ Branch 21 → 22 taken 344 times.
✗ Branch 21 → 130 not taken.
344 const size_t fieldCount = structScope->getFieldCount();
96 344 bool hasFieldsWithDefaultValue = false;
97 344 bool hasFieldsToConstruct = false;
98
2/2
✓ Branch 81 → 23 taken 809 times.
✓ Branch 81 → 82 taken 325 times.
1134 for (size_t i = 0; i < fieldCount; i++) {
99
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 809 times.
809 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
100
2/4
✓ Branch 28 → 29 taken 809 times.
✗ Branch 28 → 31 not taken.
✓ Branch 29 → 30 taken 809 times.
✗ Branch 29 → 31 not taken.
809 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
101
102
1/2
✓ Branch 32 → 33 taken 809 times.
✗ Branch 32 → 118 not taken.
809 QualType fieldType = fieldSymbol->getQualType();
103
5/8
✓ Branch 33 → 34 taken 809 times.
✗ Branch 33 → 118 not taken.
✓ Branch 34 → 35 taken 105 times.
✓ Branch 34 → 38 taken 704 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 105 times.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 809 times.
809 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 809 times.
✗ Branch 41 → 118 not taken.
✓ Branch 42 → 43 taken 8 times.
✓ Branch 42 → 44 taken 801 times.
809 if (fieldType.isRef())
108 19 return;
109
110
3/4
✓ Branch 44 → 45 taken 801 times.
✗ Branch 44 → 46 not taken.
✓ Branch 47 → 48 taken 751 times.
✓ Branch 47 → 49 taken 50 times.
801 if (const auto fieldNode = dynamic_cast<FieldNode *>(fieldSymbol->declNode)) {
111 751 hasFieldsWithDefaultValue |= fieldNode->defaultValue != nullptr;
112 } else {
113
2/4
✓ Branch 49 → 50 taken 50 times.
✗ Branch 49 → 51 not taken.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 50 times.
50 assert(dynamic_cast<DataTypeNode *>(fieldSymbol->declNode) != nullptr);
114 }
115
116
3/4
✓ Branch 54 → 55 taken 801 times.
✗ Branch 54 → 118 not taken.
✓ Branch 55 → 56 taken 76 times.
✓ Branch 55 → 79 taken 725 times.
801 if (fieldType.is(TY_STRUCT)) {
117
1/2
✓ Branch 56 → 57 taken 76 times.
✗ Branch 56 → 118 not taken.
76 Scope *bodyScope = fieldType.getBodyScope();
118 // Check if we are required to call a ctor
119
1/2
✓ Branch 57 → 58 taken 76 times.
✗ Branch 57 → 118 not taken.
76 const bool isCtorCallRequired = !fieldType.isTriviallyConstructible(node);
120 // While the outer struct is still a generic preset, a field type that is itself generic (e.g. Inner<T, bool>,
121 // the internal field of Outer<T>) cannot be matched to a concrete ctor yet. Since a default ctor takes no
122 // arguments, matching it against the still-generic 'this' type would partially substantiate a bogus
123 // manifestation (e.g. Inner<K, bool>, with K left generic) that is wrongly treated as fully substantiated
124 // later on and crashes name mangling during IR generation (see #1255). In that case we only record that the
125 // field has a default ctor via a direct scan; the concrete ctor is matched later, per manifestation, in
126 // createCtorBodyPreamble.
127
3/4
✓ Branch 58 → 59 taken 76 times.
✗ Branch 58 → 118 not taken.
✓ Branch 59 → 60 taken 9 times.
✓ Branch 59 → 65 taken 67 times.
76 if (fieldType.hasAnyGenericParts()) {
128
1/2
✓ Branch 60 → 61 taken 9 times.
✗ Branch 60 → 118 not taken.
9 const bool hasDefaultCtor = FunctionManager::hasDefaultCtor(bodyScope);
129
1/4
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 64 taken 9 times.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
9 if (!hasDefaultCtor && isCtorCallRequired)
130 return;
131 9 hasFieldsToConstruct |= hasDefaultCtor;
132 } else {
133 // Lookup ctor function
134
2/4
✓ Branch 69 → 70 taken 67 times.
✗ Branch 69 → 108 not taken.
✓ Branch 70 → 71 taken 67 times.
✗ Branch 70 → 106 not taken.
201 const Function *ctorFct = FunctionManager::match(bodyScope, CTOR_FUNCTION_NAME, fieldType, {}, {}, true, node);
135 // If we are required to construct, but no constructor is found, we can't generate a default ctor for the outer struct
136
4/4
✓ Branch 75 → 76 taken 25 times.
✓ Branch 75 → 78 taken 42 times.
✓ Branch 76 → 77 taken 11 times.
✓ Branch 76 → 78 taken 14 times.
67 if (!ctorFct && isCtorCallRequired)
137 11 return;
138 56 hasFieldsToConstruct |= ctorFct != nullptr;
139 }
140 }
141 }
142
143 // If we don't have any fields, that require us to do anything in the ctor, we can skip it
144
6/6
✓ Branch 82 → 83 taken 232 times.
✓ Branch 82 → 86 taken 93 times.
✓ Branch 83 → 84 taken 197 times.
✓ Branch 83 → 86 taken 35 times.
✓ Branch 84 → 85 taken 164 times.
✓ Branch 84 → 86 taken 33 times.
325 if (!hasFieldsWithDefaultValue && !hasFieldsToConstruct && !node->emitVTable)
145 164 return;
146
147 // Create the default ctor function
148
1/2
✓ Branch 86 → 87 taken 161 times.
✗ Branch 86 → 130 not taken.
161 const std::string entryName = Function::getSymbolTableEntryNameDefaultCtor(node->codeLoc);
149
2/4
✓ Branch 90 → 91 taken 161 times.
✗ Branch 90 → 121 not taken.
✓ Branch 91 → 92 taken 161 times.
✗ Branch 91 → 119 not taken.
483 createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, {});
150
2/2
✓ Branch 98 → 99 taken 161 times.
✓ Branch 98 → 101 taken 2906 times.
3067 }
151
152 /**
153 * Checks if the given struct scope already has a user-defined constructor and creates a default one if not.
154 *
155 * For generating a default copy ctor, the following conditions need to be met:
156 * - No user-defined copy ctor
157 *
158 * @param spiceStruct Struct instance
159 * @param structScope Scope of the struct
160 */
161 3067 void TypeChecker::createDefaultCopyCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
162
1/2
✓ Branch 2 → 3 taken 3067 times.
✗ Branch 2 → 4 not taken.
3067 const auto node = spice_pointer_cast<const StructDefNode *>(spiceStruct.declNode);
163
2/4
✓ Branch 9 → 10 taken 3067 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 3067 times.
✗ Branch 10 → 12 not taken.
3067 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
164
165 // Abort if the struct already has a user-defined copy constructor
166
1/2
✓ Branch 13 → 14 taken 3067 times.
✗ Branch 13 → 128 not taken.
3067 const QualType structType = spiceStruct.entry->getQualType();
167
3/4
✓ Branch 14 → 15 taken 3067 times.
✗ Branch 14 → 128 not taken.
✓ Branch 15 → 16 taken 391 times.
✓ Branch 15 → 17 taken 2676 times.
3067 if (FunctionManager::hasCopyCtor(structScope))
168 1216 return;
169
170 // Check if we have fields, that require us to do anything in the ctor
171
1/2
✓ Branch 17 → 18 taken 2676 times.
✗ Branch 17 → 128 not taken.
2676 const size_t fieldCount = structScope->getFieldCount();
172 2676 bool copyCtorRequired = false;
173
2/2
✓ Branch 74 → 19 taken 4713 times.
✓ Branch 74 → 75 taken 2676 times.
7389 for (size_t i = 0; i < fieldCount; i++) {
174
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 4713 times.
4713 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
175
2/4
✓ Branch 24 → 25 taken 4713 times.
✗ Branch 24 → 27 not taken.
✓ Branch 25 → 26 taken 4713 times.
✗ Branch 25 → 27 not taken.
4713 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
176
177
1/2
✓ Branch 28 → 29 taken 4713 times.
✗ Branch 28 → 113 not taken.
4713 QualType fieldType = fieldSymbol->getQualType();
178
7/8
✓ Branch 29 → 30 taken 4713 times.
✗ Branch 29 → 113 not taken.
✓ Branch 30 → 31 taken 948 times.
✓ Branch 30 → 34 taken 3765 times.
✓ Branch 32 → 33 taken 2 times.
✓ Branch 32 → 34 taken 946 times.
✓ Branch 35 → 36 taken 2 times.
✓ Branch 35 → 37 taken 4711 times.
4713 if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty())
179
1/2
✓ Branch 36 → 37 taken 2 times.
✗ Branch 36 → 113 not taken.
2 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode);
180
181 // If the field is of type struct, check if this struct has a copy ctor that has to be called
182
3/4
✓ Branch 37 → 38 taken 4713 times.
✗ Branch 37 → 113 not taken.
✓ Branch 38 → 39 taken 1966 times.
✓ Branch 38 → 67 taken 2747 times.
4713 if (fieldType.is(TY_STRUCT)) {
183 // A field whose copy is non-trivial forces the outer struct to have a copy ctor as well.
184
1/2
✓ Branch 39 → 40 taken 1966 times.
✗ Branch 39 → 113 not taken.
1966 const bool fieldRequiresCopyCtor = !fieldType.isTriviallyCopyable(node);
185 // While the outer struct is still a generic preset, a field type that is itself generic (e.g. Inner<T, bool>,
186 // the internal field of Outer<T>) cannot be matched to a concrete copy ctor yet. Matching it via the
187 // substantiating FunctionManager::match() below can partially substantiate a bogus manifestation (e.g.
188 // Inner<K, bool>, with K left generic) that is wrongly treated as fully substantiated later on and crashes
189 // name mangling during IR generation (see #1255). This can happen even though the copy ctor call carries an
190 // argument of the same (still-generic) field type, whenever the field struct's own generic type name
191 // collides with the outer struct's generic type name (e.g. both named 'V', as in Set<V> wrapping
192 // RedBlackTree<K,V>). The concrete copy ctor is matched later, per manifestation, in
193 // createCopyCtorBodyPreamble, and its result here is not otherwise consulted while the field type is still
194 // generic (fieldRequiresCopyCtor already reflects copy-ctor existence via the non-substantiating
195 // isTriviallyCopyable check above) - so skip the substantiating match entirely in that case.
196
3/4
✓ Branch 40 → 41 taken 1966 times.
✗ Branch 40 → 113 not taken.
✓ Branch 41 → 42 taken 1822 times.
✓ Branch 41 → 66 taken 144 times.
1966 if (!fieldType.hasAnyGenericParts()) {
197
1/2
✓ Branch 42 → 43 taken 1822 times.
✗ Branch 42 → 112 not taken.
1822 Scope *bodyScope = fieldType.getBodyScope();
198
2/4
✓ Branch 43 → 44 taken 1822 times.
✗ Branch 43 → 99 not taken.
✓ Branch 47 → 48 taken 1822 times.
✗ Branch 47 → 95 not taken.
3644 const ArgList args = {{fieldType.toConstRef(node), false /* we always have the field as storage */}};
199
2/4
✓ Branch 52 → 53 taken 1822 times.
✗ Branch 52 → 103 not taken.
✓ Branch 53 → 54 taken 1822 times.
✗ Branch 53 → 101 not taken.
5466 const Function *ctorFct = FunctionManager::match(bodyScope, CTOR_FUNCTION_NAME, fieldType, args, {}, true, node);
200 // If the field requires a copy ctor, but we proved none exists, we cannot synthesize one for the outer struct.
201
3/4
✓ Branch 57 → 58 taken 200 times.
✓ Branch 57 → 60 taken 1622 times.
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 60 taken 200 times.
1822 if (!ctorFct && fieldRequiresCopyCtor)
202 return;
203
1/2
✓ Branch 62 → 63 taken 1822 times.
✗ Branch 62 → 65 not taken.
1822 }
204 1966 copyCtorRequired |= fieldRequiresCopyCtor;
205 }
206
207 // If we have an owning heap pointer, we need to do a memcpy of the heap storage and therefore need a default copy ctor
208
3/4
✓ Branch 67 → 68 taken 4713 times.
✗ Branch 67 → 113 not taken.
✓ Branch 68 → 69 taken 52 times.
✓ Branch 68 → 73 taken 4661 times.
4713 if (fieldType.isHeap()) {
209
2/4
✓ Branch 69 → 70 taken 52 times.
✗ Branch 69 → 113 not taken.
✗ Branch 70 → 71 not taken.
✓ Branch 70 → 72 taken 52 times.
52 assert(fieldType.isPtr());
210 52 copyCtorRequired = true;
211 }
212 }
213
214 // If we don't have any fields, that require us to do anything in the copy ctor, we can skip it
215
4/4
✓ Branch 75 → 76 taken 1116 times.
✓ Branch 75 → 78 taken 1560 times.
✓ Branch 76 → 77 taken 825 times.
✓ Branch 76 → 78 taken 291 times.
2676 if (!copyCtorRequired && !node->emitVTable)
216 825 return;
217
218 // Create the default copy ctor function
219
1/2
✓ Branch 78 → 79 taken 1851 times.
✗ Branch 78 → 128 not taken.
1851 const std::string entryName = Function::getSymbolTableEntryNameDefaultCopyCtor(node->codeLoc);
220
2/4
✓ Branch 79 → 80 taken 1851 times.
✗ Branch 79 → 117 not taken.
✓ Branch 82 → 83 taken 1851 times.
✗ Branch 82 → 114 not taken.
5553 const ParamList paramTypes = {{structType.toConstRef(node), false}};
221
2/4
✓ Branch 86 → 87 taken 1851 times.
✗ Branch 86 → 120 not taken.
✓ Branch 87 → 88 taken 1851 times.
✗ Branch 87 → 118 not taken.
1851 createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, paramTypes);
222 1851 }
223
224 /**
225 * Checks if the given struct scope already has a user-defined move constructor and creates a default one if not.
226 *
227 * For generating a default move ctor, the following conditions need to be met:
228 * - No user-defined move ctor
229 * - At least one field requires non-trivial moving (heap pointer or struct field with move ctor)
230 *
231 * @param spiceStruct Struct instance
232 * @param structScope Scope of the struct
233 */
234 3067 void TypeChecker::createDefaultMoveCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
235
1/2
✓ Branch 2 → 3 taken 3067 times.
✗ Branch 2 → 4 not taken.
3067 const auto node = spice_pointer_cast<const StructDefNode *>(spiceStruct.declNode);
236
2/4
✓ Branch 9 → 10 taken 3067 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 3067 times.
✗ Branch 10 → 12 not taken.
3067 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
237
238 // Skip generic struct presets - we only synthesize a default move ctor for fully substantiated structs.
239 // Reason: each manifestation gets its own scope (deep-copied from the generic), and the implicit move ctor
240 // body's preamble writes to field lifecycle state via bodyScope->parent. If we created the move ctor on the
241 // generic struct, the deep-copied Function in the manifestation's scope would still carry a bodyScope pointer
242 // into the generic struct's scope, causing the preamble to update fields in the wrong scope.
243
3/4
✓ Branch 13 → 14 taken 3067 times.
✗ Branch 13 → 92 not taken.
✓ Branch 14 → 15 taken 678 times.
✓ Branch 14 → 16 taken 2389 times.
3067 if (!spiceStruct.isFullySubstantiated())
244 3057 return;
245
246 // Abort if the struct already has a user-defined move constructor.
247 // We can't just call FunctionManager::lookup with a non-const ref arg here, since the lookup permits
248 // const-param-to-non-const-arg matching ("constify") and would return the copy ctor (if one exists) as
249 // a false positive. Instead, check the function manifestations directly for a single-self-non-const-ref ctor.
250
3/4
✓ Branch 16 → 17 taken 2389 times.
✗ Branch 16 → 92 not taken.
✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 19 taken 2385 times.
2389 if (FunctionManager::hasMoveCtor(structScope))
251 4 return;
252
253 // Abort if the struct has a user-defined copy ctor. Reason: in Spice the move-vs-copy tie-breaker picks the
254 // non-const-ref (move) candidate for non-const lvalue arguments, so silently auto-generating a move ctor on
255 // top of a user-defined copy ctor would change the behavior of existing `T b = T(a)` call sites (a's heap
256 // contents would be stolen instead of deep-copied). Users that want a default move ctor in addition to their
257 // own copy ctor can write an empty `p T.ctor(T& other) {}` to opt in. We still synthesize the default move
258 // ctor when only the auto-generated copy ctor exists (heap-owning struct with no user ctors) - the user did
259 // not write any binding semantics in that case, so picking move for non-const lvalues is fine.
260
3/4
✓ Branch 19 → 20 taken 2385 times.
✗ Branch 19 → 92 not taken.
✓ Branch 20 → 21 taken 226 times.
✓ Branch 20 → 22 taken 2159 times.
2385 if (FunctionManager::hasUserCopyCtor(structScope))
261 226 return;
262
1/2
✓ Branch 22 → 23 taken 2159 times.
✗ Branch 22 → 92 not taken.
2159 const QualType structType = spiceStruct.entry->getQualType();
263
264 // Check if we have fields, that require us to do anything in the move ctor
265
1/2
✓ Branch 23 → 24 taken 2159 times.
✗ Branch 23 → 92 not taken.
2159 const size_t fieldCount = structScope->getFieldCount();
266 2159 bool moveCtorRequired = false;
267
2/2
✓ Branch 55 → 25 taken 3376 times.
✓ Branch 55 → 56 taken 2159 times.
5535 for (size_t i = 0; i < fieldCount; i++) {
268
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 3376 times.
3376 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
269
2/4
✓ Branch 30 → 31 taken 3376 times.
✗ Branch 30 → 33 not taken.
✓ Branch 31 → 32 taken 3376 times.
✗ Branch 31 → 33 not taken.
3376 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
270
271
1/2
✓ Branch 34 → 35 taken 3376 times.
✗ Branch 34 → 76 not taken.
3376 QualType fieldType = fieldSymbol->getQualType();
272
3/8
✓ Branch 35 → 36 taken 3376 times.
✗ Branch 35 → 76 not taken.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 40 taken 3376 times.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 3376 times.
3376 if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty())
273 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode);
274
275 // If the field is of type struct, check whether the field's struct has its own move ctor. We use
276 // findMoveCtor (a direct scan of the manifestations) rather than FunctionManager::match here for two
277 // reasons: (1) match permits const-param-to-non-const-arg "constify" matching and would return the
278 // field's copy ctor as a false positive, and (2) match populates the lookup cache, which would inflate
279 // the per-struct cache-miss counts even for structs that have nothing to do with move ctors.
280
3/4
✓ Branch 43 → 44 taken 3376 times.
✗ Branch 43 → 76 not taken.
✓ Branch 44 → 45 taken 1683 times.
✓ Branch 44 → 48 taken 1693 times.
3376 if (fieldType.is(TY_STRUCT)) {
281
1/2
✓ Branch 45 → 46 taken 1683 times.
✗ Branch 45 → 76 not taken.
1683 Scope *bodyScope = fieldType.getBodyScope();
282
1/2
✓ Branch 46 → 47 taken 1683 times.
✗ Branch 46 → 76 not taken.
1683 moveCtorRequired |= FunctionManager::findMoveCtor(bodyScope) != nullptr;
283 }
284
285 // If we have an owning heap pointer, we transfer ownership of the heap storage
286
3/4
✓ Branch 48 → 49 taken 3376 times.
✗ Branch 48 → 76 not taken.
✓ Branch 49 → 50 taken 7 times.
✓ Branch 49 → 54 taken 3369 times.
3376 if (fieldType.isHeap()) {
287
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());
288 7 moveCtorRequired = true;
289 }
290 }
291
292 // If we don't have any fields that require us to do anything special in the move ctor, we can skip it.
293 // Unlike the copy ctor we do NOT generate a default move ctor just because the struct emits a vtable - the
294 // vtable pointer is part of the struct layout and gets shallow-copied along with everything else, no
295 // special move handling needed. Users that want to move a vtable-bearing struct fall back to the default
296 // copy ctor.
297
2/2
✓ Branch 56 → 57 taken 2149 times.
✓ Branch 56 → 58 taken 10 times.
2159 if (!moveCtorRequired)
298 2149 return;
299
300 // Create the default move ctor function
301
1/2
✓ Branch 58 → 59 taken 10 times.
✗ Branch 58 → 92 not taken.
10 const std::string entryName = Function::getSymbolTableEntryNameDefaultMoveCtor(node->codeLoc);
302
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}};
303
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);
304 10 }
305
306 /**
307 * Checks if the given struct scope already has a user-defined destructor and creates a default one if not.
308 *
309 * For generating a default dtor, the following conditions need to be met:
310 * - No user-defined dtor
311 *
312 * @param spiceStruct Struct instance
313 * @param structScope Scope of the struct
314 */
315 3067 void TypeChecker::createDefaultDtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
316 3067 const ASTNode *node = spiceStruct.declNode;
317
2/4
✓ Branch 2 → 3 taken 3067 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 3067 times.
✗ Branch 3 → 5 not taken.
3067 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
318
319 // Abort if the struct already has a user-defined destructor
320 3067 const SymbolTableEntry *structEntry = spiceStruct.entry;
321
1/2
✓ Branch 6 → 7 taken 3067 times.
✗ Branch 6 → 157 not taken.
3067 const QualType &structType = structEntry->getQualType();
322
3/6
✓ Branch 7 → 8 taken 3067 times.
✗ Branch 7 → 111 not taken.
✓ Branch 8 → 9 taken 3067 times.
✗ Branch 8 → 111 not taken.
✓ Branch 9 → 10 taken 3067 times.
✗ Branch 9 → 109 not taken.
3067 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + DTOR_FUNCTION_NAME;
323
3/4
✓ Branch 11 → 12 taken 3067 times.
✗ Branch 11 → 155 not taken.
✓ Branch 12 → 13 taken 590 times.
✓ Branch 12 → 14 taken 2477 times.
3067 if (sourceFile->getNameRegistryEntry(fqFctName))
324 590 return;
325
326 // Check we have field types, that require use to do anything in the destructor
327
1/2
✓ Branch 14 → 15 taken 2477 times.
✗ Branch 14 → 155 not taken.
2477 const size_t fieldCount = structScope->getFieldCount();
328 2477 bool hasFieldsToDeAllocate = false;
329 2477 bool hasFieldsToDestruct = false;
330
2/2
✓ Branch 54 → 16 taken 4457 times.
✓ Branch 54 → 55 taken 2477 times.
6934 for (size_t i = 0; i < fieldCount; i++) {
331
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 4457 times.
4457 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
332
2/4
✓ Branch 21 → 22 taken 4457 times.
✗ Branch 21 → 24 not taken.
✓ Branch 22 → 23 taken 4457 times.
✗ Branch 22 → 24 not taken.
4457 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
333
334
1/2
✓ Branch 25 → 26 taken 4457 times.
✗ Branch 25 → 124 not taken.
4457 QualType fieldType = fieldSymbol->getQualType();
335
7/8
✓ Branch 26 → 27 taken 4457 times.
✗ Branch 26 → 124 not taken.
✓ Branch 27 → 28 taken 875 times.
✓ Branch 27 → 31 taken 3582 times.
✓ Branch 29 → 30 taken 2 times.
✓ Branch 29 → 31 taken 873 times.
✓ Branch 32 → 33 taken 2 times.
✓ Branch 32 → 34 taken 4455 times.
4457 if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty())
336
1/2
✓ Branch 33 → 34 taken 2 times.
✗ Branch 33 → 124 not taken.
2 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode);
337
338
1/2
✓ Branch 34 → 35 taken 4457 times.
✗ Branch 34 → 124 not taken.
4457 hasFieldsToDeAllocate |= fieldType.needsDeAllocation();
339
3/4
✓ Branch 35 → 36 taken 4457 times.
✗ Branch 35 → 124 not taken.
✓ Branch 36 → 37 taken 1839 times.
✓ Branch 36 → 53 taken 2618 times.
4457 if (fieldType.is(TY_STRUCT)) {
340
1/2
✓ Branch 37 → 38 taken 1839 times.
✗ Branch 37 → 124 not taken.
1839 Scope *fieldScope = fieldType.getBodyScope();
341 // While the outer struct is still a generic preset, a field type that is itself generic (e.g.
342 // RedBlackTree<V, bool>, the internal field of Set<V>) cannot be matched to a concrete dtor yet. Since a
343 // dtor takes no arguments, matching it against the still-generic 'this' type would partially substantiate a
344 // bogus manifestation (e.g. RedBlackTree<K, bool>, with K left generic) that is wrongly treated as fully
345 // substantiated later on and crashes name mangling during IR generation. In that case we only record that
346 // the field has a dtor via a direct scan; the concrete dtor is matched later, per manifestation, in
347 // createDtorBodyPreamble.
348
3/4
✓ Branch 38 → 39 taken 1839 times.
✗ Branch 38 → 124 not taken.
✓ Branch 39 → 40 taken 134 times.
✓ Branch 39 → 42 taken 1705 times.
1839 if (fieldType.hasAnyGenericParts()) {
349
1/2
✓ Branch 40 → 41 taken 134 times.
✗ Branch 40 → 124 not taken.
134 hasFieldsToDestruct |= FunctionManager::hasDtor(fieldScope);
350 } else {
351 // Lookup dtor function
352
2/4
✓ Branch 46 → 47 taken 1705 times.
✗ Branch 46 → 114 not taken.
✓ Branch 47 → 48 taken 1705 times.
✗ Branch 47 → 112 not taken.
5115 const Function *dtorFct = FunctionManager::match(fieldScope, DTOR_FUNCTION_NAME, fieldType, {}, {}, true, node);
353 1705 hasFieldsToDestruct |= dtorFct != nullptr;
354
1/2
✓ Branch 52 → 53 taken 1705 times.
✗ Branch 52 → 124 not taken.
1705 requestRevisitIfRequired(dtorFct);
355 }
356 }
357 }
358
359 // If we don't have any fields, that require us to do anything in the dtor, we can skip it
360
4/4
✓ Branch 55 → 56 taken 2405 times.
✓ Branch 55 → 58 taken 72 times.
✓ Branch 56 → 57 taken 949 times.
✓ Branch 56 → 58 taken 1456 times.
2477 if (!hasFieldsToDeAllocate && !hasFieldsToDestruct)
361 949 return;
362
363 // Create the default dtor function
364
1/2
✓ Branch 58 → 59 taken 1528 times.
✗ Branch 58 → 155 not taken.
1528 const std::string entryName = Function::getSymbolTableEntryNameDefaultDtor(node->codeLoc);
365
2/4
✓ Branch 62 → 63 taken 1528 times.
✗ Branch 62 → 127 not taken.
✓ Branch 63 → 64 taken 1528 times.
✗ Branch 63 → 125 not taken.
4584 createDefaultStructMethod(spiceStruct, entryName, DTOR_FUNCTION_NAME, {});
366
367 // Request memory runtime if we have fields, that are allocated on the heap
368 // The string runtime does not use it, but allocates manually to avoid circular dependencies
369
6/8
✓ Branch 67 → 68 taken 72 times.
✓ Branch 67 → 73 taken 1456 times.
✓ Branch 68 → 69 taken 72 times.
✗ Branch 68 → 153 not taken.
✓ Branch 71 → 72 taken 72 times.
✗ Branch 71 → 73 not taken.
✓ Branch 74 → 75 taken 72 times.
✓ Branch 74 → 101 taken 1456 times.
1600 if (hasFieldsToDeAllocate && !sourceFile->isStringRT()) {
370
1/2
✓ Branch 75 → 76 taken 72 times.
✗ Branch 75 → 152 not taken.
72 const SourceFile *memoryRT = sourceFile->requestRuntimeModule(MEMORY_RT);
371
1/2
✗ Branch 76 → 77 not taken.
✓ Branch 76 → 78 taken 72 times.
72 assert(memoryRT != nullptr);
372 72 Scope *matchScope = memoryRT->globalScope.get();
373 // Set dealloc function to used
374
1/2
✓ Branch 79 → 80 taken 72 times.
✗ Branch 79 → 152 not taken.
72 const QualType thisType(TY_DYN);
375
3/6
✓ Branch 80 → 81 taken 72 times.
✗ Branch 80 → 134 not taken.
✓ Branch 81 → 82 taken 72 times.
✗ Branch 81 → 134 not taken.
✓ Branch 82 → 83 taken 72 times.
✗ Branch 82 → 134 not taken.
72 QualType bytePtrRefType = QualType(TY_BYTE).toPtr(node).toRef(node);
376
1/2
✓ Branch 83 → 84 taken 72 times.
✗ Branch 83 → 152 not taken.
72 bytePtrRefType.makeHeap();
377
1/2
✓ Branch 87 → 88 taken 72 times.
✗ Branch 87 → 136 not taken.
144 const ArgList args = {{bytePtrRefType, false /* we always have the field as storage */}};
378
2/4
✓ Branch 92 → 93 taken 72 times.
✗ Branch 92 → 143 not taken.
✓ Branch 93 → 94 taken 72 times.
✗ Branch 93 → 141 not taken.
216 Function *deallocFct = FunctionManager::match(matchScope, FCT_NAME_DEALLOC, thisType, args, {}, true, node);
379
1/2
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 99 taken 72 times.
72 assert(deallocFct != nullptr);
380 72 deallocFct->used = true;
381 72 }
382
2/2
✓ Branch 104 → 105 taken 1528 times.
✓ Branch 104 → 107 taken 1539 times.
3067 }
383
384 /**
385 * Prepare the generation of the ctor body preamble. This preamble is used to initialize the VTable, construct or initialize
386 * fields.
387 */
388 6053 void TypeChecker::createCtorBodyPreamble(const Scope *bodyScope) const {
389 // Retrieve struct scope
390 6053 Scope *structScope = bodyScope->parent;
391
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 6053 times.
6053 assert(structScope != nullptr);
392
393 6053 const size_t fieldCount = structScope->getFieldCount();
394
2/2
✓ Branch 54 → 6 taken 14698 times.
✓ Branch 54 → 55 taken 6053 times.
20751 for (size_t i = 0; i < fieldCount; i++) {
395
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 14698 times.
14698 SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
396
3/6
✓ Branch 11 → 12 taken 14698 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 14698 times.
✗ Branch 12 → 70 not taken.
✓ Branch 13 → 14 taken 14698 times.
✗ Branch 13 → 15 not taken.
14698 assert(fieldSymbol != nullptr && fieldSymbol->isField());
397
2/2
✓ Branch 16 → 17 taken 1415 times.
✓ Branch 16 → 18 taken 13283 times.
14698 if (fieldSymbol->isImplicitField)
398 2866 continue;
399
400
1/2
✓ Branch 18 → 19 taken 13283 times.
✗ Branch 18 → 70 not taken.
13283 QualType fieldType = fieldSymbol->getQualType();
401
3/4
✓ Branch 19 → 20 taken 13283 times.
✗ Branch 19 → 70 not taken.
✓ Branch 20 → 21 taken 3024 times.
✓ Branch 20 → 22 taken 10259 times.
13283 if (fieldType.hasAnyGenericParts())
402
1/2
✓ Branch 21 → 22 taken 3024 times.
✗ Branch 21 → 70 not taken.
3024 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
403
404
3/4
✓ Branch 22 → 23 taken 13283 times.
✗ Branch 22 → 70 not taken.
✓ Branch 23 → 24 taken 3157 times.
✓ Branch 23 → 50 taken 10126 times.
13283 if (fieldType.is(TY_STRUCT)) {
405
1/2
✓ Branch 24 → 25 taken 3157 times.
✗ Branch 24 → 26 not taken.
3157 const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode);
406 // Match ctor function, create the concrete manifestation and set it to used
407
1/2
✓ Branch 31 → 32 taken 3157 times.
✗ Branch 31 → 70 not taken.
3157 Scope *matchScope = fieldType.getBodyScope();
408
2/4
✓ Branch 36 → 37 taken 3157 times.
✗ Branch 36 → 58 not taken.
✓ Branch 37 → 38 taken 3157 times.
✗ Branch 37 → 56 not taken.
9471 const Function *spiceFunc = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode);
409
2/2
✓ Branch 42 → 43 taken 1664 times.
✓ Branch 42 → 47 taken 1493 times.
3157 if (spiceFunc != nullptr)
410
3/6
✓ Branch 43 → 44 taken 1664 times.
✗ Branch 43 → 68 not taken.
✓ Branch 44 → 45 taken 1664 times.
✗ Branch 44 → 68 not taken.
✓ Branch 45 → 46 taken 1664 times.
✗ Branch 45 → 68 not taken.
1664 fieldSymbol->updateType(fieldType.getWithBodyScope(spiceFunc->thisType.getBodyScope()), true);
411
3/4
✓ Branch 47 → 48 taken 1493 times.
✗ Branch 47 → 70 not taken.
✓ Branch 48 → 49 taken 1451 times.
✓ Branch 48 → 50 taken 42 times.
1493 else if (!fieldType.isTriviallyConstructible(fieldNode))
412 1451 continue;
413 }
414
415
1/2
✓ Branch 50 → 51 taken 11832 times.
✗ Branch 50 → 69 not taken.
11832 fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode);
416 }
417 6053 }
418
419 /**
420 * Prepare the generation of the copy ctor body preamble. This preamble is used to initialize the VTable, construct or initialize
421 * fields.
422 */
423 3170 void TypeChecker::createCopyCtorBodyPreamble(const Scope *bodyScope) const {
424 // Retrieve struct scope
425 3170 Scope *structScope = bodyScope->parent;
426
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3170 times.
3170 assert(structScope != nullptr);
427
428 3170 const size_t fieldCount = structScope->getFieldCount();
429
2/2
✓ Branch 64 → 6 taken 4690 times.
✓ Branch 64 → 65 taken 3170 times.
7860 for (size_t i = 0; i < fieldCount; i++) {
430
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 4690 times.
4690 SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
431
3/6
✓ Branch 11 → 12 taken 4690 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 4690 times.
✗ Branch 12 → 86 not taken.
✓ Branch 13 → 14 taken 4690 times.
✗ Branch 13 → 15 not taken.
4690 assert(fieldSymbol != nullptr && fieldSymbol->isField());
432
2/2
✓ Branch 16 → 17 taken 255 times.
✓ Branch 16 → 18 taken 4435 times.
4690 if (fieldSymbol->isImplicitField)
433 255 continue;
434
435
1/2
✓ Branch 18 → 19 taken 4435 times.
✗ Branch 18 → 86 not taken.
4435 QualType fieldType = fieldSymbol->getQualType();
436
2/4
✓ Branch 19 → 20 taken 4435 times.
✗ Branch 19 → 86 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 4435 times.
4435 if (fieldType.hasAnyGenericParts())
437 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
438
439
3/4
✓ Branch 22 → 23 taken 4435 times.
✗ Branch 22 → 86 not taken.
✓ Branch 23 → 24 taken 3567 times.
✓ Branch 23 → 60 taken 868 times.
4435 if (fieldType.is(TY_STRUCT)) {
440
1/2
✓ Branch 24 → 25 taken 3567 times.
✗ Branch 24 → 26 not taken.
3567 const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode);
441 // Match ctor function, create the concrete manifestation and set it to used
442
1/2
✓ Branch 31 → 32 taken 3567 times.
✗ Branch 31 → 84 not taken.
3567 Scope *matchScope = fieldType.getBodyScope();
443
2/4
✓ Branch 32 → 33 taken 3567 times.
✗ Branch 32 → 70 not taken.
✓ Branch 36 → 37 taken 3567 times.
✗ Branch 36 → 66 not taken.
7134 const ArgList args = {{fieldType.toConstRef(fieldNode), false /* we always have the field as storage */}};
444
2/4
✓ Branch 41 → 42 taken 3567 times.
✗ Branch 41 → 74 not taken.
✓ Branch 42 → 43 taken 3567 times.
✗ Branch 42 → 72 not taken.
10701 const Function *copyCtorFct = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, fieldType, args, {}, false, fieldNode);
445
2/2
✓ Branch 46 → 47 taken 3468 times.
✓ Branch 46 → 51 taken 99 times.
3567 if (copyCtorFct != nullptr)
446
3/6
✓ Branch 47 → 48 taken 3468 times.
✗ Branch 47 → 81 not taken.
✓ Branch 48 → 49 taken 3468 times.
✗ Branch 48 → 81 not taken.
✓ Branch 49 → 50 taken 3468 times.
✗ Branch 49 → 81 not taken.
3468 fieldSymbol->updateType(fieldType.getWithBodyScope(copyCtorFct->thisType.getBodyScope()), true);
447
2/4
✓ Branch 51 → 52 taken 99 times.
✗ Branch 51 → 82 not taken.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 99 times.
99 else if (!fieldType.isTriviallyCopyable(fieldNode))
448 continue;
449
1/2
✓ Branch 56 → 57 taken 3567 times.
✗ Branch 56 → 59 not taken.
3567 }
450
451
1/2
✓ Branch 60 → 61 taken 4435 times.
✗ Branch 60 → 85 not taken.
4435 fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode);
452 }
453 3170 }
454
455 /**
456 * Prepare the generation of the move ctor body preamble. This preamble is used to initialize the VTable, move-construct or
457 * shallow-copy fields and transfer ownership of heap allocations.
458 */
459 17 void TypeChecker::createMoveCtorBodyPreamble(const Scope *bodyScope) const {
460 // Retrieve struct scope
461 17 Scope *structScope = bodyScope->parent;
462
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 17 times.
17 assert(structScope != nullptr);
463
464 17 const size_t fieldCount = structScope->getFieldCount();
465
2/2
✓ Branch 37 → 6 taken 21 times.
✓ Branch 37 → 38 taken 17 times.
38 for (size_t i = 0; i < fieldCount; i++) {
466
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 21 times.
21 SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
467
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());
468
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 21 times.
21 if (fieldSymbol->isImplicitField)
469 continue;
470
471
1/2
✓ Branch 18 → 19 taken 21 times.
✗ Branch 18 → 41 not taken.
21 QualType fieldType = fieldSymbol->getQualType();
472
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())
473 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
474
475
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)) {
476 // Find a move ctor on the field's struct. We use findMoveCtor rather than FunctionManager::match to
477 // avoid a constify-based false positive returning the copy ctor. findMoveCtor doesn't mark the
478 // function as used, so do that explicitly to ensure the inner move ctor's body is actually emitted.
479
1/2
✓ Branch 24 → 25 taken 6 times.
✗ Branch 24 → 41 not taken.
6 Scope *matchScope = fieldType.getBodyScope();
480
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)) {
481 6 moveCtorFct->used = true;
482
1/2
✓ Branch 27 → 28 taken 6 times.
✗ Branch 27 → 29 not taken.
6 if (moveCtorFct->entry)
483 6 moveCtorFct->entry->used = true;
484
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);
485 }
486 }
487
488
1/2
✓ Branch 33 → 34 taken 21 times.
✗ Branch 33 → 40 not taken.
21 fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode);
489 }
490 17 }
491
492 /**
493 * Prepare the generation of the dtor body preamble. This preamble is used to destruct all fields and to free all heap fields.
494 */
495 3325 void TypeChecker::createDtorBodyPreamble(const Scope *bodyScope) const {
496 // Retrieve struct scope
497 3325 Scope *structScope = bodyScope->parent;
498
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3325 times.
3325 assert(structScope != nullptr);
499
500 3325 const size_t fieldCount = structScope->getFieldCount();
501
2/2
✓ Branch 46 → 6 taken 5438 times.
✓ Branch 46 → 47 taken 3325 times.
8763 for (size_t i = 0; i < fieldCount; i++) {
502 5438 const size_t fieldIdx = fieldCount - 1 - i; // Destruct fields in reverse order
503
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 5438 times.
5438 const SymbolTableEntry *fieldSymbol = structScope->lookupField(fieldIdx);
504
3/6
✓ Branch 11 → 12 taken 5438 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 5438 times.
✗ Branch 12 → 60 not taken.
✓ Branch 13 → 14 taken 5438 times.
✗ Branch 13 → 15 not taken.
5438 assert(fieldSymbol != nullptr && fieldSymbol->isField());
505
2/2
✓ Branch 16 → 17 taken 303 times.
✓ Branch 16 → 18 taken 5135 times.
5438 if (fieldSymbol->isImplicitField)
506 303 continue;
507
508
1/2
✓ Branch 18 → 19 taken 5135 times.
✗ Branch 18 → 60 not taken.
5135 QualType fieldType = fieldSymbol->getQualType();
509
2/4
✓ Branch 19 → 20 taken 5135 times.
✗ Branch 19 → 60 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 5135 times.
5135 if (fieldType.hasAnyGenericParts())
510 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
511
512
3/4
✓ Branch 22 → 23 taken 5135 times.
✗ Branch 22 → 60 not taken.
✓ Branch 23 → 24 taken 3884 times.
✓ Branch 23 → 43 taken 1251 times.
5135 if (fieldType.is(TY_STRUCT)) {
513
1/2
✓ Branch 24 → 25 taken 3884 times.
✗ Branch 24 → 26 not taken.
3884 const auto fieldNode = spice_pointer_cast<const FieldNode *>(fieldSymbol->declNode);
514 // Match ctor function, create the concrete manifestation and set it to used
515
1/2
✓ Branch 31 → 32 taken 3884 times.
✗ Branch 31 → 60 not taken.
3884 Scope *matchScope = fieldType.getBodyScope();
516
2/4
✓ Branch 36 → 37 taken 3884 times.
✗ Branch 36 → 50 not taken.
✓ Branch 37 → 38 taken 3884 times.
✗ Branch 37 → 48 not taken.
11652 FunctionManager::match(matchScope, DTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode);
517 }
518 }
519 3325 }
520
521 /**
522 * Prepare the generation of a call to a method of a given struct
523 *
524 * @param entry Symbol entry to use as 'this' pointer for the method call
525 * @param methodName Name of the method to call
526 * @param args Provided arguments by the caller
527 * @param node AST node
528 */
529 6439 Function *TypeChecker::implicitlyCallStructMethod(const SymbolTableEntry *entry, const std::string &methodName,
530 const ArgList &args, const ASTNode *node) const {
531
3/6
✓ Branch 2 → 3 taken 6439 times.
✗ Branch 2 → 9 not taken.
✓ Branch 3 → 4 taken 6439 times.
✗ Branch 3 → 9 not taken.
✓ Branch 4 → 5 taken 6439 times.
✗ Branch 4 → 9 not taken.
6439 const QualType thisType = entry->getQualType().removeReferenceWrapper().toNonConst();
532
1/2
✓ Branch 5 → 6 taken 6439 times.
✗ Branch 5 → 10 not taken.
12878 return implicitlyCallStructMethod(thisType, methodName, args, node);
533 }
534
535 /**
536 * Prepare the generation of a call to a method of a given struct
537 *
538 * @param thisType Struct type to call the method on
539 * @param methodName Name of the method to call
540 * @param args Provided arguments by the caller
541 * @param node AST node
542 */
543 7486 Function *TypeChecker::implicitlyCallStructMethod(QualType thisType, const std::string &methodName, const ArgList &args,
544 const ASTNode *node) const {
545
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 7486 times.
7486 assert(thisType.is(TY_STRUCT));
546 7486 Scope *matchScope = thisType.getBodyScope();
547
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 7486 times.
7486 assert(matchScope->type == ScopeType::STRUCT);
548
549 // Search for dtor
550
2/2
✓ Branch 9 → 10 taken 5410 times.
✓ Branch 9 → 12 taken 2076 times.
7486 if (matchScope->isImportedBy(rootScope))
551
1/2
✓ Branch 10 → 11 taken 5410 times.
✗ Branch 10 → 18 not taken.
5410 thisType = mapLocalTypeToImportedScopeType(matchScope, thisType);
552
1/2
✓ Branch 13 → 14 taken 7486 times.
✗ Branch 13 → 19 not taken.
7486 return FunctionManager::match(matchScope, methodName, thisType, args, {}, true, node);
553 }
554
555 /**
556 * Prepare the generation of a call to the copy ctor of a given struct
557 *
558 * @param entry Symbol entry to use as 'this' pointer for the copy ctor call
559 * @param node Current AST node
560 */
561 Function *TypeChecker::implicitlyCallStructCopyCtor(const SymbolTableEntry *entry, const ASTNode *node) const {
562 assert(entry != nullptr && entry->getQualType().is(TY_STRUCT));
563 return implicitlyCallStructCopyCtor(entry->getQualType(), node);
564 }
565
566 /**
567 * Prepare the generation of a call to the copy ctor of a given struct
568 *
569 * @param thisType Struct type to call the copy ctor on
570 * @param node Current AST node
571 */
572 1036 Function *TypeChecker::implicitlyCallStructCopyCtor(const QualType &thisType, const ASTNode *node) const {
573
2/4
✓ Branch 2 → 3 taken 1036 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 1036 times.
✗ Branch 3 → 19 not taken.
1036 const QualType argType = thisType.removeReferenceWrapper().toConstRef(node);
574
1/2
✓ Branch 7 → 8 taken 1036 times.
✗ Branch 7 → 20 not taken.
3108 const ArgList args = {{argType, false /* we always have an entry here */}};
575
2/4
✓ Branch 11 → 12 taken 1036 times.
✗ Branch 11 → 27 not taken.
✓ Branch 12 → 13 taken 1036 times.
✗ Branch 12 → 25 not taken.
2072 return implicitlyCallStructMethod(thisType, CTOR_FUNCTION_NAME, args, node);
576 1036 }
577
578 /**
579 * Prepare the generation of a call to the move ctor of a given struct
580 *
581 * @param entry Symbol entry to use as 'this' pointer for the move ctor call
582 * @param node Current AST node
583 */
584 Function *TypeChecker::implicitlyCallStructMoveCtor(const SymbolTableEntry *entry, const ASTNode *node) const {
585 assert(entry != nullptr && entry->getQualType().is(TY_STRUCT));
586 return implicitlyCallStructMoveCtor(entry->getQualType(), node);
587 }
588
589 /**
590 * Prepare the generation of a call to the move ctor of a given struct
591 *
592 * @param thisType Struct type to call the move ctor on
593 * @param node Current AST node
594 */
595 Function *TypeChecker::implicitlyCallStructMoveCtor(const QualType &thisType, const ASTNode *node) const {
596 const QualType argType = thisType.removeReferenceWrapper().toNonConst().toRef(node);
597 const ArgList args = {{argType, false /* we always have an entry here */}};
598 return implicitlyCallStructMethod(thisType, CTOR_FUNCTION_NAME, args, node);
599 }
600
601 /**
602 * Prepare the generation of a call to the dtor of a given struct
603 *
604 * @param entry Symbol entry to use as 'this' pointer for the dtor call
605 * @param node StmtLstNode for the current scope
606 */
607 6439 void TypeChecker::implicitlyCallStructDtor(SymbolTableEntry *entry, StmtLstNode *node) const {
608 // Add the dtor to the stmt list node to call it later in codegen
609
4/6
✓ Branch 5 → 6 taken 6439 times.
✗ Branch 5 → 16 not taken.
✓ Branch 6 → 7 taken 6439 times.
✗ Branch 6 → 14 not taken.
✓ Branch 10 → 11 taken 5500 times.
✓ Branch 10 → 13 taken 939 times.
19317 if (Function *dtor = implicitlyCallStructMethod(entry, DTOR_FUNCTION_NAME, {}, node))
610
2/4
✓ Branch 11 → 12 taken 5500 times.
✗ Branch 11 → 23 not taken.
✓ Branch 12 → 13 taken 5500 times.
✗ Branch 12 → 23 not taken.
5500 node->resourcesToCleanup.at(manIdx).dtorFunctionsToCall.emplace_back(entry, dtor);
611 6439 }
612
613 /**
614 * Prepare the generation of a call to the deallocate function for a heap-allocated variable
615 *
616 * @param node Current AST node for error messages
617 */
618 426 void TypeChecker::implicitlyCallDeallocate(const ASTNode *node) const {
619
1/2
✓ Branch 2 → 3 taken 426 times.
✗ Branch 2 → 46 not taken.
426 const SourceFile *memoryRT = sourceFile->requestRuntimeModule(MEMORY_RT);
620
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 426 times.
426 assert(memoryRT != nullptr);
621 426 Scope *matchScope = memoryRT->globalScope.get();
622 // Set dealloc function to used
623
1/2
✓ Branch 6 → 7 taken 426 times.
✗ Branch 6 → 46 not taken.
426 const QualType thisType(TY_DYN);
624
3/6
✓ Branch 7 → 8 taken 426 times.
✗ Branch 7 → 28 not taken.
✓ Branch 8 → 9 taken 426 times.
✗ Branch 8 → 28 not taken.
✓ Branch 9 → 10 taken 426 times.
✗ Branch 9 → 28 not taken.
426 QualType bytePtrRefType = QualType(TY_BYTE).toPtr(node).toRef(node);
625
1/2
✓ Branch 10 → 11 taken 426 times.
✗ Branch 10 → 46 not taken.
426 bytePtrRefType.makeHeap();
626
1/2
✓ Branch 14 → 15 taken 426 times.
✗ Branch 14 → 30 not taken.
852 const ArgList args = {{bytePtrRefType, false /* we always have the field as storage */}};
627
2/4
✓ Branch 19 → 20 taken 426 times.
✗ Branch 19 → 37 not taken.
✓ Branch 20 → 21 taken 426 times.
✗ Branch 20 → 35 not taken.
1278 Function *deallocFct = FunctionManager::match(matchScope, FCT_NAME_DEALLOC, thisType, args, {}, true, node);
628
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 426 times.
426 assert(deallocFct != nullptr);
629 426 deallocFct->used = true;
630 426 }
631
632 /**
633 * Consider calls to destructors for the given scope
634 *
635 * @param node StmtLstNode for the current scope
636 */
637 66724 void TypeChecker::doScopeCleanup(StmtLstNode *node) const {
638 // Get all variables, that are approved for de-allocation
639
1/2
✓ Branch 2 → 3 taken 66724 times.
✗ Branch 2 → 69 not taken.
66724 std::vector<SymbolTableEntry *> vars = currentScope->getVarsGoingOutOfScope();
640 // Sort by reverse declaration order
641 89431 const auto comp = [this](const SymbolTableEntry *a, const SymbolTableEntry *b) {
642 22707 const ASTNode *aDeclNode = a->declNode;
643 22707 const ASTNode *bDeclNode = b->declNode;
644 // Primary sort criteria is the code location
645
3/4
✓ Branch 2 → 3 taken 22707 times.
✗ Branch 2 → 15 not taken.
✓ Branch 3 → 4 taken 22428 times.
✓ Branch 3 → 10 taken 279 times.
22707 if (aDeclNode->codeLoc != bDeclNode->codeLoc)
646
2/2
✓ Branch 4 → 5 taken 804 times.
✓ Branch 4 → 6 taken 21624 times.
44856 return aDeclNode->codeLoc > bDeclNode->codeLoc;
647 // Secondary sort criteria is the node id
648
2/4
✓ Branch 10 → 11 taken 279 times.
✗ Branch 10 → 15 not taken.
✓ Branch 11 → 12 taken 279 times.
✗ Branch 11 → 15 not taken.
279 return resourceManager.nodeToNodeId[aDeclNode] > resourceManager.nodeToNodeId[bDeclNode];
649 66724 };
650
1/2
✓ Branch 3 → 4 taken 66724 times.
✗ Branch 3 → 67 not taken.
66724 std::ranges::stable_sort(vars, comp);
651 // Call the dtor of each variable. We call the dtor in reverse declaration order
652
2/2
✓ Branch 62 → 6 taken 29901 times.
✓ Branch 62 → 63 taken 66724 times.
163349 for (SymbolTableEntry *var : vars) {
653 // Check if we have a heap-allocated pointer
654
10/14
✓ Branch 8 → 9 taken 29901 times.
✗ Branch 8 → 65 not taken.
✓ Branch 9 → 10 taken 29901 times.
✗ Branch 9 → 65 not taken.
✓ Branch 10 → 11 taken 3263 times.
✓ Branch 10 → 15 taken 26638 times.
✓ Branch 11 → 12 taken 3263 times.
✗ Branch 11 → 65 not taken.
✓ Branch 12 → 13 taken 3263 times.
✗ Branch 12 → 65 not taken.
✓ Branch 13 → 14 taken 2633 times.
✓ Branch 13 → 15 taken 630 times.
✓ Branch 16 → 17 taken 2633 times.
✓ Branch 16 → 36 taken 27268 times.
29901 if (var->getQualType().isHeap() && var->getQualType().isOneOf({TY_PTR, TY_STRING, TY_FUNCTION, TY_PROCEDURE})) {
655 // The memory runtime is ignored, because it manually allocates to avoid circular dependencies.
656 // Same goes for the string runtime.
657
8/10
✓ Branch 17 → 18 taken 2633 times.
✗ Branch 17 → 66 not taken.
✓ Branch 20 → 21 taken 2169 times.
✓ Branch 20 → 25 taken 464 times.
✓ Branch 21 → 22 taken 2169 times.
✗ Branch 21 → 66 not taken.
✓ Branch 24 → 25 taken 1600 times.
✓ Branch 24 → 26 taken 569 times.
✓ Branch 27 → 28 taken 2064 times.
✓ Branch 27 → 29 taken 569 times.
7435 if (sourceFile->isMemoryRT() || sourceFile->isStringRT())
658 2064 continue;
659 // If the local variable currently does not have the ownership, we must not deallocate its memory
660
3/4
✓ Branch 30 → 31 taken 569 times.
✗ Branch 30 → 66 not taken.
✓ Branch 31 → 32 taken 143 times.
✓ Branch 31 → 33 taken 426 times.
569 if (!var->getLifecycle().isInOwningState())
661 143 continue;
662
663
1/2
✓ Branch 33 → 34 taken 426 times.
✗ Branch 33 → 66 not taken.
426 implicitlyCallDeallocate(node); // Required to request the memory runtime
664
2/4
✓ Branch 34 → 35 taken 426 times.
✗ Branch 34 → 66 not taken.
✓ Branch 35 → 36 taken 426 times.
✗ Branch 35 → 66 not taken.
426 node->resourcesToCleanup.at(manIdx).heapVarsToFree.push_back(var);
665 }
666 // Only generate dtor call for structs and if not omitted
667
8/10
✓ Branch 36 → 37 taken 27694 times.
✗ Branch 36 → 66 not taken.
✓ Branch 37 → 38 taken 27694 times.
✗ Branch 37 → 66 not taken.
✓ Branch 38 → 39 taken 6775 times.
✓ Branch 38 → 40 taken 20919 times.
✓ Branch 39 → 40 taken 336 times.
✓ Branch 39 → 41 taken 6439 times.
✓ Branch 42 → 43 taken 21255 times.
✓ Branch 42 → 44 taken 6439 times.
27694 if (!var->getQualType().is(TY_STRUCT) || var->omitDtorCall)
668 21255 continue;
669 // Variable must be either initialized or a struct field
670
5/8
✓ Branch 45 → 46 taken 6439 times.
✗ Branch 45 → 66 not taken.
✓ Branch 46 → 47 taken 166 times.
✓ Branch 46 → 49 taken 6273 times.
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 49 taken 166 times.
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 52 taken 6439 times.
6439 if (!var->getLifecycle().isInitialized() && var->scope->type != ScopeType::STRUCT)
671 continue;
672 // Call dtor
673
1/2
✓ Branch 52 → 53 taken 6439 times.
✗ Branch 52 → 66 not taken.
6439 implicitlyCallStructDtor(var, node);
674 }
675 66724 }
676
677 } // namespace spice::compiler
678