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 3680 void TypeChecker::createDefaultStructMethod(const Struct &spiceStruct, const std::string &entryName, const std::string &name,
30 const ParamList &params) const {
31 3680 Scope *structScope = spiceStruct.scope;
32 3680 ASTNode *node = spiceStruct.declNode;
33 3680 const SymbolTableEntry *structEntry = spiceStruct.entry;
34
1/2
✓ Branch 2 → 3 taken 3680 times.
✗ Branch 2 → 97 not taken.
3680 const QualType &structType = structEntry->getQualType();
35
3/6
✓ Branch 3 → 4 taken 3680 times.
✗ Branch 3 → 68 not taken.
✓ Branch 4 → 5 taken 3680 times.
✗ Branch 4 → 68 not taken.
✓ Branch 5 → 6 taken 3680 times.
✗ Branch 5 → 66 not taken.
3680 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + name;
36
37 // Procedure type
38
1/2
✓ Branch 7 → 8 taken 3680 times.
✗ Branch 7 → 95 not taken.
3680 QualType procedureType(TY_PROCEDURE);
39
1/2
✓ Branch 8 → 9 taken 3680 times.
✗ Branch 8 → 95 not taken.
3680 procedureType.makePublic(); // Always public
40
41 // Insert symbol for function into the symbol table
42
1/2
✓ Branch 9 → 10 taken 3680 times.
✗ Branch 9 → 95 not taken.
3680 SymbolTableEntry *procEntry = structScope->insert(entryName, structEntry->declNode);
43
1/2
✓ Branch 12 → 13 taken 3680 times.
✗ Branch 12 → 95 not taken.
3680 procEntry->updateType(procedureType, true);
44
45 // Add to external name registry
46
1/2
✓ Branch 13 → 14 taken 3680 times.
✗ Branch 13 → 95 not taken.
3680 sourceFile->addNameRegistryEntry(fqFctName, TY_PROCEDURE, procEntry, structScope, true);
47
48 // Create the default method
49
1/2
✓ Branch 14 → 15 taken 3680 times.
✗ Branch 14 → 95 not taken.
3680 const std::vector<GenericType> templateTypes = spiceStruct.templateTypes;
50
1/2
✓ Branch 15 → 16 taken 3680 times.
✗ Branch 15 → 93 not taken.
3680 const QualType returnType(TY_DYN);
51
4/8
✓ Branch 16 → 17 taken 3680 times.
✗ Branch 16 → 77 not taken.
✓ Branch 17 → 18 taken 3680 times.
✗ Branch 17 → 74 not taken.
✓ Branch 18 → 19 taken 3680 times.
✗ Branch 18 → 71 not taken.
✓ Branch 19 → 20 taken 3680 times.
✗ Branch 19 → 69 not taken.
3680 Function defaultMethod(name, procEntry, structType, returnType, params, templateTypes, structEntry->declNode);
52 3680 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 793 times.
✓ Branch 46 → 47 taken 3680 times.
8153 for (const GenericType &templateType : templateTypes)
58
7/10
✓ Branch 27 → 28 taken 793 times.
✗ Branch 27 → 78 not taken.
✓ Branch 28 → 29 taken 4 times.
✓ Branch 28 → 32 taken 789 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 789 times.
793 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 3680 times.
✗ Branch 47 → 83 not taken.
✓ Branch 48 → 49 taken 3680 times.
✗ Branch 48 → 81 not taken.
3680 Scope *procScope = structScope->createChildScope(defaultMethod.getScopeName(), ScopeType::FUNC_PROC_BODY, &node->codeLoc);
63 3680 defaultMethod.bodyScope = procScope;
64
65 // Create 'this' symbol in the function scope
66
1/2
✓ Branch 52 → 53 taken 3680 times.
✗ Branch 52 → 86 not taken.
11040 SymbolTableEntry *thisEntry = procScope->insert(THIS_VARIABLE_NAME, node);
67
2/4
✓ Branch 58 → 59 taken 3680 times.
✗ Branch 58 → 90 not taken.
✓ Branch 59 → 60 taken 3680 times.
✗ Branch 59 → 90 not taken.
3680 thisEntry->updateType(structType.toPtr(node), true);
68 3680 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 3680 times.
✗ Branch 60 → 91 not taken.
✓ Branch 61 → 62 taken 3680 times.
✗ Branch 61 → 91 not taken.
3680 FunctionManager::insert(structScope, defaultMethod, structEntry->declNode->getFctManifestations(name));
72 3680 }
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 2990 void TypeChecker::createDefaultCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
84
1/2
✓ Branch 2 → 3 taken 2990 times.
✗ Branch 2 → 4 not taken.
2990 const auto node = spice_pointer_cast<StructDefNode *>(spiceStruct.declNode);
85
2/4
✓ Branch 9 → 10 taken 2990 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 2990 times.
✗ Branch 10 → 12 not taken.
2990 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
86
87 // Abort if the struct already has a user-defined constructor
88 2990 const SymbolTableEntry *structEntry = spiceStruct.entry;
89
1/2
✓ Branch 13 → 14 taken 2990 times.
✗ Branch 13 → 125 not taken.
2990 const QualType &structType = structEntry->getQualType();
90
3/6
✓ Branch 14 → 15 taken 2990 times.
✗ Branch 14 → 98 not taken.
✓ Branch 15 → 16 taken 2990 times.
✗ Branch 15 → 98 not taken.
✓ Branch 16 → 17 taken 2990 times.
✗ Branch 16 → 96 not taken.
2990 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + CTOR_FUNCTION_NAME;
91
3/4
✓ Branch 18 → 19 taken 2990 times.
✗ Branch 18 → 123 not taken.
✓ Branch 19 → 20 taken 2626 times.
✓ Branch 19 → 21 taken 364 times.
2990 if (sourceFile->getNameRegistryEntry(fqFctName))
92 2626 return;
93
94 // Check if we have fields, that require us to do anything in the ctor
95
1/2
✓ Branch 21 → 22 taken 364 times.
✗ Branch 21 → 123 not taken.
364 const size_t fieldCount = structScope->getFieldCount();
96 364 bool hasFieldsWithDefaultValue = false;
97 364 bool hasFieldsToConstruct = false;
98
2/2
✓ Branch 74 → 23 taken 930 times.
✓ Branch 74 → 75 taken 345 times.
1275 for (size_t i = 0; i < fieldCount; i++) {
99
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 930 times.
930 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
100
2/4
✓ Branch 28 → 29 taken 930 times.
✗ Branch 28 → 31 not taken.
✓ Branch 29 → 30 taken 930 times.
✗ Branch 29 → 31 not taken.
930 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
101
102
1/2
✓ Branch 32 → 33 taken 930 times.
✗ Branch 32 → 111 not taken.
930 QualType fieldType = fieldSymbol->getQualType();
103
5/8
✓ Branch 33 → 34 taken 930 times.
✗ Branch 33 → 111 not taken.
✓ Branch 34 → 35 taken 201 times.
✓ Branch 34 → 38 taken 729 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 201 times.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 930 times.
930 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 930 times.
✗ Branch 41 → 111 not taken.
✓ Branch 42 → 43 taken 8 times.
✓ Branch 42 → 44 taken 922 times.
930 if (fieldType.isRef())
108 19 return;
109
110
3/4
✓ Branch 44 → 45 taken 922 times.
✗ Branch 44 → 46 not taken.
✓ Branch 47 → 48 taken 846 times.
✓ Branch 47 → 49 taken 76 times.
922 if (const auto fieldNode = dynamic_cast<FieldNode *>(fieldSymbol->declNode)) {
111 846 hasFieldsWithDefaultValue |= fieldNode->defaultValue != nullptr;
112 } else {
113
2/4
✓ Branch 49 → 50 taken 76 times.
✗ Branch 49 → 51 not taken.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 76 times.
76 assert(dynamic_cast<DataTypeNode *>(fieldSymbol->declNode) != nullptr);
114 }
115
116
3/4
✓ Branch 54 → 55 taken 922 times.
✗ Branch 54 → 111 not taken.
✓ Branch 55 → 56 taken 72 times.
✓ Branch 55 → 72 taken 850 times.
922 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 216 times.
✓ Branch 75 → 79 taken 129 times.
✓ Branch 76 → 77 taken 184 times.
✓ Branch 76 → 79 taken 32 times.
✓ Branch 77 → 78 taken 162 times.
✓ Branch 77 → 79 taken 22 times.
345 if (!hasFieldsWithDefaultValue && !hasFieldsToConstruct && !node->emitVTable)
131 162 return;
132
133 // Create the default ctor function
134
1/2
✓ Branch 79 → 80 taken 183 times.
✗ Branch 79 → 123 not taken.
183 const std::string entryName = Function::getSymbolTableEntryNameDefaultCtor(node->codeLoc);
135
2/4
✓ Branch 83 → 84 taken 183 times.
✗ Branch 83 → 114 not taken.
✓ Branch 84 → 85 taken 183 times.
✗ Branch 84 → 112 not taken.
549 createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, {});
136
2/2
✓ Branch 91 → 92 taken 183 times.
✓ Branch 91 → 94 taken 2807 times.
2990 }
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 2990 void TypeChecker::createDefaultCopyCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
148
1/2
✓ Branch 2 → 3 taken 2990 times.
✗ Branch 2 → 4 not taken.
2990 const auto node = spice_pointer_cast<const StructDefNode *>(spiceStruct.declNode);
149
2/4
✓ Branch 9 → 10 taken 2990 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 2990 times.
✗ Branch 10 → 12 not taken.
2990 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 2990 times.
✗ Branch 13 → 130 not taken.
2990 const QualType structType = spiceStruct.entry->getQualType();
153
3/4
✓ Branch 14 → 15 taken 2990 times.
✗ Branch 14 → 130 not taken.
✓ Branch 15 → 16 taken 331 times.
✓ Branch 15 → 17 taken 2659 times.
2990 if (FunctionManager::hasCopyCtor(structScope))
154 1122 return;
155
156 // Check if we have fields, that require us to do anything in the ctor
157
1/2
✓ Branch 17 → 18 taken 2659 times.
✗ Branch 17 → 130 not taken.
2659 const size_t fieldCount = structScope->getFieldCount();
158 2659 bool copyCtorRequired = false;
159
2/2
✓ Branch 76 → 19 taken 4762 times.
✓ Branch 76 → 77 taken 2659 times.
7421 for (size_t i = 0; i < fieldCount; i++) {
160
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 4762 times.
4762 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
161
2/4
✓ Branch 24 → 25 taken 4762 times.
✗ Branch 24 → 27 not taken.
✓ Branch 25 → 26 taken 4762 times.
✗ Branch 25 → 27 not taken.
4762 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
162
163
1/2
✓ Branch 28 → 29 taken 4762 times.
✗ Branch 28 → 115 not taken.
4762 QualType fieldType = fieldSymbol->getQualType();
164
5/8
✓ Branch 29 → 30 taken 4762 times.
✗ Branch 29 → 115 not taken.
✓ Branch 30 → 31 taken 997 times.
✓ Branch 30 → 34 taken 3765 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 997 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 4762 times.
4762 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 4762 times.
✗ Branch 37 → 115 not taken.
✓ Branch 38 → 39 taken 1952 times.
✓ Branch 38 → 69 taken 2810 times.
4762 if (fieldType.is(TY_STRUCT)) {
169
1/2
✓ Branch 39 → 40 taken 1952 times.
✗ Branch 39 → 114 not taken.
1952 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 1952 times.
✗ Branch 40 → 114 not taken.
1952 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 1952 times.
✗ Branch 41 → 101 not taken.
✓ Branch 45 → 46 taken 1952 times.
✗ Branch 45 → 97 not taken.
3904 const ArgList args = {{fieldType.toConstRef(node), false /* we always have the field as storage */}};
176
2/4
✓ Branch 50 → 51 taken 1952 times.
✗ Branch 50 → 105 not taken.
✓ Branch 51 → 52 taken 1952 times.
✗ Branch 51 → 103 not taken.
5856 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 287 times.
✓ Branch 55 → 60 taken 1665 times.
✓ Branch 56 → 57 taken 61 times.
✓ Branch 56 → 60 taken 226 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 1952 times.
1952 if (!ctorFct && fieldRequiresCopyCtor && !fieldType.hasAnyGenericParts())
181 return;
182 1952 copyCtorRequired |= fieldRequiresCopyCtor;
183
1/2
✓ Branch 65 → 66 taken 1952 times.
✗ Branch 65 → 68 not taken.
1952 }
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 4762 times.
✗ Branch 69 → 115 not taken.
✓ Branch 70 → 71 taken 86 times.
✓ Branch 70 → 75 taken 4676 times.
4762 if (fieldType.isHeap()) {
187
2/4
✓ Branch 71 → 72 taken 86 times.
✗ Branch 71 → 115 not taken.
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 74 taken 86 times.
86 assert(fieldType.isPtr());
188 86 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 1072 times.
✓ Branch 77 → 80 taken 1587 times.
✓ Branch 78 → 79 taken 791 times.
✓ Branch 78 → 80 taken 281 times.
2659 if (!copyCtorRequired && !node->emitVTable)
194 791 return;
195
196 // Create the default copy ctor function
197
1/2
✓ Branch 80 → 81 taken 1868 times.
✗ Branch 80 → 130 not taken.
1868 const std::string entryName = Function::getSymbolTableEntryNameDefaultCopyCtor(node->codeLoc);
198
2/4
✓ Branch 81 → 82 taken 1868 times.
✗ Branch 81 → 119 not taken.
✓ Branch 84 → 85 taken 1868 times.
✗ Branch 84 → 116 not taken.
5604 const ParamList paramTypes = {{structType.toConstRef(node), false}};
199
2/4
✓ Branch 88 → 89 taken 1868 times.
✗ Branch 88 → 122 not taken.
✓ Branch 89 → 90 taken 1868 times.
✗ Branch 89 → 120 not taken.
1868 createDefaultStructMethod(spiceStruct, entryName, CTOR_FUNCTION_NAME, paramTypes);
200 1868 }
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 2990 void TypeChecker::createDefaultMoveCtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
213
1/2
✓ Branch 2 → 3 taken 2990 times.
✗ Branch 2 → 4 not taken.
2990 const auto node = spice_pointer_cast<const StructDefNode *>(spiceStruct.declNode);
214
2/4
✓ Branch 9 → 10 taken 2990 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 2990 times.
✗ Branch 10 → 12 not taken.
2990 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 2990 times.
✗ Branch 13 → 92 not taken.
✓ Branch 14 → 15 taken 634 times.
✓ Branch 14 → 16 taken 2356 times.
2990 if (!spiceStruct.isFullySubstantiated())
222 2980 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 2356 times.
✗ Branch 16 → 92 not taken.
✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 19 taken 2352 times.
2356 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 2352 times.
✗ Branch 19 → 92 not taken.
✓ Branch 20 → 21 taken 218 times.
✓ Branch 20 → 22 taken 2134 times.
2352 if (FunctionManager::hasUserCopyCtor(structScope))
239 218 return;
240
1/2
✓ Branch 22 → 23 taken 2134 times.
✗ Branch 22 → 92 not taken.
2134 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 2134 times.
✗ Branch 23 → 92 not taken.
2134 const size_t fieldCount = structScope->getFieldCount();
244 2134 bool moveCtorRequired = false;
245
2/2
✓ Branch 55 → 25 taken 3354 times.
✓ Branch 55 → 56 taken 2134 times.
5488 for (size_t i = 0; i < fieldCount; i++) {
246
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 3354 times.
3354 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
247
2/4
✓ Branch 30 → 31 taken 3354 times.
✗ Branch 30 → 33 not taken.
✓ Branch 31 → 32 taken 3354 times.
✗ Branch 31 → 33 not taken.
3354 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
248
249
1/2
✓ Branch 34 → 35 taken 3354 times.
✗ Branch 34 → 76 not taken.
3354 QualType fieldType = fieldSymbol->getQualType();
250
3/8
✓ Branch 35 → 36 taken 3354 times.
✗ Branch 35 → 76 not taken.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 40 taken 3354 times.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 3354 times.
3354 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 3354 times.
✗ Branch 43 → 76 not taken.
✓ Branch 44 → 45 taken 1684 times.
✓ Branch 44 → 48 taken 1670 times.
3354 if (fieldType.is(TY_STRUCT)) {
259
1/2
✓ Branch 45 → 46 taken 1684 times.
✗ Branch 45 → 76 not taken.
1684 Scope *bodyScope = fieldType.getBodyScope();
260
1/2
✓ Branch 46 → 47 taken 1684 times.
✗ Branch 46 → 76 not taken.
1684 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 3354 times.
✗ Branch 48 → 76 not taken.
✓ Branch 49 → 50 taken 7 times.
✓ Branch 49 → 54 taken 3347 times.
3354 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 2124 times.
✓ Branch 56 → 58 taken 10 times.
2134 if (!moveCtorRequired)
276 2124 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 2990 void TypeChecker::createDefaultDtorIfRequired(const Struct &spiceStruct, Scope *structScope) const {
294 2990 const ASTNode *node = spiceStruct.declNode;
295
2/4
✓ Branch 2 → 3 taken 2990 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 2990 times.
✗ Branch 3 → 5 not taken.
2990 assert(structScope != nullptr && structScope->type == ScopeType::STRUCT);
296
297 // Abort if the struct already has a user-defined destructor
298 2990 const SymbolTableEntry *structEntry = spiceStruct.entry;
299
1/2
✓ Branch 6 → 7 taken 2990 times.
✗ Branch 6 → 153 not taken.
2990 const QualType &structType = structEntry->getQualType();
300
3/6
✓ Branch 7 → 8 taken 2990 times.
✗ Branch 7 → 107 not taken.
✓ Branch 8 → 9 taken 2990 times.
✗ Branch 8 → 107 not taken.
✓ Branch 9 → 10 taken 2990 times.
✗ Branch 9 → 105 not taken.
2990 const std::string fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + DTOR_FUNCTION_NAME;
301
3/4
✓ Branch 11 → 12 taken 2990 times.
✗ Branch 11 → 151 not taken.
✓ Branch 12 → 13 taken 317 times.
✓ Branch 12 → 14 taken 2673 times.
2990 if (sourceFile->getNameRegistryEntry(fqFctName))
302 317 return;
303
304 // Check we have field types, that require use to do anything in the destructor
305
1/2
✓ Branch 14 → 15 taken 2673 times.
✗ Branch 14 → 151 not taken.
2673 const size_t fieldCount = structScope->getFieldCount();
306 2673 bool hasFieldsToDeAllocate = false;
307 2673 bool hasFieldsToDestruct = false;
308
2/2
✓ Branch 50 → 16 taken 5036 times.
✓ Branch 50 → 51 taken 2673 times.
7709 for (size_t i = 0; i < fieldCount; i++) {
309
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 5036 times.
5036 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
310
2/4
✓ Branch 21 → 22 taken 5036 times.
✗ Branch 21 → 24 not taken.
✓ Branch 22 → 23 taken 5036 times.
✗ Branch 22 → 24 not taken.
5036 assert(fieldSymbol != nullptr && fieldSymbol->declNode != nullptr);
311
312
1/2
✓ Branch 25 → 26 taken 5036 times.
✗ Branch 25 → 120 not taken.
5036 QualType fieldType = fieldSymbol->getQualType();
313
5/8
✓ Branch 26 → 27 taken 5036 times.
✗ Branch 26 → 120 not taken.
✓ Branch 27 → 28 taken 1198 times.
✓ Branch 27 → 31 taken 3838 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 1198 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 5036 times.
5036 if (fieldType.hasAnyGenericParts() && !spiceStruct.typeMapping.empty())
314 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, spiceStruct.typeMapping, fieldSymbol->declNode);
315
316
1/2
✓ Branch 34 → 35 taken 5036 times.
✗ Branch 34 → 120 not taken.
5036 hasFieldsToDeAllocate |= fieldType.needsDeAllocation();
317
3/4
✓ Branch 35 → 36 taken 5036 times.
✗ Branch 35 → 120 not taken.
✓ Branch 36 → 37 taken 1960 times.
✓ Branch 36 → 49 taken 3076 times.
5036 if (fieldType.is(TY_STRUCT)) {
318
1/2
✓ Branch 37 → 38 taken 1960 times.
✗ Branch 37 → 120 not taken.
1960 Scope *fieldScope = fieldType.getBodyScope();
319 // Lookup dtor function
320
2/4
✓ Branch 42 → 43 taken 1960 times.
✗ Branch 42 → 110 not taken.
✓ Branch 43 → 44 taken 1960 times.
✗ Branch 43 → 108 not taken.
5880 const Function *dtorFct = FunctionManager::match(fieldScope, DTOR_FUNCTION_NAME, fieldType, {}, {}, true, node);
321 1960 hasFieldsToDestruct |= dtorFct != nullptr;
322
1/2
✓ Branch 48 → 49 taken 1960 times.
✗ Branch 48 → 120 not taken.
1960 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 2508 times.
✓ Branch 51 → 54 taken 165 times.
✓ Branch 52 → 53 taken 1054 times.
✓ Branch 52 → 54 taken 1454 times.
2673 if (!hasFieldsToDeAllocate && !hasFieldsToDestruct)
328 1054 return;
329
330 // Create the default dtor function
331
1/2
✓ Branch 54 → 55 taken 1619 times.
✗ Branch 54 → 151 not taken.
1619 const std::string entryName = Function::getSymbolTableEntryNameDefaultDtor(node->codeLoc);
332
2/4
✓ Branch 58 → 59 taken 1619 times.
✗ Branch 58 → 123 not taken.
✓ Branch 59 → 60 taken 1619 times.
✗ Branch 59 → 121 not taken.
4857 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 165 times.
✓ Branch 63 → 69 taken 1454 times.
✓ Branch 64 → 65 taken 165 times.
✗ Branch 64 → 149 not taken.
✓ Branch 67 → 68 taken 165 times.
✗ Branch 67 → 69 not taken.
✓ Branch 70 → 71 taken 165 times.
✓ Branch 70 → 97 taken 1454 times.
1784 if (hasFieldsToDeAllocate && !sourceFile->isStringRT()) {
337
1/2
✓ Branch 71 → 72 taken 165 times.
✗ Branch 71 → 148 not taken.
165 const SourceFile *memoryRT = sourceFile->requestRuntimeModule(MEMORY_RT);
338
1/2
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 74 taken 165 times.
165 assert(memoryRT != nullptr);
339 165 Scope *matchScope = memoryRT->globalScope.get();
340 // Set dealloc function to used
341
1/2
✓ Branch 75 → 76 taken 165 times.
✗ Branch 75 → 148 not taken.
165 const QualType thisType(TY_DYN);
342
3/6
✓ Branch 76 → 77 taken 165 times.
✗ Branch 76 → 130 not taken.
✓ Branch 77 → 78 taken 165 times.
✗ Branch 77 → 130 not taken.
✓ Branch 78 → 79 taken 165 times.
✗ Branch 78 → 130 not taken.
165 QualType bytePtrRefType = QualType(TY_BYTE).toPtr(node).toRef(node);
343
1/2
✓ Branch 79 → 80 taken 165 times.
✗ Branch 79 → 148 not taken.
165 bytePtrRefType.makeHeap();
344
1/2
✓ Branch 83 → 84 taken 165 times.
✗ Branch 83 → 132 not taken.
330 const ArgList args = {{bytePtrRefType, false /* we always have the field as storage */}};
345
2/4
✓ Branch 88 → 89 taken 165 times.
✗ Branch 88 → 139 not taken.
✓ Branch 89 → 90 taken 165 times.
✗ Branch 89 → 137 not taken.
495 Function *deallocFct = FunctionManager::match(matchScope, FCT_NAME_DEALLOC, thisType, args, {}, true, node);
346
1/2
✗ Branch 93 → 94 not taken.
✓ Branch 93 → 95 taken 165 times.
165 assert(deallocFct != nullptr);
347 165 deallocFct->used = true;
348 165 }
349
2/2
✓ Branch 100 → 101 taken 1619 times.
✓ Branch 100 → 103 taken 1371 times.
2990 }
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 5835 void TypeChecker::createCtorBodyPreamble(const Scope *bodyScope) const {
356 // Retrieve struct scope
357 5835 Scope *structScope = bodyScope->parent;
358
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 5835 times.
5835 assert(structScope != nullptr);
359
360 5835 const size_t fieldCount = structScope->getFieldCount();
361
2/2
✓ Branch 54 → 6 taken 14197 times.
✓ Branch 54 → 55 taken 5835 times.
20032 for (size_t i = 0; i < fieldCount; i++) {
362
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 14197 times.
14197 SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
363
3/6
✓ Branch 11 → 12 taken 14197 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 14197 times.
✗ Branch 12 → 70 not taken.
✓ Branch 13 → 14 taken 14197 times.
✗ Branch 13 → 15 not taken.
14197 assert(fieldSymbol != nullptr && fieldSymbol->isField());
364
2/2
✓ Branch 16 → 17 taken 1363 times.
✓ Branch 16 → 18 taken 12834 times.
14197 if (fieldSymbol->isImplicitField)
365 2808 continue;
366
367
1/2
✓ Branch 18 → 19 taken 12834 times.
✗ Branch 18 → 70 not taken.
12834 QualType fieldType = fieldSymbol->getQualType();
368
3/4
✓ Branch 19 → 20 taken 12834 times.
✗ Branch 19 → 70 not taken.
✓ Branch 20 → 21 taken 2631 times.
✓ Branch 20 → 22 taken 10203 times.
12834 if (fieldType.hasAnyGenericParts())
369
1/2
✓ Branch 21 → 22 taken 2631 times.
✗ Branch 21 → 70 not taken.
2631 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
370
371
3/4
✓ Branch 22 → 23 taken 12834 times.
✗ Branch 22 → 70 not taken.
✓ Branch 23 → 24 taken 3065 times.
✓ Branch 23 → 50 taken 9769 times.
12834 if (fieldType.is(TY_STRUCT)) {
372
1/2
✓ Branch 24 → 25 taken 3065 times.
✗ Branch 24 → 26 not taken.
3065 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 3065 times.
✗ Branch 31 → 70 not taken.
3065 Scope *matchScope = fieldType.getBodyScope();
375
2/4
✓ Branch 36 → 37 taken 3065 times.
✗ Branch 36 → 58 not taken.
✓ Branch 37 → 38 taken 3065 times.
✗ Branch 37 → 56 not taken.
9195 const Function *spiceFunc = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode);
376
2/2
✓ Branch 42 → 43 taken 1578 times.
✓ Branch 42 → 47 taken 1487 times.
3065 if (spiceFunc != nullptr)
377
3/6
✓ Branch 43 → 44 taken 1578 times.
✗ Branch 43 → 68 not taken.
✓ Branch 44 → 45 taken 1578 times.
✗ Branch 44 → 68 not taken.
✓ Branch 45 → 46 taken 1578 times.
✗ Branch 45 → 68 not taken.
1578 fieldSymbol->updateType(fieldType.getWithBodyScope(spiceFunc->thisType.getBodyScope()), true);
378
3/4
✓ Branch 47 → 48 taken 1487 times.
✗ Branch 47 → 70 not taken.
✓ Branch 48 → 49 taken 1445 times.
✓ Branch 48 → 50 taken 42 times.
1487 else if (!fieldType.isTriviallyConstructible(fieldNode))
379 1445 continue;
380 }
381
382
1/2
✓ Branch 50 → 51 taken 11389 times.
✗ Branch 50 → 69 not taken.
11389 fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode);
383 }
384 5835 }
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 3127 void TypeChecker::createCopyCtorBodyPreamble(const Scope *bodyScope) const {
391 // Retrieve struct scope
392 3127 Scope *structScope = bodyScope->parent;
393
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3127 times.
3127 assert(structScope != nullptr);
394
395 3127 const size_t fieldCount = structScope->getFieldCount();
396
2/2
✓ Branch 64 → 6 taken 4562 times.
✓ Branch 64 → 65 taken 3127 times.
7689 for (size_t i = 0; i < fieldCount; i++) {
397
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 4562 times.
4562 SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
398
3/6
✓ Branch 11 → 12 taken 4562 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 4562 times.
✗ Branch 12 → 86 not taken.
✓ Branch 13 → 14 taken 4562 times.
✗ Branch 13 → 15 not taken.
4562 assert(fieldSymbol != nullptr && fieldSymbol->isField());
399
2/2
✓ Branch 16 → 17 taken 244 times.
✓ Branch 16 → 18 taken 4318 times.
4562 if (fieldSymbol->isImplicitField)
400 244 continue;
401
402
1/2
✓ Branch 18 → 19 taken 4318 times.
✗ Branch 18 → 86 not taken.
4318 QualType fieldType = fieldSymbol->getQualType();
403
2/4
✓ Branch 19 → 20 taken 4318 times.
✗ Branch 19 → 86 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 4318 times.
4318 if (fieldType.hasAnyGenericParts())
404 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
405
406
3/4
✓ Branch 22 → 23 taken 4318 times.
✗ Branch 22 → 86 not taken.
✓ Branch 23 → 24 taken 3427 times.
✓ Branch 23 → 60 taken 891 times.
4318 if (fieldType.is(TY_STRUCT)) {
407
1/2
✓ Branch 24 → 25 taken 3427 times.
✗ Branch 24 → 26 not taken.
3427 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 3427 times.
✗ Branch 31 → 84 not taken.
3427 Scope *matchScope = fieldType.getBodyScope();
410
2/4
✓ Branch 32 → 33 taken 3427 times.
✗ Branch 32 → 70 not taken.
✓ Branch 36 → 37 taken 3427 times.
✗ Branch 36 → 66 not taken.
6854 const ArgList args = {{fieldType.toConstRef(fieldNode), false /* we always have the field as storage */}};
411
2/4
✓ Branch 41 → 42 taken 3427 times.
✗ Branch 41 → 74 not taken.
✓ Branch 42 → 43 taken 3427 times.
✗ Branch 42 → 72 not taken.
10281 const Function *copyCtorFct = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, fieldType, args, {}, false, fieldNode);
412
2/2
✓ Branch 46 → 47 taken 3328 times.
✓ Branch 46 → 51 taken 99 times.
3427 if (copyCtorFct != nullptr)
413
3/6
✓ Branch 47 → 48 taken 3328 times.
✗ Branch 47 → 81 not taken.
✓ Branch 48 → 49 taken 3328 times.
✗ Branch 48 → 81 not taken.
✓ Branch 49 → 50 taken 3328 times.
✗ Branch 49 → 81 not taken.
3328 fieldSymbol->updateType(fieldType.getWithBodyScope(copyCtorFct->thisType.getBodyScope()), true);
414
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))
415 continue;
416
1/2
✓ Branch 56 → 57 taken 3427 times.
✗ Branch 56 → 59 not taken.
3427 }
417
418
1/2
✓ Branch 60 → 61 taken 4318 times.
✗ Branch 60 → 85 not taken.
4318 fieldSymbol->updateState(INITIALIZED, fieldSymbol->declNode);
419 }
420 3127 }
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 3655 void TypeChecker::createDtorBodyPreamble(const Scope *bodyScope) const {
463 // Retrieve struct scope
464 3655 Scope *structScope = bodyScope->parent;
465
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3655 times.
3655 assert(structScope != nullptr);
466
467 3655 const size_t fieldCount = structScope->getFieldCount();
468
2/2
✓ Branch 46 → 6 taken 7082 times.
✓ Branch 46 → 47 taken 3655 times.
10737 for (size_t i = 0; i < fieldCount; i++) {
469 7082 const size_t fieldIdx = fieldCount - 1 - i; // Destruct fields in reverse order
470
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 7082 times.
7082 const SymbolTableEntry *fieldSymbol = structScope->lookupField(fieldIdx);
471
3/6
✓ Branch 11 → 12 taken 7082 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 7082 times.
✗ Branch 12 → 60 not taken.
✓ Branch 13 → 14 taken 7082 times.
✗ Branch 13 → 15 not taken.
7082 assert(fieldSymbol != nullptr && fieldSymbol->isField());
472
2/2
✓ Branch 16 → 17 taken 832 times.
✓ Branch 16 → 18 taken 6250 times.
7082 if (fieldSymbol->isImplicitField)
473 832 continue;
474
475
1/2
✓ Branch 18 → 19 taken 6250 times.
✗ Branch 18 → 60 not taken.
6250 QualType fieldType = fieldSymbol->getQualType();
476
2/4
✓ Branch 19 → 20 taken 6250 times.
✗ Branch 19 → 60 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 6250 times.
6250 if (fieldType.hasAnyGenericParts())
477 TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, fieldSymbol->declNode);
478
479
3/4
✓ Branch 22 → 23 taken 6250 times.
✗ Branch 22 → 60 not taken.
✓ Branch 23 → 24 taken 3566 times.
✓ Branch 23 → 43 taken 2684 times.
6250 if (fieldType.is(TY_STRUCT)) {
480
1/2
✓ Branch 24 → 25 taken 3566 times.
✗ Branch 24 → 26 not taken.
3566 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 3566 times.
✗ Branch 31 → 60 not taken.
3566 Scope *matchScope = fieldType.getBodyScope();
483
2/4
✓ Branch 36 → 37 taken 3566 times.
✗ Branch 36 → 50 not taken.
✓ Branch 37 → 38 taken 3566 times.
✗ Branch 37 → 48 not taken.
10698 FunctionManager::match(matchScope, DTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode);
484 }
485 }
486 3655 }
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 6350 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 6350 times.
✗ Branch 2 → 9 not taken.
✓ Branch 3 → 4 taken 6350 times.
✗ Branch 3 → 9 not taken.
✓ Branch 4 → 5 taken 6350 times.
✗ Branch 4 → 9 not taken.
6350 const QualType thisType = entry->getQualType().removeReferenceWrapper().toNonConst();
499
1/2
✓ Branch 5 → 6 taken 6350 times.
✗ Branch 5 → 10 not taken.
12700 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 7331 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 7331 times.
7331 assert(thisType.is(TY_STRUCT));
513 7331 Scope *matchScope = thisType.getBodyScope();
514
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 7331 times.
7331 assert(matchScope->type == ScopeType::STRUCT);
515
516 // Search for dtor
517
2/2
✓ Branch 9 → 10 taken 5369 times.
✓ Branch 9 → 12 taken 1962 times.
7331 if (matchScope->isImportedBy(rootScope))
518
1/2
✓ Branch 10 → 11 taken 5369 times.
✗ Branch 10 → 18 not taken.
5369 thisType = mapLocalTypeToImportedScopeType(matchScope, thisType);
519
1/2
✓ Branch 13 → 14 taken 7331 times.
✗ Branch 13 → 19 not taken.
7331 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 970 Function *TypeChecker::implicitlyCallStructCopyCtor(const QualType &thisType, const ASTNode *node) const {
540
2/4
✓ Branch 2 → 3 taken 970 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 970 times.
✗ Branch 3 → 19 not taken.
970 const QualType argType = thisType.removeReferenceWrapper().toConstRef(node);
541
1/2
✓ Branch 7 → 8 taken 970 times.
✗ Branch 7 → 20 not taken.
2910 const ArgList args = {{argType, false /* we always have an entry here */}};
542
2/4
✓ Branch 11 → 12 taken 970 times.
✗ Branch 11 → 27 not taken.
✓ Branch 12 → 13 taken 970 times.
✗ Branch 12 → 25 not taken.
1940 return implicitlyCallStructMethod(thisType, CTOR_FUNCTION_NAME, args, node);
543 970 }
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 6350 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 6350 times.
✗ Branch 5 → 16 not taken.
✓ Branch 6 → 7 taken 6350 times.
✗ Branch 6 → 14 not taken.
✓ Branch 10 → 11 taken 4544 times.
✓ Branch 10 → 13 taken 1806 times.
19050 if (Function *dtor = implicitlyCallStructMethod(entry, DTOR_FUNCTION_NAME, {}, node))
577
2/4
✓ Branch 11 → 12 taken 4544 times.
✗ Branch 11 → 23 not taken.
✓ Branch 12 → 13 taken 4544 times.
✗ Branch 12 → 23 not taken.
4544 node->resourcesToCleanup.at(manIdx).dtorFunctionsToCall.emplace_back(entry, dtor);
578 6350 }
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 72 void TypeChecker::implicitlyCallDeallocate(const ASTNode *node) const {
586
1/2
✓ Branch 2 → 3 taken 72 times.
✗ Branch 2 → 46 not taken.
72 const SourceFile *memoryRT = sourceFile->requestRuntimeModule(MEMORY_RT);
587
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 72 times.
72 assert(memoryRT != nullptr);
588 72 Scope *matchScope = memoryRT->globalScope.get();
589 // Set dealloc function to used
590
1/2
✓ Branch 6 → 7 taken 72 times.
✗ Branch 6 → 46 not taken.
72 const QualType thisType(TY_DYN);
591
3/6
✓ Branch 7 → 8 taken 72 times.
✗ Branch 7 → 28 not taken.
✓ Branch 8 → 9 taken 72 times.
✗ Branch 8 → 28 not taken.
✓ Branch 9 → 10 taken 72 times.
✗ Branch 9 → 28 not taken.
72 QualType bytePtrRefType = QualType(TY_BYTE).toPtr(node).toRef(node);
592
1/2
✓ Branch 10 → 11 taken 72 times.
✗ Branch 10 → 46 not taken.
72 bytePtrRefType.makeHeap();
593
1/2
✓ Branch 14 → 15 taken 72 times.
✗ Branch 14 → 30 not taken.
144 const ArgList args = {{bytePtrRefType, false /* we always have the field as storage */}};
594
2/4
✓ Branch 19 → 20 taken 72 times.
✗ Branch 19 → 37 not taken.
✓ Branch 20 → 21 taken 72 times.
✗ Branch 20 → 35 not taken.
216 Function *deallocFct = FunctionManager::match(matchScope, FCT_NAME_DEALLOC, thisType, args, {}, true, node);
595
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 72 times.
72 assert(deallocFct != nullptr);
596 72 deallocFct->used = true;
597 72 }
598
599 /**
600 * Consider calls to destructors for the given scope
601 *
602 * @param node StmtLstNode for the current scope
603 */
604 61826 void TypeChecker::doScopeCleanup(StmtLstNode *node) const {
605 // Get all variables, that are approved for de-allocation
606
1/2
✓ Branch 2 → 3 taken 61826 times.
✗ Branch 2 → 69 not taken.
61826 std::vector<SymbolTableEntry *> vars = currentScope->getVarsGoingOutOfScope();
607 // Sort by reverse declaration order
608 81628 const auto comp = [this](const SymbolTableEntry *a, const SymbolTableEntry *b) {
609 19802 const ASTNode *aDeclNode = a->declNode;
610 19802 const ASTNode *bDeclNode = b->declNode;
611 // Primary sort criteria is the code location
612
3/4
✓ Branch 2 → 3 taken 19802 times.
✗ Branch 2 → 15 not taken.
✓ Branch 3 → 4 taken 19512 times.
✓ Branch 3 → 10 taken 290 times.
19802 if (aDeclNode->codeLoc != bDeclNode->codeLoc)
613
2/2
✓ Branch 4 → 5 taken 809 times.
✓ Branch 4 → 6 taken 18703 times.
39024 return aDeclNode->codeLoc > bDeclNode->codeLoc;
614 // Secondary sort criteria is the node id
615
2/4
✓ Branch 10 → 11 taken 290 times.
✗ Branch 10 → 15 not taken.
✓ Branch 11 → 12 taken 290 times.
✗ Branch 11 → 15 not taken.
290 return resourceManager.nodeToNodeId[aDeclNode] > resourceManager.nodeToNodeId[bDeclNode];
616 61826 };
617
1/2
✓ Branch 3 → 4 taken 61826 times.
✗ Branch 3 → 67 not taken.
61826 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 26836 times.
✓ Branch 62 → 63 taken 61826 times.
150488 for (SymbolTableEntry *var : vars) {
620 // Check if we have a heap-allocated pointer
621
10/14
✓ Branch 8 → 9 taken 26836 times.
✗ Branch 8 → 65 not taken.
✓ Branch 9 → 10 taken 26836 times.
✗ Branch 9 → 65 not taken.
✓ Branch 10 → 11 taken 2542 times.
✓ Branch 10 → 15 taken 24294 times.
✓ Branch 11 → 12 taken 2542 times.
✗ Branch 11 → 65 not taken.
✓ Branch 12 → 13 taken 2542 times.
✗ Branch 12 → 65 not taken.
✓ Branch 13 → 14 taken 2122 times.
✓ Branch 13 → 15 taken 420 times.
✓ Branch 16 → 17 taken 2122 times.
✓ Branch 16 → 36 taken 24714 times.
26836 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 2122 times.
✗ Branch 17 → 66 not taken.
✓ Branch 20 → 21 taken 1714 times.
✓ Branch 20 → 25 taken 408 times.
✓ Branch 21 → 22 taken 1714 times.
✗ Branch 21 → 66 not taken.
✓ Branch 24 → 25 taken 1540 times.
✓ Branch 24 → 26 taken 174 times.
✓ Branch 27 → 28 taken 1948 times.
✓ Branch 27 → 29 taken 174 times.
5958 if (sourceFile->isMemoryRT() || sourceFile->isStringRT())
625 1948 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 174 times.
✗ Branch 30 → 66 not taken.
✓ Branch 31 → 32 taken 102 times.
✓ Branch 31 → 33 taken 72 times.
174 if (!var->getLifecycle().isInOwningState())
628 102 continue;
629
630
1/2
✓ Branch 33 → 34 taken 72 times.
✗ Branch 33 → 66 not taken.
72 implicitlyCallDeallocate(node); // Required to request the memory runtime
631
2/4
✓ Branch 34 → 35 taken 72 times.
✗ Branch 34 → 66 not taken.
✓ Branch 35 → 36 taken 72 times.
✗ Branch 35 → 66 not taken.
72 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 24786 times.
✗ Branch 36 → 66 not taken.
✓ Branch 37 → 38 taken 24786 times.
✗ Branch 37 → 66 not taken.
✓ Branch 38 → 39 taken 6660 times.
✓ Branch 38 → 40 taken 18126 times.
✓ Branch 39 → 40 taken 310 times.
✓ Branch 39 → 41 taken 6350 times.
✓ Branch 42 → 43 taken 18436 times.
✓ Branch 42 → 44 taken 6350 times.
24786 if (!var->getQualType().is(TY_STRUCT) || var->omitDtorCall)
635 18436 continue;
636 // Variable must be either initialized or a struct field
637
5/8
✓ Branch 45 → 46 taken 6350 times.
✗ Branch 45 → 66 not taken.
✓ Branch 46 → 47 taken 22 times.
✓ Branch 46 → 49 taken 6328 times.
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 49 taken 22 times.
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 52 taken 6350 times.
6350 if (!var->getLifecycle().isInitialized() && var->scope->type != ScopeType::STRUCT)
638 continue;
639 // Call dtor
640
1/2
✓ Branch 52 → 53 taken 6350 times.
✗ Branch 52 → 66 not taken.
6350 implicitlyCallStructDtor(var, node);
641 }
642 61826 }
643
644 } // namespace spice::compiler
645