GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
4241 / 4457, 1 excl
95.2%
Branches:
2000 / 2259, 356 excl
88.5%
Functions:
186 / 187
99.5%

typechecker/type-checker.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/type/any";
5 import "std/data/vector";
6 import "std/data/unordered-map";
7 import "std/data/pair";
8 import "std/data/queue";
9 import "std/type/type-conversion";
10 import "std/type/types";
11 import "std/math/hash";
12
13 // Own imports
14 import "@/compiler-pass";
15 import "@/driver";
16 import "@/source-file";
17 import "@/ast/ast-nodes";
18 import "@/ast/attributes";
19 import "@/ast/abstract-ast-visitor-intf";
20 import "@/exception/error-manager";
21 import "@/exception/semantic-error";
22 import "@/global/global-resource-manager";
23 import "@/global/runtime-module-manager";
24 import "@/global/type-registry";
25 import "@/model/function";
26 import "@/model/generic-type";
27 import "@/model/interface";
28 import "@/model/struct";
29 import "@/model/struct-base";
30 import "@/model/union";
31 import "@/reader/code-loc";
32 import "@/symboltablebuilder/qual-type";
33 import "@/symboltablebuilder/capture";
34 import "@/symboltablebuilder/lifecycle";
35 import "@/symboltablebuilder/scope";
36 import "@/symboltablebuilder/scope-handle";
37 import "@/symboltablebuilder/super-type";
38 import "@/symboltablebuilder/symbol-table";
39 import "@/symboltablebuilder/symbol-table-entry";
40 import "@/symboltablebuilder/type";
41 import "@/symboltablebuilder/type-chain";
42 import "@/symboltablebuilder/type-qualifiers";
43 import "@/typechecker/builtin-functions";
44 import "@/typechecker/expr-result";
45 import "@/typechecker/function-manager";
46 import "@/typechecker/interface-manager";
47 import "@/typechecker/op-rule-manager";
48 import "@/typechecker/struct-manager";
49 import "@/typechecker/type-matcher";
50 import "@/typechecker/union-manager";
51 import "@/util/compiler-warning";
52
53 // Constants
54 const string FCT_NAME_DEALLOC = "sDealloc";
55
56 /**
57 * Global switch that tells matchStruct whether a freshly substantiated struct manifestation may still be given its
58 * compiler-generated default members (ctor, copy ctor, move ctor, dtor).
59 *
60 * Creating those mutates the symbol table and registers new functions, which is only valid while the middle end runs.
61 * The back end reaches matchStruct as well - through QualType.getStruct - but by then every manifestation exists
62 * already, so those calls never take the creation path anyway. The switch makes that an invariant instead of an
63 * assumption.
64 */
65 public bool STRUCT_DEFAULT_MEMBERS_DECIDABLE = false;
66
67 /**
68 * RAII marker for the section of the compiler in which struct manifestations are still allowed to receive their
69 * compiler-generated default members, i.e. the middle end.
70 */
71 public type DefaultMemberCreationSection struct {}
72
73 1009 public p DefaultMemberCreationSection.ctor() {
74 1009 STRUCT_DEFAULT_MEMBERS_DECIDABLE = true;
75 1009 }
76
77 763 public p DefaultMemberCreationSection.dtor() {
78 763 STRUCT_DEFAULT_MEMBERS_DECIDABLE = false;
79 763 }
80
81 public type TypeCheckerMode enum {
82 TC_MODE_PRE,
83 TC_MODE_POST
84 }
85
86 /**
87 * Jobs:
88 * - Ensure that all actual types match the expected types
89 * - Perform type inference
90 *
91 * Spice requires all methods of a struct to live in the file of the struct, so this file is split into sections, that are
92 * named after the checked constructs (top level definitions, statements, expressions, ...).
93 *
94 * Error handling: Spice has no exceptions, so this pass records a soft error and returns from the visit method of the offending node instead (SourceFile.checkForSoftErrors() aborts the
95 * compilation afterwards).
96 */
97 7588 public type TypeChecker struct : IAbstractAstVisitor {
98 compose public CompilerPass compilerPass
99 OpRuleManager opRuleManager
100 TypeCheckerMode typeCheckerMode
101 Vector<CompilerWarning>* warnings
102 TypeMapping typeMapping
103 7925 bool typeCheckedMainFct = false
104 }
105
106 7925 public p TypeChecker.ctor(GlobalResourceManager& resourceManager, SourceFile* sourceFile, TypeCheckerMode typeCheckerMode) {
107 7925 this.compilerPass = CompilerPass(resourceManager, sourceFile);
108 7925 this.opRuleManager = OpRuleManager(this);
109 7925 this.typeCheckerMode = typeCheckerMode;
110 7925 this.warnings = &sourceFile.compilerOutput.warnings;
111 7925 }
112
113 // Default behavior of every visit method that is not implemented below: visit all children. After a hard error, it stops
114 // visiting further children, because the following nodes may depend on what the erroneous node failed to set up.
115 87857 #[ignoreUnusedReturnValue]
116 87857 f<Any> TypeChecker.visitChildren(ASTNode* node) {
117 87857 const ErrorManager& errorManager = this.resourceManager.getErrorManager();
118
2/2
✓ Branch 0 taken 87817 times.
✓ Branch 1 taken 289007 times.
376824 foreach ASTNode* child : node.children {
119
2/2
✓ Branch 0 taken 288967 times.
✓ Branch 1 taken 40 times.
289007 if errorManager.hasHardError {
120 40 break;
121 }
122 288967 assert child != nil<ASTNode*>;
123 288967 assert child.visitable != nil<IVisitable*>;
124 288967 this.visit(child.visitable);
125 288967 }
126 87857 return Any();
127 }
128
129 // Visit a node, which evaluates to a type (e.g. a data type node)
130 736137 f<QualType> TypeChecker.visitType(IVisitable* node) {
131 736137 Any visitResult = this.visit(node);
132 1472274 return visitResult.get<QualType>();
133 736137 }
134
135 // Shorthand for visitType, for data type nodes
136 262087 f<QualType> TypeChecker.getDataType(ASTDataTypeNode* node) {
137 262087 return this.visitType(cast<IVisitable*>(node));
138 }
139
140 // Visit a node, which evaluates to an expression result (e.g. an expression node)
141 590026 f<ExprResult> TypeChecker.visitExpr(IVisitable* node) {
142 590026 Any visitResult = this.visit(node);
143 1180052 return visitResult.get<ExprResult>();
144 590026 }
145
146 // Return value of a visit method, that evaluates to an unresolved type after a soft error
147 28 f<Any> TypeChecker.softErrorQT(const ASTNode* node, SemanticErrorType errorType, const String& message) {
148 28 this.softError(node, errorType, message);
149 28 return Any(QualType(SuperType::TY_UNRESOLVED));
150 }
151
152 // Return value of a visit method, that evaluates to an expression result, after a soft error
153 165 f<Any> TypeChecker.softErrorER(const ASTNode* node, SemanticErrorType errorType, const String& message) {
154 165 this.softError(node, errorType, message);
155 165 return unresolvedER();
156 }
157
158 // Return value of a visit method, that evaluates to an unresolved expression result
159 231 f<Any> unresolvedER() {
160 231 return Any(ExprResult{QualType(SuperType::TY_UNRESOLVED), nil<SymbolTableEntry*>});
161 }
162
163 // ======================================================== Type checker =========================================================
164
165 10290 public f<Any> TypeChecker.visitEntry(ASTEntryNode* node) {
166 // Initialize
167 10290 this.currentScope = this.rootScope;
168
169 // Initialize AST nodes with size of 1
170 10290 const bool isPrepare = this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE;
171 10290 ASTNode* baseNode = cast<ASTNode*>(node);
172
2/2
✓ Branch 0 taken 6374 times.
✓ Branch 1 taken 3916 times.
10290 if isPrepare {
173 3916 baseNode.resizeToNumberOfManifestations(1l);
174 3916 }
175
176 // Find switch statements, that cover all items of an enum. This has to be done before visiting the children, because
177 // the control flow analysis of functions and procedures in the prepare stage relies on it
178
2/2
✓ Branch 0 taken 6374 times.
✓ Branch 1 taken 3916 times.
10290 if isPrepare {
179
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 3916 times.
3954 foreach IVisitable* switchStmt : node.switchStmts {
180 38 ASTSwitchStmtNode* switchStmtNode = downcastMut<ASTSwitchStmtNode>(getASTNode(switchStmt));
181 38 switchStmtNode.coversAllEnumItems = this.coversAllEnumItems(switchStmtNode);
182 38 }
183 3916 }
184
185 // Visit children
186 10290 this.visitChildren(baseNode);
187 // Skip the rest after a hard error (the compilation is aborted after this stage)
188 10290 const ErrorManager& errorManager = this.resourceManager.getErrorManager();
189
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 10234 times.
10290 if errorManager.hasHardError {
190 112 return Any();
191 }
192
193 // Check which implicit structures we need for each struct, defined in this source file. Manifestations that are
194 // substantiated after this point are decided on directly at their creation (see createImplicitDefaultMembers).
195
2/2
✓ Branch 0 taken 6344 times.
✓ Branch 1 taken 3890 times.
10234 if isPrepare {
196 3890 Vector<Struct*> manifestations = this.rootScope.getAllStructManifestationsInDeclarationOrder();
197
2/2
✓ Branch 0 taken 3239 times.
✓ Branch 1 taken 3890 times.
7129 foreach Struct* manifestation : manifestations {
198 3239 createImplicitDefaultMembers(*manifestation, baseNode);
199 3239 }
200 3890 }
201
202 20468 return Any();
203 10290 }
204
205 /**
206 * Check if the case constants of a switch statement cover all items of a single enum
207 *
208 * @param node Switch statement node
209 * @return All enum items covered or not
210 */
211 38 f<bool> TypeChecker.coversAllEnumItems(ASTSwitchStmtNode* node) {
212 38 ASTEnumDefNode* enumDef = nil<ASTEnumDefNode*>;
213 38 Vector<ASTEnumItemNode*> coveredItems;
214
2/2
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 18 times.
133 foreach IVisitable* caseBranch : node.caseBranches {
215 115 const ASTCaseBranchNode* caseBranchNode = downcast<ASTCaseBranchNode>(caseBranch);
216
2/2
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 116 times.
211 foreach IVisitable* constant : caseBranchNode.caseConstants {
217 116 const ASTCaseConstantNode* constantNode = downcast<ASTCaseConstantNode>(constant);
218 // Literal constants can never be enum items
219
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 97 times.
116 if constantNode.constant != nil<ASTConstantNode*> {
220 19 return false;
221 }
222
223 // Resolve the enum item via the name registry, since enum items are always referenced by their qualified name
224 97 const NameRegistryEntry* registryEntry = this.sourceFile.getNameRegistryEntry(constantNode.fqIdentifier);
225
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 97 times.
97 if registryEntry == nil<const NameRegistryEntry*> || registryEntry.targetEntry == nil<SymbolTableEntry*> {
226 ✗ return false;
227 }
228 97 ASTNode* declNode = registryEntry.targetEntry.declNode;
229
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 96 times.
97 if declNode == nil<ASTNode*> || declNode.kind != ASTNodeKind::ENUM_ITEM {
230 1 return false;
231 }
232 96 const ASTEnumItemNode* enumItemNode = downcast<ASTEnumItemNode>(declNode);
233
234 // All case constants have to be items of the same enum
235
3/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 79 times.
✓ Branch 2 taken 96 times.
✗ Branch 3 not taken.
96 if enumDef != nil<ASTEnumDefNode*> && enumItemNode.enumDef != enumDef {
236 ✗ return false;
237 }
238 96 enumDef = enumItemNode.enumDef;
239 96 bool alreadyCovered = false;
240
2/2
✓ Branch 0 taken 325 times.
✓ Branch 1 taken 96 times.
421 foreach ASTEnumItemNode* coveredItem : coveredItems {
241
1/2
✓ Branch 0 taken 325 times.
✗ Branch 1 not taken.
325 if coveredItem == enumItemNode {
242 ✗ alreadyCovered = true;
243 ✗ }
244 325 }
245
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if !alreadyCovered {
246 96 coveredItems.pushBack(enumItemNode);
247 96 }
248 96 }
249 95 }
250
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 time.
18 if enumDef == nil<ASTEnumDefNode*> {
251 1 return false;
252 }
253 17 const ASTEnumItemLstNode* itemLst = downcast<ASTEnumItemLstNode>(enumDef.itemLst);
254 34 return coveredItems.getSize() == itemLst.items.getSize();
255 38 }
256
257 /**
258 * Check if the capture rules for async lambdas are enforced if the async attribute is set
259 *
260 * Only one capture with pointer type, pass-by-val is allowed, since only then we can store it in the second field of the
261 * fat pointer and can ensure, that no stack variable is referenced inside the lambda.
262 *
263 * @param node Lambda node
264 * @param attrs Lambda attributes
265 * @return False if the rules are violated, true otherwise
266 */
267 138 #[ignoreUnusedReturnValue]
268 138 f<bool> TypeChecker.checkAsyncLambdaCaptureRules(ASTNode* node, const ASTLambdaAttrNode* attrs) {
269 // If the async attribute is not set, we can return early
270
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 117 times.
138 if attrs == nil<const ASTLambdaAttrNode*> {
271 117 return true; // Not violated
272 }
273 21 ASTAttrLstNode* attrLst = attrs.attrLst;
274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if !attrLst.hasAttr(String(ATTR_ASYNC)) {
275 ✗ return true; // Not violated
276 }
277 21 const CompileTimeValue asyncValue = attrLst.getAttrValueByName(String(ATTR_ASYNC));
278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if !asyncValue.getBoolValue() {
279 ✗ return true; // Not violated
280 }
281
282 // If we don't have any captures, we can return early
283 21 ASTLambdaBaseNode* lambdaBaseNode = asLambdaBaseNode(node);
284 21 Scope* bodyScope = lambdaBaseNode.bodyScope;
285 21 CaptureMap& captures = bodyScope.symbolTable.captures;
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if captures.isEmpty() {
287 ✗ return true; // Not violated
288 }
289
290 // Check for the capture rules
291 21 bool violated = captures.getSize() > 1ul;
292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 foreach Pair<const String&, Capture&> captureEntry : captures {
293 // Only the first capture is inspected
294 21 Capture& capture = captureEntry.getSecond();
295 21 const QualType capturedType = capture.capturedSymbol.getQualType();
296
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 14 times.
21 violated = violated || !capturedType.isPtr() || capture.getMode() != CapturePassMode::BY_VALUE;
297 21 break;
298 }
299
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 8 times.
21 if violated {
300 8 const String warningMessage = String("Async lambdas can only capture one pointer by value without storing captures in the caller stack frame, which can lead to bugs due to references, outliving the validity scope of the referenced variable.");
301 8 this.addWarning(node.codeLoc, CompilerWarningType::ASYNC_LAMBDA_CAPTURE_RULE_VIOLATION, warningMessage);
302 8 }
303
304 21 return false; // Violated
305 21 }
306
307 /**
308 * Match the copy ctor of the given struct type
309 *
310 * @param thisType Struct type
311 * @param node Current AST node
312 * @return Copy ctor, or nil if there is none
313 */
314 72 f<Function*> TypeChecker.matchCopyCtor(const QualType& thisType, const ASTNode* node) {
315 72 Scope* matchScope = thisType.getBodyScope();
316 72 assert matchScope != nil<Scope*>;
317 72 const QualType argType = thisType.toConstRef(node);
318 72 ArgList args;
319 72 args.pushBack(Pair<QualType, bool>(argType, false));
320 // The new instance being constructed is never const, regardless of whether the copy source (thisType) is const
321 72 const QualType nonConstThisType = thisType.toNonConst();
322 72 const QualTypeList noTemplateTypeHints;
323 144 return matchFunction(matchScope, String(CTOR_FUNCTION_NAME), nonConstThisType, args, noTemplateTypeHints, true, node);
324 72 }
325
326 /**
327 * Resolve the copy ctors for the by-value params of a function, that own their argument (see QualType.isOwnedByValueParam).
328 * This is required for calls, that do not resolve the copy ctors per argument, like calls of operator functions or ctor calls
329 * via __new and __placement_new. The IR generator looks the copy ctors up again, when it copies the arguments.
330 *
331 * @param callee Called function
332 * @param node Call AST node
333 */
334 5561 public p TypeChecker.matchCopyCtorsForOwnedParams(const Function* callee, const ASTNode* node) {
335
2/2
✓ Branch 0 taken 10596 times.
✓ Branch 1 taken 5561 times.
16157 foreach const QualType& paramType : callee.getParamTypes() {
336
2/2
✓ Branch 0 taken 10593 times.
✓ Branch 1 taken 3 times.
10596 if paramType.isOwnedByValueParam(node) {
337 // Only resolve the copy ctor here. The IR generator looks it up again
338 3 Function* _ = this.matchCopyCtor(paramType.toNonConst(), node);
339 3 }
340 10596 }
341 5561 }
342
343 /**
344 * Map a local type to the type in an imported scope. If the target file does not know about the struct, it is shown how to
345 * find the struct.
346 *
347 * @param targetScope Scope of the imported source file
348 * @param symbolType Local type
349 * @return Type in the imported scope
350 */
351 418842 public f<QualType> TypeChecker.mapLocalTypeToImportedScopeType(const Scope* targetScope, const QualType& symbolType) {
352 // Skip all types, except structs
353
2/2
✓ Branch 0 taken 307898 times.
✓ Branch 1 taken 110944 times.
418842 if !symbolType.isBase(SuperType::TY_STRUCT) {
354 307898 return symbolType;
355 }
356
357 // If the target scope is in the current source file, we can return the symbol type as is
358 110944 SourceFile* targetSourceFile = targetScope.sourceFile;
359
2/2
✓ Branch 0 taken 82747 times.
✓ Branch 1 taken 28197 times.
110944 if targetSourceFile == this.sourceFile {
360 28197 return symbolType;
361 }
362
363 // Check if the target file knows the struct via its name registry
364 82747 const QualType baseType = symbolType.getBase();
365 82747 const String structName = baseType.getSubType();
366
2/2
✓ Branch 0 taken 75834 times.
✓ Branch 1 taken 6913 times.
82747 if targetSourceFile.isStructKnownByNameRegistry(baseType.getBodyScope(), structName) {
367 151668 return symbolType;
368 }
369
370 // The target file does not know about the struct at all
371 // -> show it how to find the struct
372 6913 const NameRegistryEntry* origRegistryEntry = this.sourceFile.getNameRegistryEntry(structName);
373 // If even this file does not know the struct by its unqualified name (deep transitive import), there is
374 // nothing to copy over. Skip teaching the target file; the type identity itself is unaffected, and member
375 // access falls back to the resolved body scope.
376
1/2
✓ Branch 0 taken 6913 times.
✗ Branch 1 not taken.
6913 if origRegistryEntry == nil<const NameRegistryEntry*> {
377 ✗ return symbolType;
378 }
379 // Do not clobber an entry the target file already has under this name (e.g. its OWN same-named struct). Teaching
380 // uses keepNewOnCollision=false, which would otherwise ERASE the target's existing entry and break resolution of
381 // its own type. The struct's QualType identity is carried by pointer regardless.
382
2/2
✓ Branch 0 taken 1988 times.
✓ Branch 1 taken 4925 times.
6913 if targetSourceFile.exportedNameRegistry.contains(structName) {
383 3976 return symbolType;
384 }
385 4925 const unsigned long targetTypeId = origRegistryEntry.typeId;
386 4925 SymbolTableEntry* targetEntry = origRegistryEntry.targetEntry;
387 4925 targetSourceFile.addNameRegistryEntry(structName, targetTypeId, targetEntry, origRegistryEntry.targetScope, false, nil<SymbolTableEntry*>);
388
389 9850 return symbolType;
390 82747 }
391
392 /**
393 * Map a type from an imported scope to the local type. If this source file does not know about the struct, it is shown how
394 * to find the struct.
395 *
396 * @param sourceScope Scope of the imported source file
397 * @param symbolType Type in the imported scope
398 * @return Local type
399 */
400 20212 public f<QualType> TypeChecker.mapImportedScopeTypeToLocalType(const Scope* sourceScope, const QualType& symbolType) {
401 // Skip all types, except structs
402
2/2
✓ Branch 0 taken 2728 times.
✓ Branch 1 taken 17484 times.
20212 if !symbolType.isBase(SuperType::TY_STRUCT) {
403 2728 return symbolType;
404 }
405
406 // If the given source file is in the current one, we can return the symbol type as is
407 17484 SourceFile* sourceSourceFile = sourceScope.sourceFile;
408
2/2
✓ Branch 0 taken 13252 times.
✓ Branch 1 taken 4232 times.
17484 if sourceSourceFile == this.sourceFile {
409 4232 return symbolType;
410 }
411
412 // Check if this source file knows the struct via its name registry
413 13252 const QualType baseType = symbolType.getBase();
414
2/2
✓ Branch 0 taken 13033 times.
✓ Branch 1 taken 219 times.
13252 if this.sourceFile.isStructKnownByNameRegistry(baseType.getBodyScope(), baseType.getSubType()) {
415 13033 return symbolType;
416 }
417
418 // This source file does not know about the struct at all
419 // -> show it how to find the struct
420 219 const String structName = baseType.getSubType();
421 219 const NameRegistryEntry* origRegistryEntry = sourceSourceFile.getNameRegistryEntry(structName);
422 // If even the source file does not know the struct by its unqualified name (deep transitive import), there is
423 // nothing to copy over. Skip teaching this file; the type identity itself is unaffected, and member access
424 // falls back to the resolved body scope.
425
1/2
✓ Branch 0 taken 219 times.
✗ Branch 1 not taken.
219 if origRegistryEntry == nil<const NameRegistryEntry*> {
426 ✗ return symbolType;
427 }
428 // Do not clobber an entry this file already has under this name (see mapLocalTypeToImportedScopeType): teaching
429 // with keepNewOnCollision=false would ERASE this file's own same-named struct entry. The QualType identity is
430 // carried by pointer, so an already-taken name needs no (re-)teaching here.
431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 219 times.
219 if this.sourceFile.exportedNameRegistry.contains(structName) {
432 ✗ return symbolType;
433 }
434 219 const unsigned long typeId = origRegistryEntry.typeId;
435 219 SymbolTableEntry* targetEntry = origRegistryEntry.targetEntry;
436 219 this.sourceFile.addNameRegistryEntry(structName, typeId, targetEntry, origRegistryEntry.targetScope, false, nil<SymbolTableEntry*>);
437
438 438 return symbolType;
439 219 }
440
441 /**
442 * Check type name against well-known type names that require a runtime import. If found one, auto-import the runtime module.
443 *
444 * @param typeName Given type name
445 */
446 140855 p TypeChecker.ensureLoadedRuntimeForTypeName(const String& typeName) {
447 140855 RuntimeModule runtimeModule = RuntimeModule::STRING_RT;
448
4/4
✓ Branch 0 taken 111074 times.
✓ Branch 1 taken 29781 times.
✓ Branch 2 taken 125911 times.
✓ Branch 3 taken 14944 times.
140855 if getRuntimeModuleForTypeName(typeName, runtimeModule) && !this.sourceFile.isRT(runtimeModule) {
449 14944 this.sourceFile.requestRuntimeModule(runtimeModule);
450 14944 }
451 140855 }
452
453 /**
454 * Check function name against well-known function names that require a runtime import. If found one, auto-import the runtime
455 * module.
456 *
457 * @param functionName Given function name
458 */
459 40741 p TypeChecker.ensureLoadedRuntimeForFunctionName(const String& functionName) {
460 40741 RuntimeModule runtimeModule = RuntimeModule::STRING_RT;
461
4/4
✓ Branch 0 taken 36443 times.
✓ Branch 1 taken 4298 times.
✓ Branch 2 taken 36553 times.
✓ Branch 3 taken 4188 times.
40741 if getRuntimeModuleForFunctionName(functionName, runtimeModule) && !this.sourceFile.isRT(runtimeModule) {
462 4188 this.sourceFile.requestRuntimeModule(runtimeModule);
463 4188 }
464 40741 }
465
466 /**
467 * Get the operator overloading functions of the given node for the current manifestation
468 *
469 * @param node AST node, that supports operator overloading
470 * @return Operator overloading functions
471 */
472 5522 public f<Vector<const Function*>&> TypeChecker.getOpFctPointers(ASTNode* node) {
473 5522 Vector<Vector<const Function*>>* opFctPointers = node.getOpFctPointers();
474 5522 assert opFctPointers.getSize() > cast<long>(this.manIdx);
475 5522 return opFctPointers.get(this.manIdx);
476 }
477
478 /**
479 * Request a re-visit of the source file of the given function, if the function body was not type-checked yet
480 *
481 * @param fct Function to check
482 */
483 115053 public p requestRevisitIfRequired(const Function* fct) {
484
4/4
✓ Branch 0 taken 894 times.
✓ Branch 1 taken 114159 times.
✓ Branch 2 taken 44219 times.
✓ Branch 3 taken 70834 times.
115053 if fct != nil<const Function*> && !fct.alreadyTypeChecked {
485 70834 SourceFile* sourceFile = fct.entry.scope.sourceFile;
486 70834 sourceFile.reVisitRequested = true;
487 70834 }
488 115053 }
489
490 /**
491 * Add a soft error to the error list
492 */
493 247 p TypeChecker.softError(const ASTNode* node, SemanticErrorType errorType, const String& message) {
494 247 ErrorManager& errorManager = this.resourceManager.getErrorManager();
495 247 errorManager.addSoftError(node, errorType, message);
496 247 }
497
498 // Add a warning to the warning list of the current source file
499 5310 p TypeChecker.addWarning(const CodeLoc& codeLoc, CompilerWarningType warningType, const String& message) {
500 5310 this.warnings.pushBack(CompilerWarning(codeLoc, warningType, message.getRaw()));
501 5310 }
502
503 /**
504 * Check if the given ctor args denote a copy ctor call (like __new<T>(other) or __placement_new<T>(ptr, other)), i.e. there
505 * is exactly one ctor arg and it has the same type as the struct to construct
506 *
507 * @param ctorArgs Ctor args (without the target pointer of __placement_new)
508 * @param thisType Type of the struct to construct
509 * @return Copy ctor call or not
510 */
511 26 f<bool> TypeChecker.isCopyCtorCall(const ArgList& ctorArgs, const QualType& thisType) {
512
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 24 times.
26 if ctorArgs.getSize() != 1l {
513 2 return false;
514 }
515 24 const Pair<QualType, bool>& arg = ctorArgs.front();
516 24 QualType argType = arg.getFirst();
517 24 argType = argType.removeReferenceWrapper();
518 24 argType = argType.toNonConst();
519 24 return thisType.matches(argType, false, false, true);
520 }
521
522 /**
523 * Collect the generic types of a template type list (used by the prepare visitors of functions, procedures, structs and
524 * unions).
525 *
526 * @param templateTypeLst Template type list node
527 * @param genericTypes Receives the generic types
528 * @return False if an error occurred
529 */
530 7811 f<bool> TypeChecker.collectTemplateTypes(ASTTypeLstNode* templateTypeLst, Vector<GenericType>& genericTypes) {
531
2/2
✓ Branch 0 taken 9325 times.
✓ Branch 1 taken 7809 times.
17134 foreach ASTDataTypeNode* dataType : templateTypeLst.dataTypes {
532 // Visit template type
533 9325 const QualType templateType = this.getDataType(dataType);
534
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9325 times.
9325 if templateType.is(SuperType::TY_UNRESOLVED) {
535 ✗ continue;
536 }
537 // Check if it is a generic type
538
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9323 times.
9325 if !templateType.is(SuperType::TY_GENERIC) {
539 2 throwSemanticError(cast<ASTNode*>(dataType), SemanticErrorType::EXPECTED_GENERIC_TYPE, String("A template list can only contain generic types"));
540 2 return false;
541 2 }
542 // Convert generic symbol type to generic type
543 9323 GenericType* genericType = this.rootScope.lookupGenericTypeStrict(templateType.getSubType());
544 9323 assert genericType != nil<GenericType*>;
545 9323 genericTypes.pushBack(*genericType);
546 9323 }
547 7809 return true;
548 }
549
550 /**
551 * Add a template type of the 'this' type to the given generic types, if it is not included yet, and mark the last generic type
552 * as used.
553 *
554 * @param genericTypes Generic types
555 * @param templateType Template type of the 'this' type
556 */
557 11195 p addUsedTemplateType(Vector<GenericType>& genericTypes, const QualType& templateType) {
558 11195 bool isContained = false;
559
2/2
✓ Branch 0 taken 10459 times.
✓ Branch 1 taken 2728 times.
13187 foreach const GenericType& genericType : genericTypes {
560
2/2
✓ Branch 0 taken 736 times.
✓ Branch 1 taken 1992 times.
2728 if genericType.qualType == templateType {
561 736 isContained = true;
562 736 break;
563 }
564 1992 }
565
2/2
✓ Branch 0 taken 736 times.
✓ Branch 1 taken 10459 times.
11195 if !isContained {
566 10459 genericTypes.pushBack(GenericType(templateType));
567 10459 }
568 11195 GenericType& lastGenericType = genericTypes.back();
569 11195 lastGenericType.used = true;
570 11195 }
571
572 // ==================================================== Top level definitions ====================================================
573
574 2109 public f<Any> TypeChecker.visitMainFctDef(ASTMainFctDefNode* node) {
575
2/2
✓ Branch 0 taken 1135 times.
✓ Branch 1 taken 974 times.
2109 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
576 974 return this.visitMainFctDefPrepare(node);
577 }
578 1135 return this.visitMainFctDefCheck(node);
579 }
580
581 89813 public f<Any> TypeChecker.visitFctDef(ASTFctDefNode* node) {
582
2/2
✓ Branch 0 taken 61724 times.
✓ Branch 1 taken 28089 times.
89813 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
583 28089 return this.visitFctDefPrepare(node);
584 }
585 61724 return this.visitFctDefCheck(node);
586 }
587
588 57426 public f<Any> TypeChecker.visitProcDef(ASTProcDefNode* node) {
589
2/2
✓ Branch 0 taken 39806 times.
✓ Branch 1 taken 17620 times.
57426 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
590 17620 return this.visitProcDefPrepare(node);
591 }
592 39806 return this.visitProcDefCheck(node);
593 }
594
595 9310 public f<Any> TypeChecker.visitStructDef(ASTStructDefNode* node) {
596
2/2
✓ Branch 0 taken 6085 times.
✓ Branch 1 taken 3225 times.
9310 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
597 3225 return this.visitStructDefPrepare(node);
598 }
599 6085 return this.visitStructDefCheck(node);
600 }
601
602 678 public f<Any> TypeChecker.visitInterfaceDef(ASTInterfaceDefNode* node) {
603
2/2
✓ Branch 0 taken 355 times.
✓ Branch 1 taken 323 times.
678 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
604 323 return this.visitInterfaceDefPrepare(node);
605 }
606 355 return Any();
607 }
608
609 46 public f<Any> TypeChecker.visitUnionDef(ASTUnionDefNode* node) {
610
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 24 times.
46 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
611 24 return this.visitUnionDefPrepare(node);
612 }
613 22 return this.visitUnionDefCheck(node);
614 }
615
616 /**
617 * Assign the opaque type to a struct, interface, enum or alias that is referenced before it has been prepared. This
618 * effectively acts as an implicit forward declaration and is what makes circular imports work: two types in mutually
619 * importing files can reference each other, so neither can be prepared strictly before the other. The full prepare pass
620 * (visitStructDefPrepare / visitInterfaceDefPrepare / visitEnumDefPrepare / visitAliasDefPrepare) assigns the identical
621 * interned type later and additionally fills in the body scope and manifestations.
622 *
623 * Only non-generic structs/interfaces can be pre-declared this way, since the opaque type carries no template types.
624 *
625 * @param entry Symbol table entry of the referenced struct, interface, enum or alias (its type must still be invalid)
626 */
627 10 p TypeChecker.assignDeferredOpaqueType(SymbolTableEntry* entry) {
628 10 const QualType entryType = entry.getQualType();
629 10 assert entryType.is(SuperType::TY_INVALID);
630 10 ASTNode* declNode = entry.declNode;
631 10 TypeRegistry& typeRegistry = this.resourceManager.typeRegistry;
632 10 const QualTypeList noTemplateTypes;
633 10 TypeChainElementData data;
634
5/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
10 switch declNode.kind {
635 case ASTNodeKind::STRUCT_DEF: {
636 5 const ASTStructDefNode* structDef = downcast<ASTStructDefNode>(declNode);
637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if structDef.hasTemplateTypes {
638 ✗ return;
639 }
640 5 data.bodyScope = structDef.structScope;
641 5 const Type* rawType = typeRegistry.getOrInsert(SuperType::TY_STRUCT, structDef.structName, structDef.typeId, data, noTemplateTypes);
642 5 entry.updateType(QualType(rawType, structDef.qualifiers), false);
643 5 }
644 case ASTNodeKind::INTERFACE_DEF: {
645 1 const ASTInterfaceDefNode* interfaceDef = downcast<ASTInterfaceDefNode>(declNode);
646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if interfaceDef.hasTemplateTypes {
647 ✗ return;
648 }
649 1 data.bodyScope = interfaceDef.interfaceScope;
650 1 const Type* rawType = typeRegistry.getOrInsert(SuperType::TY_INTERFACE, interfaceDef.interfaceName, interfaceDef.typeId, data, noTemplateTypes);
651 1 entry.updateType(QualType(rawType, interfaceDef.qualifiers), false);
652 1 }
653 case ASTNodeKind::UNION_DEF: {
654 ✗ const ASTUnionDefNode* unionDef = downcast<ASTUnionDefNode>(declNode);
655
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if unionDef.hasTemplateTypes {
656 ✗ return;
657 }
658 ✗ data.bodyScope = unionDef.unionScope;
659 ✗ const Type* rawType = typeRegistry.getOrInsert(SuperType::TY_UNION, unionDef.unionName, unionDef.typeId, data, noTemplateTypes);
660 ✗ entry.updateType(QualType(rawType, unionDef.qualifiers), false);
661 ✗ }
662 case ASTNodeKind::ENUM_DEF: {
663 1 const ASTEnumDefNode* enumDef = downcast<ASTEnumDefNode>(declNode);
664 1 data.bodyScope = enumDef.enumScope;
665 1 const Type* rawType = typeRegistry.getOrInsert(SuperType::TY_ENUM, enumDef.enumName, enumDef.typeId, data, noTemplateTypes);
666 1 entry.updateType(QualType(rawType, enumDef.qualifiers), false);
667 1 }
668 case ASTNodeKind::ALIAS_DEF: {
669 // An alias additionally needs its aliased type resolved, so prepare it fully on demand. visitAliasDefPrepare is
670 // idempotent: the real prepare pass for the alias's own file skips it once the type is no longer invalid.
671 // The aliased type has to be resolved in the context of the file declaring the alias, not the referencing one,
672 // because the referencing file does not necessarily import the types the alias refers to.
673 2 TypeChecker declTypeChecker = TypeChecker(this.resourceManager, entry.scope.sourceFile, TypeCheckerMode::TC_MODE_PRE);
674 2 declTypeChecker.visitAliasDefPrepare(downcastMut<ASTAliasDefNode>(declNode));
675 2 }
676 1 default: {}
677 }
678 10 }
679
680 1378 public f<Any> TypeChecker.visitEnumDef(ASTEnumDefNode* node) {
681
2/2
✓ Branch 0 taken 915 times.
✓ Branch 1 taken 463 times.
1378 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
682 463 return this.visitEnumDefPrepare(node);
683 }
684 915 return Any();
685 }
686
687 9769 public f<Any> TypeChecker.visitGenericTypeDef(ASTGenericTypeDefNode* node) {
688
2/2
✓ Branch 0 taken 6707 times.
✓ Branch 1 taken 3062 times.
9769 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
689 3062 return this.visitGenericTypeDefPrepare(node);
690 }
691 6707 return Any();
692 }
693
694 405 public f<Any> TypeChecker.visitAliasDef(ASTAliasDefNode* node) {
695
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 157 times.
405 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
696 157 return this.visitAliasDefPrepare(node);
697 }
698 248 return Any();
699 }
700
701 10737 public f<Any> TypeChecker.visitGlobalVarDef(ASTGlobalVarDefNode* node) {
702
2/2
✓ Branch 0 taken 6457 times.
✓ Branch 1 taken 4280 times.
10737 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
703 4280 return this.visitGlobalVarDefPrepare(node);
704 }
705 6457 return Any();
706 }
707
708 16872 public f<Any> TypeChecker.visitExtDecl(ASTExtDeclNode* node) {
709
2/2
✓ Branch 0 taken 11646 times.
✓ Branch 1 taken 5226 times.
16872 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
710 5226 return this.visitExtDeclPrepare(node);
711 }
712 11646 return Any();
713 }
714
715 7147 public f<Any> TypeChecker.visitImportDef(ASTImportDefNode* node) {
716
2/2
✓ Branch 0 taken 4646 times.
✓ Branch 1 taken 2501 times.
7147 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_PRE {
717 2501 return this.visitImportDefPrepare(node);
718 }
719 4646 return Any();
720 }
721
722 // ============================================= Top level definitions (check stage) =============================================
723
724 1135 f<Any> TypeChecker.visitMainFctDefCheck(ASTMainFctDefNode* node) {
725 // Skip if already type-checked
726
2/2
✓ Branch 0 taken 164 times.
✓ Branch 1 taken 971 times.
1135 if this.typeCheckedMainFct {
727 164 return Any();
728 }
729
730 971 ASTNode* baseNode = cast<ASTNode*>(node);
731 971 baseNode.resizeToNumberOfManifestations(1l);
732
733 // Change to function body scope
734 971 this.currentScope = node.bodyScope;
735 // Visit statements in new scope
736 971 this.visit(node.body);
737 // Leave main function body scope
738 971 this.currentScope = this.rootScope;
739
740 // Set to type-checked
741 971 this.typeCheckedMainFct = true;
742 1942 return Any();
743 971 }
744
745 61724 f<Any> TypeChecker.visitFctDefCheck(ASTFctDefNode* node) {
746 61724 return this.checkFctOrProcDef(&node.node, cast<ASTNode*>(node), /*isFunction=*/true, /*isCtor=*/false);
747 }
748
749 39806 f<Any> TypeChecker.visitProcDefCheck(ASTProcDefNode* node) {
750 39806 return this.checkFctOrProcDef(&node.node, cast<ASTNode*>(node), /*isFunction=*/false, node.isCtor);
751 }
752
753 /**
754 * Type-check the bodies of all manifestations of a function or procedure definition
755 *
756 * @param fctDefBase Function or procedure definition node
757 * @param baseNode AST node of the definition
758 * @param isFunction Whether the definition is a function (otherwise a procedure)
759 * @param isCtor Whether the definition is a constructor
760 */
761 101530 f<Any> TypeChecker.checkFctOrProcDef(ASTFctDefBaseNode* fctDefBase, ASTNode* baseNode, bool isFunction, bool isCtor) {
762 101530 baseNode.resizeToNumberOfManifestations(fctDefBase.manifestations.getSize());
763 101530 this.manIdx = 0l; // Reset the manifestation index
764
765 // Get all manifestations for this function/procedure definition
766 // Only visit the manifestations, that exist at this point (manifestations inserted during the loop are not covered by the
767 // resize above and get checked in a revisit)
768 101530 const unsigned long manifestationCount = fctDefBase.manifestations.getSize();
769
2/2
✓ Branch 0 taken 133106 times.
✓ Branch 1 taken 101530 times.
234636 for unsigned long manifestationIdx = 0l; manifestationIdx < manifestationCount; manifestationIdx++ {
770 133106 Function* manifestation = fctDefBase.manifestations.get(manifestationIdx);
771 // Skip non-substantiated or already checked functions/procedures
772
4/4
✓ Branch 0 taken 36582 times.
✓ Branch 1 taken 96524 times.
✓ Branch 2 taken 88408 times.
✓ Branch 3 taken 44698 times.
133106 if !manifestation.isFullySubstantiated() || manifestation.alreadyTypeChecked {
773 88408 this.manIdx++; // Increase the manifestation index
774 88408 continue;
775 }
776
777 // Change scope to concrete struct specialization scope or to the interface scope for default methods
778
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 44688 times.
44698 if manifestation.isInterfaceDefaultMethod {
779 10 this.checkDefaultMethodImplementsInterfaceMethod(manifestation);
780 10 this.compilerPass.changeToScope(fctDefBase.structScope, ScopeType::INTERFACE);
781
2/2
✓ Branch 0 taken 17466 times.
✓ Branch 1 taken 27222 times.
44698 } else if fctDefBase.isMethod {
782 27222 const QualTypeList thisTemplateTypes = manifestation.thisType.getTemplateTypes();
783 27222 const String scopeName = getStructScopeName(fctDefBase.name.structName, thisTemplateTypes);
784 27222 this.compilerPass.changeToScope(scopeName, ScopeType::STRUCT);
785 44688 }
786
787 // Change to function/procedure scope
788 44698 this.compilerPass.changeToScope(manifestation.bodyScope, ScopeType::FUNC_PROC_BODY);
789
790 // Mount type mapping for this manifestation
791 44698 assert this.typeMapping.isEmpty();
792
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44698 times.
44698 this.typeMapping = manifestation.typeMapping;
793
794 // Set return type to the result variable. A manifestation for a capturing lambda argument copies the body scope of an
795 // already type-checked manifestation, so the result variable might already have a type
796
2/2
✓ Branch 0 taken 17264 times.
✓ Branch 1 taken 27434 times.
44698 if isFunction {
797 27434 SymbolTableEntry* resultVarEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME));
798 27434 assert resultVarEntry != nil<SymbolTableEntry*>;
799 27434 resultVarEntry.updateType(manifestation.returnType, true);
800 27434 resultVarEntry.used = true;
801 27434 }
802
803 // Visit parameters
804 // This happens once in the type checker prepare stage. This second time is only required if we have a generic function/procedure
805
2/2
✓ Branch 0 taken 12611 times.
✓ Branch 1 taken 32087 times.
44698 if fctDefBase.hasParams {
806 32087 this.visit(fctDefBase.paramLst);
807 32087 this.annotateLambdaCaptureParams(fctDefBase, manifestation);
808 32087 }
809
810 // Prepare generation of special ctor preamble to store VTable, default field values, etc. if required
811
2/2
✓ Branch 0 taken 39135 times.
✓ Branch 1 taken 5563 times.
44698 if isCtor {
812 5563 this.createCtorBodyPreamble(manifestation.bodyScope);
813 5563 }
814
815 // Visit statements in new scope
816 44698 this.visit(fctDefBase.body);
817
818 // Clear type mapping
819 44698 this.typeMapping.clear();
820
821 // Change to root scope
822 44698 this.currentScope = this.rootScope;
823 44698 assert this.currentScope != nil<Scope*> && this.currentScope.scopeType == ScopeType::GLOBAL;
824
825 // Do not type-check this manifestation again
826 44698 manifestation.alreadyTypeChecked = true;
827
828 44698 this.manIdx++; // Increase the manifestation index
829 44698 }
830 101530 this.manIdx = 0l; // Reset the manifestation index
831
832 101530 return Any();
833 }
834
835 /**
836 * Annotate function/procedure-typed params with lambda-capture info from the resolved manifestation type.
837 * This must happen in the TypeChecker so that the IRGenerator can treat SymbolTableEntry as immutable.
838 *
839 * @param fctDefBase Function or procedure definition node
840 * @param manifestation Manifestation, whose params to annotate
841 */
842 32086 p TypeChecker.annotateLambdaCaptureParams(ASTFctDefBaseNode* fctDefBase, const Function* manifestation) {
843 32086 const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(getASTNode(fctDefBase.paramLst));
844 32086 const QualTypeList paramTypes = manifestation.getParamTypes();
845 32086 const dyn fctOptions = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
846
2/2
✓ Branch 0 taken 50825 times.
✓ Branch 1 taken 32086 times.
82911 for unsigned long i = 0l; i < manifestation.paramList.getSize(); i++ {
847 50825 const ASTDeclStmtNode* param = paramLst.params.get(i);
848 50825 const QualType& paramType = paramTypes.get(i);
849
4/4
✓ Branch 0 taken 50701 times.
✓ Branch 1 taken 124 times.
✓ Branch 2 taken 50789 times.
✓ Branch 3 taken 36 times.
50825 if paramType.isOneOf(fctOptions, len(fctOptions)) && paramType.hasLambdaCaptures() {
850 36 SymbolTableEntry* paramSymbol = this.currentScope.lookupStrict(param.varName);
851 36 assert paramSymbol != nil<SymbolTableEntry*>;
852 36 const QualType paramSymbolType = paramSymbol.getQualType();
853 36 paramSymbol.updateType(paramSymbolType.getWithLambdaCaptures(), true);
854 36 }
855 50825 }
856 32086 }
857
858 /**
859 * Check if a function or procedure, that is defined as method of an interface, is a valid default method.
860 *
861 * @param fctDefBase Function or procedure definition node of the default method
862 * @param interfaceType Type of the interface, the default method belongs to
863 * @param paramList Parameters of the default method
864 * @return Valid default method or not
865 */
866 13 f<bool> TypeChecker.checkDefaultMethod(ASTFctDefBaseNode* fctDefBase, const QualType& interfaceType, const ParamList& paramList) {
867 13 ASTNode* baseNode = cast<ASTNode*>(fctDefBase);
868 13 const String& name = fctDefBase.name.name;
869
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 12 times.
13 if name == CTOR_FUNCTION_NAME || name == DTOR_FUNCTION_NAME {
870 1 throwSemanticError(baseNode, SemanticErrorType::INVALID_DEFAULT_METHOD, String("Interfaces cannot have constructors or destructors"));
871 1 return false;
872 1 }
873 12 const QualTypeList interfaceTemplateTypes = interfaceType.getTemplateTypes();
874
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 11 times.
12 if fctDefBase.hasTemplateTypes || !interfaceTemplateTypes.isEmpty() {
875 1 throwSemanticError(baseNode, SemanticErrorType::INVALID_DEFAULT_METHOD, String("Default methods of generic interfaces are not supported yet"));
876 1 return false;
877 1 }
878
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
16 foreach const Param& param : paramList {
879
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
6 if param.isOptional {
880 1 throwSemanticError(getASTNode(fctDefBase.paramLst), SemanticErrorType::INVALID_DEFAULT_METHOD, String("Default methods cannot have optional parameters"));
881 1 return false;
882 1 }
883 5 }
884 10 return true;
885 12 }
886
887 /**
888 * Check if a default method implements a method signature of its interface. The parameter and return types have to match
889 * exactly, because the default method is called through the vtable slot of the interface method.
890 *
891 * @param defaultMethod Default method to check
892 */
893 10 p TypeChecker.checkDefaultMethodImplementsInterfaceMethod(const Function* defaultMethod) {
894 10 const Interface* spiceInterface = defaultMethod.thisType.getInterface(defaultMethod.declNode);
895 10 assert spiceInterface != nil<const Interface*>;
896 10 const QualTypeList paramTypes = defaultMethod.getParamTypes();
897
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1 time.
20 foreach const Function* method : spiceInterface.methods {
898
6/6
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 10 times.
19 if method.name == defaultMethod.name && method.getParamTypes() == paramTypes && method.returnType == defaultMethod.returnType {
899 9 return;
900 }
901 10 }
902 1 throwSemanticError(defaultMethod.declNode, SemanticErrorType::INVALID_DEFAULT_METHOD, "The default method '" + defaultMethod.getSignature() + "' does not implement any method of the interface '" + spiceInterface.structBase.name + "'");
903 10 }
904
905 6085 f<Any> TypeChecker.visitStructDefCheck(ASTStructDefNode* node) {
906 6085 ASTNode* baseNode = cast<ASTNode*>(node);
907 6085 baseNode.resizeToNumberOfManifestations(node.structManifestations.getSize());
908 6085 this.manIdx = 0l; // Reset the manifestation index
909
910 // Get all manifestations for this procedure definition
911 // Only visit the manifestations, that exist at this point (manifestations inserted during the loop are not covered by the
912 // resize above and get checked in a revisit)
913 6085 const unsigned long manifestationCount = node.structManifestations.getSize();
914
2/2
✓ Branch 0 taken 10430 times.
✓ Branch 1 taken 6085 times.
16515 for unsigned long manifestationIdx = 0l; manifestationIdx < manifestationCount; manifestationIdx++ {
915 10430 Struct* manifestation = node.structManifestations.get(manifestationIdx);
916 // Skip non-substantiated or already checked procedures
917
2/2
✓ Branch 0 taken 2352 times.
✓ Branch 1 taken 8078 times.
10430 if !manifestation.structBase.isFullySubstantiated() {
918 2352 this.manIdx++; // Increase the manifestation index
919 2352 continue;
920 }
921
922 // Fallback for manifestations that could not be decided on when they were created, because a by-value struct field
923 // was not manifested yet at that point (circular import). By now the whole import graph is prepared, so the
924 // decision can be taken - late, but still before the struct's own default member bodies are prepared below.
925 8078 createImplicitDefaultMembers(*manifestation, baseNode, /*withMoveCtor=*/false);
926
927 // Change to struct scope
928 8078 this.compilerPass.changeToScope(manifestation.structBase.scope, ScopeType::STRUCT);
929
930 // Mount type mapping for this manifestation, so that the body preamble helpers below can substantiate
931 // generic field types (e.g. `heap T*` on `Vector<T>`). Without this, an auto-generated body preamble that
932 // touches a still-generic field would assert in substantiateTypeWithTypeMapping.
933 8078 assert this.typeMapping.isEmpty();
934
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8078 times.
8078 this.typeMapping = manifestation.structBase.typeMapping;
935
936 // Re-visit all default values. This is required, since the type of the default value might vary for different manifestations
937
2/2
✓ Branch 0 taken 20661 times.
✓ Branch 1 taken 8078 times.
28739 foreach IVisitable* fieldVisitable : node.fields {
938 20661 const ASTFieldNode* field = downcast<ASTFieldNode>(getASTNode(fieldVisitable));
939
2/2
✓ Branch 0 taken 14411 times.
✓ Branch 1 taken 6250 times.
20661 if field.defaultValue != nil<IVisitable*> {
940 6250 this.visit(field.defaultValue);
941 6250 SymbolTableEntry* fieldEntry = manifestation.structBase.scope.lookupStrict(field.fieldName);
942 6250 assert fieldEntry != nil<SymbolTableEntry*>;
943 6250 fieldEntry.updateState(LifecycleState::INITIALIZED);
944 6250 }
945 20661 }
946
947 // Build struct type
948 8078 const QualType structType = manifestation.structBase.entry.getQualType();
949
950 // Check if the struct implements all methods of all attached interfaces
951 8078 unsigned long vtableIndex = 0l;
952
2/2
✓ Branch 0 taken 1663 times.
✓ Branch 1 taken 8078 times.
9741 foreach const QualType& interfaceType : manifestation.interfaceTypes {
953 1663 const Interface* spiceInterface = interfaceType.getInterface(baseNode);
954 1663 assert spiceInterface != nil<const Interface*>;
955
956 // Check for all methods, that it is implemented by the struct
957
2/2
✓ Branch 0 taken 4145 times.
✓ Branch 1 taken 1663 times.
5808 foreach const Function* expMethod : spiceInterface.methods {
958 4145 const String methodName = expMethod.name;
959 4145 QualTypeList params = expMethod.getParamTypes();
960 4145 QualType returnType = expMethod.returnType;
961
962 // Substantiate param and return types
963 4145 substantiateTypesWithTypeMapping(params, spiceInterface.structBase.typeMapping, baseNode);
964
2/2
✓ Branch 0 taken 1748 times.
✓ Branch 1 taken 2397 times.
4145 if returnType.hasAnyGenericParts() {
965 2397 substantiateTypeWithTypeMapping(returnType, spiceInterface.structBase.typeMapping, baseNode);
966 2397 }
967
968 // Build args list
969 4145 ArgList args;
970 4145 args.reserve(params.getSize());
971
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 4145 times.
4181 foreach const QualType& param : params {
972 36 args.pushBack(Pair<QualType, bool>(param, false));
973 36 }
974
975 // Search for method that has the required signature
976 4145 const QualTypeList noTemplateTypeHints;
977 4145 Function* spiceFunction = matchFunction(this.currentScope, methodName, structType, args, noTemplateTypeHints, true, baseNode);
978 // If the struct does not implement the method itself, fall back to the default method of the interface
979
2/2
✓ Branch 0 taken 4140 times.
✓ Branch 1 taken 5 times.
4145 if spiceFunction == nil<Function*> {
980 5 spiceFunction = inheritDefaultMethod(this.currentScope, structType, spiceInterface, methodName, params, returnType);
981 5 }
982
2/2
✓ Branch 0 taken 4140 times.
✓ Branch 1 taken 5 times.
4145 if spiceFunction == nil<Function*> {
983 5 this.softError(baseNode, SemanticErrorType::INTERFACE_METHOD_NOT_IMPLEMENTED, "The struct '" + node.structName + "' does not implement method '" + expMethod.getSignature() + "'.");
984 5 continue;
985 5 }
986
987 // The default methods of two interfaces must not implement the same method of the struct
988
4/4
✓ Branch 0 taken 4124 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 4139 times.
4140 if spiceFunction.isInheritedDefaultMethod() && spiceFunction.defaultMethod.thisType.getBodyScope() != interfaceType.getBodyScope() {
989 1 this.softError(baseNode, SemanticErrorType::INTERFACE_METHOD_NOT_IMPLEMENTED, "The struct '" + node.structName + "' implements multiple interfaces with method '" + expMethod.getSignature() + "', which has a default implementation. Please implement it in the struct.");
990 1 continue;
991 1 }
992
993 // Check return type
994
4/4
✓ Branch 0 taken 3373 times.
✓ Branch 1 taken 766 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4137 times.
4139 if spiceFunction.returnType != returnType && !returnType.matchesInterfaceImplementedByStruct(spiceFunction.returnType) {
995 2 this.softError(baseNode, SemanticErrorType::INTERFACE_METHOD_NOT_IMPLEMENTED, "The struct '" + node.structName + "' does not implement method '" + expMethod.getSignature() + "'. The return type does not match.");
996 2 continue;
997 2 }
998 // Set to virtual, since it overrides the interface method
999 4137 spiceFunction.isVirtual = true;
1000 4137 spiceFunction.vtableIndex = vtableIndex++;
1001 4145 }
1002 1663 }
1003
1004 // Check default ctor body if required
1005 8078 const ArgList noArgs;
1006 8078 const Function* ctorFunc = lookupFunction(this.currentScope, String(CTOR_FUNCTION_NAME), structType, noArgs, true);
1007
4/4
✓ Branch 0 taken 5122 times.
✓ Branch 1 taken 2956 times.
✓ Branch 2 taken 7746 times.
✓ Branch 3 taken 332 times.
8078 if ctorFunc != nil<const Function*> && ctorFunc.implicitDefault {
1008 332 this.createCtorBodyPreamble(ctorFunc.bodyScope);
1009 332 assert manifestation.areAllFieldsInitialized() == nil<SymbolTableEntry*>;
1010 332 }
1011
1012 // Check default copy ctor body if required
1013 8078 ArgList args;
1014 8078 args.pushBack(Pair<QualType, bool>(structType.toConstRef(baseNode), false /* always non-temporary */));
1015 8078 const Function* copyCtorFunc = lookupFunction(this.currentScope, String(CTOR_FUNCTION_NAME), structType, args, true);
1016
4/4
✓ Branch 0 taken 5541 times.
✓ Branch 1 taken 2537 times.
✓ Branch 2 taken 6806 times.
✓ Branch 3 taken 1272 times.
8078 if copyCtorFunc != nil<const Function*> && copyCtorFunc.implicitDefault {
1017 1272 this.createCopyCtorBodyPreamble(copyCtorFunc.bodyScope);
1018 1272 assert manifestation.areAllFieldsInitialized() == nil<SymbolTableEntry*>;
1019 1272 }
1020
1021 // Check default move ctor body if required. findMoveCtor scans the manifestations directly to avoid the
1022 // constify-based false-positive that lookupFunction with a non-const ref arg can produce.
1023 8078 const Function* moveCtorFunc = findMoveCtor(this.currentScope);
1024
4/4
✓ Branch 0 taken 7900 times.
✓ Branch 1 taken 178 times.
✓ Branch 2 taken 8042 times.
✓ Branch 3 taken 36 times.
8078 if moveCtorFunc != nil<const Function*> && moveCtorFunc.implicitDefault {
1025 36 this.createMoveCtorBodyPreamble(moveCtorFunc.bodyScope);
1026 36 assert manifestation.areAllFieldsInitialized() == nil<SymbolTableEntry*>;
1027 36 }
1028
1029 // Check default dtor body if required
1030 8078 const Function* dtorFunc = lookupFunction(this.currentScope, String(DTOR_FUNCTION_NAME), structType, noArgs, true);
1031
4/4
✓ Branch 0 taken 5094 times.
✓ Branch 1 taken 2984 times.
✓ Branch 2 taken 6932 times.
✓ Branch 3 taken 1146 times.
8078 if dtorFunc != nil<const Function*> && dtorFunc.implicitDefault {
1032 1146 this.createDtorBodyPreamble(dtorFunc.bodyScope, baseNode);
1033 1146 }
1034
1035 // Reset field symbols to declared state for the next manifestation
1036 8078 manifestation.resetFieldSymbolsToDeclared();
1037
1038 // Clear type mapping
1039 8078 this.typeMapping.clear();
1040
1041 // Return to the root scope
1042 8078 this.currentScope = this.rootScope;
1043 8078 assert this.currentScope != nil<Scope*> && this.currentScope.scopeType == ScopeType::GLOBAL;
1044
1045 8078 this.manIdx++; // Increase the manifestation index
1046 8078 }
1047 6085 this.manIdx = 0l; // Reset the manifestation index
1048
1049 6085 return Any();
1050 }
1051
1052 22 f<Any> TypeChecker.visitUnionDefCheck(ASTUnionDefNode* node) {
1053 22 ASTNode* baseNode = cast<ASTNode*>(node);
1054 22 baseNode.resizeToNumberOfManifestations(node.unionManifestations.getSize());
1055 22 this.manIdx = 0l; // Reset the manifestation index
1056
1057 // Get all manifestations for this union definition
1058 // Only visit the manifestations, that exist at this point (manifestations inserted during the loop are not covered by the
1059 // resize above and get checked in a revisit)
1060 22 const unsigned long manifestationCount = node.unionManifestations.getSize();
1061
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 22 times.
46 for unsigned long manifestationIdx = 0l; manifestationIdx < manifestationCount; manifestationIdx++ {
1062 24 Union* manifestation = node.unionManifestations.get(manifestationIdx);
1063 // Skip non-substantiated manifestations
1064
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 20 times.
24 if !manifestation.structBase.isFullySubstantiated() {
1065 4 this.manIdx++; // Increase the manifestation index
1066 4 continue;
1067 }
1068
1069 // Change to union scope
1070 20 this.compilerPass.changeToScope(manifestation.structBase.scope, ScopeType::UNION);
1071
1072 // Mount type mapping for this manifestation, so that the default value below can substantiate generic field types
1073 20 assert this.typeMapping.isEmpty();
1074
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 this.typeMapping = manifestation.structBase.typeMapping;
1075
1076 // Re-visit the default value (at most one field may have one). This is required, since the type of the default
1077 // value might vary for different manifestations
1078
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 20 times.
64 foreach IVisitable* fieldVisitable : node.fields {
1079 44 const ASTFieldNode* field = downcast<ASTFieldNode>(getASTNode(fieldVisitable));
1080
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 4 times.
44 if field.defaultValue != nil<IVisitable*> {
1081 4 this.visit(field.defaultValue);
1082 4 SymbolTableEntry* fieldEntry = manifestation.structBase.scope.lookupStrict(field.fieldName);
1083 4 assert fieldEntry != nil<SymbolTableEntry*>;
1084 4 fieldEntry.updateState(LifecycleState::INITIALIZED);
1085 4 }
1086 44 }
1087
1088 // Clear type mapping
1089 20 this.typeMapping.clear();
1090
1091 // Return to the root scope
1092 20 this.currentScope = this.rootScope;
1093 20 assert this.currentScope != nil<Scope*> && this.currentScope.scopeType == ScopeType::GLOBAL;
1094
1095 20 this.manIdx++; // Increase the manifestation index
1096 20 }
1097 22 this.manIdx = 0l; // Reset the manifestation index
1098
1099 22 return Any();
1100 }
1101
1102 // ============================================ Top level definitions (prepare stage) ============================================
1103
1104 974 f<Any> TypeChecker.visitMainFctDefPrepare(ASTMainFctDefNode* node) {
1105 // Mark unreachable statements
1106 974 ASTNode* baseNode = cast<ASTNode*>(node);
1107 974 bool doSetPredecessorsUnreachable = true;
1108 974 const bool _ = baseNode.returnsOnAllControlPaths(&doSetPredecessorsUnreachable, this.manIdx);
1109
1110 // Retrieve return type
1111 974 const QualType returnType = QualType(SuperType::TY_INT);
1112
1113 // Change to function body scope
1114 974 this.currentScope = node.bodyScope;
1115
1116 // Set type of 'result' variable to int
1117 974 SymbolTableEntry* resultEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME));
1118 974 assert resultEntry != nil<SymbolTableEntry*>;
1119 974 resultEntry.updateType(returnType, false);
1120 974 resultEntry.used = true;
1121
1122 // Retrieve param types
1123 974 QualTypeList paramTypes;
1124
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 967 times.
974 if node.takesArgs {
1125 7 Any paramLstResult = this.visit(node.paramLst);
1126 7 const ParamList visitedParams = paramLstResult.get<ParamList>();
1127
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
21 foreach const Param& visitedParam : visitedParams {
1128 14 paramTypes.pushBack(visitedParam.qualType);
1129 14 }
1130 7 }
1131
1132 // Prepare type of function
1133 974 const QualType functionTypeBase = QualType(SuperType::TY_FUNCTION);
1134 974 const QualType functionType = functionTypeBase.getWithFunctionParamAndReturnTypes(returnType, paramTypes);
1135
1136 // Update main function symbol type
1137 974 SymbolTableEntry* functionEntry = this.rootScope.lookupStrict(String(MAIN_FUNCTION_NAME));
1138 974 assert functionEntry != nil<SymbolTableEntry*>;
1139 974 functionEntry.updateType(functionType, false);
1140 974 functionEntry.used = true;
1141
1142 // Leave main function body scope
1143 974 this.currentScope = this.rootScope;
1144
1145 1948 return Any();
1146 974 }
1147
1148 28089 f<Any> TypeChecker.visitFctDefPrepare(ASTFctDefNode* node) {
1149 56178 return this.prepareFctOrProcDef(&node.node, cast<ASTNode*>(node), node.getScopeId(), node.returnType);
1150 28089 }
1151
1152 17620 f<Any> TypeChecker.visitProcDefPrepare(ASTProcDefNode* node) {
1153 35240 return this.prepareFctOrProcDef(&node.node, cast<ASTNode*>(node), node.getScopeId(), nil<ASTDataTypeNode*>);
1154 17620 }
1155
1156 /**
1157 * Prepare a function or procedure definition: check its signature, build the function object and create the body scopes
1158 * of its manifestations
1159 *
1160 * @param fctDefBase Function or procedure definition node
1161 * @param baseNode AST node of the definition
1162 * @param scopeId Scope id of the definition
1163 * @param returnTypeNode Return type of a function, nil for a procedure
1164 */
1165 45706 f<Any> TypeChecker.prepareFctOrProcDef(ASTFctDefBaseNode* fctDefBase, ASTNode* baseNode, const String& scopeId, ASTDataTypeNode* returnTypeNode) {
1166 45706 const bool isFunction = returnTypeNode != nil<ASTDataTypeNode*>;
1167 45706 const string kind = isFunction ? "function" : "procedure";
1168
1169 // Check if name is dtor
1170
4/4
✓ Branch 0 taken 17617 times.
✓ Branch 1 taken 28089 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 45705 times.
45706 if isFunction && fctDefBase.name.name == DTOR_FUNCTION_NAME {
1171 1 this.softError(baseNode, SemanticErrorType::DTOR_MUST_BE_PROCEDURE, String("Destructors are not allowed to be of type function"));
1172 2 return Any();
1173 1 }
1174
1175 // Mark unreachable statements and check if all control paths in a function return
1176 45705 bool doSetPredecessorsUnreachable = true;
1177 45705 const bool returnsOnAllControlPaths = baseNode.returnsOnAllControlPaths(&doSetPredecessorsUnreachable, this.manIdx);
1178
4/4
✓ Branch 0 taken 17619 times.
✓ Branch 1 taken 28086 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 45702 times.
45705 if isFunction && !returnsOnAllControlPaths {
1179 3 this.softError(baseNode, SemanticErrorType::MISSING_RETURN_STMT, String("Not all control paths of this function have a return statement"));
1180 6 return Any();
1181 3 }
1182
1183 // Check if dtor and has params
1184
6/6
✓ Branch 0 taken 17620 times.
✓ Branch 1 taken 28082 times.
✓ Branch 2 taken 4922 times.
✓ Branch 3 taken 12698 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 45701 times.
45702 if !isFunction && fctDefBase.hasParams && fctDefBase.name.name == DTOR_FUNCTION_NAME {
1185 1 throwSemanticError(baseNode, SemanticErrorType::DTOR_WITH_PARAMS, String("It is not allowed to specify parameters for destructors"));
1186 2 return Any();
1187 1 }
1188
1189 // Change to function/procedure scope
1190 45701 this.currentScope = fctDefBase.scope;
1191 45701 assert this.currentScope.scopeType == ScopeType::FUNC_PROC_BODY;
1192
1193 // Retrieve template types
1194 45701 Vector<GenericType> usedGenericTypes;
1195
2/2
✓ Branch 0 taken 38817 times.
✓ Branch 1 taken 6884 times.
45701 if fctDefBase.hasTemplateTypes {
1196
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6882 times.
6884 if !this.collectTemplateTypes(fctDefBase.templateTypeLst, usedGenericTypes) {
1197 4 return Any();
1198 }
1199 6882 }
1200
1201 // Retrieve 'this' type
1202 45699 QualType thisType = QualType(SuperType::TY_DYN); // If the function/procedure is not a method, the default this type is TY_DYN
1203
2/2
✓ Branch 0 taken 19121 times.
✓ Branch 1 taken 26578 times.
45699 if fctDefBase.isMethod {
1204 26578 Scope* structParentScope = fctDefBase.structScope.parent;
1205 26578 SymbolTableEntry* structEntry = structParentScope.lookupStrict(fctDefBase.name.structName);
1206 26578 assert structEntry != nil<SymbolTableEntry*>;
1207 // Set struct to used
1208 26578 structEntry.used = true;
1209 // Get type and ptr type
1210 26578 thisType = structEntry.getQualType();
1211 26578 const QualType thisPtrType = thisType.toPtr(baseNode);
1212 // Collect template types of 'this' type
1213 26578 const QualTypeList thisTemplateTypes = thisType.getTemplateTypes();
1214
2/2
✓ Branch 0 taken 10530 times.
✓ Branch 1 taken 26578 times.
37108 foreach const QualType& templateType : thisTemplateTypes {
1215 10530 addUsedTemplateType(usedGenericTypes, templateType);
1216 10530 }
1217
1218 // Set type of 'this' variable
1219 26578 SymbolTableEntry* thisEntry = this.currentScope.lookupStrict(String(THIS_VARIABLE_NAME));
1220 26578 assert thisEntry != nil<SymbolTableEntry*>;
1221 26578 thisEntry.updateType(thisPtrType, false);
1222 26578 }
1223
1224 // Visit parameters
1225 45699 QualTypeList paramTypes;
1226 45699 ParamList paramList;
1227
2/2
✓ Branch 0 taken 12664 times.
✓ Branch 1 taken 33035 times.
45699 if fctDefBase.hasParams {
1228 // Visit param list to retrieve the params
1229 33035 Any paramLstResult = this.visit(fctDefBase.paramLst);
1230 33035 const ParamList visitedParams = paramLstResult.get<ParamList>();
1231
2/2
✓ Branch 0 taken 53935 times.
✓ Branch 1 taken 33029 times.
86964 foreach const Param& visitedParam : visitedParams {
1232 53935 const QualType qualType = visitedParam.qualType;
1233
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 53932 times.
53935 if qualType.is(SuperType::TY_UNRESOLVED) {
1234 6 return Any();
1235 }
1236 53932 paramTypes.pushBack(qualType);
1237 53932 paramList.pushBack(Param{qualType, visitedParam.isOptional});
1238 // Check if the type is present in the template for generic types
1239
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 53929 times.
53932 if !qualType.isCoveredByGenericTypeList(usedGenericTypes) {
1240 3 const String message = String("Generic param type not included in the template type list of the ") + kind;
1241 3 throwSemanticError(getASTNode(fctDefBase.paramLst), SemanticErrorType::GENERIC_TYPE_NOT_IN_TEMPLATE, message);
1242 6 return Any();
1243 3 }
1244 53929 }
1245 33035 }
1246
1247 // Retrieve return type. Procedures have no return type, which is represented by TY_DYN
1248 45693 QualType returnType = QualType(SuperType::TY_DYN);
1249
2/2
✓ Branch 0 taken 17613 times.
✓ Branch 1 taken 28080 times.
45693 if isFunction {
1250 28080 returnType = this.getDataType(returnTypeNode);
1251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28080 times.
28080 if returnType.is(SuperType::TY_UNRESOLVED) {
1252 ✗ return Any();
1253 }
1254
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 28078 times.
28080 if returnType.is(SuperType::TY_DYN) {
1255 2 this.softError(baseNode, SemanticErrorType::UNEXPECTED_DYN_TYPE, String("Dyn return types are not allowed"));
1256 4 return Any();
1257 2 }
1258
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 28077 times.
28078 if !returnType.isCoveredByGenericTypeList(usedGenericTypes) {
1259 1 this.softError(cast<ASTNode*>(returnTypeNode), SemanticErrorType::GENERIC_TYPE_NOT_IN_TEMPLATE, String("Generic return type not included in the template type list of the function"));
1260 2 return Any();
1261 1 }
1262 28077 }
1263
1264 // Check if the function/procedure is a valid default method, if it is defined as method of an interface
1265
4/4
✓ Branch 0 taken 45677 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 45687 times.
45690 if thisType.is(SuperType::TY_INTERFACE) && !this.checkDefaultMethod(fctDefBase, thisType, paramList) {
1266 3 this.currentScope = this.rootScope;
1267 6 return Any();
1268 }
1269
1270 // Leave function/procedure body scope
1271 45687 this.currentScope = fctDefBase.scope.parent;
1272 45687 assert this.currentScope.scopeType == ScopeType::GLOBAL || this.currentScope.scopeType == ScopeType::STRUCT || this.currentScope.scopeType == ScopeType::INTERFACE;
1273
1274 // Prepare type of function/procedure
1275
2/2
✓ Branch 0 taken 28076 times.
✓ Branch 1 taken 17611 times.
45687 const QualType functionTypeBase = QualType(isFunction ? SuperType::TY_FUNCTION : SuperType::TY_PROCEDURE);
1276 45687 QualType functionType = functionTypeBase.getWithFunctionParamAndReturnTypes(returnType, paramTypes);
1277 45687 functionType.setQualifiers(fctDefBase.qualifiers);
1278
1279 // Update type of function/procedure entry
1280 45687 const String entryName = fctDefBase.getSymbolTableEntryName();
1281 45687 SymbolTableEntry* functionEntry = this.currentScope.lookupStrict(entryName);
1282 45687 assert functionEntry != nil<SymbolTableEntry*>;
1283 45687 functionEntry.updateType(functionType, false);
1284
1285 // Build function object
1286 45687 Function spiceFunc = Function(fctDefBase.name.name, functionEntry, thisType, returnType, paramList, usedGenericTypes, baseNode);
1287 45687 spiceFunc.bodyScope = fctDefBase.scope;
1288 45687 spiceFunc.isInterfaceDefaultMethod = thisType.is(SuperType::TY_INTERFACE);
1289 45687 insertFunction(this.currentScope, spiceFunc, &fctDefBase.manifestations);
1290 // Stop at a duplicate declaration, the compilation is aborted after this stage
1291 45687 const ErrorManager& errorManager = this.resourceManager.getErrorManager();
1292
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 45685 times.
45687 if errorManager.hasHardError {
1293 2 this.currentScope = this.rootScope;
1294 4 return Any();
1295 }
1296
1297 // Check function/procedure attributes
1298
2/2
✓ Branch 0 taken 44571 times.
✓ Branch 1 taken 1114 times.
45685 if fctDefBase.attrs != nil<IVisitable*> {
1299 1114 const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(fctDefBase.attrs);
1300 1114 ASTAttrLstNode* attrLst = attrs.attrLst;
1301 1114 Function* firstManifestation = fctDefBase.manifestations.front();
1302
2/2
✓ Branch 0 taken 1112 times.
✓ Branch 1 taken 2 times.
1114 if attrLst.hasAttr(String(ATTR_CORE_COMPILER_MANGLE)) {
1303 2 const CompileTimeValue value = attrLst.getAttrValueByName(String(ATTR_CORE_COMPILER_MANGLE));
1304 2 firstManifestation.mangleFunctionName = value.getBoolValue();
1305 2 }
1306
2/2
✓ Branch 0 taken 1113 times.
✓ Branch 1 taken 1 time.
1114 if attrLst.hasAttr(String(ATTR_CORE_COMPILER_MANGLED_NAME)) {
1307 1 const CompileTimeValue value = attrLst.getAttrValueByName(String(ATTR_CORE_COMPILER_MANGLED_NAME));
1308 1 firstManifestation.predefinedMangledName = this.resourceManager.getCompileTimeStringValue(value.getStringValueOffset());
1309 1 }
1310 // The TPDE backend always uses the red zone in x86_64 leaf functions (see the --no-red-zone check in the driver)
1311
2/2
✓ Branch 0 taken 1109 times.
✓ Branch 1 taken 5 times.
1114 if attrLst.hasAttr(String(ATTR_CORE_COMPILER_RED_ZONE)) {
1312 5 const CompileTimeValue value = attrLst.getAttrValueByName(String(ATTR_CORE_COMPILER_RED_ZONE));
1313
6/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 4 times.
5 if !value.getBoolValue() && this.cliOptions.backend == Backend::TPDE && this.cliOptions.targetArch == "x86_64" {
1314 1 throwSemanticError(getASTNode(attrLst), SemanticErrorType::UNSUPPORTED_ATTR_VALUE, String("The TPDE backend does not support disabling the red zone on x86_64"));
1315 2 return Any();
1316 1 }
1317 5 }
1318
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1111 times.
✓ Branch 2 taken 1075 times.
✓ Branch 3 taken 38 times.
1113 if isFunction && attrLst.hasAttr(String(ATTR_TEST)) {
1319 38 const CompileTimeValue value = attrLst.getAttrValueByName(String(ATTR_TEST));
1320
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 36 times.
38 if value.getBoolValue() {
1321 // Make sure that the function has the correct signature
1322
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 35 times.
36 if fctDefBase.hasParams {
1323 1 throwSemanticError(getASTNode(fctDefBase.paramLst), SemanticErrorType::TEST_FUNCTION_WITH_PARAMS, String("Test function may not have parameters"));
1324 2 return Any();
1325 1 }
1326
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 34 times.
35 if !returnType.is(SuperType::TY_BOOL) {
1327 1 throwSemanticError(cast<ASTNode*>(returnTypeNode), SemanticErrorType::TEST_FUNCTION_WRONG_RETURN_TYPE, String("Test function must return a bool"));
1328 2 return Any();
1329 1 }
1330 // Add to test function list
1331 34 firstManifestation.entry.used = true; // Avoid printing unused warnings
1332 // Only keep test functions, when the test main calls them implicitly. Otherwise, they are not emitted at all
1333
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 21 times.
34 if this.cliOptions.generateTestMain {
1334 21 firstManifestation.used = true;
1335 21 }
1336 34 const Function* testFunction = firstManifestation;
1337 34 this.sourceFile.testFunctions.pushBack(testFunction);
1338 34 }
1339 38 }
1340 1111 }
1341
1342 // Duplicate / rename the original child scope to reflect the substantiated versions of the function/procedure
1343
2/2
✓ Branch 0 taken 2685 times.
✓ Branch 1 taken 45682 times.
48367 for unsigned long i = 1l; i < fctDefBase.manifestations.getSize(); i++ {
1344 2685 Function* manifestation = fctDefBase.manifestations.get(i);
1345 2685 manifestation.bodyScope = this.currentScope.copyChildScope(scopeId, manifestation.getScopeName());
1346 2685 }
1347 45682 Function* firstManifestation = fctDefBase.manifestations.front();
1348 45682 this.currentScope.renameChildScope(scopeId, firstManifestation.getScopeName());
1349
1350 // Change to the root scope
1351 45682 this.currentScope = this.rootScope;
1352 45682 assert this.currentScope.scopeType == ScopeType::GLOBAL;
1353
1354 91364 return Any();
1355 45701 }
1356
1357 /**
1358 * Determine whether a field type (transitively) contains a struct or union with the given origin body scope by value,
1359 * which would give the origin struct/union an infinite size. Only by-value struct/union fields are followed, including
1360 * through fixed-size arrays thereof (e.g. `T[3]`), since those embed their element by value as well. Dynamic arrays,
1361 * pointers and references break the cycle and have a fixed size. Struct/union types that are not manifested yet (e.g.
1362 * still being prepared as part of a circular import) end a branch - such a cycle is still detected once the last one
1363 * in it is prepared, when all the others are manifested.
1364 *
1365 * @param fieldType Type of the field to inspect
1366 * @param originScope Body scope of the struct/union whose infinite size we are checking for
1367 * @param node Accessing AST node (for struct/union lookup diagnostics)
1368 * @param visited Already-visited struct/union scopes, to keep the walk finite
1369 * @return true if the origin struct/union is reachable by value, i.e. it has infinite size
1370 */
1371 11884 f<bool> fieldContainsAggregateByValue(const QualType& fieldType, const Scope* originScope, const ASTNode* node, Vector<const Scope*>& visited) {
1372 // Unwrap fixed-size arrays, so that e.g. a `T[3]` field is treated like a `T` field for cycle detection. Dynamic
1373 // arrays, pointers and references keep breaking the cycle, since they do not embed the element by value.
1374
4/4
✓ Branch 0 taken 11515 times.
✓ Branch 1 taken 369 times.
✓ Branch 2 taken 367 times.
✓ Branch 3 taken 11517 times.
11884 if fieldType.isArray() && fieldType.getArraySize() > 0u {
1375 367 const QualType containedType = fieldType.getContained();
1376 367 return fieldContainsAggregateByValue(containedType, originScope, node, visited);
1377 }
1378 11517 const dyn aggregateTypes = [SuperType::TY_STRUCT, SuperType::TY_UNION];
1379
2/2
✓ Branch 0 taken 10121 times.
✓ Branch 1 taken 1396 times.
11517 if !fieldType.isOneOf(aggregateTypes, len(aggregateTypes)) {
1380 10121 return false;
1381 }
1382 1396 const Scope* fieldScope = fieldType.getBodyScope();
1383
2/2
✓ Branch 0 taken 1392 times.
✓ Branch 1 taken 4 times.
1396 if fieldScope == originScope {
1384 4 return true;
1385 }
1386
1/2
✓ Branch 0 taken 1392 times.
✗ Branch 1 not taken.
1392 if fieldScope == nil<const Scope*> {
1387 ✗ return false;
1388 }
1389
2/2
✓ Branch 0 taken 1723 times.
✓ Branch 1 taken 1222 times.
2945 foreach const Scope* visitedScope : visited {
1390
2/2
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 1553 times.
1723 if visitedScope == fieldScope {
1391 170 return false;
1392 }
1393 1553 }
1394 1222 visited.pushBack(fieldScope);
1395
1396 1222 QualTypeList memberTypes;
1397
2/2
✓ Branch 0 taken 1218 times.
✓ Branch 1 taken 4 times.
1222 if fieldType.is(SuperType::TY_STRUCT) {
1398 1218 const Struct* spiceStruct = fieldType.getStruct(node);
1399
2/2
✓ Branch 0 taken 1217 times.
✓ Branch 1 taken 1 time.
1218 if spiceStruct == nil<const Struct*> {
1400 1 return false; // Not manifested yet; the cycle is caught once the last aggregate in it is prepared
1401 }
1402 1217 memberTypes = spiceStruct.fieldTypes;
1403 1217 } else {
1404 4 const Union* spiceUnion = fieldType.getUnion(node);
1405
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if spiceUnion == nil<const Union*> {
1406 ✗ return false; // Not manifested yet; the cycle is caught once the last aggregate in it is prepared
1407 }
1408 4 memberTypes = spiceUnion.fieldTypes;
1409 4 }
1410
1411
2/2
✓ Branch 0 taken 3682 times.
✓ Branch 1 taken 1220 times.
4902 foreach const QualType& memberType : memberTypes {
1412
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3681 times.
3682 if fieldContainsAggregateByValue(memberType, originScope, node, visited) {
1413 1 return true;
1414 }
1415 3681 }
1416 1220 return false;
1417 1222 }
1418
1419 3223 f<Any> TypeChecker.visitStructDefPrepare(ASTStructDefNode* node) {
1420 3223 ASTNode* baseNode = cast<ASTNode*>(node);
1421 3223 QualTypeList usedTemplateTypes;
1422 3223 Vector<GenericType> templateTypesGeneric;
1423
1424 // Retrieve struct template types
1425
2/2
✓ Branch 0 taken 2300 times.
✓ Branch 1 taken 923 times.
3223 if node.hasTemplateTypes {
1426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 923 times.
923 if !this.collectTemplateTypes(node.templateTypeLst, templateTypesGeneric) {
1427 ✗ return Any();
1428 }
1429
2/2
✓ Branch 0 taken 1226 times.
✓ Branch 1 taken 923 times.
2149 foreach const GenericType& genericType : templateTypesGeneric {
1430 1226 usedTemplateTypes.pushBack(genericType.qualType);
1431 1226 }
1432 923 }
1433
1434 // Retrieve implemented interfaces
1435 3223 QualTypeList interfaceTypes;
1436
2/2
✓ Branch 0 taken 2800 times.
✓ Branch 1 taken 423 times.
3223 if node.hasInterfaces {
1437
2/2
✓ Branch 0 taken 421 times.
✓ Branch 1 taken 424 times.
845 foreach ASTDataTypeNode* interfaceNode : node.interfaceTypeLst.dataTypes {
1438 // Visit interface type
1439 424 const QualType interfaceType = this.getDataType(interfaceNode);
1440
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 423 times.
424 if interfaceType.is(SuperType::TY_UNRESOLVED) {
1441 1 continue;
1442 }
1443 // Check if it is an interface type
1444
2/2
✓ Branch 0 taken 422 times.
✓ Branch 1 taken 1 time.
423 if !interfaceType.is(SuperType::TY_INTERFACE) {
1445 1 throwSemanticError(cast<ASTNode*>(interfaceNode), SemanticErrorType::EXPECTED_INTERFACE_TYPE, "Expected interface type, got " + interfaceType.getName(false));
1446 2 return Any();
1447 1 }
1448 // Check for visibility
1449 422 Scope* interfaceScope = interfaceType.getBodyScope();
1450
4/4
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 382 times.
✓ Branch 2 taken 421 times.
✓ Branch 3 taken 1 time.
422 if interfaceScope.isImportedBy(this.rootScope) && !interfaceType.isPublic() {
1451 1 throwSemanticError(baseNode, SemanticErrorType::INSUFFICIENT_VISIBILITY, "Cannot access interface '" + interfaceType.getSubType() + "' due to its private visibility");
1452 2 return Any();
1453 1 }
1454 // Add to interface types
1455 421 interfaceTypes.pushBack(interfaceType);
1456 // Update the type of the entry for that interface field
1457 421 ASTCustomDataTypeNode* customDataType = interfaceNode.baseDataType.customDataType;
1458 421 const String& interfaceName = customDataType.typeNameFragments.back();
1459 421 SymbolTableEntry* interfaceEntry = node.structScope.lookupStrict("this." + interfaceName);
1460 421 assert interfaceEntry != nil<SymbolTableEntry*>;
1461 421 interfaceEntry.updateType(interfaceType, false);
1462 421 }
1463 421 }
1464
1465 // Update type of struct entry
1466 3221 assert node.entry != nil<SymbolTableEntry*>;
1467 3221 TypeChainElementData data;
1468 3221 data.bodyScope = node.structScope;
1469 3221 const Type* rawType = this.resourceManager.typeRegistry.getOrInsert(SuperType::TY_STRUCT, node.structName, node.typeId, data, usedTemplateTypes);
1470 // If the entry was implicitly forward-declared because it is referenced across a circular import
1471 // (assignDeferredOpaqueType), the existing type was already set to the opaque struct type. Overwriting it with the
1472 // identical interned type is expected here.
1473 3221 const QualType existingType = node.entry.getQualType();
1474 3221 const bool overwrite = existingType.is(SuperType::TY_STRUCT);
1475 3221 node.entry.updateType(QualType(rawType, node.qualifiers), overwrite);
1476
1477 // Change to struct scope
1478 3221 this.currentScope = node.structScope;
1479 3221 assert this.currentScope.scopeType == ScopeType::STRUCT;
1480
1481 // Retrieve field types
1482 3221 QualTypeList fieldTypes;
1483 // Shared across all fields: the origin (node.structScope) is constant, so a struct already proven not to reach it
1484 // via one field need not be re-walked for another.
1485 3221 Vector<const Scope*> visitedScopes;
1486
2/2
✓ Branch 0 taken 7783 times.
✓ Branch 1 taken 3218 times.
11001 foreach IVisitable* fieldVisitable : node.fields {
1487 7783 const ASTFieldNode* field = downcast<ASTFieldNode>(fieldVisitable);
1488 7783 ASTNode* fieldBaseNode = cast<ASTNode*>(field);
1489
1490 // Visit field type
1491 7783 const QualType fieldType = this.visitType(fieldVisitable);
1492
2/2
✓ Branch 0 taken 7781 times.
✓ Branch 1 taken 2 times.
7783 if fieldType.is(SuperType::TY_UNRESOLVED) {
1493 2 this.sourceFile.checkForSoftErrors(); // We get into trouble if we continue without the field type -> abort
1494 2 }
1495
1496 // Check for struct with infinite size. This happens if a struct (transitively, through by-value struct fields)
1497 // contains itself - either directly (struct A has a field of type A) or through a cycle of structs in mutually
1498 // importing files (A has a field of type B and B has a field of type A).
1499
2/2
✓ Branch 0 taken 7781 times.
✓ Branch 1 taken 2 times.
7783 if fieldContainsAggregateByValue(fieldType, node.structScope, fieldBaseNode, visitedScopes) {
1500 2 throwSemanticError(fieldBaseNode, SemanticErrorType::STRUCT_INFINITE_SIZE, String("Struct with infinite size detected"));
1501 4 return Any();
1502 2 }
1503
1504 // Add to field types
1505 7781 fieldTypes.pushBack(fieldType);
1506
1507 // Update type of field entry
1508 7781 SymbolTableEntry* fieldEntry = this.currentScope.lookupStrict(field.fieldName);
1509 7781 assert fieldEntry != nil<SymbolTableEntry*>;
1510 7781 fieldEntry.updateType(fieldType, false);
1511
1512 // Check if the template type list contains this type
1513
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7780 times.
7781 if !fieldType.isCoveredByGenericTypeList(templateTypesGeneric) {
1514 1 throwSemanticError(cast<ASTNode*>(field.dataType), SemanticErrorType::GENERIC_TYPE_NOT_IN_TEMPLATE, String("Generic field type not included in struct template"));
1515 2 return Any();
1516 1 }
1517 7780 }
1518
1519 // Change to the root scope
1520 3218 this.currentScope = this.rootScope;
1521 3218 assert this.currentScope.scopeType == ScopeType::GLOBAL;
1522
1523 // Build struct object
1524 3218 Struct spiceStruct = Struct(node.structName, node.entry, node.structScope, fieldTypes, templateTypesGeneric, interfaceTypes, baseNode);
1525 3218 insertStruct(this.currentScope, spiceStruct, &node.structManifestations);
1526 3218 spiceStruct.scope = node.structScope;
1527
1528 // Request RTTI runtime if the struct is polymorphic
1529
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3218 times.
3218 node.emitVTable = node.emitVTable || node.hasInterfaces;
1530
2/2
✓ Branch 0 taken 3016 times.
✓ Branch 1 taken 202 times.
3218 if node.attrs != nil<IVisitable*> {
1531 202 const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(node.attrs);
1532 202 ASTAttrLstNode* attrLst = attrs.attrLst;
1533
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 192 times.
202 if attrLst.hasAttr(String(ATTR_CORE_COMPILER_EMIT_VTABLE)) {
1534 192 const CompileTimeValue value = attrLst.getAttrValueByName(String(ATTR_CORE_COMPILER_EMIT_VTABLE));
1535
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
192 node.emitVTable = node.emitVTable || value.getBoolValue();
1536 192 }
1537 202 }
1538
4/4
✓ Branch 0 taken 2605 times.
✓ Branch 1 taken 613 times.
✓ Branch 2 taken 2790 times.
✓ Branch 3 taken 428 times.
3218 if node.emitVTable && !this.sourceFile.isRttiRT() {
1539 428 this.sourceFile.requestRuntimeModule(RuntimeModule::RTTI_RT);
1540 428 }
1541
1542 6436 return Any();
1543 3223 }
1544
1545 323 f<Any> TypeChecker.visitInterfaceDefPrepare(ASTInterfaceDefNode* node) {
1546 323 ASTNode* baseNode = cast<ASTNode*>(node);
1547 323 QualTypeList usedTemplateTypes;
1548 323 Vector<GenericType> templateTypesGeneric;
1549
1550 // Retrieve interface template types
1551
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 258 times.
323 if node.hasTemplateTypes {
1552
2/2
✓ Branch 0 taken 258 times.
✓ Branch 1 taken 258 times.
516 foreach ASTDataTypeNode* dataType : node.templateTypeLst.dataTypes {
1553 // Visit template type
1554 258 const QualType templateType = this.getDataType(dataType);
1555
1/2
✓ Branch 0 taken 258 times.
✗ Branch 1 not taken.
258 if templateType.is(SuperType::TY_UNRESOLVED) {
1556 ✗ return Any();
1557 }
1558 // Check if it is a generic type
1559
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 258 times.
258 if !templateType.is(SuperType::TY_GENERIC) {
1560 ✗ this.softError(cast<ASTNode*>(dataType), SemanticErrorType::EXPECTED_GENERIC_TYPE, String("A template list can only contain generic types"));
1561 ✗ continue;
1562 ✗ }
1563 // Convert generic symbol type to generic type
1564 258 GenericType* genericType = this.rootScope.lookupGenericTypeStrict(templateType.getSubType());
1565 258 assert genericType != nil<GenericType*>;
1566 258 usedTemplateTypes.pushBack(genericType.qualType);
1567 258 templateTypesGeneric.pushBack(*genericType);
1568 258 }
1569 258 }
1570
1571 // Update type of interface entry
1572 323 TypeChainElementData data;
1573 323 data.bodyScope = node.interfaceScope;
1574 323 const Type* rawType = this.resourceManager.typeRegistry.getOrInsert(SuperType::TY_INTERFACE, node.interfaceName, node.typeId, data, usedTemplateTypes);
1575 323 const QualType interfaceType = QualType(rawType, node.qualifiers);
1576 323 assert node.entry != nil<SymbolTableEntry*>;
1577 // If the entry was implicitly forward-declared because it is referenced across a circular import
1578 // (assignDeferredOpaqueType), the existing type was already set to the opaque interface type. Overwriting it with the
1579 // identical interned type is expected here.
1580 323 const QualType existingType = node.entry.getQualType();
1581 323 const bool overwrite = existingType.is(SuperType::TY_INTERFACE);
1582 323 node.entry.updateType(interfaceType, overwrite);
1583
1584 // Change to interface scope
1585 323 this.currentScope = node.interfaceScope;
1586 323 assert this.currentScope.scopeType == ScopeType::INTERFACE;
1587
1588 // Visit methods
1589 323 unsigned long vtableIndex = 0l;
1590 323 Vector<Function*> methods;
1591 323 const QualTypeList interfaceTemplateTypes = interfaceType.getTemplateTypes();
1592
2/2
✓ Branch 0 taken 742 times.
✓ Branch 1 taken 322 times.
1064 foreach IVisitable* signature : node.signatures {
1593 742 Any signatureResult = this.visit(signature);
1594 742 Vector<Function*>* method = signatureResult.get<Vector<Function*>*>();
1595
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 741 times.
742 if method == nil<Vector<Function*>*> {
1596 2 return Any();
1597 }
1598
1599 // Set 'this' type
1600
2/2
✓ Branch 0 taken 741 times.
✓ Branch 1 taken 741 times.
1482 foreach Function* m : *method {
1601 741 m.isVirtual = true; // Interface methods are always virtual
1602 741 m.vtableIndex = vtableIndex;
1603 741 m.thisType = interfaceType;
1604
1605 // Merge the enclosing interface's own template types into the method's template types, mirroring how struct
1606 // methods inherit the struct's generic params in visitFctDefPrepare. Without this, a method that does not
1607 // redeclare its own template list (e.g. isValid() inside IIterator<T>) ends up with an empty templateTypes
1608 // list, even though its thisType references the interface's generic type - so generic type resolution fails
1609 // while matching the 'this' type of a call through an interface reference.
1610
2/2
✓ Branch 0 taken 665 times.
✓ Branch 1 taken 741 times.
1406 foreach const QualType& templateType : interfaceTemplateTypes {
1611 665 addUsedTemplateType(m.templateTypes, templateType);
1612 665 }
1613 741 }
1614
1615
2/2
✓ Branch 0 taken 741 times.
✓ Branch 1 taken 741 times.
1482 foreach Function* m : *method {
1616 741 methods.pushBack(m);
1617 741 }
1618 741 vtableIndex++;
1619 742 }
1620
1621 // Change to root scope
1622 322 this.currentScope = this.rootScope;
1623 322 assert this.currentScope.scopeType == ScopeType::GLOBAL;
1624
1625 // Build interface object
1626 322 Interface spiceInterface = Interface(node.interfaceName, node.entry, node.interfaceScope, methods, templateTypesGeneric, baseNode);
1627 322 insertInterface(this.currentScope, spiceInterface, &node.interfaceManifestations);
1628 322 spiceInterface.scope = node.interfaceScope;
1629
1630 // Request RTTI runtime, that is always required when dealing with interfaces due to polymorphism
1631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 322 times.
322 if !this.sourceFile.isRttiRT() {
1632 322 this.sourceFile.requestRuntimeModule(RuntimeModule::RTTI_RT);
1633 322 }
1634
1635 644 return Any();
1636 323 }
1637
1638 24 f<Any> TypeChecker.visitUnionDefPrepare(ASTUnionDefNode* node) {
1639 24 ASTNode* baseNode = cast<ASTNode*>(node);
1640 24 QualTypeList usedTemplateTypes;
1641 24 Vector<GenericType> templateTypesGeneric;
1642
1643 // Retrieve union template types
1644
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 if node.hasTemplateTypes {
1645
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if !this.collectTemplateTypes(node.templateTypeLst, templateTypesGeneric) {
1646 ✗ return Any();
1647 }
1648
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 foreach const GenericType& genericType : templateTypesGeneric {
1649 4 usedTemplateTypes.pushBack(genericType.qualType);
1650 4 }
1651 4 }
1652
1653 // Update type of union entry
1654 24 assert node.entry != nil<SymbolTableEntry*>;
1655 24 TypeChainElementData data;
1656 24 data.bodyScope = node.unionScope;
1657 24 const Type* rawType = this.resourceManager.typeRegistry.getOrInsert(SuperType::TY_UNION, node.unionName, node.typeId, data, usedTemplateTypes);
1658 // If the entry was implicitly forward-declared because it is referenced across a circular import
1659 // (assignDeferredOpaqueType), the existing type was already set to the opaque union type. Overwriting it with the
1660 // identical interned type is expected here.
1661 24 const QualType existingType = node.entry.getQualType();
1662 24 const bool overwrite = existingType.is(SuperType::TY_UNION);
1663 24 node.entry.updateType(QualType(rawType, node.qualifiers), overwrite);
1664
1665 // Change to union scope
1666 24 this.currentScope = node.unionScope;
1667 24 assert this.currentScope.scopeType == ScopeType::UNION;
1668
1669 // Retrieve field types
1670 24 QualTypeList fieldTypes;
1671 // Shared across all fields: the origin (node.unionScope) is constant, so an aggregate already proven not to reach
1672 // it via one field need not be re-walked for another.
1673 24 Vector<const Scope*> visitedScopes;
1674 24 unsigned long defaultFieldIndex = ULONG_MAX_VALUE;
1675
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 22 times.
74 for unsigned long i = 0l; i < node.fields.getSize(); i++ {
1676 52 IVisitable* fieldVisitable = node.fields.get(i);
1677 52 const ASTFieldNode* field = downcast<ASTFieldNode>(fieldVisitable);
1678 52 ASTNode* fieldBaseNode = cast<ASTNode*>(field);
1679
1680 // Visit field type
1681 52 const QualType fieldType = this.visitType(fieldVisitable);
1682
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 if fieldType.is(SuperType::TY_UNRESOLVED) {
1683 ✗ this.sourceFile.checkForSoftErrors(); // We get into trouble if we continue without the field type -> abort
1684 ✗ }
1685
1686 // Check for union/struct with infinite size. This happens if the union (transitively, through by-value
1687 // struct/union fields) contains itself - either directly or through a cycle of aggregates in mutually
1688 // importing files.
1689
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 2 times.
52 if fieldContainsAggregateByValue(fieldType, node.unionScope, fieldBaseNode, visitedScopes) {
1690 2 throwSemanticError(fieldBaseNode, SemanticErrorType::UNION_INFINITE_SIZE, String("Union with infinite size detected"));
1691 4 return Any();
1692 2 }
1693
1694 // Union fields must not be references
1695
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1 time.
50 if fieldType.isRef() {
1696 1 this.softError(fieldBaseNode, SemanticErrorType::UNION_FIELD_MUST_NOT_BE_REFERENCE, String("A union field must not be a reference"));
1697 1 }
1698
1699 // Union fields must be trivially constructible, copyable and destructible, since the union does not know which
1700 // field is currently active and therefore cannot run any non-trivial special member on its own.
1701
7/8
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 48 times.
✓ Branch 6 taken 49 times.
✓ Branch 7 taken 1 time.
50 if !fieldType.isRef() && (!fieldType.isTriviallyConstructible(fieldBaseNode) || !fieldType.isTriviallyCopyable(fieldBaseNode) || !fieldType.isTriviallyDestructible(fieldBaseNode)) {
1702 1 this.softError(fieldBaseNode, SemanticErrorType::UNION_FIELD_TYPE_NOT_TRIVIAL, "The type of the union field '" + field.fieldName + "' is not trivial. Only trivial types are allowed as union fields");
1703 1 }
1704
1705 // At most one field may carry a default value
1706
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 4 times.
50 if field.defaultValue != nil<IVisitable*> {
1707
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 if defaultFieldIndex != ULONG_MAX_VALUE {
1708 1 this.softError(fieldBaseNode, SemanticErrorType::DUPLICATE_UNION_DEFAULT_VALUE, String("A union may only have one field with a default value"));
1709 1 } else {
1710 3 defaultFieldIndex = i;
1711 3 }
1712 4 }
1713
1714 // Add to field types
1715 50 fieldTypes.pushBack(fieldType);
1716
1717 // Update type of field entry
1718 50 SymbolTableEntry* fieldEntry = this.currentScope.lookupStrict(field.fieldName);
1719 50 assert fieldEntry != nil<SymbolTableEntry*>;
1720 50 fieldEntry.updateType(fieldType, false);
1721
1722 // Check if the template type list contains this type
1723
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if !fieldType.isCoveredByGenericTypeList(templateTypesGeneric) {
1724 ✗ throwSemanticError(cast<ASTNode*>(field.dataType), SemanticErrorType::GENERIC_TYPE_NOT_IN_TEMPLATE, String("Generic field type not included in union template"));
1725 ✗ return Any();
1726 ✗ }
1727 50 }
1728
1729 // Change to the root scope
1730 22 this.currentScope = this.rootScope;
1731 22 assert this.currentScope.scopeType == ScopeType::GLOBAL;
1732
1733 // Build union object
1734 22 Union spiceUnion = Union(node.unionName, node.entry, node.unionScope, fieldTypes, templateTypesGeneric, baseNode);
1735 22 spiceUnion.defaultFieldIndex = defaultFieldIndex;
1736 22 insertUnion(this.currentScope, spiceUnion, &node.unionManifestations);
1737
1738 44 return Any();
1739 24 }
1740
1741 463 f<Any> TypeChecker.visitEnumDefPrepare(ASTEnumDefNode* node) {
1742 // Update type of enum entry
1743 463 TypeChainElementData data;
1744 463 data.bodyScope = node.enumScope;
1745 463 const QualTypeList noTemplateTypes;
1746 463 const Type* rawType = this.resourceManager.typeRegistry.getOrInsert(SuperType::TY_ENUM, node.enumName, node.typeId, data, noTemplateTypes);
1747 463 assert node.entry != nil<SymbolTableEntry*>;
1748 // The enum may already carry its opaque type if it was referenced across a circular import before being prepared
1749 // (assignDeferredOpaqueType). Overwriting it with the identical interned type is expected here.
1750 463 const QualType existingType = node.entry.getQualType();
1751 463 const bool overwrite = existingType.is(SuperType::TY_ENUM);
1752 463 node.entry.updateType(QualType(rawType, node.qualifiers), overwrite);
1753
1754 // Change to enum scope
1755 463 this.currentScope = node.enumScope;
1756 463 assert this.currentScope.scopeType == ScopeType::ENUM;
1757
1758 // Loop through all items with values
1759 463 const ASTEnumItemLstNode* itemLst = downcast<ASTEnumItemLstNode>(node.itemLst);
1760 463 Vector<String> names;
1761 463 Vector<unsigned int> values;
1762
2/2
✓ Branch 0 taken 1740 times.
✓ Branch 1 taken 463 times.
2203 foreach ASTEnumItemNode* enumItem : itemLst.items {
1763 // Save name
1764 1740 names.pushBack(enumItem.itemName);
1765 // Check for duplicate value
1766
2/2
✓ Branch 0 taken 583 times.
✓ Branch 1 taken 1157 times.
1740 if enumItem.hasValue {
1767
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1156 times.
1157 if containsValue(values, enumItem.itemValue) {
1768 1 this.softError(cast<ASTNode*>(enumItem), SemanticErrorType::DUPLICATE_ENUM_ITEM_VALUE, String("Duplicate enum item value, please use another"));
1769 1 continue;
1770 1 }
1771 1156 values.pushBack(enumItem.itemValue);
1772 1156 }
1773 1739 }
1774
1775 // Loop through all items without values
1776 463 unsigned int nextValue = 0u;
1777 463 const QualType intSymbolType = QualType(SuperType::TY_INT);
1778
2/2
✓ Branch 0 taken 1740 times.
✓ Branch 1 taken 463 times.
2203 foreach ASTEnumItemNode* enumItem : itemLst.items {
1779 // Update type of enum item entry
1780 1740 SymbolTableEntry* itemEntry = this.currentScope.lookupStrict(enumItem.itemName);
1781 1740 assert itemEntry != nil<SymbolTableEntry*>;
1782 1740 itemEntry.updateType(intSymbolType, false);
1783 // Fill in value if not filled yet
1784
2/2
✓ Branch 0 taken 1157 times.
✓ Branch 1 taken 583 times.
1740 if !enumItem.hasValue {
1785
2/2
✓ Branch 0 taken 491 times.
✓ Branch 1 taken 583 times.
1074 while containsValue(values, nextValue) {
1786 491 nextValue++;
1787 491 }
1788 583 enumItem.itemValue = nextValue;
1789 583 values.pushBack(nextValue);
1790 583 }
1791 1740 }
1792
1793 // Change to root scope
1794 463 this.currentScope = this.rootScope;
1795 463 assert this.currentScope.scopeType == ScopeType::GLOBAL;
1796
1797 926 return Any();
1798 463 }
1799
1800 // Check if the given list of enum item values contains the given value
1801 2231 f<bool> containsValue(const Vector<unsigned int>& values, unsigned int value) {
1802
2/2
✓ Branch 0 taken 16733 times.
✓ Branch 1 taken 1739 times.
18472 foreach const unsigned int existingValue : values {
1803
2/2
✓ Branch 0 taken 492 times.
✓ Branch 1 taken 16241 times.
16733 if existingValue == value {
1804 492 return true;
1805 }
1806 16241 }
1807 1739 return false;
1808 }
1809
1810 3062 f<Any> TypeChecker.visitGenericTypeDefPrepare(ASTGenericTypeDefNode* node) {
1811 // Retrieve type conditions
1812 3062 const ASTTypeAltsLstNode* typeAltsLst = downcast<ASTTypeAltsLstNode>(node.typeAltsLst);
1813 3062 QualTypeList typeConditions;
1814
2/2
✓ Branch 0 taken 5501 times.
✓ Branch 1 taken 3062 times.
8563 foreach ASTDataTypeNode* typeAlt : typeAltsLst.dataTypes {
1815 5501 const QualType typeCondition = this.getDataType(typeAlt);
1816
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5501 times.
5501 if typeCondition.is(SuperType::TY_UNRESOLVED) {
1817 ✗ return Any();
1818 }
1819 5501 typeConditions.pushBack(typeCondition);
1820 5501 }
1821
1822 // Add generic type to the scope
1823 3062 const GenericType genericType = GenericType(node.typeName, typeConditions);
1824 3062 this.rootScope.insertGenericType(node.typeName, genericType);
1825
1826 // Check if only one type condition is set
1827
2/2
✓ Branch 0 taken 1700 times.
✓ Branch 1 taken 1362 times.
3062 if typeConditions.getSize() == 1l {
1828 1362 const QualType& typeCondition = typeConditions.front();
1829
2/2
✓ Branch 0 taken 1361 times.
✓ Branch 1 taken 1 time.
1362 if !typeCondition.is(SuperType::TY_DYN) {
1830 1 this.addWarning(typeAltsLst.codeLoc, CompilerWarningType::SINGLE_GENERIC_TYPE_CONDITION, String("Generic type is locked to one type"));
1831 1 }
1832 1362 }
1833
1834 // Check if the list contains the dyn type, along with other types
1835 3062 bool containsDynType = false;
1836
2/2
✓ Branch 0 taken 5501 times.
✓ Branch 1 taken 3062 times.
8563 foreach const QualType& typeCondition : typeConditions {
1837
2/2
✓ Branch 0 taken 4138 times.
✓ Branch 1 taken 1363 times.
5501 if typeCondition.is(SuperType::TY_DYN) {
1838 1363 containsDynType = true;
1839 1363 }
1840 5501 }
1841
4/4
✓ Branch 0 taken 1699 times.
✓ Branch 1 taken 1363 times.
✓ Branch 2 taken 3060 times.
✓ Branch 3 taken 2 times.
3062 if containsDynType && typeConditions.getSize() > 1l {
1842 2 this.addWarning(typeAltsLst.codeLoc, CompilerWarningType::INEFFECTIVE_GENERIC_TYPE_CONDITION, String("One or several type conditions are superfluous, because the dyn type condition is given"));
1843 2 }
1844
1845 6124 return Any();
1846 3062 }
1847
1848 159 #[ignoreUnusedReturnValue]
1849 159 f<Any> TypeChecker.visitAliasDefPrepare(ASTAliasDefNode* node) {
1850 159 assert node.entry != nil<SymbolTableEntry*> && node.aliasedTypeContainerEntry != nil<SymbolTableEntry*>;
1851
1852 // The alias may already have been prepared on demand because it was referenced across a circular import
1853 // (assignDeferredOpaqueType). In that case there is nothing left to do.
1854 159 const QualType existingType = node.entry.getQualType();
1855
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 157 times.
159 if existingType.is(SuperType::TY_ALIAS) {
1856 2 return Any();
1857 }
1858
1859 // Ensure the aliased-type subtree is sized for manifestations before it is visited below. On the normal path
1860 // visitEntry already sized the whole file to 1, so this is a no-op; on the on-demand path (a cross-import reference
1861 // reaching this alias before its own file's visitEntry ran) it establishes the precondition setEvaluatedSymbolType
1862 // requires.
1863 157 ASTNode* dataTypeBaseNode = cast<ASTNode*>(node.dataType);
1864 157 dataTypeBaseNode.resizeToNumberOfManifestations(1l);
1865
1866 // Update type of alias entry
1867 157 const TypeChainElementData noData;
1868 157 const QualTypeList noTemplateTypes;
1869 157 const Type* rawType = this.resourceManager.typeRegistry.getOrInsert(SuperType::TY_ALIAS, node.aliasName, node.typeId, noData, noTemplateTypes);
1870 157 node.entry.updateType(QualType(rawType, node.qualifiers), false);
1871
1872 // Update type of the aliased type container entry
1873 157 const QualType aliasedType = this.getDataType(node.dataType);
1874
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 156 times.
157 if aliasedType.is(SuperType::TY_UNRESOLVED) {
1875 2 return Any();
1876 }
1877 156 node.aliasedTypeContainerEntry.updateType(aliasedType, false);
1878 156 node.aliasedTypeContainerEntry.used = true; // The container type is always used per default
1879
1880 312 return Any();
1881 157 }
1882
1883 4280 f<Any> TypeChecker.visitGlobalVarDefPrepare(ASTGlobalVarDefNode* node) {
1884 // Insert variable name to symbol table
1885 4280 QualType globalVarType = this.getDataType(node.dataType);
1886
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4280 times.
4280 if globalVarType.is(SuperType::TY_UNRESOLVED) {
1887 ✗ return Any();
1888 }
1889 4280 const bool isInferred = globalVarType.is(SuperType::TY_DYN);
1890 4280 const bool isDeclaredConst = globalVarType.qualifiers.isConst;
1891 4280 const bool isDeclaredPublic = globalVarType.qualifiers.isPublic;
1892
1893
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 4271 times.
4280 if node.constant != nil<ASTConstantNode*> { // Variable is initialized here
1894 4271 const ExprResult constantResult = this.visitExpr(cast<IVisitable*>(node.constant));
1895 4271 const QualType rhsType = constantResult.ty;
1896
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4271 times.
4271 if rhsType.is(SuperType::TY_UNRESOLVED) {
1897 ✗ return Any();
1898 }
1899
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4259 times.
4271 if globalVarType.is(SuperType::TY_DYN) { // Perform type inference
1900 12 globalVarType = rhsType;
1901
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4257 times.
4271 } else if !globalVarType.matches(rhsType, false, true, true) { // Check if types are matching
1902 2 this.softError(cast<ASTNode*>(node.constant), SemanticErrorType::OPERATOR_WRONG_DATA_TYPE, "Expected " + globalVarType.getName(false) + ", but got " + rhsType.getName(false));
1903 4 return Any();
1904 4259 }
1905 4269 }
1906
1907 // Check if the type is still missing
1908
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4277 times.
4278 if globalVarType.is(SuperType::TY_DYN) {
1909 1 this.softError(cast<ASTNode*>(node.dataType), SemanticErrorType::GLOBAL_OF_TYPE_DYN, String("Global variables must have an explicit data type"));
1910 2 return Any();
1911 1 }
1912
1913 // Check if we would need to insert instructions in the global scope to initialize the variable
1914
4/4
✓ Branch 0 taken 4270 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 4276 times.
4277 if !globalVarType.isPrimitive() && !globalVarType.isPtr() {
1915 1 this.softError(cast<ASTNode*>(node.dataType), SemanticErrorType::GLOBAL_OF_INVALID_TYPE, String("Spice does only support global variables of primitive or pointer type"));
1916 2 return Any();
1917 1 }
1918
1919 // The inferred type comes from the value, so it does not carry the declared qualifiers yet
1920
2/2
✓ Branch 0 taken 4264 times.
✓ Branch 1 taken 12 times.
4276 if isInferred {
1921 12 globalVarType.makeConst(isDeclaredConst);
1922 12 globalVarType.makePublic(isDeclaredPublic);
1923 12 }
1924
1925 // Update type of global var entry
1926 4276 assert node.entry != nil<SymbolTableEntry*>;
1927 4276 node.entry.updateType(globalVarType, false);
1928
1929 // Check if a value is attached
1930
4/4
✓ Branch 0 taken 4269 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 4275 times.
4276 if node.constant == nil<ASTConstantNode*> && globalVarType.isConst() {
1931 1 this.softError(cast<ASTNode*>(node), SemanticErrorType::GLOBAL_CONST_WITHOUT_VALUE, String("You must specify a value for constant global variables"));
1932 2 return Any();
1933 1 }
1934
1935 4275 return Any();
1936 }
1937
1938 5226 f<Any> TypeChecker.visitExtDeclPrepare(ASTExtDeclNode* node) {
1939 5226 ASTNode* baseNode = cast<ASTNode*>(node);
1940
1941 // Collect argument types
1942 5226 QualTypeList argTypes;
1943 5226 ParamList argList;
1944 5226 ASTTypeLstWithEllipsisNode* argTypeLst = nil<ASTTypeLstWithEllipsisNode*>;
1945
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 5154 times.
5226 if node.argTypeLst != nil<IVisitable*> {
1946 5154 argTypeLst = downcast<ASTTypeLstWithEllipsisNode>(node.argTypeLst);
1947 5154 }
1948
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 5154 times.
5226 if node.hasArgs {
1949
2/2
✓ Branch 0 taken 11111 times.
✓ Branch 1 taken 5154 times.
16265 foreach ASTDataTypeNode* arg : argTypeLst.typeLst.dataTypes {
1950 // Visit argument
1951 11111 const QualType argType = this.getDataType(arg);
1952
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11111 times.
11111 if argType.is(SuperType::TY_UNRESOLVED) {
1953 ✗ return Any();
1954 }
1955 // Check if the argument type is 'dyn'
1956
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 11110 times.
11111 if argType.is(SuperType::TY_DYN) {
1957 1 this.softError(cast<ASTNode*>(arg), SemanticErrorType::UNEXPECTED_DYN_TYPE, String("Dyn data type is not allowed as arg type for external functions"));
1958 1 continue;
1959 1 }
1960 // Save argument
1961 11110 argTypes.pushBack(argType);
1962 11110 argList.pushBack(Param{argType, false});
1963 11110 }
1964 5154 }
1965
1966 // Retrieve return type
1967 5226 QualType returnType = QualType(SuperType::TY_DYN);
1968 5226 const bool isFunction = node.returnType != nil<ASTDataTypeNode*>;
1969
2/2
✓ Branch 0 taken 679 times.
✓ Branch 1 taken 4547 times.
5226 if isFunction { // External function
1970 4547 returnType = this.getDataType(node.returnType);
1971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4547 times.
4547 if returnType.is(SuperType::TY_UNRESOLVED) {
1972 ✗ return Any();
1973 }
1974 // Check if return type is dyn
1975
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4546 times.
4547 if returnType.is(SuperType::TY_DYN) {
1976 1 this.softError(cast<ASTNode*>(node.returnType), SemanticErrorType::UNEXPECTED_DYN_TYPE, String("dyn is not allowed as return type for external functions"));
1977 2 return Any();
1978 1 }
1979 4546 }
1980
1981 // Add function to current scope
1982 5225 const Vector<GenericType> noTemplateTypes;
1983 5225 const Function spiceFunc = Function(node.extFunctionName, node.entry, QualType(SuperType::TY_DYN), returnType, argList, noTemplateTypes, baseNode);
1984 5225 node.extFunction = insertFunction(this.currentScope, spiceFunc, &node.extFunctionManifestations);
1985 5225 node.extFunction.mangleFunctionName = false;
1986 5225 node.extFunction.alreadyTypeChecked = true;
1987
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 5154 times.
5225 node.extFunction.isVararg = argTypeLst != nil<ASTTypeLstWithEllipsisNode*> && argTypeLst.hasEllipsis;
1988
1989 // Check procedure attributes
1990
2/2
✓ Branch 0 taken 5224 times.
✓ Branch 1 taken 1 time.
5225 if node.attrs != nil<IVisitable*> {
1991 1 const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(node.attrs);
1992 1 ASTAttrLstNode* attrLst = attrs.attrLst;
1993
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if attrLst.hasAttr(String(ATTR_CORE_COMPILER_MANGLE)) {
1994 ✗ const CompileTimeValue value = attrLst.getAttrValueByName(String(ATTR_CORE_COMPILER_MANGLE));
1995 ✗ node.extFunction.mangleFunctionName = value.getBoolValue();
1996 ✗ }
1997
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if attrLst.hasAttr(String(ATTR_CORE_COMPILER_MANGLED_NAME)) {
1998 1 const CompileTimeValue value = attrLst.getAttrValueByName(String(ATTR_CORE_COMPILER_MANGLED_NAME));
1999 1 node.extFunction.predefinedMangledName = this.resourceManager.getCompileTimeStringValue(value.getStringValueOffset());
2000 1 }
2001 1 }
2002
2003 // Prepare ext function type
2004
2/2
✓ Branch 0 taken 4546 times.
✓ Branch 1 taken 679 times.
5225 const SuperType superType = isFunction ? SuperType::TY_FUNCTION : SuperType::TY_PROCEDURE;
2005 5225 const QualType extFunctionTypeBase = QualType(superType);
2006 5225 const QualType extFunctionType = extFunctionTypeBase.getWithFunctionParamAndReturnTypes(returnType, argTypes);
2007
2008 // Set type of external function
2009 5225 node.entry.updateType(extFunctionType, false);
2010
2011 // Rename the original child scope to reflect the substantiated versions of the external function
2012 5225 this.currentScope.renameChildScope(node.getScopeId(), spiceFunc.getScopeName());
2013
2014 10450 return Any();
2015 5226 }
2016
2017 2501 f<Any> TypeChecker.visitImportDefPrepare(ASTImportDefNode* node) {
2018 // Set entry to import type
2019 2501 const QualType importType = QualType(SuperType::TY_IMPORT, node.importName);
2020 2501 assert node.entry != nil<SymbolTableEntry*>;
2021 2501 node.entry.updateType(importType, false);
2022
2023 2501 return Any();
2024 }
2025
2026 // ============================================================ Meta =============================================================
2027
2028 65224 public f<Any> TypeChecker.visitParamLst(ASTParamLstNode* node) {
2029 65224 ParamList params;
2030 65224 bool metOptional = false;
2031
2032
2/2
✓ Branch 0 taken 107493 times.
✓ Branch 1 taken 65224 times.
172717 foreach ASTDeclStmtNode* param : node.params {
2033 107493 ASTNode* paramBaseNode = cast<ASTNode*>(param);
2034
2035 // Visit param
2036 107493 const QualType paramType = this.visitType(cast<IVisitable*>(param));
2037
2038 // Check if the type could be inferred. Dyn without a default value is forbidden
2039
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 107490 times.
107493 if paramType.is(SuperType::TY_DYN) {
2040 3 this.softError(cast<ASTNode*>(node), SemanticErrorType::FCT_PARAM_IS_TYPE_DYN, "Type of parameter '" + param.varName + "' is invalid");
2041 3 continue;
2042 3 }
2043
2044 // Ensure that no optional param comes after a mandatory param
2045
2/2
✓ Branch 0 taken 8013 times.
✓ Branch 1 taken 99477 times.
107490 if param.hasAssignment {
2046 8013 metOptional = true;
2047
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 99471 times.
107490 } else if metOptional {
2048 6 this.softError(paramBaseNode, SemanticErrorType::INVALID_PARAM_ORDER, String("Mandatory parameters must go before any optional parameters"));
2049 6 continue;
2050 99477 }
2051
2052 // Warn about non-trivially copyable types being passed by value, since that requires an implicit copy on every call.
2053 // Only check in the check stage (not the prepare stage), because generic param types are not substantiated yet there.
2054
4/4
✓ Branch 0 taken 53949 times.
✓ Branch 1 taken 53535 times.
✓ Branch 2 taken 107383 times.
✓ Branch 3 taken 101 times.
107484 if this.typeCheckerMode == TypeCheckerMode::TC_MODE_POST && !paramType.isTriviallyCopyable(paramBaseNode) {
2055 101 const String message = "Parameter '" + param.varName + "' has the non-trivially copyable type '" + paramType.getName() + "' and is passed by value, which requires an implicit copy on every call. Consider passing it by reference instead.";
2056 101 this.addWarning(param.codeLoc, CompilerWarningType::NON_TRIVIAL_TYPE_PASSED_BY_VALUE, message);
2057 101 }
2058
2059 // Add parameter to param list
2060 107484 params.pushBack(Param{paramType, metOptional});
2061 107484 }
2062
2063 130448 return Any(params);
2064 65224 }
2065
2066 7837 public f<Any> TypeChecker.visitField(ASTFieldNode* node) {
2067 7837 const QualType fieldType = this.getDataType(node.dataType);
2068
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7836 times.
7837 if fieldType.is(SuperType::TY_UNRESOLVED) {
2069 1 return Any(fieldType);
2070 }
2071
2072
2/2
✓ Branch 0 taken 5936 times.
✓ Branch 1 taken 1900 times.
7836 if node.defaultValue != nil<IVisitable*> {
2073 1900 const ExprResult defaultValueResult = this.visitExpr(node.defaultValue);
2074 1900 const QualType defaultValueType = defaultValueResult.ty;
2075
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1900 times.
1900 if defaultValueType.is(SuperType::TY_UNRESOLVED) {
2076 ✗ return Any(defaultValueType);
2077 }
2078
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1899 times.
1900 if !fieldType.matches(defaultValueType, false, true, true) {
2079 2 return this.softErrorQT(cast<ASTNode*>(node), SemanticErrorType::FIELD_TYPE_NOT_MATCHING, String("Type of the default values does not match the field type"));
2080 1 }
2081 1899 }
2082
2083 7835 return Any(fieldType);
2084 }
2085
2086 742 public f<Any> TypeChecker.visitSignature(ASTSignatureNode* node) {
2087 742 const bool isFunction = node.signatureType == SignatureType::TYPE_FUNCTION;
2088 742 const Any noSignatureManifestations = Any(nil<Vector<Function*>*>);
2089
2090 // Retrieve function template types
2091 742 Vector<GenericType> usedGenericTypes;
2092
2/2
✓ Branch 0 taken 351 times.
✓ Branch 1 taken 391 times.
742 if node.hasTemplateTypes {
2093
2/2
✓ Branch 0 taken 390 times.
✓ Branch 1 taken 391 times.
781 foreach ASTDataTypeNode* dataType : node.templateTypeLst.dataTypes {
2094 // Visit template type
2095 391 const QualType templateType = this.getDataType(dataType);
2096
1/2
✓ Branch 0 taken 391 times.
✗ Branch 1 not taken.
391 if templateType.is(SuperType::TY_UNRESOLVED) {
2097 ✗ return noSignatureManifestations;
2098 }
2099 // Check if it is a generic type
2100
2/2
✓ Branch 0 taken 390 times.
✓ Branch 1 taken 1 time.
391 if !templateType.is(SuperType::TY_GENERIC) {
2101 1 this.softError(cast<ASTNode*>(dataType), SemanticErrorType::EXPECTED_GENERIC_TYPE, String("A template list can only contain generic types"));
2102 2 return noSignatureManifestations;
2103 1 }
2104 // Convert generic symbol type to generic type
2105 390 GenericType* genericType = this.rootScope.lookupGenericTypeStrict(templateType.getSubType());
2106 390 assert genericType != nil<GenericType*>;
2107 390 usedGenericTypes.pushBack(*genericType);
2108 390 }
2109 390 }
2110
2111 // Visit return type
2112 741 QualType returnType = QualType(SuperType::TY_DYN);
2113
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 576 times.
741 if isFunction {
2114 576 returnType = this.getDataType(node.returnType);
2115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 576 times.
576 if returnType.is(SuperType::TY_UNRESOLVED) {
2116 ✗ return noSignatureManifestations;
2117 }
2118
2119
2/2
✓ Branch 0 taken 573 times.
✓ Branch 1 taken 3 times.
576 if !returnType.isCoveredByGenericTypeList(usedGenericTypes) {
2120 3 this.softError(cast<ASTNode*>(node.returnType), SemanticErrorType::GENERIC_TYPE_NOT_IN_TEMPLATE, String("Generic return type not included in the template type list of the function"));
2121 3 }
2122 576 }
2123
2124 // Visit params
2125 741 QualTypeList paramTypes;
2126 741 ParamList paramList;
2127
2/2
✓ Branch 0 taken 723 times.
✓ Branch 1 taken 18 times.
741 if node.hasParams {
2128
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 18 times.
39 foreach ASTDataTypeNode* param : node.paramTypeLst.dataTypes {
2129 21 const QualType paramType = this.getDataType(param);
2130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if paramType.is(SuperType::TY_UNRESOLVED) {
2131 ✗ return noSignatureManifestations;
2132 }
2133
2134 // Check if the type is present in the template for generic types
2135
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 19 times.
21 if !paramType.isCoveredByGenericTypeList(usedGenericTypes) {
2136 2 this.softError(cast<ASTNode*>(node.paramTypeLst), SemanticErrorType::GENERIC_TYPE_NOT_IN_TEMPLATE, String("Generic param type not included in the template type list of the function"));
2137 2 continue;
2138 2 }
2139
2140 19 paramTypes.pushBack(paramType);
2141 19 paramList.pushBack(Param{paramType, false});
2142 19 }
2143 18 }
2144
2145 // Build signature object
2146 741 const Function signature = Function(node.methodName, nil<SymbolTableEntry*>, QualType(SuperType::TY_DYN), returnType, paramList, usedGenericTypes, cast<ASTNode*>(node));
2147
2148 // Add signature to current scope
2149 741 Function* manifestation = insertFunction(this.currentScope, signature, &node.signatureManifestations);
2150 741 manifestation.entry = node.entry;
2151 741 manifestation.used = true;
2152
2153 // Prepare signature type
2154
2/2
✓ Branch 0 taken 576 times.
✓ Branch 1 taken 165 times.
741 const SuperType superType = isFunction ? SuperType::TY_FUNCTION : SuperType::TY_PROCEDURE;
2155 741 const QualType signatureTypeBase = QualType(superType);
2156 741 QualType signatureType = signatureTypeBase.getWithFunctionParamAndReturnTypes(returnType, paramTypes);
2157 741 signatureType.setQualifiers(node.signatureQualifiers);
2158
2159 // Set entry to signature type
2160 741 assert node.entry != nil<SymbolTableEntry*>;
2161 741 node.entry.updateType(signatureType, false);
2162 741 node.entry.used = true;
2163
2164 1482 return Any(&node.signatureManifestations);
2165 742 }
2166
2167 262093 public f<Any> TypeChecker.visitDataType(ASTDataTypeNode* node) {
2168 262093 ASTNode* baseNode = cast<ASTNode*>(node);
2169
2170 // Visit base data type
2171 262093 QualType qualType = this.visitType(cast<IVisitable*>(node.baseDataType));
2172
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 262081 times.
262093 if qualType.is(SuperType::TY_UNRESOLVED) {
2173 12 return Any(qualType);
2174 }
2175
2176 262081 Queue<TypeModifier> tmQueue = node.tmQueue;
2177
2/2
✓ Branch 0 taken 75018 times.
✓ Branch 1 taken 262064 times.
337082 while tmQueue.getSize() > 0l {
2178 75018 const TypeModifier typeModifier = tmQueue.front();
2179
2180 // Only the outermost array can have an unknown size
2181
4/4
✓ Branch 0 taken 74963 times.
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 75017 times.
✓ Branch 3 taken 1 time.
75018 if qualType.isArray() && qualType.getArraySize() == ARRAY_SIZE_UNKNOWN {
2182 2 return this.softErrorQT(baseNode, SemanticErrorType::ARRAY_SIZE_INVALID, String("Usage of incomplete array type. Only the outermost array type may have unknown size"));
2183 1 }
2184
2185
4/4
✓ Branch 0 taken 36335 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 37822 times.
✓ Branch 3 taken 854 times.
75017 switch typeModifier.modifierType {
2186 case TypeModifierType::TYPE_PTR: {
2187 37822 qualType = qualType.toPtr(baseNode);
2188 37822 }
2189 case TypeModifierType::TYPE_REF: {
2190 36335 qualType = qualType.toRef(baseNode);
2191 36335 }
2192 case TypeModifierType::TYPE_ARRAY: {
2193 854 unsigned int hardcodedSize = typeModifier.hardcodedSize;
2194 854 const String& varName = typeModifier.sizeVarName;
2195
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 422 times.
854 if !varName.isEmpty() {
2196 422 const SymbolTableEntry* globalVar = this.rootScope.lookupStrict(varName);
2197
2/2
✓ Branch 0 taken 421 times.
✓ Branch 1 taken 1 time.
422 if globalVar == nil<const SymbolTableEntry*> {
2198 2 return this.softErrorQT(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE, "Could not find global variable '" + varName + "' ");
2199 1 }
2200 421 const QualType globalVarType = globalVar.getQualType();
2201
2/2
✓ Branch 0 taken 420 times.
✓ Branch 1 taken 1 time.
421 if !globalVarType.isConst() {
2202 2 return this.softErrorQT(baseNode, SemanticErrorType::EXPECTED_CONST_VARIABLE, String("The size of the array must be known at compile time"));
2203 1 }
2204
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 419 times.
420 if !globalVarType.is(SuperType::TY_INT) {
2205 2 return this.softErrorQT(baseNode, SemanticErrorType::OPERATOR_WRONG_DATA_TYPE, String("Expected variable of type int"));
2206 1 }
2207 419 const CompileTimeValue globalVarValue = globalVar.declNode.getCompileTimeValue(this.manIdx);
2208 419 hardcodedSize = cast<unsigned int>(globalVarValue.getIntValue());
2209 419 }
2210
2211
4/4
✓ Branch 0 taken 193 times.
✓ Branch 1 taken 658 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 850 times.
851 if typeModifier.hasSize && hardcodedSize <= 1u {
2212 2 return this.softErrorQT(baseNode, SemanticErrorType::ARRAY_SIZE_INVALID, String("The size of an array must be > 1 and explicitly stated"));
2213 1 }
2214 850 qualType = qualType.toArr(baseNode, hardcodedSize);
2215 850 }
2216 }
2217 75013 tmQueue.pop();
2218 75018 }
2219
2220 // Attach the qualifiers to the type
2221
2/2
✓ Branch 0 taken 150368 times.
✓ Branch 1 taken 111696 times.
262064 if node.qualifierLst != nil<ASTQualifierLstNode*> {
2222 111696 const QualType baseType = qualType.getBase();
2223 111696 const dyn signableTypes = [SuperType::TY_INT, SuperType::TY_LONG, SuperType::TY_SHORT, SuperType::TY_BYTE, SuperType::TY_CHAR, SuperType::TY_GENERIC];
2224
2/2
✓ Branch 0 taken 124694 times.
✓ Branch 1 taken 111694 times.
236388 foreach ASTQualifierNode* qualifier : node.qualifierLst.qualifiers {
2225 124694 ASTNode* qualifierBaseNode = cast<ASTNode*>(qualifier);
2226 124694 TypeQualifiers& qualifiers = qualType.getQualifiers();
2227
2/2
✓ Branch 0 taken 46697 times.
✓ Branch 1 taken 77997 times.
124694 if qualifier.qualifierType == QualifierType::CONST {
2228 46697 qualifiers.isConst = true;
2229
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 77990 times.
124694 } else if qualifier.qualifierType == QualifierType::SIGNED {
2230
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if !baseType.isOneOf(signableTypes, len(signableTypes)) {
2231 ✗ return this.softErrorQT(qualifierBaseNode, SemanticErrorType::QUALIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this qualifier on type " + baseType.getName(false));
2232 ✗ }
2233 7 qualifiers.isSigned = true;
2234 7 qualifiers.isUnsigned = false;
2235
2/2
✓ Branch 0 taken 52009 times.
✓ Branch 1 taken 25981 times.
77997 } else if qualifier.qualifierType == QualifierType::UNSIGNED {
2236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52009 times.
52009 if !baseType.isOneOf(signableTypes, len(signableTypes)) {
2237 ✗ return this.softErrorQT(qualifierBaseNode, SemanticErrorType::QUALIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this qualifier on type " + baseType.getName(false));
2238 ✗ }
2239 52009 qualifiers.isSigned = false;
2240 52009 qualifiers.isUnsigned = true;
2241
2/2
✓ Branch 0 taken 23571 times.
✓ Branch 1 taken 2410 times.
77990 } else if qualifier.qualifierType == QualifierType::HEAP {
2242 // Heap variables can only be pointers
2243 23571 const QualType unwrappedType = qualType.removeReferenceWrapper();
2244 23571 const dyn heapTypes = [SuperType::TY_PTR, SuperType::TY_ARRAY, SuperType::TY_STRING];
2245
2/2
✓ Branch 0 taken 23570 times.
✓ Branch 1 taken 1 time.
23571 if !unwrappedType.isOneOf(heapTypes, len(heapTypes)) {
2246 2 return this.softErrorQT(qualifierBaseNode, SemanticErrorType::QUALIFIER_AT_ILLEGAL_CONTEXT, "The heap qualifier can only be applied to symbols of pointer type, you provided " + baseType.getName(false));
2247 1 }
2248 23570 qualifiers.isHeap = true;
2249
4/4
✓ Branch 0 taken 2391 times.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 2391 times.
25980 } else if qualifier.qualifierType == QualifierType::COMPOSE && node.isFieldType {
2250
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 18 times.
19 if !qualType.is(SuperType::TY_STRUCT) {
2251 2 return this.softErrorQT(qualifierBaseNode, SemanticErrorType::QUALIFIER_AT_ILLEGAL_CONTEXT, String("The compose qualifier can only be used on plain struct fields"));
2252 1 }
2253 18 qualifiers.isComposition = true;
2254
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2391 times.
✓ Branch 2 taken 238 times.
✓ Branch 3 taken 2153 times.
✓ Branch 4 taken 2391 times.
✗ Branch 5 not taken.
2409 } else if qualifier.qualifierType == QualifierType::PUBLIC && (node.isFieldType || node.isGlobalType) {
2255 2391 qualifiers.isPublic = true;
2256 2391 } else {
2257 ✗ String entryName = String("local variable");
2258
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if node.isGlobalType {
2259 ✗ entryName = String("global variable");
2260
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if node.isFieldType {
2261 ✗ entryName = String("field");
2262
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if node.isParamType {
2263 ✗ entryName = String("param");
2264
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if node.isReturnType {
2265 ✗ entryName = String("return variable");
2266 ✗ }
2267 ✗ return this.softErrorQT(qualifierBaseNode, SemanticErrorType::QUALIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this qualifier on a " + entryName + " definition");
2268 77995 }
2269 124692 }
2270 111694 }
2271
2272 524124 return Any(node.node.setEvaluatedSymbolType(qualType, this.manIdx));
2273 262069 }
2274
2275 262087 public f<Any> TypeChecker.visitBaseDataType(ASTBaseDataTypeNode* node) {
2276 // Note: The type is assigned in the cases and returned afterwards
2277 262087 QualType qualType = QualType(SuperType::TY_DYN);
2278
11/11
✓ Branch 0 taken 1857 times.
✓ Branch 1 taken 23063 times.
✓ Branch 2 taken 3567 times.
✓ Branch 3 taken 42863 times.
✓ Branch 4 taken 15123 times.
✓ Branch 5 taken 31752 times.
✓ Branch 6 taken 30879 times.
✓ Branch 7 taken 1702 times.
✓ Branch 8 taken 15126 times.
✓ Branch 9 taken 95273 times.
✓ Branch 10 taken 882 times.
262087 switch node.baseType {
2279 case BaseDataTypeType::TYPE_DOUBLE: {
2280 1857 qualType = QualType(SuperType::TY_DOUBLE);
2281 1857 }
2282 case BaseDataTypeType::TYPE_INT: {
2283 23063 qualType = QualType(SuperType::TY_INT);
2284 23063 }
2285 case BaseDataTypeType::TYPE_SHORT: {
2286 3567 qualType = QualType(SuperType::TY_SHORT);
2287 3567 }
2288 case BaseDataTypeType::TYPE_LONG: {
2289 42863 qualType = QualType(SuperType::TY_LONG);
2290 42863 }
2291 case BaseDataTypeType::TYPE_BYTE: {
2292 15123 qualType = QualType(SuperType::TY_BYTE);
2293 15123 }
2294 case BaseDataTypeType::TYPE_CHAR: {
2295 31752 qualType = QualType(SuperType::TY_CHAR);
2296 31752 }
2297 case BaseDataTypeType::TYPE_STRING: {
2298 30879 qualType = QualType(SuperType::TY_STRING);
2299 30879 }
2300 case BaseDataTypeType::TYPE_BOOL: {
2301 15126 qualType = QualType(SuperType::TY_BOOL);
2302 15126 }
2303 case BaseDataTypeType::TYPE_CUSTOM: {
2304 95273 qualType = this.visitType(cast<IVisitable*>(node.customDataType));
2305
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 95261 times.
95273 if qualType.is(SuperType::TY_UNRESOLVED) {
2306 12 return Any(qualType);
2307 }
2308 95261 }
2309 case BaseDataTypeType::TYPE_FUNCTION: {
2310 882 qualType = this.visitType(node.functionDataType);
2311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 882 times.
882 if qualType.is(SuperType::TY_UNRESOLVED) {
2312 ✗ return Any(qualType);
2313 }
2314 882 }
2315 }
2316 262075 return Any(node.node.setEvaluatedSymbolType(qualType, this.manIdx));
2317 }
2318
2319 95273 public f<Any> TypeChecker.visitCustomDataType(ASTCustomDataTypeNode* node) {
2320 95273 ASTNode* baseNode = cast<ASTNode*>(node);
2321
2322 // It is a struct type -> get the access scope
2323 95273 const String firstFragment = node.typeNameFragments.front();
2324
2325 // Check this type requires a runtime module
2326
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 95177 times.
95273 if node.typeNameFragments.getSize() == 1l {
2327 95177 this.ensureLoadedRuntimeForTypeName(firstFragment);
2328 95177 }
2329
2330 // A type can either be a single fragment like "Test" or multiple fragments "a.b.Test", which means it is imported.
2331 95273 bool isImported = node.typeNameFragments.getSize() > 1l;
2332 95273 GenericType* genericType = this.rootScope.lookupGenericTypeStrict(firstFragment);
2333
2/2
✓ Branch 0 taken 46641 times.
✓ Branch 1 taken 48632 times.
95273 if genericType != nil<GenericType*> {
2334 46641 assert !isImported;
2335 // Take the concrete replacement type for the name of this generic type if available
2336 46641 QualType symbolType = genericType.qualType;
2337
2/2
✓ Branch 0 taken 28939 times.
✓ Branch 1 taken 17702 times.
46641 if this.typeMapping.contains(firstFragment) {
2338 17702 symbolType = this.typeMapping.get(firstFragment);
2339 17702 }
2340
2341 // Check if the replacement requires a runtime module
2342
2/2
✓ Branch 0 taken 41704 times.
✓ Branch 1 taken 4937 times.
46641 if symbolType.is(SuperType::TY_STRUCT) {
2343 4937 this.ensureLoadedRuntimeForTypeName(symbolType.getSubType());
2344 4937 }
2345
2346 93282 return Any(node.node.setEvaluatedSymbolType(symbolType, this.manIdx));
2347 }
2348
2349 // Check if the type exists in the exported names registry
2350 48632 const NameRegistryEntry* registryEntry = this.sourceFile.getNameRegistryEntry(node.fqTypeName);
2351
2/2
✓ Branch 0 taken 48627 times.
✓ Branch 1 taken 5 times.
48632 if registryEntry == nil<const NameRegistryEntry*> {
2352
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if this.sourceFile.isAmbiguousName(node.fqTypeName) {
2353 4 return this.softErrorQT(baseNode, SemanticErrorType::AMBIGUOUS_SYMBOL, this.sourceFile.getAmbiguousNameMessage(node.fqTypeName));
2354 2 }
2355 6 return this.softErrorQT(baseNode, SemanticErrorType::UNKNOWN_DATATYPE, "Unknown datatype '" + node.fqTypeName + "'");
2356 3 }
2357 48627 assert registryEntry.targetEntry != nil<SymbolTableEntry*> && registryEntry.targetScope != nil<Scope*>;
2358 48627 SymbolTableEntry* entry = registryEntry.targetEntry;
2359 48627 entry.used = true;
2360 48627 Scope* defScope = registryEntry.targetScope.parent;
2361 48627 QualType entryType = entry.getQualType();
2362
2363 // With circular imports, two structs/interfaces in mutually importing files can reference each other, so neither can be
2364 // prepared strictly before the other. If we reach one here before it has been prepared (its type is still invalid),
2365 // assign its opaque type now - an implicit forward declaration. The full prepare pass assigns the identical interned
2366 // type later and additionally fills in the body and manifestations.
2367
2/2
✓ Branch 0 taken 48617 times.
✓ Branch 1 taken 10 times.
48627 if entryType.is(SuperType::TY_INVALID) {
2368 10 this.assignDeferredOpaqueType(entry);
2369 10 entryType = entry.getQualType();
2370 10 }
2371
2372 // Enums can early-return
2373
2/2
✓ Branch 0 taken 1705 times.
✓ Branch 1 taken 46922 times.
48627 if entryType.is(SuperType::TY_ENUM) {
2374 3410 return Any(QualType(SuperType::TY_INT));
2375 }
2376
2377 46922 const dyn aggregateTypes = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_UNION];
2378
2/2
✓ Branch 0 taken 43058 times.
✓ Branch 1 taken 3864 times.
46922 if entryType.isOneOf(aggregateTypes, len(aggregateTypes)) {
2379 43058 assert baseNode.parent.parent.kind == ASTNodeKind::DATA_TYPE;
2380
2381 // Remember how many template types this struct/interface/union actually declares, before it gets overwritten below
2382 43058 const QualTypeList declaredTemplateTypes = entryType.getTemplateTypes();
2383 43058 const long requiredTemplateTypeCount = declaredTemplateTypes.getSize();
2384
2385 // Collect the concrete template types
2386 43058 bool allTemplateTypesConcrete = true;
2387 43058 QualTypeList templateTypes;
2388
2/2
✓ Branch 0 taken 30229 times.
✓ Branch 1 taken 12829 times.
43058 if node.templateTypeLst != nil<IVisitable*> {
2389 12829 assert defScope != nil<Scope*>;
2390 12829 isImported = defScope.isImportedBy(this.rootScope);
2391
2392 12829 const ASTTypeLstNode* templateTypeLst = downcast<ASTTypeLstNode>(node.templateTypeLst);
2393
2/2
✓ Branch 0 taken 16718 times.
✓ Branch 1 taken 12829 times.
29547 foreach ASTDataTypeNode* dataType : templateTypeLst.dataTypes {
2394 16718 const QualType templateType = this.getDataType(dataType);
2395
1/2
✓ Branch 0 taken 16718 times.
✗ Branch 1 not taken.
16718 if templateType.is(SuperType::TY_UNRESOLVED) {
2396 ✗ return Any(templateType);
2397 }
2398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16718 times.
16718 if entryType.is(SuperType::TY_GENERIC) {
2399 ✗ allTemplateTypesConcrete = false;
2400
2/2
✓ Branch 0 taken 10718 times.
✓ Branch 1 taken 6000 times.
16718 } else if isImported {
2401 // Introduce the local type to the imported source file
2402 6000 const QualType importedType = this.mapLocalTypeToImportedScopeType(defScope, templateType);
2403 6000 assert importedType.is(templateType.getSuperType());
2404 16718 }
2405 16718 templateTypes.pushBack(templateType);
2406 16718 }
2407 12829 entryType = entryType.getWithTemplateTypes(templateTypes);
2408 12829 }
2409
2410 // Reject usage with a wrong number of template type arguments (including none, if some are required)
2411
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 43056 times.
43058 if templateTypes.getSize() != requiredTemplateTypeCount {
2412 2 String kind = String("Union");
2413
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if entryType.is(SuperType::TY_STRUCT) {
2414 2 kind = String("Struct");
2415
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
2 } else if entryType.is(SuperType::TY_INTERFACE) {
2416 ✗ kind = String("Interface");
2417 ✗ }
2418 2 const String errorMessage = kind + " '" + node.fqTypeName + "' requires " + toString(requiredTemplateTypeCount) + " template type argument(s), but got " + toString(templateTypes.getSize());
2419 4 return this.softErrorQT(baseNode, SemanticErrorType::INVALID_TEMPLATE_TYPES, errorMessage);
2420 2 }
2421
2422 // Check if struct is defined before the current code location, if defined in the same source file. Across files
2423 // (e.g. circular imports) the order does not matter, so the check only applies within the same source file.
2424 43056 const CodeLoc& declCodeLoc = entry.declNode.codeLoc;
2425 43056 const CodeLoc& codeLoc = node.codeLoc;
2426
4/4
✓ Branch 0 taken 13608 times.
✓ Branch 1 taken 29448 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 43053 times.
43056 if String(declCodeLoc.sourceFilePath) == String(codeLoc.sourceFilePath) && declCodeLoc.isAfter(codeLoc) {
2427
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 if entryType.is(SuperType::TY_STRUCT) {
2428 2 return this.softErrorQT(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_STRUCT, String("Structs must be defined before usage"));
2429 1 }
2430
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if entryType.is(SuperType::TY_INTERFACE) {
2431 2 return this.softErrorQT(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_INTERFACE, String("Interfaces must be defined before usage"));
2432 1 }
2433
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if entryType.is(SuperType::TY_UNION) {
2434 2 return this.softErrorQT(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_UNION, String("Unions must be defined before usage"));
2435 1 }
2436 ✗ assert false; // Unexpected entry type
2437 }
2438
2439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43053 times.
43053 if allTemplateTypesConcrete { // Only do the next step, if we have concrete template types
2440 // Set the struct/interface/union instance to used, if found
2441 // Here, it is allowed to accept, that the struct/interface/union cannot be found, because there are
2442 // self-referencing ones
2443 43053 const String& typeName = node.typeNameFragments.back();
2444
2/2
✓ Branch 0 taken 42373 times.
✓ Branch 1 taken 680 times.
43053 if entryType.is(SuperType::TY_STRUCT) {
2445 42373 const Struct* spiceStruct = matchStruct(defScope, typeName, templateTypes, baseNode);
2446
2/2
✓ Branch 0 taken 42211 times.
✓ Branch 1 taken 162 times.
42373 if spiceStruct != nil<const Struct*> {
2447 42211 entryType = entryType.getWithBodyScope(spiceStruct.scope);
2448 42211 }
2449
2/2
✓ Branch 0 taken 649 times.
✓ Branch 1 taken 31 times.
43053 } else if entryType.is(SuperType::TY_INTERFACE) {
2450 649 const Interface* spiceInterface = matchInterface(defScope, typeName, templateTypes, baseNode);
2451
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 648 times.
649 if spiceInterface != nil<const Interface*> {
2452 648 entryType = entryType.getWithBodyScope(spiceInterface.scope);
2453 648 }
2454 649 } else {
2455 31 assert entryType.is(SuperType::TY_UNION);
2456 31 const Union* spiceUnion = matchUnion(defScope, typeName, templateTypes, baseNode);
2457
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 4 times.
31 if spiceUnion != nil<const Union*> {
2458 27 entryType = entryType.getWithBodyScope(spiceUnion.scope);
2459 27 }
2460 680 }
2461 43053 }
2462
2463 86106 return Any(node.node.setEvaluatedSymbolType(entryType, this.manIdx));
2464 43058 }
2465
2466
2/2
✓ Branch 0 taken 3862 times.
✓ Branch 1 taken 2 times.
3864 if entryType.is(SuperType::TY_ALIAS) {
2467 3862 const QualType aliasedType = entryType.getAliased(entry);
2468 7724 return Any(node.node.setEvaluatedSymbolType(aliasedType, this.manIdx));
2469 }
2470
2471 // We tried everything to resolve it, but this type is still unknown
2472
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if entryType.is(SuperType::TY_INVALID) {
2473 2 return this.softErrorQT(baseNode, SemanticErrorType::EXPECTED_TYPE, String("Used type before declared"));
2474 1 }
2475 2 return this.softErrorQT(baseNode, SemanticErrorType::EXPECTED_TYPE, "Expected type, but got " + entryType.getName());
2476 95273 }
2477
2478 882 public f<Any> TypeChecker.visitFunctionDataType(ASTFunctionDataTypeNode* node) {
2479 // Visit return type
2480 882 QualType returnType = QualType(SuperType::TY_DYN);
2481
2/2
✓ Branch 0 taken 436 times.
✓ Branch 1 taken 446 times.
882 if node.isFunction {
2482 446 returnType = this.getDataType(node.returnType);
2483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 446 times.
446 if returnType.is(SuperType::TY_UNRESOLVED) {
2484 ✗ return Any(returnType);
2485 }
2486
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 446 times.
446 if returnType.is(SuperType::TY_DYN) {
2487 ✗ return this.softErrorQT(cast<ASTNode*>(node.returnType), SemanticErrorType::UNEXPECTED_DYN_TYPE, String("Function types cannot have return type dyn"));
2488 ✗ }
2489 446 }
2490
2491 // Visit param types
2492 882 QualTypeList paramTypes;
2493
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 631 times.
882 if node.paramTypeLst != nil<ASTTypeLstNode*> {
2494
2/2
✓ Branch 0 taken 706 times.
✓ Branch 1 taken 631 times.
1337 foreach ASTDataTypeNode* paramTypeNode : node.paramTypeLst.dataTypes {
2495 706 const QualType paramType = this.getDataType(paramTypeNode);
2496
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 706 times.
706 if paramType.is(SuperType::TY_UNRESOLVED) {
2497 ✗ return Any(paramType);
2498 }
2499 706 paramTypes.pushBack(paramType);
2500 706 }
2501 631 }
2502
2503 // Build function type
2504
2/2
✓ Branch 0 taken 446 times.
✓ Branch 1 taken 436 times.
882 const SuperType superType = node.isFunction ? SuperType::TY_FUNCTION : SuperType::TY_PROCEDURE;
2505 882 const QualType functionTypeBase = QualType(superType);
2506 882 const QualType functionType = functionTypeBase.getWithFunctionParamAndReturnTypes(returnType, paramTypes);
2507
2508 1764 return Any(node.node.setEvaluatedSymbolType(functionType, this.manIdx));
2509 882 }
2510
2511 // ========================================================== Statements ==========================================================
2512
2513 94713 public f<Any> TypeChecker.visitStmtLst(ASTStmtLstNode* node) {
2514 // Visit nodes in this scope
2515
2/2
✓ Branch 0 taken 188126 times.
✓ Branch 1 taken 94713 times.
282839 foreach IVisitable* stmt : node.statements {
2516
1/2
✓ Branch 0 taken 188126 times.
✗ Branch 1 not taken.
188126 if stmt == nil<IVisitable*> {
2517 ✗ continue;
2518 }
2519 // Print warning if statement is unreachable
2520 188126 ASTStmtNode* stmtNode = asStmtNode(getASTNode(stmt));
2521
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 188123 times.
188126 if stmtNode.unreachable {
2522 3 this.addWarning(stmtNode.codeLoc, CompilerWarningType::UNREACHABLE_CODE, String("This statement is unreachable"));
2523 3 continue;
2524 3 }
2525 // Visit the statement
2526 188123 this.visit(stmt);
2527 188123 }
2528
2529 // Do cleanup of this scope, e.g. dtor calls for struct instances
2530 94713 this.doScopeCleanup(node.resourcesToCleanup.get(this.manIdx), cast<ASTNode*>(node));
2531
2532 94713 return Any();
2533 }
2534
2535 141536 public f<Any> TypeChecker.visitDeclStmt(ASTDeclStmtNode* node) {
2536 141536 ASTNode* baseNode = cast<ASTNode*>(node);
2537 141536 DeclStmtData& data = node.data.get(this.manIdx);
2538
2539 // Retrieve entry of the lhs variable
2540 141536 SymbolTableEntry* localVarEntry = this.currentScope.lookupStrict(node.varName);
2541 141536 assert localVarEntry != nil<SymbolTableEntry*>;
2542
2543 141536 QualType localVarType = QualType(SuperType::TY_INVALID);
2544
2/2
✓ Branch 0 taken 40138 times.
✓ Branch 1 taken 101398 times.
141536 if node.hasAssignment {
2545 // Visit the right side
2546 40138 const ExprResult rhs = this.visitExpr(node.assignExpr);
2547 40138 const QualType rhsTy = rhs.ty;
2548 40138 SymbolTableEntry* rhsEntry = rhs.entry;
2549
2550 // Capture anonymous info up front: getAssignResultType may delete the anonymous entry (temp stealing
2551 // in performStructAssign), turning rhsEntry into a dangling pointer.
2552
2/2
✓ Branch 0 taken 32536 times.
✓ Branch 1 taken 7602 times.
40138 const bool rhsIsAnonymous = rhsEntry != nil<SymbolTableEntry*> && rhsEntry.anonymous;
2553 40138 String rhsEntryName = String("");
2554
2/2
✓ Branch 0 taken 37547 times.
✓ Branch 1 taken 2591 times.
40138 if rhsIsAnonymous {
2555 2591 rhsEntryName = rhsEntry.name;
2556 2591 }
2557
2558 // Visit data type
2559 40138 localVarType = this.getDataType(node.dataType);
2560
2561 // Check if type has to be inferred or both types are fixed
2562
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 40132 times.
✓ Branch 2 taken 40083 times.
✓ Branch 3 taken 55 times.
40138 if !localVarType.is(SuperType::TY_UNRESOLVED) && !rhsTy.is(SuperType::TY_UNRESOLVED) {
2563 40083 const bool isDeclaredConst = localVarType.qualifiers.isConst;
2564 40083 const ExprResult lhsResult = ExprResult{localVarType, localVarEntry};
2565 40083 Pair<QualType, Function*> assignResult = this.opRuleManager.getAssignResultType(baseNode, lhsResult, rhs, true);
2566 40083 localVarType = assignResult.getFirst();
2567 40083 data.calledCopyCtor = assignResult.getSecond();
2568 // Some assignment rules (e.g. for structs, pointers and references) derive the type from the rhs, so the variable
2569 // would get the constness of the rhs instead of the declared one. A declared const always holds. Otherwise, a copy is
2570 // not const. Only pointers and references keep the const of the rhs, because it belongs to the pointee or referent
2571
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 40070 times.
40083 if !localVarType.is(SuperType::TY_UNRESOLVED) {
2572
2/2
✓ Branch 0 taken 12197 times.
✓ Branch 1 taken 27873 times.
40070 if isDeclaredConst {
2573 12197 localVarType.makeConst();
2574
4/4
✓ Branch 0 taken 4752 times.
✓ Branch 1 taken 23121 times.
✓ Branch 2 taken 7061 times.
✓ Branch 3 taken 20812 times.
40070 } else if !localVarType.isPtr() && !localVarType.isRef() {
2575 20812 localVarType.makeConst(false);
2576 27873 }
2577 40070 }
2578
2579 // If this is a struct type, check if the type is known. If not, error out. A type that already carries a
2580 // body scope is genuinely resolved (e.g. it reached here through a generic substantiation or a deep
2581 // transitive import); only its unqualified name happens to be absent from this file's name registry, which
2582 // is not an error.
2583
2/2
✓ Branch 0 taken 33500 times.
✓ Branch 1 taken 6583 times.
40083 if localVarType.isBase(SuperType::TY_STRUCT) {
2584 6583 const QualType localVarBaseType = localVarType.getBase();
2585 6583 const String structName = localVarBaseType.getSubType();
2586
3/4
✓ Branch 0 taken 6579 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6583 times.
✗ Branch 3 not taken.
6583 if this.sourceFile.getNameRegistryEntry(structName) == nil<const NameRegistryEntry*> && localVarBaseType.getBodyScope() == nil<Scope*> {
2587 ✗ this.softError(cast<ASTNode*>(node.dataType), SemanticErrorType::UNKNOWN_DATATYPE, String("Unknown struct type '") + structName + "'. Forgot to import?");
2588 ✗ localVarType = QualType(SuperType::TY_UNRESOLVED);
2589 ✗ }
2590 6583 }
2591 40083 } else {
2592 55 localVarType = QualType(SuperType::TY_UNRESOLVED);
2593 55 }
2594
2595 // If there is an anonymous entry attached (e.g. for struct instantiation) and we take over ownership, delete it.
2596 // Safe to call even if performStructAssign already deleted it: erasing by key is a no-op when absent.
2597
4/4
✓ Branch 0 taken 2939 times.
✓ Branch 1 taken 37199 times.
✓ Branch 2 taken 37624 times.
✓ Branch 3 taken 2514 times.
40138 if !localVarType.isRef() && rhsIsAnonymous {
2598 2514 this.currentScope.symbolTable.deleteAnonymous(rhsEntryName);
2599 2514 }
2600 40138 } else {
2601 // Visit data type
2602 101398 localVarType = this.getDataType(node.dataType);
2603
2604 // References with no initialization are illegal
2605
6/6
✓ Branch 0 taken 28399 times.
✓ Branch 1 taken 72999 times.
✓ Branch 2 taken 28028 times.
✓ Branch 3 taken 371 times.
✓ Branch 4 taken 101397 times.
✓ Branch 5 taken 1 time.
101398 if localVarType.isRef() && !node.isFctParam && !node.isForEachItem {
2606 1 this.softError(baseNode, SemanticErrorType::REFERENCE_WITHOUT_INITIALIZER, String("References must always be initialized directly"));
2607 1 }
2608
2609 // If this is a struct, check for the default ctor
2610
6/6
✓ Branch 0 taken 2844 times.
✓ Branch 1 taken 98554 times.
✓ Branch 2 taken 1752 times.
✓ Branch 3 taken 1092 times.
✓ Branch 4 taken 100333 times.
✓ Branch 5 taken 1065 times.
101398 if localVarType.is(SuperType::TY_STRUCT) && !node.isFctParam && !node.isForEachItem {
2611 1065 Scope* matchScope = localVarType.getBodyScope();
2612 1065 assert matchScope != nil<Scope*>;
2613 // Check if we are required to call a ctor
2614 1065 data.isCtorCallRequired = !localVarType.isTriviallyConstructible(baseNode);
2615 // Check if we have a no-args ctor to call
2616 1065 const QualType& thisType = localVarType;
2617 1065 const ArgList noArgs;
2618 1065 const QualTypeList noTemplateTypeHints;
2619 1065 data.calledInitCtor = matchFunction(matchScope, String(CTOR_FUNCTION_NAME), thisType, noArgs, noTemplateTypeHints, false, baseNode);
2620
4/4
✓ Branch 0 taken 1022 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1059 times.
1065 if data.calledInitCtor == nil<Function*> && data.isCtorCallRequired {
2621 6 const String& structName = localVarType.getSubType();
2622 12 return this.softErrorQT(baseNode, SemanticErrorType::NO_MATCHING_CTOR_FOUND, "Struct '" + structName + "' is not trivially constructible and has no no-args constructor.");
2623 6 }
2624 1065 }
2625 101392 }
2626
2627 // Update the type of the variable
2628 141530 localVarEntry.updateType(localVarType, true);
2629 141530 SymbolTableEntry*& entrySlot = node.entries.get(this.manIdx);
2630 141530 entrySlot = localVarEntry;
2631
2632 // Update the state of the variable
2633 141530 localVarEntry.updateState(LifecycleState::INITIALIZED);
2634
2635 141530 return Any(localVarType);
2636 }
2637
2638 42557 public f<Any> TypeChecker.visitReturnStmt(ASTReturnStmtNode* node) {
2639 42557 ASTNode* baseNode = cast<ASTNode*>(node);
2640
2641 // Retrieve return variable entry
2642 42557 SymbolTableEntry* returnVar = this.currentScope.lookupResultVar();
2643 42557 const bool isFunction = returnVar != nil<SymbolTableEntry*>;
2644 42557 QualType returnType = QualType(SuperType::TY_DYN);
2645
2/2
✓ Branch 0 taken 4263 times.
✓ Branch 1 taken 38294 times.
42557 if isFunction {
2646 38294 returnType = returnVar.getQualType();
2647 38294 }
2648
2649 // Check if procedure with return value
2650
2/2
✓ Branch 0 taken 4263 times.
✓ Branch 1 taken 38294 times.
42557 if !isFunction {
2651
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4260 times.
4263 if node.hasReturnValue {
2652 6 return this.softErrorER(getASTNode(node.assignExpr), SemanticErrorType::RETURN_WITH_VALUE_IN_PROCEDURE, String("Return with value in procedure is not allowed"));
2653 3 }
2654 4260 return Any();
2655 }
2656
2657
4/4
✓ Branch 0 taken 38287 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 38292 times.
38294 if !node.hasReturnValue && !returnVar.isInitialized() {
2658 4 return this.softErrorQT(baseNode, SemanticErrorType::RETURN_WITHOUT_VALUE_RESULT, String("Return without value, but result variable is not initialized yet"));
2659 2 }
2660
2661
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 38287 times.
38292 if !node.hasReturnValue {
2662 5 return Any();
2663 }
2664
2665 // Visit right side
2666 38287 const ExprResult rhs = this.visitExpr(node.assignExpr);
2667
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 38283 times.
38287 if rhs.ty.is(SuperType::TY_UNRESOLVED) { return Any(rhs.ty); }
2668
2669 // A native lambda stores its captures in this frame, so returning a capturing lambda would let it outlive its
2670 // captures. Direct the user to the owning std Lambda wrapper. Non-capturing lambdas (plain function pointers)
2671 // are safe to return. The std Lambda wrapper hands its captures-flagged lambda back from get(), so std is exempt.
2672 38283 const QualType rhsBaseType = rhs.ty.getBase();
2673 38283 const dyn fctOptions = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
2674
6/6
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 38219 times.
✓ Branch 2 taken 63 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 38282 times.
38283 if rhsBaseType.isOneOf(fctOptions, len(fctOptions)) && rhs.ty.hasLambdaCaptures() && !this.sourceFile.isStdFile {
2675 1 const QualType nonCapturingType = rhsBaseType.getWithLambdaCaptures(false);
2676 1 const String message = String("A capturing lambda cannot be returned, because its captures live in this frame and would dangle. Wrap it in 'Lambda<") + nonCapturingType.getName(false) + ">' from std/type/lambda instead.";
2677 2 return this.softErrorER(getASTNode(node.assignExpr), SemanticErrorType::LAMBDA_CAPTURE_ESCAPE, message);
2678 1 }
2679
2680 // Capture anonymous info up front: getAssignResultType may delete the anonymous entry (temp stealing
2681 // in performStructAssign), turning rhs.entry into a dangling pointer.
2682 38282 SymbolTableEntry* rhsEntry = rhs.entry;
2683
2/2
✓ Branch 0 taken 27233 times.
✓ Branch 1 taken 11049 times.
38282 const bool rhsIsAnonymous = rhsEntry != nil<SymbolTableEntry*> && rhsEntry.anonymous;
2684 38282 String rhsEntryName = String("");
2685
2/2
✓ Branch 0 taken 35592 times.
✓ Branch 1 taken 2690 times.
38282 if rhsIsAnonymous {
2686 2690 rhsEntryName = rhsEntry.name;
2687 2690 }
2688 38282 const bool rhsIsOptimizable = this.isReturnValueOptimizable(rhs);
2689
2690 // Check if types match
2691 38282 const ExprResult returnResult = ExprResult{returnType, returnVar};
2692 38282 Pair<QualType, Function*> assignResult = this.opRuleManager.getAssignResultType(getASTNode(node.assignExpr), returnResult, rhs, false, true, ERROR_MSG_RETURN);
2693 38282 ReturnStmtData& data = node.data.get(this.manIdx);
2694 38282 data.calledCopyCtor = assignResult.getSecond();
2695
2696 // Check if the dtor call on the return value can be skipped
2697 38282 SymbolTableEntry*& returnedLocal = node.returnedLocals.get(this.manIdx);
2698 38282 returnedLocal = nil<SymbolTableEntry*>;
2699
2/2
✓ Branch 0 taken 27233 times.
✓ Branch 1 taken 11049 times.
38282 if rhsEntry != nil<SymbolTableEntry*> {
2700
2/2
✓ Branch 0 taken 2690 times.
✓ Branch 1 taken 8359 times.
11049 if rhsIsAnonymous {
2701 // If there is an anonymous entry attached (e.g. for struct instantiation), delete it.
2702 // Safe even if performStructAssign already deleted it: erasing by key is a no-op when absent.
2703 2690 this.currentScope.symbolTable.deleteAnonymous(rhsEntryName);
2704
2/2
✓ Branch 0 taken 6062 times.
✓ Branch 1 taken 2297 times.
11049 } else if rhsIsOptimizable {
2705 // Otherwise omit the destructor call of the local variable on the path of this return statement, because the caller
2706 // destructs the value. Other paths out of the scope of the variable (e.g. other returns) still have to destruct it.
2707 2297 returnedLocal = rhsEntry;
2708 8359 }
2709 11049 }
2710
2711 38282 data.returnType = returnType;
2712 76564 return Any(returnType);
2713 38282 }
2714
2715 760 public f<Any> TypeChecker.visitBreakStmt(ASTBreakStmtNode* node) {
2716 760 ASTNode* baseNode = cast<ASTNode*>(node);
2717
2718 // Check if the stated number is valid
2719
2/2
✓ Branch 0 taken 759 times.
✓ Branch 1 taken 1 time.
760 if node.breakTimes < 1 {
2720 2 return this.softErrorER(baseNode, SemanticErrorType::INVALID_BREAK_NUMBER, String("Break count must be >= 1, you provided ") + toString(node.breakTimes));
2721 1 }
2722
2723 // Check if we can break this often
2724 759 const unsigned int maxBreaks = this.currentScope.getLoopNestingDepth();
2725
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 758 times.
759 if cast<unsigned int>(node.breakTimes) > maxBreaks {
2726 2 return this.softErrorER(baseNode, SemanticErrorType::INVALID_BREAK_NUMBER, String("We can only break ") + toString(cast<long>(maxBreaks)) + " time(s) here");
2727 1 }
2728
2729 758 return Any();
2730 }
2731
2732 1437 public f<Any> TypeChecker.visitContinueStmt(ASTContinueStmtNode* node) {
2733 1437 ASTNode* baseNode = cast<ASTNode*>(node);
2734
2735 // Check if the stated number is valid
2736
2/2
✓ Branch 0 taken 1436 times.
✓ Branch 1 taken 1 time.
1437 if node.continueTimes < 1 {
2737 2 return this.softErrorER(baseNode, SemanticErrorType::INVALID_CONTINUE_NUMBER, String("Continue count must be >= 1, you provided ") + toString(node.continueTimes));
2738 1 }
2739
2740 // Check if we can continue this often
2741 1436 const unsigned int maxContinues = this.currentScope.getLoopNestingDepth();
2742
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1435 times.
1436 if cast<unsigned int>(node.continueTimes) > maxContinues {
2743 2 return this.softErrorER(baseNode, SemanticErrorType::INVALID_CONTINUE_NUMBER, String("We can only continue ") + toString(cast<long>(maxContinues)) + " time(s) here");
2744 1 }
2745
2746 1435 return Any();
2747 }
2748
2749 16 public f<Any> TypeChecker.visitFallthroughStmt(ASTFallthroughStmtNode* node) {
2750 // Check if we can do a fallthrough here
2751
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14 times.
16 if !this.currentScope.isInCaseBranch() {
2752 4 return this.softErrorER(cast<ASTNode*>(node), SemanticErrorType::FALLTHROUGH_NOT_ALLOWED, String("Fallthrough is only allowed in case branches"));
2753 2 }
2754
2755 // Check if there is a branch to fall through to. This is not the case in the last case branch of a switch without default
2756 14 ASTNode* caseBranch = node.parent;
2757
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 14 times.
34 while caseBranch.kind != ASTNodeKind::CASE_BRANCH {
2758 20 caseBranch = caseBranch.parent;
2759 20 }
2760 14 assert caseBranch.parent.kind == ASTNodeKind::SWITCH_STMT;
2761 14 const ASTSwitchStmtNode* switchStmt = downcast<ASTSwitchStmtNode>(caseBranch.parent);
2762
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
14 if !switchStmt.hasDefaultBranch && getASTNode(switchStmt.caseBranches.back()) == caseBranch {
2763 4 return this.softErrorER(cast<ASTNode*>(node), SemanticErrorType::FALLTHROUGH_NOT_ALLOWED, String("There is no branch to fall through to from the last case branch of a switch without default branch"));
2764 2 }
2765
2766 12 return Any();
2767 }
2768
2769 4819 public f<Any> TypeChecker.visitAssertStmt(ASTAssertStmtNode* node) {
2770 // Visit condition
2771 4819 const ExprResult condition = this.visitExpr(node.assignExpr);
2772 4819 const QualType conditionType = condition.ty;
2773
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4819 times.
4819 if conditionType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
2774
2775 // Check if condition evaluates to bool
2776
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4818 times.
4819 if !conditionType.is(SuperType::TY_BOOL) {
2777 2 return this.softErrorER(getASTNode(node.assignExpr), SemanticErrorType::ASSERTION_CONDITION_BOOL, String("The asserted condition must be of type bool"));
2778 1 }
2779
2780 // A failed assertion prints the stack trace, so the runtime that does that has to be loaded. Assertions are not
2781 // generated in release builds, so nothing needs to be loaded for them there
2782
5/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4815 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 4815 times.
✓ Branch 5 taken 3 times.
4818 if this.cliOptions.printsStackTraceOnAbort() && this.cliOptions.buildMode != BuildMode::RELEASE && !this.sourceFile.isRT(RuntimeModule::STACK_TRACE_RT) {
2783 3 this.sourceFile.requestRuntimeModule(RuntimeModule::STACK_TRACE_RT);
2784 3 }
2785
2786 4818 return Any();
2787 }
2788
2789 // ====================================================== Control structures =======================================================
2790
2791 12565 public f<Any> TypeChecker.visitUnsafeBlock(ASTUnsafeBlockNode* node) {
2792 // Change to unsafe block body scope
2793 12565 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::UNSAFE_BODY);
2794
2795 // Visit body
2796 12565 this.visit(node.body);
2797
2798 25130 return Any();
2799 12565 }
2800
2801 5269 public f<Any> TypeChecker.visitForLoop(ASTForLoopNode* node) {
2802 // Change to for head scope
2803 5269 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::LOOP_HEAD);
2804
2805 // Visit loop variable declaration
2806 5269 this.visit(node.initDecl);
2807
2808 // Visit condition
2809 5269 const ExprResult condition = this.visitInExprScope(node.condAssign);
2810 5269 const QualType conditionType = condition.ty;
2811
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5269 times.
5269 if conditionType.is(SuperType::TY_UNRESOLVED) { return Any(); }
2812 // Check if condition evaluates to bool
2813
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5268 times.
5269 if !conditionType.is(SuperType::TY_BOOL) {
2814 2 return this.softErrorER(getASTNode(node.condAssign), SemanticErrorType::CONDITION_MUST_BE_BOOL, String("For loop condition must be of type bool"));
2815 1 }
2816
2817 // Visit incrementer
2818 5268 this.visitInExprScope(node.incAssign);
2819
2820 // Visit body
2821 5268 ScopeHandle bodyScopeHandle = ScopeHandle(&this.compilerPass, node.getBodyScopeId(), ScopeType::FOR_BODY);
2822 5268 this.visit(node.body);
2823 5268 bodyScopeHandle.leaveScopeEarly();
2824
2825 // Do cleanup of the head scope, e.g. the dtor call for the loop variable. It is executed once the loop is left
2826 5268 this.doScopeCleanup(node.headResourcesToCleanup.get(this.manIdx), cast<ASTNode*>(node));
2827
2828 10536 return Any();
2829 5269 }
2830
2831 447 public f<Any> TypeChecker.visitForeachLoop(ASTForeachLoopNode* node) {
2832 447 ASTNode* baseNode = cast<ASTNode*>(node);
2833
2834 // Visit iterator assignment
2835 447 IVisitable* iteratorNode = node.iteratorAssign;
2836 447 ASTNode* iteratorBaseNode = getASTNode(iteratorNode);
2837 447 const ExprResult iteratorResult = this.visitExpr(iteratorNode);
2838 447 QualType iteratorOrIterableType = iteratorResult.ty;
2839
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 447 times.
447 if iteratorOrIterableType.is(SuperType::TY_UNRESOLVED) { return Any(); }
2840 447 iteratorOrIterableType = iteratorOrIterableType.removeReferenceWrapper();
2841
2842 // Retrieve iterator type
2843 447 QualType iteratorType = iteratorOrIterableType;
2844
2845 447 ForeachLoopData& data = node.data.get(this.manIdx);
2846 447 const ArgList noArgs;
2847 447 const QualTypeList noTemplateTypeHints;
2848
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 405 times.
447 if iteratorOrIterableType.isIterable(baseNode) {
2849 405 const QualType& iterableType = iteratorOrIterableType;
2850
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 397 times.
405 if iteratorOrIterableType.isArray() { // Array
2851 8 const NameRegistryEntry* nameRegistryEntry = this.sourceFile.getNameRegistryEntry(String(ARRAY_ITERATOR_NAME));
2852
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 time.
8 if nameRegistryEntry == nil<const NameRegistryEntry*> {
2853 1 this.softError(baseNode, SemanticErrorType::UNKNOWN_DATATYPE, String("Forgot to import \"std/iterator/array-iterator\"?"));
2854 2 return Any();
2855 1 }
2856 7 nameRegistryEntry.targetEntry.used = true;
2857 7 nameRegistryEntry.importEntry.used = true;
2858 7 Scope* arrayIteratorScope = nameRegistryEntry.targetScope;
2859 7 Scope* matchScope = arrayIteratorScope.parent;
2860 7 assert matchScope.scopeType == ScopeType::GLOBAL;
2861 7 QualType unsignedLongType = QualType(SuperType::TY_LONG);
2862 7 unsignedLongType.makeUnsigned(true);
2863 7 ArgList argTypes;
2864 7 argTypes.pushBack(Pair<QualType, bool>(iterableType, false));
2865 7 argTypes.pushBack(Pair<QualType, bool>(unsignedLongType, false));
2866 7 const QualType thisType = QualType(SuperType::TY_DYN);
2867 7 data.getIteratorFct = matchFunction(matchScope, String("iterate"), thisType, argTypes, noTemplateTypeHints, true, iteratorBaseNode);
2868 8 } else { // Struct, implementing Iterator interface
2869 397 Scope* matchScope = iterableType.getBodyScope();
2870 // Methods have no constness, so a const iterable is iterated like any other method is called on a const struct
2871 397 const QualType iterableThisType = iterableType.toNonConst();
2872 397 data.getIteratorFct = matchFunction(matchScope, String("getIterator"), iterableThisType, noArgs, noTemplateTypeHints, true, iteratorBaseNode);
2873 397 }
2874
2/2
✓ Branch 0 taken 403 times.
✓ Branch 1 taken 1 time.
404 if data.getIteratorFct == nil<Function*> {
2875 1 throwSemanticError(iteratorBaseNode, SemanticErrorType::INVALID_ITERATOR, String("No getIterator() function found for the given iterable type"));
2876 2 return Any();
2877 1 }
2878
2879 403 const Function* getIteratorFct = data.getIteratorFct;
2880 403 iteratorType = getIteratorFct.returnType;
2881 // Add anonymous symbol to keep track of dtor call, if non-trivially destructible
2882
2/2
✓ Branch 0 taken 402 times.
✓ Branch 1 taken 1 time.
403 if !iteratorType.isTriviallyDestructible(iteratorBaseNode) {
2883 1 this.currentScope.symbolTable.insertAnonymous(iteratorType, iteratorBaseNode);
2884 1 }
2885 403 }
2886
2887 // Change to foreach head scope
2888 445 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::LOOP_HEAD);
2889
2890 // Check iterator type
2891
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 444 times.
445 if !iteratorType.isIterator(baseNode) {
2892 1 const String errMsg = String("Can only iterate over arrays or data structures, inheriting from IIterator or IIterable. You provided ") + iteratorType.getName(false, true);
2893 1 this.softError(getASTNode(node.iteratorAssign), SemanticErrorType::OPERATOR_WRONG_DATA_TYPE, errMsg);
2894 2 return Any();
2895 1 }
2896 444 const QualTypeList iteratorTemplateTypes = iteratorType.getTemplateTypes();
2897
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 443 times.
444 if iteratorTemplateTypes.isEmpty() {
2898 2 return this.softErrorER(getASTNode(node.iteratorAssign), SemanticErrorType::INVALID_ITERATOR, String("Iterator has no generic arguments so that the item type could not be inferred"));
2899 1 }
2900
2901 443 const bool hasIdx = node.idxVarDecl != nil<IVisitable*>;
2902
2/2
✓ Branch 0 taken 409 times.
✓ Branch 1 taken 34 times.
443 if hasIdx {
2903 // Visit index declaration or assignment
2904 34 const QualType indexType = this.visitType(node.idxVarDecl);
2905
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if indexType.is(SuperType::TY_UNRESOLVED) { return Any(); }
2906 // Check if index type is int
2907
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 33 times.
34 if !indexType.is(SuperType::TY_LONG) {
2908 1 const String message = String("Index in foreach loop must be of type long. You provided ") + indexType.getName(false);
2909 2 return this.softErrorER(getASTNode(node.idxVarDecl), SemanticErrorType::FOREACH_IDX_NOT_LONG, message);
2910 1 }
2911 33 }
2912
2913 // Retrieve .get(), .getIdx(), .isValid() and .next() functions
2914 442 Scope* matchScope = iteratorType.getBodyScope();
2915 442 QualType iteratorItemType = QualType(SuperType::TY_INVALID);
2916
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 409 times.
442 if hasIdx {
2917 33 data.getIdxFct = matchFunction(matchScope, String("getIdx"), iteratorType, noArgs, noTemplateTypeHints, false, baseNode);
2918
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
33 if data.getIdxFct == nil<Function*> { return Any(); }
2919 33 const Function* getIdxFct = data.getIdxFct;
2920 33 const QualTypeList idxTemplateTypes = getIdxFct.returnType.getTemplateTypes();
2921 33 iteratorItemType = idxTemplateTypes.back();
2922 33 } else {
2923 409 data.getFct = matchFunction(matchScope, String("get"), iteratorType, noArgs, noTemplateTypeHints, false, baseNode);
2924
1/2
✓ Branch 0 taken 409 times.
✗ Branch 1 not taken.
409 if data.getFct == nil<Function*> { return Any(); }
2925 409 const Function* getFct = data.getFct;
2926 409 iteratorItemType = getFct.returnType;
2927 409 }
2928 442 data.isValidFct = matchFunction(matchScope, String("isValid"), iteratorType, noArgs, noTemplateTypeHints, false, baseNode);
2929
1/2
✓ Branch 0 taken 442 times.
✗ Branch 1 not taken.
442 if data.isValidFct == nil<Function*> { return Any(); }
2930 442 data.nextFct = matchFunction(matchScope, String("next"), iteratorType, noArgs, noTemplateTypeHints, false, baseNode);
2931
1/2
✓ Branch 0 taken 442 times.
✗ Branch 1 not taken.
442 if data.nextFct == nil<Function*> { return Any(); }
2932
2933 // Change to foreach body scope. The item lives there, since it is copied anew in each round
2934 442 ScopeHandle bodyScopeHandle = ScopeHandle(&this.compilerPass, node.getBodyScopeId(), ScopeType::FOREACH_BODY);
2935
2936 // Retrieve item variable entry
2937 442 const ASTDeclStmtNode* itemVarDecl = downcast<ASTDeclStmtNode>(node.itemVarDecl);
2938 442 SymbolTableEntry* itemVarSymbol = this.currentScope.lookupStrict(itemVarDecl.varName);
2939 442 assert itemVarSymbol != nil<SymbolTableEntry*>;
2940
2941 // Check type of the item
2942 442 QualType itemType = this.visitType(node.itemVarDecl);
2943
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 442 times.
442 if itemType.is(SuperType::TY_UNRESOLVED) { return Any(); }
2944
2/2
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 12 times.
442 if itemType.is(SuperType::TY_DYN) { // Perform type inference
2945 // Update evaluated symbol type of the declaration data type
2946 12 itemVarDecl.dataType.node.setEvaluatedSymbolType(iteratorItemType, this.manIdx);
2947 // Update item type
2948 12 itemType = iteratorItemType;
2949 12 }
2950
2951 // Check result type. This is important, also for copy ctor calls, etc.
2952 442 const ExprResult itemResult = ExprResult{itemType, itemVarSymbol};
2953 442 const ExprResult iteratorItemResult = ExprResult{iteratorItemType, nil<SymbolTableEntry*> /* always a temporary */};
2954 442 Pair<QualType, Function*> assignResult = this.opRuleManager.getAssignResultType(cast<ASTNode*>(itemVarDecl), itemResult, iteratorItemResult, true, false, ERROR_FOREACH_ITEM);
2955 442 data.calledItemCopyCtor = assignResult.getSecond();
2956
2957 // Update type of item
2958 442 itemVarSymbol.updateType(itemType, true);
2959
2960 // Visit body
2961 442 this.visit(node.body);
2962 442 bodyScopeHandle.leaveScopeEarly();
2963
2964 // Do cleanup of the head scope, e.g. the lifetime of the index variable. It is executed once the loop is left
2965 442 this.doScopeCleanup(node.headResourcesToCleanup.get(this.manIdx), baseNode);
2966
2967 884 return Any();
2968 447 }
2969
2970 1876 public f<Any> TypeChecker.visitWhileLoop(ASTWhileLoopNode* node) {
2971 // Change to while body scope
2972 1876 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::WHILE_BODY);
2973
2974 // Visit condition
2975 1876 const ExprResult condition = this.visitInExprScope(node.condition);
2976 1876 const QualType conditionType = condition.ty;
2977
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1874 times.
1876 if conditionType.is(SuperType::TY_UNRESOLVED) { return Any(); }
2978 // Check if condition evaluates to bool
2979
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1873 times.
1874 if !conditionType.is(SuperType::TY_BOOL) {
2980 2 return this.softErrorER(getASTNode(node.condition), SemanticErrorType::CONDITION_MUST_BE_BOOL, String("While loop condition must be of type bool"));
2981 1 }
2982
2983 // Visit body
2984 1873 this.visit(node.body);
2985
2986 3746 return Any();
2987 1876 }
2988
2989 82 public f<Any> TypeChecker.visitDoWhileLoop(ASTDoWhileLoopNode* node) {
2990 // Change to while body scope
2991 82 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::WHILE_BODY);
2992
2993 // Visit body
2994 82 this.visit(node.body);
2995
2996 // Visit condition
2997 82 const ExprResult condition = this.visitInExprScope(node.condition);
2998 82 const QualType conditionType = condition.ty;
2999
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if conditionType.is(SuperType::TY_UNRESOLVED) { return Any(); }
3000 // Check if condition evaluates to bool
3001
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 81 times.
82 if !conditionType.is(SuperType::TY_BOOL) {
3002 2 return this.softErrorER(getASTNode(node.condition), SemanticErrorType::CONDITION_MUST_BE_BOOL, String("Do-While loop condition must be of type bool"));
3003 1 }
3004
3005 162 return Any();
3006 82 }
3007
3008 27736 public f<Any> TypeChecker.visitIfStmt(ASTIfStmtNode* node) {
3009 // Change to then body scope
3010 27736 ScopeHandle scopeHandle = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::IF_ELSE_BODY);
3011
3012 // Visit condition
3013 27736 const ExprResult condition = this.visitInExprScope(node.condition);
3014 27736 const QualType conditionType = condition.ty;
3015
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 27731 times.
27736 if conditionType.is(SuperType::TY_UNRESOLVED) { return Any(); }
3016 // Check if condition evaluates to bool
3017 27731 ASTNode* conditionNode = getASTNode(node.condition);
3018
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 27729 times.
27731 if !conditionType.is(SuperType::TY_BOOL) {
3019 4 return this.softErrorER(conditionNode, SemanticErrorType::CONDITION_MUST_BE_BOOL, String("If condition must be of type bool"));
3020 2 }
3021
3022 // Warning for bool assignment
3023
2/2
✓ Branch 0 taken 27728 times.
✓ Branch 1 taken 1 time.
27729 if conditionNode.kind == ASTNodeKind::ASSIGN_EXPR {
3024 1 const ASTAssignExprNode* assignExprNode = downcast<ASTAssignExprNode>(conditionNode);
3025
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if assignExprNode.op == AssignOp::OP_ASSIGN {
3026 1 this.addWarning(conditionNode.codeLoc, CompilerWarningType::BOOL_ASSIGN_AS_CONDITION, String("If you want to compare the values, use '=='"));
3027 1 }
3028 1 }
3029
3030 // Update the information, if one of the branches should be skipped.
3031 // This is important to check again here, because the constness of the condition can have changed after type checking.
3032 27729 const bool constantCondition = conditionNode.hasCompileTimeValue(this.manIdx);
3033 27729 bool compileThenBranch = true;
3034 27729 bool compileElseBranch = true;
3035
2/2
✓ Branch 0 taken 27342 times.
✓ Branch 1 taken 387 times.
27729 if constantCondition {
3036 387 const CompileTimeValue compileTimeValue = conditionNode.getCompileTimeValue(this.manIdx);
3037 387 compileThenBranch = compileTimeValue.getBoolValue();
3038 387 compileElseBranch = !compileTimeValue.getBoolValue();
3039 387 }
3040 27729 bool& compileThenBranchSlot = node.compileThenBranch.get(this.manIdx);
3041 27729 compileThenBranchSlot = compileThenBranch;
3042 27729 bool& compileElseBranchSlot = node.compileElseBranch.get(this.manIdx);
3043 27729 compileElseBranchSlot = compileElseBranch;
3044
3045 // Visit body
3046
2/2
✓ Branch 0 taken 149 times.
✓ Branch 1 taken 27580 times.
27729 if compileThenBranch {
3047 27580 this.visit(node.thenBody);
3048 27580 }
3049
3050 // Leave then body scope
3051 27729 scopeHandle.leaveScopeEarly();
3052
3053 // Visit else statement if existing
3054
4/4
✓ Branch 0 taken 238 times.
✓ Branch 1 taken 27491 times.
✓ Branch 2 taken 26403 times.
✓ Branch 3 taken 1326 times.
27729 if compileElseBranch && node.elseStmt != nil<IVisitable*> {
3055 1326 this.visit(node.elseStmt);
3056 1326 }
3057
3058 55458 return Any();
3059 27736 }
3060
3061 1326 public f<Any> TypeChecker.visitElseStmt(ASTElseStmtNode* node) {
3062 // Visit if statement in the case of an else if branch
3063
2/2
✓ Branch 0 taken 566 times.
✓ Branch 1 taken 760 times.
1326 if node.isElseIf {
3064 566 this.visit(node.ifStmt);
3065 1132 return Any();
3066 566 }
3067
3068 // Change to else body scope
3069 760 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::IF_ELSE_BODY);
3070
3071 // Visit body
3072 760 this.visit(node.body);
3073
3074 1520 return Any();
3075 760 }
3076
3077 36 public f<Any> TypeChecker.visitSwitchStmt(ASTSwitchStmtNode* node) {
3078 // Check expression type
3079 36 const ExprResult switchExpr = this.visitExpr(node.assignExpr);
3080 36 const QualType exprType = switchExpr.ty;
3081
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if exprType.is(SuperType::TY_UNRESOLVED) { return Any(); }
3082 36 const dyn switchOptions = [SuperType::TY_INT, SuperType::TY_SHORT, SuperType::TY_LONG, SuperType::TY_BYTE, SuperType::TY_CHAR, SuperType::TY_BOOL];
3083
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 35 times.
36 if !exprType.isOneOf(switchOptions, len(switchOptions)) {
3084 2 return this.softErrorER(getASTNode(node.assignExpr), SemanticErrorType::SWITCH_EXPR_MUST_BE_PRIMITIVE, String("Switch expression must be of int, short, long, byte, char or bool type"));
3085 1 }
3086
3087 // Visit children
3088 35 this.visitChildren(cast<ASTNode*>(node));
3089
3090 // Check if case constant types match switch expression type
3091
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 191 times.
224 foreach IVisitable* caseBranch : node.caseBranches {
3092 191 const ASTCaseBranchNode* caseBranchNode = downcast<ASTCaseBranchNode>(caseBranch);
3093
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 189 times.
385 foreach IVisitable* constantNode : caseBranchNode.caseConstants {
3094 196 const ExprResult constantResult = this.visitExpr(constantNode);
3095 196 const QualType constantType = constantResult.ty;
3096
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 194 times.
196 if !constantType.matches(exprType, false, true, true) {
3097 4 return this.softErrorER(getASTNode(constantNode), SemanticErrorType::SWITCH_CASE_TYPE_MISMATCH, String("Case value type does not match the switch expression type"));
3098 2 }
3099 194 }
3100 189 }
3101
3102 66 return Any();
3103 35 }
3104
3105 191 public f<Any> TypeChecker.visitCaseBranch(ASTCaseBranchNode* node) {
3106 // Change to case body scope
3107 191 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::CASE_BODY);
3108
3109 // Visit constant list
3110
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 191 times.
387 foreach IVisitable* constant : node.caseConstants {
3111 196 this.visit(constant);
3112 196 }
3113
3114 // Visit body
3115 191 this.visit(node.body);
3116
3117 382 return Any();
3118 191 }
3119
3120 392 public f<Any> TypeChecker.visitCaseConstant(ASTCaseConstantNode* node) {
3121 // If we have a normal constant, we can take the symbol type from there
3122
2/2
✓ Branch 0 taken 200 times.
✓ Branch 1 taken 192 times.
392 if node.constant != nil<ASTConstantNode*> {
3123 200 return this.visitConstant(node.constant);
3124 }
3125 192 ASTNode* baseNode = cast<ASTNode*>(node);
3126
3127 // Check if a local or global variable can be found by searching for the name
3128 192 SymbolTableEntry* entry = nil<SymbolTableEntry*>;
3129
2/2
✓ Branch 0 taken 188 times.
✓ Branch 1 taken 4 times.
192 if node.identifierFragments.getSize() == 1l {
3130 4 entry = this.currentScope.lookup(node.identifierFragments.back());
3131 4 }
3132
3133 // If no local or global was found, search in the name registry
3134
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 188 times.
192 if entry == nil<SymbolTableEntry*> {
3135 188 const NameRegistryEntry* registryEntry = this.sourceFile.getNameRegistryEntry(node.fqIdentifier);
3136
1/2
✓ Branch 0 taken 188 times.
✗ Branch 1 not taken.
188 if registryEntry == nil<const NameRegistryEntry*> {
3137
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if this.sourceFile.isAmbiguousName(node.fqIdentifier) {
3138 ✗ return this.softErrorER(baseNode, SemanticErrorType::AMBIGUOUS_SYMBOL, this.sourceFile.getAmbiguousNameMessage(node.fqIdentifier));
3139 ✗ }
3140 ✗ const String message = String("The variable '") + node.fqIdentifier + "' could not be found";
3141 ✗ return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE, message);
3142 ✗ }
3143 188 entry = registryEntry.targetEntry;
3144 188 }
3145 192 assert entry != nil<SymbolTableEntry*>;
3146 192 entry.used = true;
3147 192 node.entry = entry;
3148
3149 // Check for the correct type
3150 192 const QualType qualType = entry.getQualType();
3151 192 const dyn caseConstantOptions = [SuperType::TY_INT, SuperType::TY_SHORT, SuperType::TY_LONG, SuperType::TY_BYTE, SuperType::TY_CHAR, SuperType::TY_BOOL];
3152
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 190 times.
192 if !qualType.isOneOf(caseConstantOptions, len(caseConstantOptions)) {
3153 4 return this.softErrorER(baseNode, SemanticErrorType::CASE_CONSTANT_INVALID_TYPE, String("Case constants must be of type int, short, long, byte, char, bool or enum"));
3154 2 }
3155
3156 190 return Any(ExprResult{node.node.setEvaluatedSymbolType(qualType, this.manIdx), nil<SymbolTableEntry*>});
3157 }
3158
3159 20 public f<Any> TypeChecker.visitDefaultBranch(ASTDefaultBranchNode* node) {
3160 // Change to default body scope
3161 20 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::DEFAULT_BODY);
3162
3163 // Visit body
3164 20 this.visit(node.body);
3165
3166 40 return Any();
3167 20 }
3168
3169 126 public f<Any> TypeChecker.visitAnonymousBlockStmt(ASTAnonymousBlockStmtNode* node) {
3170 // Change to anonymous scope body scope
3171 126 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::ANONYMOUS_BLOCK_BODY);
3172
3173 // Visit body
3174 126 this.visit(node.body);
3175
3176 252 return Any();
3177 126 }
3178
3179 // ========================================================= Expressions ==========================================================
3180
3181 /**
3182 * Check if a temporary struct is moved into the lhs of an assignment (temp stealing) and the old value of the lhs can
3183 * safely be destructed before it gets overwritten.
3184 *
3185 * @param node Assign expression node
3186 * @param rhsType Type of the right side of the assignment
3187 * @param isRhsTemporary Whether the right side of the assignment is a temporary
3188 * @return Destructible temp steal target or not
3189 */
3190 741 f<bool> TypeChecker.isDestructibleTempStealTarget(ASTAssignExprNode* node, const QualType& rhsType, bool isRhsTemporary) {
3191 // The rhs must be a temporary struct value, that is moved into the lhs, and no assign operator overload may take over
3192
5/6
✓ Branch 0 taken 533 times.
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 533 times.
✓ Branch 4 taken 272 times.
✓ Branch 5 taken 469 times.
741 if rhsType.isRef() || !rhsType.is(SuperType::TY_STRUCT) || !isRhsTemporary {
3193 272 return false;
3194 }
3195 469 const Vector<const Function*>& opFcts = this.getOpFctPointers(cast<ASTNode*>(node));
3196
2/2
✓ Branch 0 taken 424 times.
✓ Branch 1 taken 45 times.
469 if opFcts.front() != nil<const Function*> {
3197 424 return false;
3198 }
3199 // The initialization state of the lhs entry does not tell, whether the overwritten value was actually constructed at
3200 // runtime (e.g. for '*ptr = ...', 'array[i] = ...' or 'result.field = ...'), so check the lhs expression itself
3201 45 return this.isAlwaysConstructedLvalue(getASTNode(node.lhs));
3202 }
3203
3204 /**
3205 * Check if the value, that an lvalue expression denotes, has always been constructed at runtime, when it is accessed.
3206 * This is the case for variables (except the result variable) and chains of field accesses on them or on 'this', as long
3207 * as each involved field value is referenced or its struct has a no-args ctor. Variables are constructed at their
3208 * declaration: either explicitly, by the no-args ctor or, for trivially constructible structs, by zero-initialization. All
3209 * other structs require an initial value. Field values of structs with a no-args ctor are constructed in the ctor
3210 * preamble. Field values of other structs might still be uninitialized memory, e.g. in a ctor body.
3211 *
3212 * @param node Lvalue expression
3213 * @return Always constructed or not
3214 */
3215 2564 f<bool> TypeChecker.isAlwaysConstructedLvalue(ASTNode* node) {
3216 // Check the accessed value itself
3217 2564 ASTExprNode* exprNode = asExprNode(node);
3218 2564 const QualType lvalueType = exprNode.getEvaluatedSymbolType(this.manIdx);
3219 2564 ASTAtomicExprNode* atomicExpr = nil<ASTAtomicExprNode*>;
3220
2/2
✓ Branch 0 taken 2500 times.
✓ Branch 1 taken 64 times.
2564 if node.kind == ASTNodeKind::ATOMIC_EXPR {
3221 64 atomicExpr = downcastMut<ASTAtomicExprNode>(node);
3222 64 }
3223
2/2
✓ Branch 0 taken 2500 times.
✓ Branch 1 taken 64 times.
2564 const bool isThis = atomicExpr != nil<ASTAtomicExprNode*> && atomicExpr.fqIdentifier == THIS_VARIABLE_NAME;
3224
4/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 2537 times.
✓ Branch 2 taken 45 times.
✓ Branch 3 taken 2519 times.
2564 if !isThis && !lvalueType.isRef() {
3225
2/2
✓ Branch 0 taken 2051 times.
✓ Branch 1 taken 468 times.
2519 if !lvalueType.is(SuperType::TY_STRUCT) {
3226 2051 return false;
3227 }
3228 // Field values additionally require a no-args ctor (see above)
3229 468 const QualType structType = lvalueType.toNonConst();
3230 468 const ArgList noArgs;
3231
4/4
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 445 times.
✓ Branch 2 taken 404 times.
✓ Branch 3 taken 64 times.
468 if atomicExpr == nil<ASTAtomicExprNode*> && lookupFunction(structType.getBodyScope(), String(CTOR_FUNCTION_NAME), structType, noArgs, false) == nil<const Function*> {
3232 404 return false;
3233 }
3234 468 }
3235
3236 // Variables are constructed, except the result variable, which is only constructed by assigning it
3237
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 59 times.
109 if atomicExpr != nil<ASTAtomicExprNode*> {
3238 50 return atomicExpr.fqIdentifier != RETURN_VARIABLE_NAME;
3239 }
3240
3241 // Fields are constructed, if the value they are accessed on is constructed
3242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
59 if node.kind != ASTNodeKind::POSTFIX_UNARY_EXPR {
3243 ✗ return false;
3244 }
3245 59 const ASTPostfixUnaryExprNode* postfixUnaryExpr = downcast<ASTPostfixUnaryExprNode>(node);
3246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
59 if postfixUnaryExpr.op != PostfixUnaryOp::OP_MEMBER_ACCESS {
3247 ✗ return false;
3248 }
3249 59 return this.isAlwaysConstructedLvalue(getASTNode(postfixUnaryExpr.postfixUnaryExpr));
3250 }
3251
3252 /**
3253 * Check if the value of a return statement can be returned without copying it (RVO). This is only the case for local
3254 * variables of the current function/procedure/lambda, because their dtor call can be omitted and the caller takes over
3255 * the ownership. Fields, globals, captured variables and values that are reached through a variable (e.g. by
3256 * dereferencing a pointer) are still owned by someone else, so they have to be copied.
3257 *
3258 * @param returnValue Value of the return statement
3259 * @return Return value optimizable or not
3260 */
3261 38868 public f<bool> TypeChecker.isReturnValueOptimizable(const ExprResult& returnValue) {
3262 // The return value must be a variable, not a temporary or a reference
3263
4/4
✓ Branch 0 taken 29955 times.
✓ Branch 1 taken 8913 times.
✓ Branch 2 taken 30426 times.
✓ Branch 3 taken 8442 times.
38868 if returnValue.isTemporary() || returnValue.ty.isRef() {
3264 30426 return false;
3265 }
3266 // The return value must be the variable itself, not a value that is reached through it
3267 8442 const SymbolTableEntry* entry = returnValue.entry;
3268 8442 const QualType entryType = entry.getQualType();
3269
2/2
✓ Branch 0 taken 1227 times.
✓ Branch 1 taken 7215 times.
8442 if !entryType.matches(returnValue.ty, false, true, true) {
3270 1227 return false;
3271 }
3272 // The variable must be declared in the current function/procedure/lambda
3273 7215 const Scope* functionScope = this.currentScope.getFunctionScope();
3274 7215 const Scope* scope = entry.scope;
3275
2/2
✓ Branch 0 taken 14153 times.
✓ Branch 1 taken 2835 times.
16988 while scope != nil<const Scope*> {
3276
4/4
✓ Branch 0 taken 4376 times.
✓ Branch 1 taken 9777 times.
✓ Branch 2 taken 4380 times.
✓ Branch 3 taken 9773 times.
14153 if scope.scopeType == ScopeType::FUNC_PROC_BODY || scope.scopeType == ScopeType::LAMBDA_BODY {
3277 4380 return scope == functionScope;
3278 }
3279 9773 scope = scope.parent;
3280 9773 }
3281 2835 return false;
3282 }
3283
3284 30513 public f<Any> TypeChecker.visitAssignExpr(ASTAssignExprNode* node) {
3285 30513 ASTNode* baseNode = cast<ASTNode*>(node);
3286
3287 // Check if ternary
3288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30513 times.
30513 if node.ternaryExpr != nil<IVisitable*> {
3289 ✗ const ExprResult ternaryResult = this.visitExpr(node.ternaryExpr);
3290 ✗ node.node.setEvaluatedSymbolType(ternaryResult.ty, this.manIdx);
3291 ✗ return Any(ternaryResult);
3292 }
3293
3294 // Check if assignment
3295
1/2
✓ Branch 0 taken 30513 times.
✗ Branch 1 not taken.
30513 if node.op == AssignOp::OP_NONE {
3296 ✗ panic(Error("AssignExpr fall-through"));
3297 }
3298
3299 // Visit the right side first
3300 30513 const ExprResult rhs = this.visitExpr(node.rhs);
3301 30513 QualType rhsType = rhs.ty;
3302
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 30512 times.
30513 if rhsType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3303 // Then visit the left side
3304 30512 const ExprResult lhs = this.visitExpr(node.lhs);
3305 30512 const QualType lhsType = lhs.ty;
3306 30512 SymbolTableEntry* lhsVar = lhs.entry;
3307
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 30509 times.
30512 if lhsType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3308
3309 // A native lambda stores its captures in the stack frame that created it. Storing a capturing lambda in a
3310 // struct field lets it outlive those captures, so the captures would dangle. Reject this and direct the user
3311 // to the owning std Lambda wrapper. Non-capturing lambdas (plain function pointers) are safe to store and are
3312 // not affected. The std Lambda wrapper itself stores the captures-flagged lambda in its own field, so std
3313 // files are exempt.
3314
6/6
✓ Branch 0 taken 27742 times.
✓ Branch 1 taken 2767 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 27741 times.
✓ Branch 4 taken 8818 times.
✓ Branch 5 taken 21691 times.
30509 if node.op == AssignOp::OP_ASSIGN && lhs.entry != nil<SymbolTableEntry*> && lhs.entry.isField() {
3315 21691 const QualType rhsBaseType = rhsType.getBase();
3316 21691 const dyn fctOptions = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
3317
6/6
✓ Branch 0 taken 209 times.
✓ Branch 1 taken 21482 times.
✓ Branch 2 taken 195 times.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 21690 times.
21691 if rhsBaseType.isOneOf(fctOptions, len(fctOptions)) && rhsType.hasLambdaCaptures() && !this.sourceFile.isStdFile {
3318 1 const QualType nonCapturingType = rhsBaseType.getWithLambdaCaptures(false);
3319 1 const String typeName = nonCapturingType.getName(false);
3320 1 const String message = String("A capturing lambda cannot be stored in a field, because its captures live in the frame that created it and would dangle. Wrap it in 'Lambda<") + typeName + ">' from std/type/lambda instead.";
3321 2 return this.softErrorER(getASTNode(node.rhs), SemanticErrorType::LAMBDA_CAPTURE_ESCAPE, message);
3322 1 }
3323 21690 }
3324
3325 // Take a look at the operator
3326
2/2
✓ Branch 0 taken 27741 times.
✓ Branch 1 taken 2767 times.
30508 if node.op == AssignOp::OP_ASSIGN {
3327
4/4
✓ Branch 0 taken 27740 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 6050 times.
✓ Branch 3 taken 21690 times.
27741 const bool isDecl = lhs.entry != nil<SymbolTableEntry*> && lhs.entry.isField() && !lhs.entry.isInitialized();
3328 // Save this before checking the assignment, because stealing a temporary deletes its anonymous symbol table entry
3329 27741 const bool isRhsTemporary = rhs.isTemporary();
3330 // A field is only initialized by an assignment to a direct field of 'this' in a ctor body, which also binds a reference
3331 // field (like the IR generator does). Elsewhere, the assignment writes the field (or through the reference), so it gets
3332 // checked as such, e.g. for constness
3333
2/2
✓ Branch 0 taken 17158 times.
✓ Branch 1 taken 10583 times.
27741 const bool isFieldInitAssign = this.currentScope.isInCtorBody() && isDirectFieldOfThis(getASTNode(node.lhs));
3334
2/2
✓ Branch 0 taken 24513 times.
✓ Branch 1 taken 3228 times.
27741 const bool isDeclAssign = isDecl && isFieldInitAssign;
3335 27741 Pair<QualType, Function*> assignResult = this.opRuleManager.getAssignResultType(baseNode, lhs, rhs, isDeclAssign);
3336 27741 rhsType = assignResult.getFirst();
3337 27741 const Function* copyCtor = assignResult.getSecond();
3338 // If the assignment overwrites an already initialized struct, the old value of the lhs must be destructed first.
3339 // Otherwise its owning members (heap pointers, strings, ...) would leak. This applies when a new value is copied
3340 // into the lhs (non-null copy ctor) and when a temporary is moved into it (temp stealing).
3341 // 'isInitialized()' is false for declarations, uninitialized fields and moved-from values, so those are skipped.
3342 // The lifecycle of a field is tracked per struct, not per instance, so the first assignment to a field in the source code
3343 // can overwrite the value of a constructed object as well, if the field is reached via a variable or 'this'. Only the
3344 // direct fields of 'this' in a ctor body might still be uninitialized. Nested fields were constructed in the ctor
3345 // preamble. Fields reached via a pointer might still be uninitialized memory (e.g. a raw allocation, that is
3346 // initialized field by field).
3347 // Unsafe blocks are excluded on purpose: code that manually manages object lifetimes there (e.g. the raw
3348 // element shifts in container implementations) relies on assignments not implicitly destructing the lhs.
3349
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 27740 times.
27741 bool overwritesValue = lhs.entry != nil<SymbolTableEntry*> && lhs.entry.isInitialized();
3350
2/2
✓ Branch 0 taken 24513 times.
✓ Branch 1 taken 3228 times.
27741 if isDecl {
3351
2/2
✓ Branch 0 taken 2459 times.
✓ Branch 1 taken 769 times.
3228 const bool isFieldInit = this.currentScope.isInCtorBody() && isDirectFieldOfThis(getASTNode(node.lhs));
3352
2/2
✓ Branch 0 taken 768 times.
✓ Branch 1 taken 2460 times.
3228 overwritesValue = !isFieldInit && this.isAlwaysConstructedLvalue(getASTNode(node.lhs));
3353 3228 }
3354
5/6
✓ Branch 0 taken 23385 times.
✓ Branch 1 taken 4356 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23385 times.
✓ Branch 4 taken 9975 times.
✓ Branch 5 taken 17766 times.
27741 if overwritesValue && lhs.entry != nil<SymbolTableEntry*> && !this.currentScope.doesAllowUnsafeOperations() {
3355 17766 const QualType lhsUnwrappedType = lhs.ty.removeReferenceWrapper();
3356 17766 const QualType lhsSType = lhsUnwrappedType.toNonConst();
3357
8/8
✓ Branch 0 taken 1676 times.
✓ Branch 1 taken 16090 times.
✓ Branch 2 taken 908 times.
✓ Branch 3 taken 768 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 741 times.
✓ Branch 6 taken 17722 times.
✓ Branch 7 taken 44 times.
17766 if lhsSType.is(SuperType::TY_STRUCT) && !lhsSType.isTriviallyDestructible(baseNode) && (copyCtor != nil<const Function*> || this.isDestructibleTempStealTarget(node, rhs.ty, isRhsTemporary)) {
3358 44 const ArgList noArgs;
3359 44 const Function*& lhsDtorFct = node.lhsDtorFct.get(this.manIdx);
3360 44 lhsDtorFct = this.implicitlyCallStructMethod(lhsSType, String(DTOR_FUNCTION_NAME), noArgs, baseNode);
3361 44 }
3362 17766 }
3363
2/2
✓ Branch 0 taken 1998 times.
✓ Branch 1 taken 769 times.
30508 } else if node.op == AssignOp::OP_PLUS_EQUAL {
3364 1998 const ExprResult plusEqualResult = this.opRuleManager.getPlusEqualResultType(baseNode, lhs, rhs);
3365 1998 rhsType = plusEqualResult.ty;
3366
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 675 times.
2767 } else if node.op == AssignOp::OP_MINUS_EQUAL {
3367 94 const ExprResult minusEqualResult = this.opRuleManager.getMinusEqualResultType(baseNode, lhs, rhs);
3368 94 rhsType = minusEqualResult.ty;
3369
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 515 times.
769 } else if node.op == AssignOp::OP_MUL_EQUAL {
3370 160 const ExprResult mulEqualResult = this.opRuleManager.getMulEqualResultType(baseNode, lhs, rhs);
3371 160 rhsType = mulEqualResult.ty;
3372
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 407 times.
675 } else if node.op == AssignOp::OP_DIV_EQUAL {
3373 108 const ExprResult divEqualResult = this.opRuleManager.getDivEqualResultType(baseNode, lhs, rhs);
3374 108 rhsType = divEqualResult.ty;
3375
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 345 times.
515 } else if node.op == AssignOp::OP_REM_EQUAL {
3376 62 rhsType = this.opRuleManager.getRemEqualResultType(baseNode, lhs, rhs);
3377
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 332 times.
407 } else if node.op == AssignOp::OP_SHL_EQUAL {
3378 13 rhsType = this.opRuleManager.getSHLEqualResultType(baseNode, lhs, rhs);
3379
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 312 times.
345 } else if node.op == AssignOp::OP_SHR_EQUAL {
3380 20 rhsType = this.opRuleManager.getSHREqualResultType(baseNode, lhs, rhs);
3381
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 282 times.
332 } else if node.op == AssignOp::OP_AND_EQUAL {
3382 30 rhsType = this.opRuleManager.getAndEqualResultType(baseNode, lhs, rhs);
3383
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 250 times.
312 } else if node.op == AssignOp::OP_OR_EQUAL {
3384 32 rhsType = this.opRuleManager.getOrEqualResultType(baseNode, lhs, rhs);
3385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 250 times.
282 } else if node.op == AssignOp::OP_XOR_EQUAL {
3386 250 rhsType = this.opRuleManager.getXorEqualResultType(baseNode, lhs, rhs);
3387 2767 }
3388
3389
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 30507 times.
30508 if lhsVar != nil<SymbolTableEntry*> { // Variable is involved on the left side
3390 // Perform type inference
3391
2/2
✓ Branch 0 taken 30505 times.
✓ Branch 1 taken 2 times.
30507 if lhsType.is(SuperType::TY_DYN) {
3392 2 lhsVar.updateType(rhsType, false);
3393 2 }
3394
3395 // In case the lhs variable is captured, notify the capture about the write access
3396 30507 Capture* lhsCapture = this.currentScope.symbolTable.lookupCapture(lhsVar.name);
3397
2/2
✓ Branch 0 taken 30486 times.
✓ Branch 1 taken 21 times.
30507 if lhsCapture != nil<Capture*> {
3398 21 lhsCapture.setAccessType(CaptureAccessType::READ_WRITE);
3399 21 }
3400
3401 // Update the state of the variable
3402 30507 lhsVar.updateState(LifecycleState::INITIALIZED);
3403 30507 }
3404
3405 30508 return Any(ExprResult{node.node.setEvaluatedSymbolType(rhsType, this.manIdx), nil<SymbolTableEntry*>});
3406 }
3407
3408 2294 public f<Any> TypeChecker.visitTernaryExpr(ASTTernaryExprNode* node) {
3409 // Check if there is a ternary operator applied
3410
1/2
✓ Branch 0 taken 2294 times.
✗ Branch 1 not taken.
2294 if node.falseExpr == nil<IVisitable*> {
3411 ✗ return this.visit(node.condition);
3412 }
3413 2294 ASTNode* baseNode = cast<ASTNode*>(node);
3414
3415 // Visit condition
3416 2294 const ExprResult condition = this.visitExpr(node.condition);
3417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2294 times.
2294 if condition.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3418 // Visit the branches. Only one of them is evaluated, so each one has a scope of its own for its temporaries. The dtor calls for
3419 // those are considered further down, once it is clear which temporaries the result takes over.
3420 2294 Pair<ExprResult, Scope*> trueBranch = Pair<ExprResult, Scope*>(condition, nil<Scope*>);
3421
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2292 times.
2294 if !node.isShortened {
3422 2292 trueBranch = this.visitTernaryBranch(node.trueExpr);
3423 2292 }
3424 2294 const ExprResult trueExpr = trueBranch.getFirst();
3425 2294 Scope* trueScope = trueBranch.getSecond();
3426 2294 const QualType trueType = trueExpr.ty;
3427 2294 SymbolTableEntry* trueEntry = trueExpr.entry;
3428
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2294 times.
2294 if trueType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3429 2294 Pair<ExprResult, Scope*> falseBranch = this.visitTernaryBranch(node.falseExpr);
3430 2294 const ExprResult falseExpr = falseBranch.getFirst();
3431 2294 Scope* falseScope = falseBranch.getSecond();
3432 2294 const QualType falseType = falseExpr.ty;
3433 2294 SymbolTableEntry* falseEntry = falseExpr.entry;
3434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2294 times.
2294 if falseType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3435
3436 // Check if the condition evaluates to bool
3437
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2293 times.
2294 if !condition.ty.is(SuperType::TY_BOOL) {
3438 2 return this.softErrorER(getASTNode(node.condition), SemanticErrorType::OPERATOR_WRONG_DATA_TYPE, String("Condition operand in ternary must be a bool"));
3439 1 }
3440
3441 // Check if trueType and falseType are matching
3442 2293 const QualType trueTypeModified = trueType.removeReferenceWrapper();
3443 2293 const QualType falseTypeModified = falseType.removeReferenceWrapper();
3444
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2292 times.
2293 if !trueTypeModified.matches(falseTypeModified, false, true, false) {
3445 1 const String message = String("True and false operands in ternary must be of same data type. Got ") + trueType.getName(true) + " and " + falseType.getName(true);
3446 2 return this.softErrorER(baseNode, SemanticErrorType::OPERATOR_WRONG_DATA_TYPE, message);
3447 1 }
3448
3449 // An anonymous entry marks a temporary (e.g. a fresh struct instantiation or a non-trivial function-call
3450 // result), whose storage does not outlive this expression. The only exception is an anonymous entry of reference type. It
3451 // belongs to a nested ternary with a reference result, which just refers to the storage of one of its operands.
3452 2292 const bool trueIsTemporary = isTemporaryTernaryOperand(trueEntry);
3453 2292 const bool falseIsTemporary = isTemporaryTernaryOperand(falseEntry);
3454
3455 // Whether the copy ctor is called depends on the operand types, so it is decided anew for each manifestation
3456 2292 TernaryExprData& data = node.data.get(this.manIdx);
3457 2292 data.trueSideCallsCopyCtor = false;
3458 2292 data.falseSideCallsCopyCtor = false;
3459 2292 data.calledCopyCtor = nil<Function*>;
3460
3461 // The result type must be a reference if one of true/false branch is of reference type. Otherwise,
3462 // the copy ctor is not called correctly. This can only be done if neither branch is a temporary though:
3463 // a temporary's storage ends with this expression, so the result must take a copy of it instead of
3464 // referencing it, or the copy ctor call for the temporary would end up being skipped further down,
3465 // leaking it.
3466 2292 QualType resultType = trueTypeModified;
3467 // If both branches are lvalues, the result is one as well, so it can be bound to a non-const reference.
3468
6/6
✓ Branch 0 taken 1104 times.
✓ Branch 1 taken 1188 times.
✓ Branch 2 taken 536 times.
✓ Branch 3 taken 568 times.
✓ Branch 4 taken 425 times.
✓ Branch 5 taken 111 times.
2292 const bool bothLvalues = trueEntry != nil<SymbolTableEntry*> && falseEntry != nil<SymbolTableEntry*> && !trueIsTemporary && !falseIsTemporary;
3469
8/8
✓ Branch 0 taken 1867 times.
✓ Branch 1 taken 425 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1858 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 1831 times.
✓ Branch 6 taken 35 times.
✓ Branch 7 taken 2257 times.
2292 if !trueIsTemporary && !falseIsTemporary && (trueType.isRef() || falseType.isRef()) {
3470
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 8 times.
35 resultType = trueType.isRef() ? trueType : falseType;
3471
2/2
✓ Branch 0 taken 2173 times.
✓ Branch 1 taken 84 times.
2292 } else if bothLvalues {
3472 84 resultType = trueTypeModified.toRef(baseNode);
3473 2257 }
3474 // Infer the const-ness from the more restrictive operand
3475
2/2
✓ Branch 0 taken 528 times.
✓ Branch 1 taken 1764 times.
2292 resultType.makeConst(trueType.isConst() || falseType.isConst());
3476
3477 // If there is an anonymous symbol attached to left or right, remove it,
3478 // since the result takes over the ownership of any destructible object.
3479 // The symbol lives in the scope of its branch, or in the current scope if the branch has no scope of its own.
3480 2292 bool removedAnonymousSymbols = false;
3481
2/2
✓ Branch 0 taken 1188 times.
✓ Branch 1 taken 1104 times.
2292 if trueEntry != nil<SymbolTableEntry*> {
3482
2/2
✓ Branch 0 taken 425 times.
✓ Branch 1 taken 679 times.
1104 if trueIsTemporary {
3483
1/2
✓ Branch 0 taken 425 times.
✗ Branch 1 not taken.
425 Scope* anonymousSymbolScope = trueScope != nil<Scope*> ? trueScope : this.currentScope;
3484 425 const String anonymousSymbolName = trueEntry.name;
3485 425 anonymousSymbolScope.symbolTable.deleteAnonymous(anonymousSymbolName);
3486 425 removedAnonymousSymbols = true;
3487
4/4
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 576 times.
✓ Branch 2 taken 670 times.
✓ Branch 3 taken 9 times.
1104 } else if !resultType.isRef() && !trueTypeModified.isTriviallyCopyable(baseNode) {
3488 9 data.trueSideCallsCopyCtor = true;
3489 679 }
3490 1104 }
3491
2/2
✓ Branch 0 taken 1114 times.
✓ Branch 1 taken 1178 times.
2292 if falseEntry != nil<SymbolTableEntry*> {
3492
2/2
✓ Branch 0 taken 417 times.
✓ Branch 1 taken 761 times.
1178 if falseIsTemporary {
3493
1/2
✓ Branch 0 taken 417 times.
✗ Branch 1 not taken.
417 Scope* anonymousSymbolScope = falseScope != nil<Scope*> ? falseScope : this.currentScope;
3494 417 const String anonymousSymbolName = falseEntry.name;
3495 417 anonymousSymbolScope.symbolTable.deleteAnonymous(anonymousSymbolName);
3496 417 removedAnonymousSymbols = true;
3497
4/4
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 659 times.
✓ Branch 2 taken 744 times.
✓ Branch 3 taken 17 times.
1178 } else if !resultType.isRef() && !falseTypeModified.isTriviallyCopyable(baseNode) {
3498 17 data.falseSideCallsCopyCtor = true;
3499 761 }
3500 1178 }
3501
3502 // Create a new anonymous symbol for the result if required
3503 2292 SymbolTableEntry* anonymousSymbol = nil<SymbolTableEntry*>;
3504
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2283 times.
2292 const bool calledCopyCtor = data.trueSideCallsCopyCtor || data.falseSideCallsCopyCtor;
3505
5/6
✓ Branch 0 taken 1858 times.
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1858 times.
✓ Branch 4 taken 1739 times.
✓ Branch 5 taken 553 times.
2292 if removedAnonymousSymbols || calledCopyCtor || resultType.isRef() {
3506 553 anonymousSymbol = this.currentScope.symbolTable.insertAnonymous(resultType, baseNode);
3507 553 }
3508
3509 // Look up the copy ctor if at least one side needs it
3510
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2283 times.
✓ Branch 2 taken 2266 times.
✓ Branch 3 taken 26 times.
2292 if data.trueSideCallsCopyCtor || data.falseSideCallsCopyCtor {
3511 26 data.calledCopyCtor = this.matchCopyCtor(trueTypeModified, baseNode);
3512 26 }
3513
3514 // Consider the dtor calls for the temporaries of the branches. A result of reference type may refer to a temporary of its
3515 // branch, so those must not be destructed. Not destructing them is the safe choice here, as it only leaks the temporary.
3516 2292 const bool resultIsRef = resultType.isRef();
3517
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2290 times.
2292 if !node.isShortened {
3518 2290 this.doTernaryBranchCleanup(node.trueExpr, trueScope, resultIsRef);
3519 2290 }
3520 2292 this.doTernaryBranchCleanup(node.falseExpr, falseScope, resultIsRef);
3521
3522 2292 return Any(ExprResult{node.node.setEvaluatedSymbolType(resultType, this.manIdx), anonymousSymbol});
3523 }
3524
3525 // Visit a ternary branch in its expression scope
3526 4586 f<Pair<ExprResult, Scope*>> TypeChecker.visitTernaryBranch(IVisitable* branch) {
3527 4586 ExprScopeHandle exprScopeHandle = ExprScopeHandle(&this.compilerPass, branch);
3528 4586 const ExprResult branchResult = this.visitExpr(branch);
3529 9172 return Pair<ExprResult, Scope*>(branchResult, exprScopeHandle.getExprScope());
3530 4586 }
3531
3532 // Consider the dtor calls for the temporaries of a ternary branch
3533 4582 p TypeChecker.doTernaryBranchCleanup(IVisitable* branch, const Scope* branchScope, bool resultIsRef) {
3534
2/2
✓ Branch 0 taken 4570 times.
✓ Branch 1 taken 12 times.
4582 if branchScope == nil<const Scope*> {
3535 12 return;
3536 }
3537 4570 ExprScopeHandle exprScopeHandle = ExprScopeHandle(&this.compilerPass, branch);
3538
2/2
✓ Branch 0 taken 238 times.
✓ Branch 1 taken 4332 times.
4570 if resultIsRef {
3539 238 Scope* exprScope = exprScopeHandle.getExprScope();
3540 238 exprScope.temporaryDtorsToCall.clear();
3541 238 } else {
3542 4332 this.doExprScopeCleanup(branch);
3543 4332 }
3544 4570 }
3545
3546 // Check if a ternary operand is a temporary
3547 4584 f<bool> isTemporaryTernaryOperand(const SymbolTableEntry* entry) {
3548
4/4
✓ Branch 0 taken 2302 times.
✓ Branch 1 taken 2282 times.
✓ Branch 2 taken 3737 times.
✓ Branch 3 taken 847 times.
4584 if entry == nil<const SymbolTableEntry*> || !entry.anonymous {
3549 3737 return false;
3550 }
3551 847 const QualType entryType = entry.getQualType();
3552 847 return !entryType.isRef();
3553 }
3554
3555 3455 public f<Any> TypeChecker.visitLogicalOrExpr(ASTLogicalOrExprNode* node) {
3556 // Check if a logical or operator is applied
3557
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3455 times.
3455 if node.operands.getSize() == 1l {
3558 ✗ return this.visit(node.operands.front());
3559 }
3560 3455 ASTNode* baseNode = cast<ASTNode*>(node);
3561
3562 // Visit leftmost operand
3563 3455 ExprResult currentOperand = this.visitExpr(node.operands.get(0));
3564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3455 times.
3455 if currentOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3565
3566 // Loop through all remaining operands
3567
2/2
✓ Branch 0 taken 4421 times.
✓ Branch 1 taken 3455 times.
7876 for unsigned long i = 1l; i < node.operands.getSize(); i++ {
3568 // The operand is only evaluated if the ones before did not short-circuit, so it gets its temporaries destructed on its own
3569 4421 const ExprResult rhsOperand = this.visitInExprScope(node.operands.get(i));
3570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4421 times.
4421 if rhsOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3571 4421 currentOperand = ExprResult{this.opRuleManager.getLogicalOrResultType(baseNode, currentOperand, rhsOperand), nil<SymbolTableEntry*>};
3572 4421 }
3573
3574 3455 node.node.setEvaluatedSymbolType(currentOperand.ty, this.manIdx);
3575 3455 return Any(currentOperand);
3576 }
3577
3578 2004 public f<Any> TypeChecker.visitLogicalAndExpr(ASTLogicalAndExprNode* node) {
3579 // Check if a logical and operator is applied
3580
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2004 times.
2004 if node.operands.getSize() == 1l {
3581 ✗ return this.visit(node.operands.front());
3582 }
3583 2004 ASTNode* baseNode = cast<ASTNode*>(node);
3584
3585 // Visit leftmost operand
3586 2004 ExprResult currentOperand = this.visitExpr(node.operands.get(0));
3587
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2004 times.
2004 if currentOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3588
3589 // Loop through all remaining operands
3590
2/2
✓ Branch 0 taken 2257 times.
✓ Branch 1 taken 2004 times.
4261 for unsigned long i = 1l; i < node.operands.getSize(); i++ {
3591 // The operand is only evaluated if the ones before did not short-circuit, so it gets its temporaries destructed on its own
3592 2257 const ExprResult rhsOperand = this.visitInExprScope(node.operands.get(i));
3593
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2257 times.
2257 if rhsOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3594 2257 currentOperand = ExprResult{this.opRuleManager.getLogicalAndResultType(baseNode, currentOperand, rhsOperand), nil<SymbolTableEntry*>};
3595 2257 }
3596
3597 2004 node.node.setEvaluatedSymbolType(currentOperand.ty, this.manIdx);
3598 2004 return Any(currentOperand);
3599 }
3600
3601 438 public f<Any> TypeChecker.visitBitwiseOrExpr(ASTBitwiseOrExprNode* node) {
3602 // Check if a bitwise or operator is applied
3603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
438 if node.operands.getSize() == 1l {
3604 ✗ return this.visit(node.operands.front());
3605 }
3606 438 ASTNode* baseNode = cast<ASTNode*>(node);
3607
3608 // Visit leftmost operand
3609 438 ExprResult currentOperand = this.visitExpr(node.operands.get(0));
3610
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
438 if currentOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3611
3612 // Loop through all remaining operands
3613
2/2
✓ Branch 0 taken 446 times.
✓ Branch 1 taken 438 times.
884 for unsigned long i = 1l; i < node.operands.getSize(); i++ {
3614 446 const ExprResult rhsOperand = this.visitExpr(node.operands.get(i));
3615
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 446 times.
446 if rhsOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3616 446 currentOperand = this.opRuleManager.getBitwiseOrResultType(baseNode, currentOperand, rhsOperand, i - 1l);
3617 446 }
3618
3619 438 node.node.setEvaluatedSymbolType(currentOperand.ty, this.manIdx);
3620 438 return Any(currentOperand);
3621 }
3622
3623 85 public f<Any> TypeChecker.visitBitwiseXorExpr(ASTBitwiseXorExprNode* node) {
3624 // Check if a bitwise xor operator is applied
3625
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
85 if node.operands.getSize() == 1l {
3626 ✗ return this.visit(node.operands.front());
3627 }
3628 85 ASTNode* baseNode = cast<ASTNode*>(node);
3629
3630 // Visit leftmost operand
3631 85 ExprResult currentOperand = this.visitExpr(node.operands.get(0));
3632
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
85 if currentOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3633
3634 // Loop through all remaining operands
3635
2/2
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 85 times.
176 for unsigned long i = 1l; i < node.operands.getSize(); i++ {
3636 91 const ExprResult rhsOperand = this.visitExpr(node.operands.get(i));
3637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 91 times.
91 if rhsOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3638 91 currentOperand = this.opRuleManager.getBitwiseXorResultType(baseNode, currentOperand, rhsOperand, i - 1l);
3639 91 }
3640
3641 85 node.node.setEvaluatedSymbolType(currentOperand.ty, this.manIdx);
3642 85 return Any(currentOperand);
3643 }
3644
3645 308 public f<Any> TypeChecker.visitBitwiseAndExpr(ASTBitwiseAndExprNode* node) {
3646 // Check if a bitwise and operator is applied
3647
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 308 times.
308 if node.operands.getSize() == 1l {
3648 ✗ return this.visit(node.operands.front());
3649 }
3650 308 ASTNode* baseNode = cast<ASTNode*>(node);
3651
3652 // Visit leftmost operand
3653 308 ExprResult currentOperand = this.visitExpr(node.operands.get(0));
3654
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 308 times.
308 if currentOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3655
3656 // Loop through all remaining operands
3657
2/2
✓ Branch 0 taken 314 times.
✓ Branch 1 taken 308 times.
622 for unsigned long i = 1l; i < node.operands.getSize(); i++ {
3658 314 const ExprResult rhsOperand = this.visitExpr(node.operands.get(i));
3659
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 314 times.
314 if rhsOperand.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3660 314 currentOperand = this.opRuleManager.getBitwiseAndResultType(baseNode, currentOperand, rhsOperand, i - 1l);
3661 314 }
3662
3663 308 node.node.setEvaluatedSymbolType(currentOperand.ty, this.manIdx);
3664 308 return Any(currentOperand);
3665 }
3666
3667 28633 public f<Any> TypeChecker.visitEqualityExpr(ASTEqualityExprNode* node) {
3668 // Check if at least one equality operator is applied
3669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28633 times.
28633 if node.operands.getSize() == 1l {
3670 ✗ return this.visit(node.operands.front());
3671 }
3672 28633 ASTNode* baseNode = cast<ASTNode*>(node);
3673
3674 // Visit right side first, then left side
3675 28633 const ExprResult rhs = this.visitExpr(node.operands.get(1));
3676
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28633 times.
28633 if rhs.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3677 28633 const ExprResult lhs = this.visitExpr(node.operands.get(0));
3678
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28633 times.
28633 if lhs.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3679
3680 // Check if we need the string runtime to perform a string comparison
3681
6/6
✓ Branch 0 taken 551 times.
✓ Branch 1 taken 28082 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 545 times.
✓ Branch 4 taken 28088 times.
✓ Branch 5 taken 545 times.
28633 if lhs.ty.is(SuperType::TY_STRING) && rhs.ty.is(SuperType::TY_STRING) && !this.sourceFile.isStringRT() {
3682 545 this.sourceFile.requestRuntimeModule(RuntimeModule::STRING_RT);
3683 545 }
3684
3685 // Check operator
3686 28633 ExprResult equalityResult = ExprResult{QualType(SuperType::TY_INVALID), nil<SymbolTableEntry*>};
3687
2/2
✓ Branch 0 taken 23659 times.
✓ Branch 1 taken 4974 times.
28633 if node.op == EqualityOp::OP_EQUAL { // Operator was equal
3688 23659 equalityResult = this.opRuleManager.getEqualResultType(baseNode, lhs, rhs);
3689
1/2
✓ Branch 0 taken 4974 times.
✗ Branch 1 not taken.
28633 } else if node.op == EqualityOp::OP_NOT_EQUAL { // Operator was not equal
3690 4974 equalityResult = this.opRuleManager.getNotEqualResultType(baseNode, lhs, rhs);
3691 4974 } else {
3692 ✗ panic(Error("EqualityExpr fall-through"));
3693 4974 }
3694
3695 28633 node.node.setEvaluatedSymbolType(equalityResult.ty, this.manIdx);
3696 28633 return Any(equalityResult);
3697 }
3698
3699 17814 public f<Any> TypeChecker.visitRelationalExpr(ASTRelationalExprNode* node) {
3700 // Check if a relational operator is applied
3701
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17814 times.
17814 if node.operands.getSize() == 1l {
3702 ✗ return this.visit(node.operands.front());
3703 }
3704 17814 ASTNode* baseNode = cast<ASTNode*>(node);
3705
3706 // Visit right side first, then left side
3707 17814 const ExprResult rhs = this.visitExpr(node.operands.get(1));
3708
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 17813 times.
17814 if rhs.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3709 17813 const ExprResult lhs = this.visitExpr(node.operands.get(0));
3710
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17813 times.
17813 if lhs.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3711
3712 // Check operator
3713 17813 ExprResult relationalResult = ExprResult{QualType(SuperType::TY_INVALID), nil<SymbolTableEntry*>};
3714
2/2
✓ Branch 0 taken 7303 times.
✓ Branch 1 taken 10510 times.
17813 if node.op == RelationalOp::OP_LESS { // Operator was less
3715 7303 relationalResult = this.opRuleManager.getLessResultType(baseNode, lhs, rhs);
3716
2/2
✓ Branch 0 taken 3862 times.
✓ Branch 1 taken 6648 times.
17813 } else if node.op == RelationalOp::OP_GREATER { // Operator was greater
3717 3862 relationalResult = this.opRuleManager.getGreaterResultType(baseNode, lhs, rhs);
3718
2/2
✓ Branch 0 taken 2302 times.
✓ Branch 1 taken 4346 times.
10510 } else if node.op == RelationalOp::OP_LESS_EQUAL { // Operator was less equal
3719 2302 relationalResult = this.opRuleManager.getLessEqualResultType(baseNode, lhs, rhs);
3720
1/2
✓ Branch 0 taken 4346 times.
✗ Branch 1 not taken.
6648 } else if node.op == RelationalOp::OP_GREATER_EQUAL { // Operator was greater equal
3721 4346 relationalResult = this.opRuleManager.getGreaterEqualResultType(baseNode, lhs, rhs);
3722 4346 } else {
3723 ✗ panic(Error("RelationalExpr fall-through"));
3724 10510 }
3725
3726 17813 node.node.setEvaluatedSymbolType(relationalResult.ty, this.manIdx);
3727 17813 return Any(relationalResult);
3728 }
3729
3730 757 public f<Any> TypeChecker.visitShiftExpr(ASTShiftExprNode* node) {
3731 // Check if at least one shift operator is applied
3732
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 757 times.
757 if node.operands.getSize() == 1l {
3733 ✗ return this.visit(node.operands.front());
3734 }
3735 757 ASTNode* baseNode = cast<ASTNode*>(node);
3736
3737 // Visit leftmost operand
3738 757 ExprResult currentResult = this.visitExpr(node.operands.get(0));
3739
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 757 times.
757 if currentResult.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3740
3741 // Loop through remaining operands
3742
2/2
✓ Branch 0 taken 757 times.
✓ Branch 1 taken 990 times.
1747 for unsigned long i = 0l; i < node.opQueue.getSize(); i++ {
3743 990 const ExprResult operandResult = this.visitExpr(node.operands.get(i + 1l));
3744
1/2
✓ Branch 0 taken 990 times.
✗ Branch 1 not taken.
990 if operandResult.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3745
3746 // Check operator
3747 990 Pair<ShiftOp, QualType>& queueFront = node.opQueue.front();
3748 990 const ShiftOp op = queueFront.getFirst();
3749
2/2
✓ Branch 0 taken 324 times.
✓ Branch 1 taken 666 times.
990 if op == ShiftOp::OP_SHIFT_LEFT {
3750 666 currentResult = this.opRuleManager.getShiftLeftResultType(baseNode, currentResult, operandResult, i);
3751
1/2
✓ Branch 0 taken 324 times.
✗ Branch 1 not taken.
990 } else if op == ShiftOp::OP_SHIFT_RIGHT {
3752 324 currentResult = this.opRuleManager.getShiftRightResultType(baseNode, currentResult, operandResult, i);
3753 324 } else {
3754 ✗ panic(Error("ShiftExpr fall-through"));
3755 324 }
3756
3757 // Push the new item and pop the old one on the other side of the queue
3758 990 node.opQueue.push(Pair<ShiftOp, QualType>(op, currentResult.ty));
3759 990 node.opQueue.pop();
3760 990 }
3761
3762 757 node.node.setEvaluatedSymbolType(currentResult.ty, this.manIdx);
3763 757 return Any(currentResult);
3764 }
3765
3766 16204 public f<Any> TypeChecker.visitAdditiveExpr(ASTAdditiveExprNode* node) {
3767 // Check if at least one additive operator is applied
3768
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16204 times.
16204 if node.operands.getSize() == 1l {
3769 ✗ return this.visit(node.operands.front());
3770 }
3771 16204 ASTNode* baseNode = cast<ASTNode*>(node);
3772
3773 // Visit leftmost operand
3774 16204 ExprResult currentResult = this.visitExpr(node.operands.get(0));
3775
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 16203 times.
16204 if currentResult.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3776
3777 // Loop through remaining operands
3778
2/2
✓ Branch 0 taken 16203 times.
✓ Branch 1 taken 17993 times.
34196 for unsigned long i = 0l; i < node.opQueue.getSize(); i++ {
3779 17993 const ExprResult operandResult = this.visitExpr(node.operands.get(i + 1l));
3780
1/2
✓ Branch 0 taken 17993 times.
✗ Branch 1 not taken.
17993 if operandResult.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3781
3782 // Check operator
3783 17993 Pair<AdditiveOp, QualType>& queueFront = node.opQueue.front();
3784 17993 const AdditiveOp op = queueFront.getFirst();
3785
2/2
✓ Branch 0 taken 7075 times.
✓ Branch 1 taken 10918 times.
17993 if op == AdditiveOp::OP_PLUS {
3786 10918 currentResult = this.opRuleManager.getPlusResultType(baseNode, currentResult, operandResult, i);
3787
1/2
✓ Branch 0 taken 7075 times.
✗ Branch 1 not taken.
17993 } else if op == AdditiveOp::OP_MINUS {
3788 7075 currentResult = this.opRuleManager.getMinusResultType(baseNode, currentResult, operandResult, i);
3789 7075 } else {
3790 ✗ panic(Error("AdditiveExpr fall-through"));
3791 7075 }
3792
3793 // Push the new item and pop the old one on the other side of the queue
3794 17993 node.opQueue.push(Pair<AdditiveOp, QualType>(op, currentResult.ty));
3795 17993 node.opQueue.pop();
3796 17993 }
3797
3798 16203 node.node.setEvaluatedSymbolType(currentResult.ty, this.manIdx);
3799 16203 return Any(currentResult);
3800 }
3801
3802 2943 public f<Any> TypeChecker.visitMultiplicativeExpr(ASTMultiplicativeExprNode* node) {
3803 // Check if at least one multiplicative operator is applied
3804
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2943 times.
2943 if node.operands.getSize() == 1l {
3805 ✗ return this.visit(node.operands.front());
3806 }
3807 2943 ASTNode* baseNode = cast<ASTNode*>(node);
3808
3809 // Visit leftmost operand
3810 2943 ExprResult currentResult = this.visitExpr(node.operands.get(0));
3811
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2943 times.
2943 if currentResult.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3812 // Loop through remaining operands
3813
2/2
✓ Branch 0 taken 3041 times.
✓ Branch 1 taken 2943 times.
5984 for unsigned long i = 0l; i < node.opQueue.getSize(); i++ {
3814 3041 const ExprResult operandResult = this.visitExpr(node.operands.get(i + 1l));
3815
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3041 times.
3041 if operandResult.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3816
3817 // Check operator
3818 3041 Pair<MultiplicativeOp, QualType>& queueFront = node.opQueue.front();
3819 3041 const MultiplicativeOp op = queueFront.getFirst();
3820
2/2
✓ Branch 0 taken 559 times.
✓ Branch 1 taken 2482 times.
3041 if op == MultiplicativeOp::OP_MUL {
3821 2482 currentResult = this.opRuleManager.getMulResultType(baseNode, currentResult, operandResult, i);
3822
2/2
✓ Branch 0 taken 349 times.
✓ Branch 1 taken 210 times.
3041 } else if op == MultiplicativeOp::OP_DIV {
3823 349 currentResult = this.opRuleManager.getDivResultType(baseNode, currentResult, operandResult, i);
3824
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 210 times.
559 } else if op == MultiplicativeOp::OP_REM {
3825 210 currentResult = this.opRuleManager.getRemResultType(baseNode, currentResult, operandResult);
3826 210 } else {
3827 ✗ panic(Error("Multiplicative fall-through"));
3828 559 }
3829
3830 // Push the new item and pop the old one on the other side of the queue
3831 3041 node.opQueue.push(Pair<MultiplicativeOp, QualType>(op, currentResult.ty));
3832 3041 node.opQueue.pop();
3833 3041 }
3834
3835 2943 node.node.setEvaluatedSymbolType(currentResult.ty, this.manIdx);
3836 2943 return Any(currentResult);
3837 }
3838
3839 12175 public f<Any> TypeChecker.visitCastExpr(ASTCastExprNode* node) {
3840 // Check if cast is applied
3841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12175 times.
12175 if !node.isCast {
3842 ✗ return this.visit(node.prefixUnaryExpr);
3843 }
3844 12175 ASTNode* baseNode = cast<ASTNode*>(node);
3845
3846 // Visit destination type
3847 12175 const QualType dstType = this.getDataType(node.dataType);
3848
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12175 times.
12175 if dstType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3849 // Visit source type
3850 12175 const ExprResult src = this.visitExpr(node.assignExpr);
3851
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12175 times.
12175 if src.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3852
3853 // Check for identity cast (casts that only change the signedness are not considered identity casts). Within a generic
3854 // manifestation, the cast may only be an identity cast for this particular substitution of the template types, so skip it
3855
6/6
✓ Branch 0 taken 1352 times.
✓ Branch 1 taken 10823 times.
✓ Branch 2 taken 852 times.
✓ Branch 3 taken 500 times.
✓ Branch 4 taken 12156 times.
✓ Branch 5 taken 19 times.
12175 if src.ty == dstType && src.ty.qualifiers == dstType.qualifiers && this.typeMapping.isEmpty() {
3856 19 this.addWarning(baseNode.codeLoc, CompilerWarningType::IDENTITY_CAST, String("You cast from a type to itself. Thus, this can be simplified."));
3857 19 }
3858
3859 // Get result type
3860 12175 const QualType resultType = this.opRuleManager.getCastResultType(baseNode, dstType, src);
3861
3862 12175 const bool typesMatch = dstType.matches(src.ty, false, true, true);
3863 12175 const bool sameContainerType = src.ty.isSameContainerTypeAs(dstType);
3864
4/4
✓ Branch 0 taken 1352 times.
✓ Branch 1 taken 10823 times.
✓ Branch 2 taken 3465 times.
✓ Branch 3 taken 8710 times.
12175 SymbolTableEntry* entry = typesMatch || sameContainerType ? src.entry : nil<SymbolTableEntry*>;
3865 12175 return Any(ExprResult{node.node.setEvaluatedSymbolType(resultType, this.manIdx), entry});
3866 }
3867
3868 7700 public f<Any> TypeChecker.visitPrefixUnaryExpr(ASTPrefixUnaryExprNode* node) {
3869 // If no operator is applied, simply visit the postfix unary expression
3870
1/2
✓ Branch 0 taken 7700 times.
✗ Branch 1 not taken.
7700 if node.op == PrefixUnaryOp::OP_NONE {
3871 ✗ return this.visit(node.postfixUnaryExpr);
3872 }
3873 7700 ASTNode* baseNode = cast<ASTNode*>(node);
3874
3875 // Visit the right side
3876 7700 const ExprResult operand = this.visitExpr(node.prefixUnaryExpr);
3877 7700 QualType operandType = operand.ty;
3878 7700 SymbolTableEntry* operandEntry = operand.entry;
3879
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7699 times.
7700 if operandType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3880 // Determine action, based on the given operator
3881
7/8
✓ Branch 0 taken 948 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 45 times.
✓ Branch 3 taken 4072 times.
✓ Branch 4 taken 33 times.
✓ Branch 5 taken 999 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1577 times.
7699 switch node.op {
3882 case PrefixUnaryOp::OP_MINUS: {
3883 948 operandType = this.opRuleManager.getPrefixMinusResultType(baseNode, operand);
3884 948 }
3885 case PrefixUnaryOp::OP_PLUS_PLUS: {
3886 25 operandType = this.opRuleManager.getPrefixPlusPlusResultType(baseNode, operand);
3887
3888
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 18 times.
25 if operandEntry != nil<SymbolTableEntry*> {
3889 // In case the lhs is captured, notify the capture about the write access
3890 18 Capture* lhsCapture = this.currentScope.symbolTable.lookupCapture(operandEntry.name);
3891
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if lhsCapture != nil<Capture*> {
3892 ✗ lhsCapture.setAccessType(CaptureAccessType::READ_WRITE);
3893 ✗ }
3894
3895 // Update the state of the variable
3896 18 operandEntry.updateState(LifecycleState::INITIALIZED);
3897 18 }
3898 25 }
3899 case PrefixUnaryOp::OP_MINUS_MINUS: {
3900 45 operandType = this.opRuleManager.getPrefixMinusMinusResultType(baseNode, operand);
3901
3902
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 37 times.
45 if operandEntry != nil<SymbolTableEntry*> {
3903 // In case the lhs is captured, notify the capture about the write access
3904 37 Capture* lhsCapture = this.currentScope.symbolTable.lookupCapture(operandEntry.name);
3905
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if lhsCapture != nil<Capture*> {
3906 ✗ lhsCapture.setAccessType(CaptureAccessType::READ_WRITE);
3907 ✗ }
3908
3909 // Update the state of the variable
3910 37 operandEntry.updateState(LifecycleState::INITIALIZED);
3911 37 }
3912 45 }
3913 case PrefixUnaryOp::OP_NOT: {
3914 4072 operandType = this.opRuleManager.getPrefixNotResultType(baseNode, operand);
3915 4072 }
3916 case PrefixUnaryOp::OP_BITWISE_NOT: {
3917 33 const ExprResult bitwiseNotResult = this.opRuleManager.getPrefixBitwiseNotResultType(baseNode, operand);
3918 33 operandType = bitwiseNotResult.ty;
3919 33 operandEntry = bitwiseNotResult.entry;
3920 33 }
3921 case PrefixUnaryOp::OP_DEREFERENCE: {
3922 999 operandType = this.opRuleManager.getPrefixMulResultType(baseNode, operand);
3923 999 }
3924 case PrefixUnaryOp::OP_ADDRESS_OF: {
3925 1577 operandType = this.opRuleManager.getPrefixBitwiseAndResultType(baseNode, operand);
3926 1577 }
3927 default: {
3928 ✗ panic(Error("PrefixUnaryExpr fall-through"));
3929 }
3930 }
3931
3932 7699 return Any(ExprResult{node.node.setEvaluatedSymbolType(operandType, this.manIdx), operandEntry});
3933 }
3934
3935 106782 public f<Any> TypeChecker.visitPostfixUnaryExpr(ASTPostfixUnaryExprNode* node) {
3936 // If no operator is applied, simply visit the atomic expression
3937
1/2
✓ Branch 0 taken 106782 times.
✗ Branch 1 not taken.
106782 if node.op == PostfixUnaryOp::OP_NONE {
3938 ✗ return this.visit(node.atomicExpr);
3939 }
3940 106782 ASTNode* baseNode = cast<ASTNode*>(node);
3941
3942 // Visit left side
3943 106782 const ExprResult operand = this.visitExpr(node.postfixUnaryExpr);
3944 106782 QualType operandType = operand.ty;
3945 106782 SymbolTableEntry* operandEntry = operand.entry;
3946
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 106771 times.
106782 if operandType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3947
3948
5/6
✗ Branch 0 not taken.
✓ Branch 1 taken 13177 times.
✓ Branch 2 taken 84193 times.
✓ Branch 3 taken 8526 times.
✓ Branch 4 taken 779 times.
✓ Branch 5 taken 96 times.
106771 switch node.op {
3949 case PostfixUnaryOp::OP_SUBSCRIPT: {
3950 // Visit index assignment
3951 13177 IVisitable* indexAssignExpr = node.subscriptIndexExpr;
3952 13177 const ExprResult index = this.visitExpr(indexAssignExpr);
3953
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13177 times.
13177 if index.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
3954
3955 // Check is there is an overloaded operator function available, if yes accept it
3956 13177 Vector<ExprResult> operatorOperands;
3957 13177 operatorOperands.pushBack(operand);
3958 13177 operatorOperands.pushBack(index);
3959 13177 const ExprResult overloadResult = this.opRuleManager.isOperatorOverloadingFctAvailable(baseNode, OP_FCT_SUBSCRIPT, operatorOperands, 0l);
3960
2/2
✓ Branch 0 taken 957 times.
✓ Branch 1 taken 12220 times.
13177 if !overloadResult.ty.is(SuperType::TY_INVALID) {
3961 957 operandType = overloadResult.ty;
3962 957 } else {
3963 12220 operandType = operandType.removeReferenceWrapper();
3964
3965 // Check if the index is of the right type
3966 12220 const dyn indexOptions = [SuperType::TY_INT, SuperType::TY_LONG];
3967
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12219 times.
12220 if !index.ty.isOneOf(indexOptions, len(indexOptions)) {
3968 2 return this.softErrorER(baseNode, SemanticErrorType::ARRAY_INDEX_NOT_INT_OR_LONG, String("Array index must be of type int or long"));
3969 1 }
3970
3971 // Check if we can apply the subscript operator on the lhs type
3972 12219 const dyn subscriptOptions = [SuperType::TY_ARRAY, SuperType::TY_PTR, SuperType::TY_STRING];
3973
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12219 times.
12219 if !operandType.isOneOf(subscriptOptions, len(subscriptOptions)) {
3974 ✗ const String message = String("Can only apply subscript operator on array type, got ") + operandType.getName(true);
3975 ✗ return this.softErrorER(baseNode, SemanticErrorType::OPERATOR_WRONG_DATA_TYPE, message);
3976 ✗ }
3977
3978 // Check if we have an unsafe operation
3979
4/4
✓ Branch 0 taken 3145 times.
✓ Branch 1 taken 9074 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 12218 times.
12219 if operandType.isPtr() && !this.currentScope.doesAllowUnsafeOperations() {
3980 2 return this.softErrorER(baseNode, SemanticErrorType::UNSAFE_OPERATION_IN_SAFE_CONTEXT, String("The subscript operator on pointers is an unsafe operation. Use unsafe blocks if you know what you are doing."));
3981 1 }
3982
3983 // In case of compile time index value and known array size, perform a compile time out-of-bounds check
3984 12218 ASTNode* indexNode = getASTNode(indexAssignExpr);
3985
6/6
✓ Branch 0 taken 1879 times.
✓ Branch 1 taken 10339 times.
✓ Branch 2 taken 220 times.
✓ Branch 3 taken 1659 times.
✓ Branch 4 taken 11602 times.
✓ Branch 5 taken 616 times.
12218 if operandType.isArray() && cast<unsigned long>(operandType.getArraySize()) != ARRAY_SIZE_UNKNOWN && indexNode.hasCompileTimeValue(this.manIdx) {
3986 616 const CompileTimeValue compileTimeValue = indexNode.getCompileTimeValue(this.manIdx);
3987 616 const int constIndex = compileTimeValue.getIntValue();
3988 616 const unsigned int constSize = operandType.getArraySize();
3989 // Check if we are accessing out-of-bounds memory
3990
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 615 times.
616 if constIndex >= cast<int>(constSize) {
3991 1 const String idxStr = toString(constIndex);
3992 1 const String sizeStr = toString(cast<long>(constSize));
3993 1 const String message = String("You are trying to access element with index ") + idxStr + " of an array with size " + sizeStr;
3994 2 return this.softErrorER(baseNode, SemanticErrorType::ARRAY_INDEX_OUT_OF_BOUNDS, message);
3995 1 }
3996 615 }
3997
3998 // Get item type
3999 12217 operandType = operandType.getContained();
4000
4001 // Remove heap qualifier
4002 12217 TypeQualifiers& qualifiers = operandType.getQualifiers();
4003 12217 qualifiers.isHeap = false;
4004 12217 }
4005 13177 }
4006 case PostfixUnaryOp::OP_MEMBER_ACCESS: {
4007 84193 const String& fieldName = node.identifier;
4008
4009 // Check if lhs is enum or strobj
4010 84193 const QualType lhsBaseTy = operandType.autoDereference();
4011
4012 // Union field access is handled separately: a union field is always a direct, non-composed lookup by name
4013 // (unlike struct fields, union fields never nest/compose), and reading/writing it requires a runtime tag
4014 // check/update in the IR generator, which relies on the field entry resolved here.
4015
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 84127 times.
84193 if lhsBaseTy.is(SuperType::TY_UNION) {
4016 66 const String unionName = lhsBaseTy.getSubType();
4017 66 Scope* unionScope = lhsBaseTy.getBodyScope();
4018
4019 // If we only have the generic union scope, lookup the concrete manifestation scope
4020
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 if unionScope.isGenericScope {
4021 ✗ const Union* spiceUnion = lhsBaseTy.getUnion(baseNode);
4022 ✗ assert spiceUnion != nil<const Union*>;
4023 ✗ unionScope = spiceUnion.scope;
4024 ✗ }
4025 66 assert !unionScope.isGenericScope; // At this point we always expect a substantiation scope
4026
4027 // Get accessed field
4028 66 SymbolTableEntry* memberEntry = unionScope.symbolTable.lookupStrict(fieldName);
4029
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 1 time.
66 if memberEntry == nil<SymbolTableEntry*> {
4030 1 const String message = String("Field '") + node.identifier + "' not found in union " + unionName;
4031 2 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE, message);
4032 1 }
4033 65 const QualType memberType = memberEntry.getQualType();
4034
4035 // Check for insufficient visibility
4036 65 const QualType memberBaseType = memberType.getBase();
4037
2/4
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 65 times.
65 if unionScope.isImportedBy(this.rootScope) && !memberBaseType.isPublic() {
4038 ✗ const String message = String("Cannot access field '") + fieldName + "' due to its private visibility";
4039 ✗ return this.softErrorER(baseNode, SemanticErrorType::INSUFFICIENT_VISIBILITY, message);
4040 ✗ }
4041
4042 // Set field to used
4043 65 memberEntry.used = true;
4044
4045 // A read of an inactive field panics, which prints the stack trace, so the runtime that does that has to be loaded
4046
2/4
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✗ Branch 3 not taken.
65 if this.cliOptions.printsStackTraceOnAbort() && !this.sourceFile.isRT(RuntimeModule::STACK_TRACE_RT) {
4047 ✗ this.sourceFile.requestRuntimeModule(RuntimeModule::STACK_TRACE_RT);
4048 ✗ }
4049
4050 // Overwrite type and entry of left side with member type and entry
4051 65 operandType = memberType;
4052 65 operandEntry = memberEntry;
4053 66 } else {
4054
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 84125 times.
84127 if !lhsBaseTy.is(SuperType::TY_STRUCT) {
4055 2 const String message = String("Cannot apply member access operator on ") + operandType.getName(false);
4056 4 return this.softErrorER(baseNode, SemanticErrorType::INVALID_MEMBER_ACCESS, message);
4057 2 }
4058
4059 // Retrieve registry entry
4060 84125 const String structName = lhsBaseTy.getSubType();
4061 84125 Struct* spiceStruct = lhsBaseTy.getStruct(baseNode);
4062 84125 assert spiceStruct != nil<Struct*>;
4063 84125 Scope* structScope = spiceStruct.scope;
4064 84125 assert !structScope.isGenericScope; // At this point we always expect a substantiation scope
4065
4066 // Get accessed field
4067 84125 Vector<unsigned long> indexPath;
4068 84125 SymbolTableEntry* memberEntry = spiceStruct.lookupInComposedFields(fieldName, indexPath);
4069
2/2
✓ Branch 0 taken 84123 times.
✓ Branch 1 taken 2 times.
84125 if memberEntry == nil<SymbolTableEntry*> {
4070 2 const String message = String("Field '") + node.identifier + "' not found in struct " + structName;
4071 4 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE, message);
4072 2 }
4073 84123 const QualType memberType = memberEntry.getQualType();
4074
4075 // Check for insufficient visibility
4076 84123 const QualType memberBaseType = memberType.getBase();
4077
3/4
✓ Branch 0 taken 83740 times.
✓ Branch 1 taken 383 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 84123 times.
84123 if structScope.isImportedBy(this.rootScope) && !memberBaseType.isPublic() {
4078 ✗ const String message = String("Cannot access field '") + fieldName + "' due to its private visibility";
4079 ✗ return this.softErrorER(baseNode, SemanticErrorType::INSUFFICIENT_VISIBILITY, message);
4080 ✗ }
4081
4082 // Set field to used
4083 84123 memberEntry.used = true;
4084
4085 // Overwrite type and entry of left side with member type and entry
4086 84123 operandType = memberType;
4087 84123 operandEntry = memberEntry;
4088
4089 // A field of a const struct cannot be modified either. This does not apply to pointer and reference fields,
4090 // because their const would belong to the pointee or referent instead of the field itself
4091
6/6
✓ Branch 0 taken 9675 times.
✓ Branch 1 taken 74448 times.
✓ Branch 2 taken 1891 times.
✓ Branch 3 taken 7784 times.
✓ Branch 4 taken 76341 times.
✓ Branch 5 taken 7782 times.
84123 if lhsBaseTy.isConst() && !memberType.isPtr() && !memberType.isRef() {
4092 7782 operandType.makeConst();
4093 7782 }
4094 84125 }
4095 84188 }
4096 case PostfixUnaryOp::OP_PLUS_PLUS: {
4097 8526 const ExprResult plusPlusResult = this.opRuleManager.getPostfixPlusPlusResultType(baseNode, operand);
4098 8526 operandType = plusPlusResult.ty;
4099
4100
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 8516 times.
8526 if operandEntry != nil<SymbolTableEntry*> {
4101 // In case the lhs is captured, notify the capture about the write access
4102 8516 Capture* lhsCapture = this.currentScope.symbolTable.lookupCapture(operandEntry.name);
4103
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 8506 times.
8516 if lhsCapture != nil<Capture*> {
4104 10 lhsCapture.setAccessType(CaptureAccessType::READ_WRITE);
4105 10 }
4106
4107 // Update the state of the variable
4108 8516 operandEntry.updateState(LifecycleState::INITIALIZED);
4109 8516 }
4110 8526 }
4111 case PostfixUnaryOp::OP_MINUS_MINUS: {
4112 779 const ExprResult minusMinusResult = this.opRuleManager.getPostfixMinusMinusResultType(baseNode, operand);
4113 779 operandType = minusMinusResult.ty;
4114
4115
2/2
✓ Branch 0 taken 770 times.
✓ Branch 1 taken 9 times.
779 if operandEntry != nil<SymbolTableEntry*> {
4116 // In case the lhs is captured, notify the capture about the write access
4117 770 Capture* lhsCapture = this.currentScope.symbolTable.lookupCapture(operandEntry.name);
4118
1/2
✓ Branch 0 taken 770 times.
✗ Branch 1 not taken.
770 if lhsCapture != nil<Capture*> {
4119 ✗ lhsCapture.setAccessType(CaptureAccessType::READ_WRITE);
4120 ✗ }
4121
4122 // Update the state of the variable
4123 770 operandEntry.updateState(LifecycleState::INITIALIZED);
4124 770 }
4125 779 }
4126 case PostfixUnaryOp::OP_ERR_PROPAGATION: {
4127 // Check if the operand is of type Result<T>
4128 96 const QualType resultType = operandType.removeReferenceWrapper();
4129
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 95 times.
96 if !resultType.isResultObj() {
4130 1 const String message = String("The error propagation operator '!' can only be applied to a value of type Result<T>, got ") + operandType.getName(false);
4131 2 return this.softErrorER(baseNode, SemanticErrorType::ERR_PROPAGATION_INVALID_OPERAND, message);
4132 1 }
4133
4134 // Check if the enclosing function/procedure/lambda itself returns Result<U>
4135 95 const SymbolTableEntry* enclosingReturnVar = this.currentScope.lookupResultVar();
4136 95 QualType enclosingReturnType = QualType(SuperType::TY_DYN);
4137
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 94 times.
95 if enclosingReturnVar != nil<const SymbolTableEntry*> {
4138 94 enclosingReturnType = enclosingReturnVar.getQualType();
4139 94 }
4140
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 93 times.
95 if enclosingReturnVar == nil<const SymbolTableEntry*> || !enclosingReturnType.isResultObj() {
4141 4 return this.softErrorER(baseNode, SemanticErrorType::ERR_PROPAGATION_INVALID_CONTEXT, String("The error propagation operator '!' can only be used inside a function that itself returns Result<U>"));
4142 2 }
4143
4144 // Retrieve the substantiation scope of the operand's Result<T>
4145 93 Scope* resultScope = resultType.getBodyScope();
4146
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 if resultScope.isGenericScope {
4147 ✗ const Struct* resultStruct = resultType.getStruct(baseNode);
4148 ✗ assert resultStruct != nil<const Struct*>;
4149 ✗ resultScope = resultStruct.scope;
4150 ✗ }
4151 93 assert !resultScope.isGenericScope;
4152
4153 // Resolve Result<T>.isErr(), Result<T>.unwrap() and Result<T>.getErr()
4154 93 const ArgList noArgs;
4155 93 const QualTypeList noTemplateTypeHints;
4156 93 ErrPropagationData& errPropData = node.errPropData.get(this.manIdx);
4157 93 errPropData.isErrFct = matchFunction(resultScope, String("isErr"), resultType, noArgs, noTemplateTypeHints, false, baseNode);
4158 93 errPropData.unwrapFct = matchFunction(resultScope, String("unwrap"), resultType, noArgs, noTemplateTypeHints, false, baseNode);
4159 93 errPropData.getErrFct = matchFunction(resultScope, String("getErr"), resultType, noArgs, noTemplateTypeHints, false, baseNode);
4160
3/6
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 93 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 93 times.
93 if errPropData.isErrFct == nil<const Function*> || errPropData.unwrapFct == nil<const Function*> || errPropData.getErrFct == nil<const Function*> {
4161 ✗ return this.softErrorER(baseNode, SemanticErrorType::ERR_PROPAGATION_INVALID_CONTEXT, String("Failed to resolve the Result<T> helper methods required for the error propagation operator"));
4162 ✗ }
4163
4164 // Resolve err<U>(const Error&), located next to Result in the same module
4165 93 const NameRegistryEntry* resultRegistryEntry = this.sourceFile.getNameRegistryEntry(String(RESULTOBJ_NAME));
4166 93 assert resultRegistryEntry != nil<const NameRegistryEntry*>;
4167 93 Scope* resultStructScope = resultRegistryEntry.targetScope;
4168 93 Scope* resultModuleScope = resultStructScope.parent;
4169 93 const QualTypeList enclosingTemplateTypes = enclosingReturnType.getTemplateTypes();
4170 93 const Function* getErrFct = errPropData.getErrFct;
4171 93 ArgList errArgs;
4172 93 errArgs.pushBack(Pair<QualType, bool>(getErrFct.returnType, false));
4173 93 errPropData.ctorFct = matchFunction(resultModuleScope, String("err"), QualType(SuperType::TY_DYN), errArgs, enclosingTemplateTypes, false, baseNode);
4174
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 if errPropData.ctorFct == nil<const Function*> {
4175 ✗ return this.softErrorER(baseNode, SemanticErrorType::ERR_PROPAGATION_INVALID_CONTEXT, String("Failed to resolve 'err<U>(const Error&)' required for the error propagation operator"));
4176 ✗ }
4177
4178 // The whole expression evaluates to the unwrapped payload
4179 93 const Function* unwrapFct = errPropData.unwrapFct;
4180 93 operandType = unwrapFct.returnType;
4181 93 operandEntry = nil<SymbolTableEntry*>;
4182 93 }
4183 default: {
4184 ✗ panic(Error("PostfixUnaryExpr fall-through"));
4185 }
4186 }
4187
4188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106760 times.
106760 if operandType.is(SuperType::TY_INVALID) {
4189 ✗ String varName = String("");
4190
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if operandEntry != nil<SymbolTableEntry*> {
4191 ✗ varName = operandEntry.name;
4192 ✗ }
4193 ✗ const String message = String("Variable '") + varName + "' was referenced before declared";
4194 ✗ return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE, message);
4195 ✗ }
4196
4197 106760 return Any(ExprResult{node.node.setEvaluatedSymbolType(operandType, this.manIdx), operandEntry});
4198 }
4199
4200 424415 public f<Any> TypeChecker.visitAtomicExpr(ASTAtomicExprNode* node) {
4201 // Check if constant
4202
2/2
✓ Branch 0 taken 86013 times.
✓ Branch 1 taken 338402 times.
424415 if node.constant != nil<ASTConstantNode*> {
4203 86013 return this.visitConstant(node.constant);
4204 }
4205
4206 // Check if value
4207
2/2
✓ Branch 0 taken 92446 times.
✓ Branch 1 taken 245956 times.
338402 if node.value != nil<IVisitable*> {
4208 92446 return this.visit(node.value);
4209 }
4210
4211 // Check for assign expression within parentheses
4212
2/2
✓ Branch 0 taken 2028 times.
✓ Branch 1 taken 243928 times.
245956 if node.assignExpr != nil<IVisitable*> {
4213 2028 return this.visit(node.assignExpr);
4214 }
4215
4216 // Identifier (local or global variable access)
4217 243928 assert !node.fqIdentifier.isEmpty();
4218 243928 ASTNode* baseNode = cast<ASTNode*>(node);
4219
4220 243928 VarAccessData& accessData = node.data.get(this.manIdx);
4221 243928 accessData.accessScope = this.currentScope;
4222
4223 // Check if a local or global variable can be found by searching for the name
4224
2/2
✓ Branch 0 taken 2429 times.
✓ Branch 1 taken 241499 times.
243928 if node.identifierFragments.getSize() == 1l {
4225 241499 accessData.entry = accessData.accessScope.lookup(node.identifierFragments.back());
4226 241499 }
4227
4228 // If no local or global was found, search in the name registry
4229
2/2
✓ Branch 0 taken 241123 times.
✓ Branch 1 taken 2805 times.
243928 if accessData.entry == nil<SymbolTableEntry*> {
4230 2805 const NameRegistryEntry* registryEntry = this.sourceFile.getNameRegistryEntry(node.fqIdentifier);
4231
2/2
✓ Branch 0 taken 2803 times.
✓ Branch 1 taken 2 times.
2805 if registryEntry == nil<const NameRegistryEntry*> {
4232
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if this.sourceFile.isAmbiguousName(node.fqIdentifier) {
4233 2 return this.softErrorER(baseNode, SemanticErrorType::AMBIGUOUS_SYMBOL, this.sourceFile.getAmbiguousNameMessage(node.fqIdentifier));
4234 1 }
4235 1 const String message = String("The variable '") + node.fqIdentifier + "' could not be found";
4236 2 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE, message);
4237 1 }
4238 2803 accessData.entry = registryEntry.targetEntry;
4239 2803 accessData.accessScope = registryEntry.targetScope;
4240 2803 }
4241 243926 assert accessData.entry != nil<SymbolTableEntry*>;
4242 243926 SymbolTableEntry* entry = accessData.entry;
4243 243926 entry.used = true;
4244 243926 accessData.capture = accessData.accessScope.symbolTable.lookupCapture(entry.name);
4245
4246 243926 const QualType varType = entry.getQualType();
4247
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 243910 times.
243926 if varType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
4248
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 243908 times.
243910 if varType.is(SuperType::TY_INVALID) {
4249 2 const String message = String("Symbol '") + entry.name + "' was used before declared.";
4250 4 return this.softErrorER(baseNode, SemanticErrorType::USED_BEFORE_DECLARED, message);
4251 2 }
4252
4253 243908 const dyn fctOptions = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
4254
4/4
✓ Branch 0 taken 243659 times.
✓ Branch 1 taken 249 times.
✓ Branch 2 taken 243832 times.
✓ Branch 3 taken 76 times.
243908 if varType.isOneOf(fctOptions, len(fctOptions)) && entry.global {
4255 // Check if overloaded function was referenced
4256 76 Vector<Function*>* manifestations = entry.declNode.getFctManifestations(entry.name);
4257
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 75 times.
76 if manifestations.getSize() > 1l {
4258 2 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_OVERLOADED_FCT, String("Overloaded functions / functions with optional params cannot be referenced"));
4259 1 }
4260 // Set referenced function to used
4261 75 Function* referencedFunction = manifestations.front();
4262
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 74 times.
75 if !referencedFunction.templateTypes.isEmpty() {
4263 2 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_OVERLOADED_FCT, String("Generic functions cannot be referenced"));
4264 1 }
4265 74 referencedFunction.used = true;
4266 74 referencedFunction.entry.used = true;
4267 74 }
4268
4269 // The base type should be an extended primitive
4270 243906 const QualType baseType = varType.getBase();
4271
4/4
✓ Branch 0 taken 243900 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 243905 times.
243906 if !baseType.isExtendedPrimitive() && !baseType.is(SuperType::TY_DYN) {
4272 1 const String message = String("A symbol of type ") + varType.getName(false) + " cannot be accessed here";
4273 2 return this.softErrorER(baseNode, SemanticErrorType::INVALID_SYMBOL_ACCESS, message);
4274 1 }
4275
4276 // Check if we have seen a 'this.' prefix, because the generator needs that
4277 243905 const String& firstFragment = node.identifierFragments.front();
4278
4/4
✓ Branch 0 taken 243904 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 243904 times.
243905 if entry.scope.scopeType == ScopeType::STRUCT && firstFragment != THIS_VARIABLE_NAME {
4279 1 const String message = String("The symbol '") + node.fqIdentifier + "' could not be found. Missing 'this.' prefix?";
4280 2 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE, message);
4281 1 }
4282
4283 // Ensure that the entry is public, if the symbol is imported.
4284 // An exception are enum items. There it is sufficient, that the enum itself is public.
4285
4/4
✓ Branch 0 taken 243363 times.
✓ Branch 1 taken 541 times.
✓ Branch 2 taken 243572 times.
✓ Branch 3 taken 332 times.
243904 if accessData.accessScope.isImportedBy(this.rootScope) && accessData.accessScope.scopeType != ScopeType::ENUM {
4286
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 331 times.
332 if !baseType.isPublic() {
4287 1 const String message = String("Cannot access '") + entry.name + "' due to its private visibility";
4288 2 return this.softErrorER(baseNode, SemanticErrorType::INSUFFICIENT_VISIBILITY, message);
4289 1 }
4290 331 }
4291
4292 // For enum item access, use access scope of the enum
4293
2/2
✓ Branch 0 taken 241484 times.
✓ Branch 1 taken 2419 times.
243903 if entry.scope.scopeType == ScopeType::ENUM {
4294 2419 accessData.accessScope = entry.scope;
4295 2419 }
4296
4297 // For struct access, use access scope of the struct
4298
2/2
✓ Branch 0 taken 147053 times.
✓ Branch 1 taken 96850 times.
243903 if baseType.is(SuperType::TY_STRUCT) {
4299 96850 const String structName = baseType.getSubType();
4300 96850 const NameRegistryEntry* nameRegistryEntry = this.sourceFile.getNameRegistryEntry(structName);
4301 // The struct may only be reachable transitively (i.e. it is not present in this file's name registry under
4302 // its unqualified name). In that case, fall back to the struct's body scope, which is exactly what the
4303 // registry entry's targetScope would point to (see SymbolTableBuilder struct registration). This keeps
4304 // member access working across deep transitive imports instead of crashing.
4305
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 96845 times.
96850 if nameRegistryEntry != nil<const NameRegistryEntry*> {
4306 96845 accessData.accessScope = nameRegistryEntry.targetScope;
4307 96845 } else {
4308 5 accessData.accessScope = baseType.getBodyScope();
4309 5 }
4310 96850 assert accessData.accessScope != nil<Scope*>;
4311 96850 }
4312
4313 243903 return Any(ExprResult{node.node.setEvaluatedSymbolType(varType, this.manIdx), entry});
4314 }
4315
4316 // ============================================================ Values ============================================================
4317
4318 92446 public f<Any> TypeChecker.visitValue(ASTValueNode* node) {
4319 // Function call
4320
2/2
✓ Branch 0 taken 79415 times.
✓ Branch 1 taken 13031 times.
92446 if node.fctCall != nil<IVisitable*> {
4321 79415 return this.visit(node.fctCall);
4322 }
4323
4324 // Array initialization
4325
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 12847 times.
13031 if node.arrayInitialization != nil<IVisitable*> {
4326 184 return this.visit(node.arrayInitialization);
4327 }
4328
4329 // Struct instantiation
4330
2/2
✓ Branch 0 taken 1741 times.
✓ Branch 1 taken 11106 times.
12847 if node.structInstantiation != nil<IVisitable*> {
4331 1741 return this.visit(node.structInstantiation);
4332 }
4333
4334 // Lambda function
4335
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 11039 times.
11106 if node.lambdaFunc != nil<IVisitable*> {
4336 67 return this.visit(node.lambdaFunc);
4337 }
4338
4339 // Lambda procedure
4340
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 10966 times.
11039 if node.lambdaProc != nil<IVisitable*> {
4341 73 return this.visit(node.lambdaProc);
4342 }
4343
4344 // Lambda expression
4345
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10962 times.
10966 if node.lambdaExpr != nil<IVisitable*> {
4346 4 return this.visit(node.lambdaExpr);
4347 }
4348
4349 // Typed nil
4350
1/2
✓ Branch 0 taken 10962 times.
✗ Branch 1 not taken.
10962 if node.isNil {
4351 10962 const QualType nilType = this.getDataType(node.nilType);
4352
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10962 times.
10962 if nilType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
4353
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 10961 times.
10962 if nilType.is(SuperType::TY_DYN) {
4354 2 return this.softErrorER(cast<ASTNode*>(node.nilType), SemanticErrorType::UNEXPECTED_DYN_TYPE, String("Nil must have an explicit type"));
4355 1 }
4356 10961 return Any(ExprResult{node.node.setEvaluatedSymbolType(nilType, this.manIdx), nil<SymbolTableEntry*>});
4357 }
4358
4359 − panic(Error("Value fall-through")); // LCOV_EXCL_LINE
4360 }
4361
4362 96020 public f<Any> TypeChecker.visitConstant(ASTConstantNode* node) {
4363 96020 SuperType superType = SuperType::TY_INVALID;
4364
7/8
✓ Branch 0 taken 1391 times.
✓ Branch 1 taken 15673 times.
✓ Branch 2 taken 2312 times.
✓ Branch 3 taken 41286 times.
✓ Branch 4 taken 8948 times.
✓ Branch 5 taken 15665 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 10745 times.
96020 switch node.primitiveValueType {
4365 case PrimitiveValueType::TYPE_DOUBLE: {
4366 1391 superType = SuperType::TY_DOUBLE;
4367 1391 }
4368 case PrimitiveValueType::TYPE_INT: {
4369 15673 superType = SuperType::TY_INT;
4370 15673 }
4371 case PrimitiveValueType::TYPE_SHORT: {
4372 2312 superType = SuperType::TY_SHORT;
4373 2312 }
4374 case PrimitiveValueType::TYPE_LONG: {
4375 41286 superType = SuperType::TY_LONG;
4376 41286 }
4377 case PrimitiveValueType::TYPE_CHAR: {
4378 8948 superType = SuperType::TY_CHAR;
4379 8948 }
4380 case PrimitiveValueType::TYPE_STRING: {
4381 15665 superType = SuperType::TY_STRING;
4382 15665 }
4383 case PrimitiveValueType::TYPE_BOOL: {
4384 10745 superType = SuperType::TY_BOOL;
4385 10745 }
4386 default: {
4387 ✗ panic(Error("Constant fall-through"));
4388 }
4389 }
4390 96020 QualType constantType = QualType(superType);
4391 // Integer literals with an 'u' suffix are unsigned
4392
2/2
✓ Branch 0 taken 92044 times.
✓ Branch 1 taken 3976 times.
96020 if node.isUnsigned {
4393 3976 constantType.makeUnsigned();
4394 3976 }
4395 96020 return Any(ExprResult{node.node.setEvaluatedSymbolType(constantType, this.manIdx), nil<SymbolTableEntry*>});
4396 }
4397
4398 79415 public f<Any> TypeChecker.visitFctCall(ASTFctCallNode* node) {
4399 79415 ASTNode* baseNode = cast<ASTNode*>(node);
4400 79415 FctCallData& data = node.data.get(this.manIdx);
4401
4402 // Retrieve arg types
4403 79415 data.args.clear();
4404 // Scopes and names of the anonymous symbols of temporary args. The scope is nil if the arg is no such temporary
4405 79415 Vector<Scope*> argTemporaryScopes;
4406 79415 Vector<String> argTemporaryNames;
4407
2/2
✓ Branch 0 taken 20342 times.
✓ Branch 1 taken 59073 times.
79415 if node.hasArgs {
4408 59073 ASTArgLstNode* argLst = node.argLst;
4409 59073 data.args.reserve(argLst.args.getSize());
4410
2/2
✓ Branch 0 taken 97739 times.
✓ Branch 1 taken 59048 times.
156787 foreach IVisitable* arg : argLst.args {
4411 // Visit argument
4412 97739 const ExprResult argResult = this.visitExpr(arg);
4413
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 97714 times.
97739 if argResult.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
4414 97714 assert !argResult.ty.hasAnyGenericParts();
4415 // Save arg type to arg types list
4416 97714 data.args.pushBack(Pair<QualType, bool>(argResult.ty, argResult.isTemporary()));
4417
2/2
✓ Branch 0 taken 37760 times.
✓ Branch 1 taken 59954 times.
97714 const bool isAnonymous = argResult.entry != nil<SymbolTableEntry*> && argResult.entry.anonymous;
4418
2/2
✓ Branch 0 taken 1202 times.
✓ Branch 1 taken 96512 times.
97714 argTemporaryScopes.pushBack(isAnonymous ? argResult.entry.scope : nil<Scope*>);
4419
2/2
✓ Branch 0 taken 1202 times.
✓ Branch 1 taken 96512 times.
97714 argTemporaryNames.pushBack(isAnonymous ? argResult.entry.name : String());
4420 97714 }
4421 59048 }
4422
4423 // Retrieve template types
4424 79390 data.templateTypes.clear();
4425
2/2
✓ Branch 0 taken 73537 times.
✓ Branch 1 taken 5853 times.
79390 if node.hasTemplateTypes {
4426
2/2
✓ Branch 0 taken 5852 times.
✓ Branch 1 taken 6927 times.
12779 foreach ASTDataTypeNode* templateTypeNode : node.templateTypeLst.dataTypes {
4427 6927 const QualType templateType = this.getDataType(templateTypeNode);
4428 6927 assert !templateType.is(SuperType::TY_INVALID);
4429
4430 // Abort if the type is unresolved
4431
1/2
✓ Branch 0 taken 6927 times.
✗ Branch 1 not taken.
6927 if templateType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
4432
4433 // Check if the given type is generic
4434
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6926 times.
6927 if templateType.is(SuperType::TY_GENERIC) {
4435 2 return this.softErrorER(cast<ASTNode*>(templateTypeNode), SemanticErrorType::EXPECTED_NON_GENERIC_TYPE, String("You must specify a concrete type here"));
4436 1 }
4437
4438 6926 data.templateTypes.pushBack(templateType);
4439 6926 }
4440 5852 }
4441
4442 // Check if this is a builtin call
4443 79389 BuiltinFunctionInfo builtinFunctionInfo;
4444
2/2
✓ Branch 0 taken 9908 times.
✓ Branch 1 taken 69481 times.
79389 if lookupBuiltinFunction(node.fqFunctionName, builtinFunctionInfo) {
4445 19816 return this.visitBuiltinCall(node);
4446 }
4447
4448 // Retrieve entry of the first fragment
4449 69481 const dyn structOrInterface = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE];
4450 69481 const dyn fctOrProc = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
4451 69481 const String& firstFrag = node.functionNameFragments.front();
4452 69481 SymbolTableEntry* firstFragEntry = this.currentScope.lookup(firstFrag);
4453
2/2
✓ Branch 0 taken 25823 times.
✓ Branch 1 taken 43658 times.
69481 if firstFragEntry != nil<SymbolTableEntry*> {
4454 // Check if we have seen a 'this.' prefix, because the generator needs that
4455
4/4
✓ Branch 0 taken 43657 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 43657 times.
43658 if firstFragEntry.scope.scopeType == ScopeType::STRUCT && firstFrag != THIS_VARIABLE_NAME {
4456 1 const String message = String("The symbol '") + firstFrag + "' could not be found. Missing 'this.' prefix?";
4457 2 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE, message);
4458 1 }
4459
4460 43657 firstFragEntry.used = true;
4461 // Decide of which type the function call is
4462 43657 const QualType firstFragType = firstFragEntry.getQualType();
4463 43657 const QualType baseType = firstFragType.getBase();
4464
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 43652 times.
43657 if baseType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
4465
2/2
✓ Branch 0 taken 31245 times.
✓ Branch 1 taken 12407 times.
43652 if baseType.isOneOf(structOrInterface, len(structOrInterface)) {
4466
2/2
✓ Branch 0 taken 28354 times.
✓ Branch 1 taken 2891 times.
31245 if firstFragEntry.scope.scopeType == ScopeType::GLOBAL {
4467 2891 data.callType = FctCallType::TYPE_CTOR;
4468 2891 } else {
4469 28354 data.callType = FctCallType::TYPE_METHOD;
4470 28354 }
4471
4/4
✓ Branch 0 taken 227 times.
✓ Branch 1 taken 12180 times.
✓ Branch 2 taken 12235 times.
✓ Branch 3 taken 172 times.
43652 } else if baseType.isOneOf(fctOrProc, len(fctOrProc)) && firstFragEntry.scope.scopeType != ScopeType::GLOBAL {
4472 172 data.callType = FctCallType::TYPE_FCT_PTR;
4473 12407 }
4474 43652 }
4475
4476 // Get struct name. Retrieve it from alias if required
4477 69475 Pair<SymbolTableEntry*, bool> aliasResolution = this.rootScope.symbolTable.lookupWithAliasResolution(node.fqFunctionName);
4478 69475 SymbolTableEntry* structEntry = aliasResolution.getFirst();
4479 69475 const bool isAlias = aliasResolution.getSecond();
4480 69475 String fqFunctionName = node.fqFunctionName;
4481
4482 // Get the concrete template types
4483
2/2
✓ Branch 0 taken 69472 times.
✓ Branch 1 taken 3 times.
69475 if isAlias {
4484 3 const QualType aliasedType = structEntry.getQualType();
4485 3 fqFunctionName = aliasedType.getSubType();
4486 // Retrieve concrete template types from type alias
4487 3 data.templateTypes = aliasedType.getTemplateTypes();
4488 // Check if the aliased type specified template types and the struct instantiation does
4489
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2 times.
3 if !data.templateTypes.isEmpty() && node.hasTemplateTypes {
4490 2 return this.softErrorER(cast<ASTNode*>(node.templateTypeLst), SemanticErrorType::ALIAS_WITH_TEMPLATE_LIST, String("The aliased type already has a template list"));
4491 1 }
4492 3 }
4493
4494 // Check if this is a method call or a normal function call
4495
2/2
✓ Branch 0 taken 28354 times.
✓ Branch 1 taken 41120 times.
69474 if data.isMethodCall() {
4496 // This is a method call
4497 28354 data.thisType = firstFragEntry.getQualType();
4498 28354 const QualType thisBaseType = data.thisType.getBase();
4499 28354 Scope* structBodyScope = thisBaseType.getBodyScope();
4500 28354 assert structBodyScope != nil<Scope*>;
4501
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 28352 times.
28354 if !this.visitMethodCall(node, structBodyScope) { // Check if soft errors occurred
4502 4 return Any(ExprResult{node.node.setEvaluatedSymbolType(QualType(SuperType::TY_UNRESOLVED), this.manIdx), nil<SymbolTableEntry*>});
4503 }
4504 28352 assert data.calleeParentScope != nil<Scope*>;
4505
2/2
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 40948 times.
69472 } else if data.isFctPtrCall() {
4506 // This is a function pointer call
4507 172 const QualType firstFragType = firstFragEntry.getQualType();
4508 172 const QualType functionType = firstFragType.getBase();
4509 172 assert functionType.isOneOf(fctOrProc, len(fctOrProc));
4510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 172 times.
172 if !this.visitFctPtrCall(node, functionType) { // Check if soft errors occurred
4511 ✗ return Any(ExprResult{node.node.setEvaluatedSymbolType(QualType(SuperType::TY_UNRESOLVED), this.manIdx), nil<SymbolTableEntry*>});
4512 }
4513 172 } else {
4514 // This is an ordinary function call
4515 40948 assert data.isOrdinaryCall() || data.isCtorCall();
4516
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 40943 times.
40948 if !this.visitOrdinaryFctCall(node, fqFunctionName) { // Check if soft errors occurred
4517 10 return Any(ExprResult{node.node.setEvaluatedSymbolType(QualType(SuperType::TY_UNRESOLVED), this.manIdx), nil<SymbolTableEntry*>});
4518 }
4519 40943 assert data.calleeParentScope != nil<Scope*>;
4520
4521 // If the call is no ordinary call, it must be a constructor, which takes a struct as this type.
4522 40943 assert data.isOrdinaryCall() || data.thisType.is(SuperType::TY_STRUCT);
4523 41115 }
4524
4525 69467 const Function* callee = data.callee;
4526
2/2
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 69295 times.
69467 if !data.isFctPtrCall() {
4527 // Check if we were able to find a function
4528
2/2
✓ Branch 0 taken 69274 times.
✓ Branch 1 taken 21 times.
69295 if callee == nil<const Function*> {
4529 // Build error message
4530 21 String functionName = node.functionNameFragments.back();
4531
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 2 times.
21 if data.isCtorCall() {
4532 2 functionName = String(CTOR_FUNCTION_NAME);
4533 2 }
4534 21 ParamList errArgTypes;
4535 21 errArgTypes.reserve(data.args.getSize());
4536
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 21 times.
40 foreach const Arg& arg : data.args {
4537 // Copy the arg type into a local first, instead of initializing a struct literal field with a reference to
4538 // a type with an overloaded assign operator
4539 19 const QualType argType = arg.getFirst();
4540 19 errArgTypes.pushBack(Param{argType, false});
4541 19 }
4542 21 const QualTypeList noTemplateTypes;
4543 21 const String signature = getFunctionSignature(functionName, data.thisType, QualType(SuperType::TY_DYN), errArgTypes, noTemplateTypes, false);
4544 // Throw error
4545 42 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_FUNCTION, String("Function/procedure '") + signature + "' could not be found");
4546 21 }
4547
4548 // Check if we need to request a re-visit, because the function body was not type-checked yet
4549 69274 requestRevisitIfRequired(callee);
4550
4551 // Get function entry from function object
4552 69274 SymbolTableEntry* functionEntry = callee.entry;
4553
4554 // Check if the called function has sufficient visibility
4555 69274 data.isImported = data.calleeParentScope.isImportedBy(this.rootScope);
4556 69274 const QualType functionEntryType = functionEntry.getQualType();
4557
4/4
✓ Branch 0 taken 44315 times.
✓ Branch 1 taken 24959 times.
✓ Branch 2 taken 69272 times.
✓ Branch 3 taken 2 times.
69274 if data.isImported && !functionEntryType.isPublic() {
4558 2 const String signature = callee.getSignature();
4559
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if functionEntryType.is(SuperType::TY_FUNCTION) {
4560 4 return this.softErrorER(baseNode, SemanticErrorType::INSUFFICIENT_VISIBILITY, String("Function '") + signature + "' has insufficient visibility");
4561 2 } else {
4562 ✗ return this.softErrorER(baseNode, SemanticErrorType::INSUFFICIENT_VISIBILITY, String("Procedure '") + signature + "' has insufficient visibility");
4563 ✗ }
4564 2 }
4565
4566 // A virtual call can only return the interface part of the struct, that the implementation of the method returns.
4567 // All other fields of the struct would be lost, so calls through an interface must not return an interface by value
4568
4/4
✓ Branch 0 taken 69204 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 69268 times.
69272 if data.thisType.is(SuperType::TY_INTERFACE) && callee.returnType.is(SuperType::TY_INTERFACE) {
4569 4 const String message = String("The method '") + callee.name + "' returns the interface '" + callee.returnType.getName(false, true) + "' by value, so it cannot be called through the interface '" + data.thisType.getName(false, true) + "'. Call it on the struct, that implements the interface, instead";
4570 8 return this.softErrorER(baseNode, SemanticErrorType::INTERFACE_RETURNED_BY_VALUE, message);
4571 4 }
4572
4573 // Mark direct calls to err<T>(...) as an error-return-trace origin site when tracing is enabled for this file.
4574 // err<T>'s own internal delegation to Result<T>'s error ctor is a distinct ctor call and is never matched here,
4575 // so this only ever fires for the user's own call site, not for that nested one.
4576
5/6
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 69234 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
✓ Branch 4 taken 69263 times.
✓ Branch 5 taken 5 times.
69268 if this.sourceFile.errorReturnTracing && callee.bodyScope != nil<Scope*> && callee.name == "err" {
4577 5 Scope* calleeBodyScope = callee.bodyScope;
4578 5 SourceFile* calleeSourceFile = calleeBodyScope.sourceFile;
4579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if calleeSourceFile.isResultRT() {
4580 5 node.isErrorTraceOrigin = true;
4581 5 }
4582 5 }
4583 69268 }
4584
4585 // Generate arg infos
4586
2/2
✓ Branch 0 taken 16963 times.
✓ Branch 1 taken 52477 times.
69440 if node.hasArgs {
4587 52477 QualTypeList paramTypes;
4588
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 52357 times.
52477 if data.isFctPtrCall() {
4589 120 const QualType firstFragType = firstFragEntry.getQualType();
4590 120 const QualType functionType = firstFragType.getBase();
4591 120 paramTypes = functionType.getFunctionParamTypes();
4592 120 } else {
4593 52357 assert callee != nil<const Function*>;
4594 52357 paramTypes = callee.getParamTypes();
4595 52357 }
4596
4597 52477 ASTArgLstNode* argLst = node.argLst;
4598 52477 Vector<ArgInfo>& argInfos = argLst.argInfos.get(this.manIdx);
4599 52477 argInfos.clear();
4600
2/2
✓ Branch 0 taken 88368 times.
✓ Branch 1 taken 52477 times.
140845 for unsigned long argIdx = 0l; argIdx < data.args.getSize(); argIdx++ {
4601 88368 const QualType& expectedType = paramTypes.get(argIdx);
4602 88368 const Arg& arg = data.args.get(argIdx);
4603 88368 const QualType& actualType = arg.getFirst();
4604
4605 // By-value params of non-trivially copyable types get a copy of the argument, also if it is a reference
4606 88368 Function* copyCtor = nil<Function*>;
4607 88368 const QualType actualValueType = actualType.removeReferenceWrapper();
4608
4/4
✓ Branch 0 taken 88307 times.
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 88307 times.
✓ Branch 3 taken 61 times.
88368 if expectedType.isOwnedByValueParam(baseNode) && actualValueType.is(SuperType::TY_STRUCT) {
4609 // The called function destructs its by-value params. External functions do not, so the caller has to destruct
4610 // the arg instead.
4611
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 55 times.
61 const bool isExtCallee = callee != nil<const Function*> && callee.declNode.kind == ASTNodeKind::EXT_DECL;
4612 61 Scope* argTemporaryScope = argTemporaryScopes.get(argIdx);
4613
5/6
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 51 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 43 times.
61 if !isExtCallee && !actualType.isRef() && argTemporaryScope != nil<Scope*> {
4614 // Move the temporary into the param instead of copying it: the called function takes it over, so the
4615 // caller must not destruct it anymore
4616 18 argTemporaryScope.symbolTable.deleteAnonymous(argTemporaryNames.get(argIdx));
4617 18 } else {
4618 43 copyCtor = this.matchCopyCtor(actualValueType, baseNode);
4619 // Insert an anonymous symbol to track the dtor call of the copy for external functions
4620
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 if isExtCallee {
4621 ✗ ASTNode* argNode = getASTNode(argLst.args.get(argIdx));
4622 ✗ this.currentScope.symbolTable.insertAnonymous(actualValueType, argNode, ULONG_MAX_VALUE);
4623 ✗ }
4624 43 }
4625 61 }
4626
4627 88368 argInfos.pushBack(ArgInfo{copyCtor});
4628 88368 }
4629 52477 assert argInfos.getSize() == argLst.args.getSize();
4630 52477 }
4631
4632 // Retrieve return type
4633 69440 bool isFct = false;
4634 69440 QualType returnType = QualType(SuperType::TY_DYN);
4635
2/2
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 69268 times.
69440 if data.isFctPtrCall() {
4636 172 const QualType firstFragType = firstFragEntry.getQualType();
4637 172 const QualType functionType = firstFragType.getBase();
4638 172 isFct = functionType.is(SuperType::TY_FUNCTION);
4639
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 99 times.
172 if isFct {
4640 73 returnType = functionType.getFunctionReturnType();
4641 73 } else {
4642 99 returnType = QualType(SuperType::TY_BOOL);
4643 99 }
4644 172 } else {
4645 69268 isFct = callee.isFunction();
4646
2/2
✓ Branch 0 taken 9158 times.
✓ Branch 1 taken 60110 times.
69268 if data.isCtorCall() {
4647 9158 returnType = data.thisType;
4648
2/2
✓ Branch 0 taken 16771 times.
✓ Branch 1 taken 43339 times.
69268 } else if callee.isProcedure() {
4649 16771 returnType = QualType(SuperType::TY_DYN);
4650 16771 } else {
4651 43339 returnType = callee.returnType;
4652 60110 }
4653 69268 }
4654
4655 69440 const QualType returnBaseType = returnType.getBase();
4656
4657 // Make sure this source file knows about the return type
4658
2/2
✓ Branch 0 taken 52699 times.
✓ Branch 1 taken 16741 times.
69440 if returnBaseType.is(SuperType::TY_STRUCT) {
4659 16741 returnType = this.mapImportedScopeTypeToLocalType(returnBaseType.getBodyScope(), returnType);
4660 16741 }
4661
4662 // Add anonymous symbol to keep track of dtor call, if non-trivially destructible
4663 69440 SymbolTableEntry* anonymousSymbol = nil<SymbolTableEntry*>;
4664
4/4
✓ Branch 0 taken 55394 times.
✓ Branch 1 taken 14046 times.
✓ Branch 2 taken 61368 times.
✓ Branch 3 taken 8072 times.
69440 if returnType.is(SuperType::TY_STRUCT) && !returnType.isTriviallyDestructible(baseNode) {
4665 8072 anonymousSymbol = this.currentScope.symbolTable.insertAnonymous(returnType, baseNode);
4666 8072 }
4667
4668 // Remove public qualifier to not have public local variables
4669 69440 TypeQualifiers& returnTypeQualifiers = returnType.getQualifiers();
4670 69440 returnTypeQualifiers.isPublic = false;
4671
4672 // Check if the return value gets discarded
4673
4/4
✓ Branch 0 taken 26028 times.
✓ Branch 1 taken 43412 times.
✓ Branch 2 taken 64107 times.
✓ Branch 3 taken 5333 times.
69440 if isFct && !node.hasReturnValueReceiver() {
4674 // Discarding a Result<T> is always a hard error when explicit error handling is enabled for this module,
4675 // regardless of ignoreUnusedReturnValue (which only ever weakens the generic warning below).
4676 5333 const QualType unwrappedReturnType = returnType.removeReferenceWrapper();
4677
4/4
✓ Branch 0 taken 5330 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 5331 times.
5333 if this.sourceFile.explicitErrorHandling && unwrappedReturnType.isResultObj() {
4678 2 const String message = String("A Result<T> returned from a call must be handled: bind it to a variable, propagate it with the '!' operator, handle it with '.unwrapOr(...)'/'.unwrapOrElse(...)', or explicitly discard it by binding it to a variable with a name starting with '_'");
4679 4 return this.softErrorER(baseNode, SemanticErrorType::DISCARDED_RESULT_VALUE, message);
4680 2 }
4681
4682 // Check if we want to ignore the discarded return value
4683 5331 bool ignoreUnusedReturnValue = false;
4684
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5327 times.
5331 if !data.isFctPtrCall() {
4685 5327 assert callee != nil<const Function*>;
4686 5327 ASTNode* calleeDeclNode = callee.declNode;
4687
2/2
✓ Branch 0 taken 4936 times.
✓ Branch 1 taken 391 times.
5327 if calleeDeclNode.kind == ASTNodeKind::FCT_DEF {
4688 391 const ASTFctDefNode* fctDef = downcast<ASTFctDefNode>(calleeDeclNode);
4689
2/2
✓ Branch 0 taken 235 times.
✓ Branch 1 taken 156 times.
391 if fctDef.node.attrs != nil<IVisitable*> {
4690 156 const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(fctDef.node.attrs);
4691 156 ASTAttrLstNode* attrLst = attrs.attrLst;
4692 156 ignoreUnusedReturnValue = attrLst.hasAttr(String(ATTR_IGNORE_UNUSED_RETURN_VALUE));
4693 156 }
4694 391 }
4695 5327 }
4696
4697
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 5175 times.
5331 if !ignoreUnusedReturnValue {
4698 5175 this.addWarning(baseNode.codeLoc, CompilerWarningType::UNUSED_RETURN_VALUE, String("The return value of the function call is unused"));
4699 5175 }
4700 5331 }
4701
4702 138876 return Any(ExprResult{node.node.setEvaluatedSymbolType(returnType, this.manIdx), anonymousSymbol});
4703 79415 }
4704
4705 40948 f<bool> TypeChecker.visitOrdinaryFctCall(ASTFctCallNode* node, const String& origFqFunctionName) {
4706 40948 ASTNode* baseNode = cast<ASTNode*>(node);
4707 40948 String fqFunctionName = origFqFunctionName;
4708 40948 FctCallData& data = node.data.get(this.manIdx);
4709
4710 // Check if this is a well-known ctor/fct call
4711
2/2
✓ Branch 0 taken 207 times.
✓ Branch 1 taken 40741 times.
40948 if node.functionNameFragments.getSize() == 1l {
4712 40741 this.ensureLoadedRuntimeForTypeName(fqFunctionName);
4713 40741 this.ensureLoadedRuntimeForFunctionName(fqFunctionName);
4714 40741 }
4715
4716 // Check if the type is generic (possible in case of ctor call)
4717 40948 const GenericType* genericType = this.rootScope.lookupGenericTypeStrict(fqFunctionName);
4718
4/4
✓ Branch 0 taken 40938 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 40938 times.
✓ Branch 3 taken 10 times.
40948 if genericType != nil<const GenericType*> && this.typeMapping.contains(fqFunctionName) {
4719 10 const QualType& replacementType = this.typeMapping.get(fqFunctionName);
4720
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if replacementType.is(SuperType::TY_STRUCT) {
4721 10 fqFunctionName = replacementType.getSubType();
4722 10 }
4723 10 }
4724
4725 // Check if the exported name registry contains that function name
4726 40948 const NameRegistryEntry* functionRegistryEntry = this.sourceFile.getNameRegistryEntry(fqFunctionName);
4727
2/2
✓ Branch 0 taken 40944 times.
✓ Branch 1 taken 4 times.
40948 if functionRegistryEntry == nil<const NameRegistryEntry*> {
4728
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 if this.sourceFile.isAmbiguousName(fqFunctionName) {
4729 1 this.softError(baseNode, SemanticErrorType::AMBIGUOUS_SYMBOL, this.sourceFile.getAmbiguousNameMessage(fqFunctionName));
4730 1 return false;
4731 1 }
4732 3 const String message = String("Function/procedure/struct '") + node.functionNameFragments.back() + "' could not be found";
4733 3 this.softError(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_FUNCTION, message);
4734 3 return false;
4735 3 }
4736 40944 const SymbolTableEntry* functionEntry = functionRegistryEntry.targetEntry;
4737 40944 data.calleeParentScope = functionRegistryEntry.targetScope;
4738
4739 // Check if the target symbol is a struct -> this must be a constructor call
4740 40944 String functionName = node.functionNameFragments.back();
4741
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40944 times.
40944 if functionEntry != nil<const SymbolTableEntry*> {
4742 40944 const QualType structEntryType = functionEntry.getQualType();
4743
2/2
✓ Branch 0 taken 31783 times.
✓ Branch 1 taken 9161 times.
40944 if structEntryType.is(SuperType::TY_STRUCT) {
4744 9161 data.callType = FctCallType::TYPE_CTOR;
4745 9161 functionName = String(CTOR_FUNCTION_NAME);
4746
4747 // Substantiate potentially generic this struct
4748 9161 const Struct* thisStruct = structEntryType.getStruct(baseNode, data.templateTypes);
4749
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 9160 times.
9161 if thisStruct == nil<const Struct*> {
4750 1 const SymbolTableEntry* structEntry = functionRegistryEntry.targetEntry;
4751 1 const String signature = getStructSignature(structEntry.name, data.templateTypes);
4752 1 this.softError(baseNode, SemanticErrorType::UNKNOWN_DATATYPE, String("Could not find struct candidate for struct '") + signature + "'. Do the template types match?");
4753 1 return false;
4754 1 }
4755
4756 // Set the 'this' type of the function to the struct type
4757 9160 data.thisType = structEntryType.getWithBodyScope(thisStruct.scope);
4758 9160 data.calleeParentScope = thisStruct.scope;
4759 9161 }
4760 40943 }
4761
4762 // Attach the concrete template types to the 'this' type
4763
4/4
✓ Branch 0 taken 31783 times.
✓ Branch 1 taken 9160 times.
✓ Branch 2 taken 38843 times.
✓ Branch 3 taken 2100 times.
40943 if !data.thisType.is(SuperType::TY_DYN) && !data.templateTypes.isEmpty() {
4764 2100 data.thisType = data.thisType.getWithTemplateTypes(data.templateTypes);
4765 2100 }
4766
4767 // Map local arg types to imported types
4768
2/2
✓ Branch 0 taken 68352 times.
✓ Branch 1 taken 40943 times.
109295 for unsigned long i = 0l; i < data.args.getSize(); i++ {
4769 68352 Arg& arg = data.args.get(i);
4770 68352 QualType& argType = arg.getFirst();
4771 68352 argType = this.mapLocalTypeToImportedScopeType(data.calleeParentScope, argType);
4772 68352 }
4773
4774 // Map local template types to imported types
4775
2/2
✓ Branch 0 taken 4769 times.
✓ Branch 1 taken 40943 times.
45712 for unsigned long i = 0l; i < data.templateTypes.getSize(); i++ {
4776 4769 QualType& templateType = data.templateTypes.get(i);
4777 4769 templateType = this.mapLocalTypeToImportedScopeType(data.calleeParentScope, templateType);
4778 4769 }
4779
4780 // Retrieve function object
4781 40943 Scope* matchScope = data.calleeParentScope;
4782 40943 data.callee = matchFunction(matchScope, functionName, data.thisType, data.args, data.templateTypes, false, baseNode);
4783
4784 40943 return true;
4785 40948 }
4786
4787 172 f<bool> TypeChecker.visitFctPtrCall(ASTFctCallNode* node, const QualType& functionType) {
4788 172 const FctCallData& data = node.data.get(this.manIdx);
4789
4790 // Check if the given argument types match the type
4791 172 const QualTypeList expectedArgTypes = functionType.getFunctionParamTypes();
4792
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 172 times.
172 if data.args.getSize() != expectedArgTypes.getSize() {
4793 ✗ this.softError(cast<ASTNode*>(node), SemanticErrorType::REFERENCED_UNDEFINED_FUNCTION, String("Expected and actual number of arguments do not match"));
4794 ✗ return false;
4795 ✗ }
4796
4797 // Never resolve a generic type here, which an empty list of candidate template types does
4798 172 const Vector<GenericType> noCandidateTemplateTypes;
4799
4800
2/2
✓ Branch 0 taken 139 times.
✓ Branch 1 taken 172 times.
311 for unsigned long i = 0l; i < data.args.getSize(); i++ {
4801 139 const Arg& arg = data.args.get(i);
4802 139 const QualType& actualType = arg.getFirst();
4803 139 const QualType& expectedType = expectedArgTypes.get(i);
4804 139 TypeMapping tm;
4805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
139 if !matchRequestedToCandidateType(expectedType, actualType, tm, noCandidateTemplateTypes, false, false) {
4806 ✗ const String message = String("Expected ") + expectedType.getName(false) + " but got " + actualType.getName(false);
4807 ✗ this.softError(getASTNode(node.argLst.args.get(i)), SemanticErrorType::REFERENCED_UNDEFINED_FUNCTION, message);
4808 ✗ return false;
4809 ✗ }
4810 139 }
4811 172 return true;
4812 172 }
4813
4814 28354 f<bool> TypeChecker.visitMethodCall(ASTFctCallNode* node, Scope* structScope) {
4815 28354 ASTNode* baseNode = cast<ASTNode*>(node);
4816 28354 FctCallData& data = node.data.get(this.manIdx);
4817 28354 Scope* currentStructScope = structScope;
4818
4819 // Traverse through structs - the first fragment is already looked up and the last one is the method name
4820
2/2
✓ Branch 0 taken 3749 times.
✓ Branch 1 taken 28352 times.
32101 for unsigned long i = 1l; i < node.functionNameFragments.getSize() - 1l; i++ {
4821 3749 const String& identifier = node.functionNameFragments.get(i);
4822
4823 // Retrieve field entry, also looking through composed fields
4824 3749 const QualType currentStructType = data.thisType.getBase();
4825 3749 Struct* spiceStruct = currentStructType.getStruct(baseNode);
4826 3749 assert spiceStruct != nil<Struct*>;
4827 3749 Vector<unsigned long> indexPath;
4828 3749 SymbolTableEntry* fieldEntry = spiceStruct.lookupInComposedFields(identifier, indexPath);
4829
2/2
✓ Branch 0 taken 3748 times.
✓ Branch 1 taken 1 time.
3749 if fieldEntry == nil<SymbolTableEntry*> {
4830 1 const QualType thisBaseType = data.thisType.getBase();
4831 1 const String errorMsg = String("The type '") + thisBaseType.getName(false, true) + "' does not have a member with the name '" + identifier + "'";
4832 1 this.softError(baseNode, SemanticErrorType::ACCESS_TO_NON_EXISTING_MEMBER, errorMsg);
4833 1 return false;
4834 1 }
4835 3748 const QualType fieldType = fieldEntry.getQualType();
4836 3748 const QualType fieldBaseType = fieldType.getBase();
4837 3748 const dyn structOrInterface = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE];
4838
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3747 times.
3748 if !fieldBaseType.isOneOf(structOrInterface, len(structOrInterface)) {
4839 1 const String message = String("Cannot call a method on '") + identifier + "', since it is no struct or interface";
4840 1 this.softError(baseNode, SemanticErrorType::INVALID_MEMBER_ACCESS, message);
4841 1 return false;
4842 1 }
4843 3747 fieldEntry.used = true;
4844
4845 // Get struct type and scope
4846 3747 data.thisType = fieldType;
4847 3747 currentStructScope = fieldBaseType.getBodyScope();
4848 3747 assert currentStructScope != nil<Scope*>;
4849 3749 }
4850
4851
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28352 times.
28352 if data.thisType.is(SuperType::TY_INTERFACE) {
4852 ✗ this.softError(baseNode, SemanticErrorType::INVALID_MEMBER_ACCESS, String("Cannot call a method on an interface"));
4853 ✗ return false;
4854 ✗ }
4855
4856 // Map local arg types to imported types
4857 28352 data.calleeParentScope = currentStructScope;
4858 28352 Scope* matchScope = currentStructScope;
4859
2/2
✓ Branch 0 taken 19896 times.
✓ Branch 1 taken 28352 times.
48248 for unsigned long i = 0l; i < data.args.getSize(); i++ {
4860 19896 Arg& arg = data.args.get(i);
4861 19896 QualType& argType = arg.getFirst();
4862 19896 argType = this.mapLocalTypeToImportedScopeType(data.calleeParentScope, argType);
4863 19896 }
4864
4865 // Map local template types to imported types
4866
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 28352 times.
28381 for unsigned long i = 0l; i < data.templateTypes.getSize(); i++ {
4867 29 QualType& templateType = data.templateTypes.get(i);
4868 29 templateType = this.mapLocalTypeToImportedScopeType(data.calleeParentScope, templateType);
4869 29 }
4870
4871 // 'this' type
4872 28352 data.thisType = data.thisType.autoDereference();
4873 28352 data.thisType = this.mapLocalTypeToImportedScopeType(data.calleeParentScope, data.thisType);
4874
4875 // Retrieve function object
4876 28352 const String& functionName = node.functionNameFragments.back();
4877 28352 data.callee = matchFunction(matchScope, functionName, data.thisType, data.args, data.templateTypes, false, baseNode);
4878
4879 28352 return true;
4880 }
4881
4882 184 public f<Any> TypeChecker.visitArrayInitialization(ASTArrayInitializationNode* node) {
4883 184 ASTNode* baseNode = cast<ASTNode*>(node);
4884
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 183 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 183 times.
184 if node.itemLst == nil<ASTArgLstNode*> || node.itemLst.args.isEmpty() {
4885 2 return this.softErrorER(baseNode, SemanticErrorType::ARRAY_SIZE_INVALID, String("Array initializers must at least contain one value"));
4886 1 }
4887 183 node.actualSize = node.itemLst.args.getSize();
4888
4889 183 QualType actualItemType = QualType(SuperType::TY_DYN);
4890 // Check if all values have the same type
4891
2/2
✓ Branch 0 taken 1455 times.
✓ Branch 1 taken 182 times.
1637 foreach IVisitable* arg : node.itemLst.args {
4892 1455 const ExprResult itemResult = this.visitExpr(arg);
4893 1455 const QualType itemType = itemResult.ty;
4894
1/2
✓ Branch 0 taken 1455 times.
✗ Branch 1 not taken.
1455 if itemType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
4895
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 1272 times.
1455 if actualItemType.is(SuperType::TY_DYN) { // Perform type inference
4896 183 actualItemType = itemType;
4897
2/2
✓ Branch 0 taken 1271 times.
✓ Branch 1 taken 1 time.
1455 } else if itemType != actualItemType { // Check if types are matching
4898 1 const String message = String("All provided values have to be of the same data type. You provided ") + actualItemType.getName(false) + " and " + itemType.getName(false);
4899 2 return this.softErrorER(getASTNode(arg), SemanticErrorType::ARRAY_ITEM_TYPE_NOT_MATCHING, message);
4900 1272 }
4901 1454 }
4902 182 assert !actualItemType.is(SuperType::TY_DYN);
4903
4904 182 const QualType arrayType = actualItemType.toArr(baseNode, cast<unsigned int>(node.actualSize), true);
4905 182 return Any(ExprResult{node.node.setEvaluatedSymbolType(arrayType, this.manIdx), nil<SymbolTableEntry*>});
4906 }
4907
4908 1741 public f<Any> TypeChecker.visitStructInstantiation(ASTStructInstantiationNode* node) {
4909 1741 ASTNode* baseNode = cast<ASTNode*>(node);
4910
4911 // Retrieve struct name
4912 1741 Pair<SymbolTableEntry*, bool> aliasResolution = this.rootScope.symbolTable.lookupWithAliasResolution(node.fqStructName);
4913 1741 SymbolTableEntry* aliasedEntry = aliasResolution.getFirst();
4914 1741 const bool isAlias = aliasResolution.getSecond();
4915 1741 String structName = node.fqStructName;
4916
2/2
✓ Branch 0 taken 1738 times.
✓ Branch 1 taken 3 times.
1741 if isAlias {
4917 3 const QualType aliasedType = aliasedEntry.getQualType();
4918 3 structName = aliasedType.getSubType();
4919 3 }
4920
4921 // Check if the struct type is generic
4922 1741 const GenericType* genericType = this.rootScope.lookupGenericTypeStrict(structName);
4923
2/4
✓ Branch 0 taken 1741 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1741 times.
✗ Branch 3 not taken.
1741 if genericType != nil<const GenericType*> && this.typeMapping.contains(structName) {
4924 ✗ const QualType& replacementType = this.typeMapping.get(structName);
4925
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if replacementType.is(SuperType::TY_STRUCT) {
4926 ✗ structName = replacementType.getSubType();
4927 ✗ }
4928 ✗ }
4929
4930 // Retrieve struct
4931 1741 const NameRegistryEntry* registryEntry = this.sourceFile.getNameRegistryEntry(structName);
4932
2/2
✓ Branch 0 taken 1738 times.
✓ Branch 1 taken 3 times.
1741 if registryEntry == nil<const NameRegistryEntry*> {
4933
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 if this.sourceFile.isAmbiguousName(structName) {
4934 2 return this.softErrorER(baseNode, SemanticErrorType::AMBIGUOUS_SYMBOL, this.sourceFile.getAmbiguousNameMessage(structName));
4935 1 }
4936 4 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_STRUCT, String("Cannot find struct '") + structName + "'");
4937 2 }
4938 1738 assert registryEntry.targetEntry != nil<SymbolTableEntry*> && registryEntry.targetScope != nil<Scope*>;
4939 1738 SymbolTableEntry* structEntry = registryEntry.targetEntry;
4940
4941 // Check visibility
4942 1738 const QualType structEntryType = structEntry.getQualType();
4943
4/4
✓ Branch 0 taken 753 times.
✓ Branch 1 taken 985 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1737 times.
1738 if !structEntryType.isPublic() && structEntry.scope.isImportedBy(this.currentScope) {
4944 2 return this.softErrorER(baseNode, SemanticErrorType::INSUFFICIENT_VISIBILITY, String("Struct '") + structName + "' has insufficient visibility");
4945 1 }
4946
4947 // Get struct type
4948 1737 QualType structType = structEntryType;
4949
4950 // Get the concrete template types
4951 1737 QualTypeList concreteTemplateTypes;
4952
2/2
✓ Branch 0 taken 1734 times.
✓ Branch 1 taken 3 times.
1737 if isAlias {
4953 // Retrieve concrete template types from type alias
4954 3 const QualType aliasedType = aliasedEntry.getQualType();
4955 3 concreteTemplateTypes = aliasedType.getTemplateTypes();
4956 // Check if the aliased type specified template types and the struct instantiation does
4957
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2 times.
3 if !concreteTemplateTypes.isEmpty() && node.templateTypeLst != nil<ASTTypeLstNode*> {
4958 2 return this.softErrorER(cast<ASTNode*>(node.templateTypeLst), SemanticErrorType::ALIAS_WITH_TEMPLATE_LIST, String("The aliased type already has a template list"));
4959 1 }
4960 3 }
4961
4962
2/2
✓ Branch 0 taken 1704 times.
✓ Branch 1 taken 32 times.
1736 if node.templateTypeLst != nil<ASTTypeLstNode*> {
4963 32 concreteTemplateTypes.reserve(node.templateTypeLst.dataTypes.getSize());
4964
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 31 times.
68 foreach ASTDataTypeNode* dataType : node.templateTypeLst.dataTypes {
4965 37 const QualType concreteType = this.getDataType(dataType);
4966
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if concreteType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
4967 // Check if generic type
4968
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 36 times.
37 if concreteType.is(SuperType::TY_GENERIC) {
4969 2 return this.softErrorER(cast<ASTNode*>(dataType), SemanticErrorType::EXPECTED_NON_GENERIC_TYPE, String("Struct instantiations may only take concrete template types"));
4970 1 }
4971 36 concreteTemplateTypes.pushBack(concreteType);
4972 36 }
4973 31 }
4974
4975 // Get the struct instance
4976 1735 Struct* spiceStruct = structType.getStructAndAdjustType(baseNode, concreteTemplateTypes);
4977 1735 Struct*& instantiatedStruct = node.instantiatedStructs.get(this.manIdx);
4978 1735 instantiatedStruct = spiceStruct;
4979
2/2
✓ Branch 0 taken 1734 times.
✓ Branch 1 taken 1 time.
1735 if spiceStruct == nil<Struct*> {
4980 1 const String message = String("Struct '") + getStructSignature(structName, concreteTemplateTypes) + "' could not be found";
4981 2 return this.softErrorER(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_STRUCT, message);
4982 1 }
4983
4984 // Struct instantiation for an inheriting struct is forbidden, because the vtable needs to be initialized and this is done in
4985 // the ctor of the struct, which is never called in case of struct instantiation
4986
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1733 times.
1734 if !spiceStruct.interfaceTypes.isEmpty() {
4987 2 return this.softErrorER(baseNode, SemanticErrorType::INVALID_STRUCT_INSTANTIATION, String("Struct instantiations for inheriting structs are forbidden"));
4988 1 }
4989
4990 // Check if the number of fields matches
4991
2/2
✓ Branch 0 taken 1684 times.
✓ Branch 1 taken 49 times.
1733 if node.fieldLst != nil<ASTArgLstNode*> { // Check if any fields are passed. Empty braces are also allowed
4992 1684 ASTArgLstNode* fieldLst = node.fieldLst;
4993
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1682 times.
1684 if spiceStruct.fieldTypes.getSize() != fieldLst.args.getSize() {
4994 4 return this.softErrorER(cast<ASTNode*>(fieldLst), SemanticErrorType::NUMBER_OF_FIELDS_NOT_MATCHING, String("You've passed too less/many field values. Pass either none or all of them"));
4995 2 }
4996
4997 // Check if the field types are matching
4998 1682 const unsigned long fieldCount = spiceStruct.fieldTypes.getSize();
4999 1682 Scope* structScope = spiceStruct.scope;
5000 1682 const unsigned long explicitFieldsStartIdx = structScope.getFieldCount() - fieldCount;
5001 // Per-manifestation: a struct literal inside a generic function is re-type-checked once per manifestation of
5002 // that function, and the struct itself may resolve to a different concrete type each time (e.g. HashEntry<K,
5003 // V> inside HashTable<K, V>.upsert()), so the copy-ctor set from one manifestation must not leak into another.
5004 1682 Vector<Function*>& fieldCopyCtors = node.fieldCopyCtors.get(this.manIdx);
5005 1682 fieldCopyCtors.clear();
5006
2/2
✓ Branch 0 taken 4120 times.
✓ Branch 1 taken 1682 times.
5802 for unsigned long i = 0l; i < fieldLst.args.getSize(); i++ {
5007 // Get actual type
5008 4120 IVisitable* assignExpr = fieldLst.args.get(i);
5009 4120 ASTNode* assignExprNode = getASTNode(assignExpr);
5010 4120 ExprResult fieldResult = this.visitExpr(assignExpr);
5011
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4120 times.
4120 if fieldResult.ty.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
5012 // Get expected type
5013 4120 SymbolTableEntry* expectedField = structScope.lookupField(cast<unsigned int>(explicitFieldsStartIdx + i));
5014 4120 assert expectedField != nil<SymbolTableEntry*>;
5015 // Copy the field type into a local first, instead of initializing a struct literal field with a reference to a
5016 // type with an overloaded assign operator
5017 4120 const QualType expectedFieldType = expectedField.getQualType();
5018 4120 const ExprResult expected = ExprResult{expectedFieldType, expectedField};
5019 4120 const bool rhsIsImmediate = assignExprNode.hasCompileTimeValue(this.manIdx);
5020
5021 // Capture anonymous info up front: getFieldAssignResultType may delete the anonymous entry (temp stealing
5022 // in performStructAssign), turning fieldResult.entry into a dangling pointer.
5023
2/2
✓ Branch 0 taken 2978 times.
✓ Branch 1 taken 1142 times.
4120 const bool rhsIsAnonymous = fieldResult.entry != nil<SymbolTableEntry*> && fieldResult.entry.anonymous;
5024 4120 String rhsEntryName = String("");
5025
2/2
✓ Branch 0 taken 4107 times.
✓ Branch 1 taken 13 times.
4120 if rhsIsAnonymous {
5026 13 rhsEntryName = fieldResult.entry.name;
5027 13 }
5028
5029 // Check if actual type matches expected type. A non-anonymous, non-trivially-copyable rhs (e.g. a plain
5030 // local variable) needs its value deep-copied into the field: the struct instantiation does not
5031 // consume/move it, so the caller keeps its own copy alive and destructs it independently.
5032 4120 Pair<QualType, Function*> fieldAssignResult = this.opRuleManager.getFieldAssignResultType(assignExprNode, expected, fieldResult, rhsIsImmediate, true);
5033 4120 fieldCopyCtors.pushBack(fieldAssignResult.getSecond());
5034
5035 // If there is an anonymous entry attached (e.g. for struct instantiation), delete it.
5036 // Safe to call even if performStructAssign already deleted it: erasing by key is a no-op when absent.
5037
2/2
✓ Branch 0 taken 4107 times.
✓ Branch 1 taken 13 times.
4120 if rhsIsAnonymous {
5038 13 this.currentScope.symbolTable.deleteAnonymous(rhsEntryName);
5039 13 fieldResult.entry = nil<SymbolTableEntry*>;
5040 13 }
5041 4120 }
5042 1682 assert fieldCopyCtors.getSize() == fieldLst.args.getSize();
5043 1682 } else {
5044 49 bool hasReferenceField = false;
5045
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 49 times.
149 foreach const QualType& fieldType : spiceStruct.fieldTypes {
5046
2/2
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 1 time.
100 if fieldType.isRef() {
5047 1 hasReferenceField = true;
5048 1 }
5049 100 }
5050
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 48 times.
49 if hasReferenceField {
5051 2 return this.softErrorER(baseNode, SemanticErrorType::REFERENCE_WITHOUT_INITIALIZER, String("The struct takes at least one reference field. You need to instantiate it with all fields."));
5052 1 }
5053 48 }
5054
5055 // Update type of struct entry
5056 1730 structEntry.updateType(structType, true);
5057
5058 // Add anonymous symbol to keep track of dtor call, if non-trivially destructible
5059 1730 SymbolTableEntry* anonymousEntry = nil<SymbolTableEntry*>;
5060
2/2
✓ Branch 0 taken 1665 times.
✓ Branch 1 taken 65 times.
1730 if !structType.isTriviallyDestructible(baseNode) {
5061 65 anonymousEntry = this.currentScope.symbolTable.insertAnonymous(structType, baseNode);
5062 65 }
5063
5064 // Remove public qualifier to not have public local variables
5065 1730 TypeQualifiers& structTypeQualifiers = structType.getQualifiers();
5066 1730 structTypeQualifiers.isPublic = false;
5067
5068 3460 return Any(ExprResult{node.node.setEvaluatedSymbolType(structType, this.manIdx), anonymousEntry});
5069 1741 }
5070
5071 67 public f<Any> TypeChecker.visitLambdaFunc(ASTLambdaFuncNode* node) {
5072 67 ASTNode* baseNode = cast<ASTNode*>(node);
5073
5074 // Check if all control paths in the lambda body return
5075 67 bool returnsOnAllControlPaths = true;
5076
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 66 times.
67 if !baseNode.returnsOnAllControlPaths(&returnsOnAllControlPaths, this.manIdx) {
5077 2 return this.softErrorER(baseNode, SemanticErrorType::MISSING_RETURN_STMT, String("Not all control paths of this lambda function have a return statement"));
5078 1 }
5079
5080 // Change to function scope
5081 66 Scope* bodyScope = this.currentScope.getChildScope(node.getScopeId());
5082 66 ScopeHandle scopeHandle = ScopeHandle(&this.compilerPass, bodyScope, ScopeType::LAMBDA_BODY);
5083
5084 // Visit return type
5085 66 const QualType returnType = this.getDataType(node.returnType);
5086
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if returnType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
5087
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 65 times.
66 if returnType.is(SuperType::TY_DYN) {
5088 2 return this.softErrorER(baseNode, SemanticErrorType::UNEXPECTED_DYN_TYPE, String("Dyn return types are not allowed"));
5089 1 }
5090
5091 // Set the type of the result variable
5092 65 SymbolTableEntry* resultVarEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME));
5093 65 assert resultVarEntry != nil<SymbolTableEntry*>;
5094 65 resultVarEntry.updateType(returnType, true);
5095 65 resultVarEntry.used = true;
5096
5097 // Visit parameters
5098 65 QualTypeList paramTypes;
5099 65 ParamList paramList;
5100
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 58 times.
65 if node.hasParams {
5101 58 this.visitLambdaParams(baseNode, node.paramLst, paramTypes, paramList);
5102 58 }
5103
5104 // Visit lambda body
5105 65 this.visit(node.body);
5106
5107 // Leave function body scope
5108 65 scopeHandle.leaveScopeEarly();
5109
5110 // Prepare type of function
5111 65 const QualType functionTypeBase = QualType(SuperType::TY_FUNCTION);
5112 65 const QualType functionTypeWithSignature = functionTypeBase.getWithFunctionParamAndReturnTypes(returnType, paramTypes);
5113 65 const QualType functionType = functionTypeWithSignature.getWithLambdaCaptures(!bodyScope.symbolTable.captures.isEmpty());
5114
5115 // Create function object
5116 65 this.createLambdaManifestation(baseNode, bodyScope, returnType, paramList);
5117
5118 // Check special requirements if this is an async lambda
5119 65 this.checkAsyncLambdaCaptureRules(baseNode, node.lambdaAttr);
5120
5121 130 return Any(ExprResult{node.node.node.setEvaluatedSymbolType(functionType, this.manIdx), nil<SymbolTableEntry*>});
5122 66 }
5123
5124 73 public f<Any> TypeChecker.visitLambdaProc(ASTLambdaProcNode* node) {
5125 73 ASTNode* baseNode = cast<ASTNode*>(node);
5126
5127 // Mark unreachable statements
5128 73 bool doSetPredecessorsUnreachable = true;
5129 73 const bool _ = baseNode.returnsOnAllControlPaths(&doSetPredecessorsUnreachable, this.manIdx);
5130
5131 // Change to function scope
5132 73 Scope* bodyScope = this.currentScope.getChildScope(node.getScopeId());
5133 73 ScopeHandle scopeHandle = ScopeHandle(&this.compilerPass, bodyScope, ScopeType::LAMBDA_BODY);
5134
5135 // Visit parameters
5136 73 QualTypeList paramTypes;
5137 73 ParamList paramList;
5138
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 32 times.
73 if node.hasParams {
5139 32 this.visitLambdaParams(baseNode, node.paramLst, paramTypes, paramList);
5140 32 }
5141
5142 // Visit lambda body
5143 73 this.visit(node.body);
5144
5145 // Leave function body scope
5146 73 scopeHandle.leaveScopeEarly();
5147
5148 // Prepare type of function
5149 73 const QualType functionTypeBase = QualType(SuperType::TY_PROCEDURE);
5150 73 const QualType functionTypeWithSignature = functionTypeBase.getWithFunctionParamAndReturnTypes(QualType(SuperType::TY_DYN), paramTypes);
5151 73 const QualType functionType = functionTypeWithSignature.getWithLambdaCaptures(!bodyScope.symbolTable.captures.isEmpty());
5152
5153 // Create function object
5154 73 this.createLambdaManifestation(baseNode, bodyScope, QualType(SuperType::TY_DYN), paramList);
5155
5156 // Check special requirements if this is an async lambda
5157 73 this.checkAsyncLambdaCaptureRules(baseNode, node.lambdaAttr);
5158
5159 146 return Any(ExprResult{node.node.node.setEvaluatedSymbolType(functionType, this.manIdx), nil<SymbolTableEntry*>});
5160 73 }
5161
5162 4 public f<Any> TypeChecker.visitLambdaExpr(ASTLambdaExprNode* node) {
5163 4 ASTNode* baseNode = cast<ASTNode*>(node);
5164
5165 // Change to function scope
5166 4 Scope* bodyScope = this.currentScope.getChildScope(node.getScopeId());
5167 4 ScopeHandle scopeHandle = ScopeHandle(&this.compilerPass, bodyScope, ScopeType::LAMBDA_BODY);
5168
5169 // Visit parameters
5170 4 QualTypeList paramTypes;
5171 4 ParamList paramList;
5172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if node.hasParams {
5173 4 this.visitLambdaParams(baseNode, node.paramLst, paramTypes, paramList);
5174 4 }
5175
5176 // Visit lambda expression
5177 4 const ExprResult lambdaExprResult = this.visitExpr(node.lambdaExpr);
5178 4 const QualType returnType = lambdaExprResult.ty;
5179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if returnType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
5180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if returnType.is(SuperType::TY_DYN) {
5181 ✗ return this.softErrorER(baseNode, SemanticErrorType::UNEXPECTED_DYN_TYPE, String("Dyn return types are not allowed"));
5182 ✗ }
5183
5184 // Leave function body scope
5185 4 scopeHandle.leaveScopeEarly();
5186
5187 // Prepare type of function
5188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 const SuperType superType = returnType.is(SuperType::TY_DYN) ? SuperType::TY_PROCEDURE : SuperType::TY_FUNCTION;
5189 4 const QualType functionTypeBase = QualType(superType);
5190 4 const QualType functionTypeWithSignature = functionTypeBase.getWithFunctionParamAndReturnTypes(returnType, paramTypes);
5191 4 const QualType functionType = functionTypeWithSignature.getWithLambdaCaptures(!bodyScope.symbolTable.captures.isEmpty());
5192
5193 // Create function object
5194 4 this.createLambdaManifestation(baseNode, bodyScope, returnType, paramList);
5195
5196 8 return Any(ExprResult{node.node.node.setEvaluatedSymbolType(functionType, this.manIdx), nil<SymbolTableEntry*>});
5197 4 }
5198
5199 /**
5200 * Visit the param list of a lambda and collect the param types (shared part of visitLambdaFunc/Proc/Expr)
5201 *
5202 * @param node Lambda node
5203 * @param paramLst Param list node
5204 * @param paramTypes Param types
5205 * @param paramList Params
5206 */
5207 94 p TypeChecker.visitLambdaParams(ASTNode* node, IVisitable* paramLst, QualTypeList& paramTypes, ParamList& paramList) {
5208 // Visit param list to retrieve the params
5209 94 Any paramLstResult = this.visit(paramLst);
5210 94 const ParamList visitedParams = paramLstResult.get<ParamList>();
5211
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 94 times.
218 foreach const Param& param : visitedParams {
5212
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 1 time.
124 if param.isOptional {
5213 1 this.softError(node, SemanticErrorType::LAMBDA_WITH_OPTIONAL_PARAMS, String("Lambdas cannot have optional parameters"));
5214 1 }
5215
5216 124 paramTypes.pushBack(param.qualType);
5217 124 paramList.pushBack(Param{param.qualType, param.isOptional});
5218 124 }
5219 94 }
5220
5221 /**
5222 * Create the function object of a lambda for the current manifestation (shared part of visitLambdaFunc/Proc/Expr)
5223 *
5224 * @param node Lambda node
5225 * @param bodyScope Body scope of the lambda
5226 * @param returnType Return type of the lambda
5227 * @param paramList Params of the lambda
5228 */
5229 142 p TypeChecker.createLambdaManifestation(ASTNode* node, Scope* bodyScope, const QualType& returnType, const ParamList& paramList) {
5230 142 const String fctName = String("lambda.") + node.codeLoc.toPrettyLineAndColumn();
5231 142 ASTLambdaBaseNode* lambdaBaseNode = asLambdaBaseNode(node);
5232 142 Function& manifestation = lambdaBaseNode.manifestations.get(this.manIdx);
5233 142 const Vector<GenericType> noTemplateTypes;
5234 142 manifestation = Function(fctName, nil<SymbolTableEntry*>, QualType(SuperType::TY_DYN), returnType, paramList, noTemplateTypes, node);
5235 142 manifestation.bodyScope = bodyScope;
5236 142 manifestation.mangleSuffix = String(".") + toString(cast<long>(this.manIdx));
5237 142 }
5238
5239 // ====================================================== Builtin functions =======================================================
5240
5241 /**
5242 * Build the error message for a mismatching number of template types or arguments of a builtin call
5243 *
5244 * @param min Min count
5245 * @param max Max count
5246 * @param actual Actual count
5247 * @param suffix Suffix to describe what is counted
5248 * @return Error message
5249 */
5250 27 f<String> buildBuiltinCountErrorMessage(unsigned int min, unsigned int max, unsigned long actual, string suffix) {
5251 27 String expectedStr;
5252 // The counts are widened to unsigned long, because the int overload of toString would print large counts as negative
5253
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 12 times.
27 if min == max {
5254
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 4 times.
15 expectedStr = min == 0u ? String("no") : toString(cast<unsigned long>(min));
5255 15 } else {
5256 12 expectedStr = "between " + toString(cast<unsigned long>(min)) + " and " + toString(cast<unsigned long>(max));
5257 12 }
5258 54 return "This builtin expects " + expectedStr + " " + suffix + ", but got " + toString(actual);
5259 27 }
5260
5261 9908 f<Any> TypeChecker.visitBuiltinCall(ASTFctCallNode* node) {
5262 9908 BuiltinFunctionInfo info;
5263 9908 const bool isBuiltin = lookupBuiltinFunction(node.fqFunctionName, info);
5264 9908 assert isBuiltin; // Builtin function not implemented
5265 9908 ASTNode* baseNode = cast<ASTNode*>(node);
5266
5267 // Do basic checks of template types and args, based on the builtin function info
5268
2/2
✓ Branch 0 taken 2098 times.
✓ Branch 1 taken 7810 times.
9908 const unsigned long numTemplateTypes = node.hasTemplateTypes ? node.templateTypeLst.dataTypes.getSize() : 0l;
5269
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9904 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 9896 times.
9908 if numTemplateTypes < cast<unsigned long>(info.minTemplateTypes) || numTemplateTypes > cast<unsigned long>(info.maxTemplateTypes) {
5270 12 const String msg = buildBuiltinCountErrorMessage(info.minTemplateTypes, info.maxTemplateTypes, numTemplateTypes, "template type(s)");
5271 24 return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_TEMPLATE_TYPE_COUNT_MISMATCH, msg);
5272 12 }
5273
5274
2/2
✓ Branch 0 taken 6547 times.
✓ Branch 1 taken 3349 times.
9896 const unsigned long numArgs = node.hasArgs ? node.argLst.args.getSize() : 0l;
5275
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9892 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 9881 times.
9896 if numArgs < cast<unsigned long>(info.minArgTypes) || numArgs > cast<unsigned long>(info.maxArgTypes) {
5276 15 const String msg = buildBuiltinCountErrorMessage(info.minArgTypes, info.maxArgTypes, numArgs, "argument(s)");
5277 30 return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_ARG_COUNT_MISMATCH, msg);
5278 15 }
5279
5280
2/2
✓ Branch 0 taken 8864 times.
✓ Branch 1 taken 1017 times.
9881 if info.allTemplateTypesOrAllArgTypes {
5281
4/4
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 897 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1013 times.
1017 if numTemplateTypes > 0l && numArgs > 0l {
5282 8 return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_SIGNATURE_MISMATCH, String("This builtin expects either template types or arguments, but got both"));
5283 4 }
5284
4/4
✓ Branch 0 taken 893 times.
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1009 times.
1013 if numTemplateTypes == 0l && numArgs == 0l {
5285 8 return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_SIGNATURE_MISMATCH, String("This builtin expects either template types or arguments, but got none"));
5286 4 }
5287 1009 }
5288
5289 // Call to TypeChecker delegate to execute further checks. This is a switch, since Spice has no method pointers
5290 9873 const String& name = node.fqFunctionName;
5291
2/2
✓ Branch 0 taken 2407 times.
✓ Branch 1 taken 7466 times.
9873 if name == BUILTIN_FCT_NAME_PRINTF { return this.visitBuiltinPrintfCall(node); }
5292
2/2
✓ Branch 0 taken 916 times.
✓ Branch 1 taken 6550 times.
7466 if name == BUILTIN_FCT_NAME_SIZEOF { return this.visitBuiltinSizeOfCall(node); }
5293
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 6515 times.
6550 if name == BUILTIN_FCT_NAME_ALIGNOF { return this.visitBuiltinAlignOfCall(node); }
5294
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 6475 times.
6515 if name == BUILTIN_FCT_NAME_OFFSETOF { return this.visitBuiltinOffsetOfCall(node); }
5295
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 6440 times.
6475 if name == BUILTIN_FCT_NAME_TYPEID { return this.visitBuiltinTypeIdCall(node); }
5296
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 6417 times.
6440 if name == BUILTIN_FCT_NAME_TYPENAME { return this.visitBuiltinTypeNameCall(node); }
5297
2/2
✓ Branch 0 taken 150 times.
✓ Branch 1 taken 6267 times.
6417 if name == BUILTIN_FCT_NAME_LEN { return this.visitBuiltinLenCall(node); }
5298
2/2
✓ Branch 0 taken 3033 times.
✓ Branch 1 taken 3234 times.
6267 if name == BUILTIN_FCT_NAME_PANIC { return this.visitBuiltinPanicCall(node); }
5299
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3226 times.
3234 if name == BUILTIN_FCT_NAME_SYSCALL { return this.visitBuiltinSyscallCall(node); }
5300
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 3208 times.
3226 if name == BUILTIN_FCT_NAME_IS_SAME { return this.visitBuiltinIsSameCall(node); }
5301
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3205 times.
3208 if name == BUILTIN_FCT_NAME_IMPLEMENTS_INTERFACE { return this.visitBuiltinImplementsInterfaceCall(node); }
5302
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 3179 times.
3205 if name == BUILTIN_FCT_NAME_GET_BUILD_VAR { return this.visitBuiltinGetBuildVarCall(node); }
5303
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3171 times.
3179 if name == BUILTIN_FCT_NAME_IS_TRIVIALLY_CONSTRUCTIBLE { return this.visitBuiltinIsTriviallyConstructible(node); }
5304
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 3157 times.
3171 if name == BUILTIN_FCT_NAME_IS_TRIVIALLY_COPYABLE { return this.visitBuiltinIsTriviallyCopyable(node); }
5305
2/2
✓ Branch 0 taken 338 times.
✓ Branch 1 taken 2819 times.
3157 if name == BUILTIN_FCT_NAME_IS_TRIVIALLY_DESTRUCTIBLE { return this.visitBuiltinIsTriviallyDestructible(node); }
5306
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2811 times.
2819 if name == BUILTIN_FCT_NAME_IS_HEAP { return this.visitBuiltinIsHeap(node); }
5307
2/2
✓ Branch 0 taken 241 times.
✓ Branch 1 taken 2570 times.
2811 if name == BUILTIN_FCT_NAME_NEW { return this.visitBuiltinNewCall(node); }
5308
2/2
✓ Branch 0 taken 530 times.
✓ Branch 1 taken 2040 times.
2570 if name == BUILTIN_FCT_NAME_PLACEMENT_NEW { return this.visitBuiltinPlacementNewCall(node); }
5309
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2037 times.
2040 if name == BUILTIN_FCT_NAME_SOURCE_FILE { return this.visitBuiltinSourceFileCall(node); }
5310
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2031 times.
2037 if name == BUILTIN_FCT_NAME_SOURCE_LINE { return this.visitBuiltinSourceLineCall(node); }
5311
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2025 times.
2031 if name == BUILTIN_FCT_NAME_SOURCE_COLUMN { return this.visitBuiltinSourceColumnCall(node); }
5312
2/2
✓ Branch 0 taken 996 times.
✓ Branch 1 taken 1029 times.
2025 if name == BUILTIN_FCT_NAME_ERR_TRACE_BUFFER { return this.visitBuiltinErrTraceBufferCall(node); }
5313
1/2
✓ Branch 0 taken 1029 times.
✗ Branch 1 not taken.
1029 if name == BUILTIN_FCT_NAME_STDERR { return this.visitBuiltinStdErrCall(node); }
5314
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if name == BUILTIN_FCT_NAME_FRAME_ADDRESS { return this.visitBuiltinFrameAddressCall(node); }
5315 ✗ return Any();
5316 }
5317
5318 // Evaluated symbol type of an argument of a function call
5319 7895 f<QualType> TypeChecker.getArgType(ASTFctCallNode* node, unsigned long argIdx) {
5320 7895 ASTExprNode* argNode = asExprNode(getASTNode(node.argLst.args.get(argIdx)));
5321 7895 return argNode.getEvaluatedSymbolType(this.manIdx);
5322 }
5323
5324 // Evaluated symbol type of the template type or (if there is no template type) the argument of a builtin call like sizeof
5325 1009 f<QualType> TypeChecker.getTemplateTypeOrArgType(ASTFctCallNode* node) {
5326
2/2
✓ Branch 0 taken 893 times.
✓ Branch 1 taken 116 times.
1009 if node.hasTemplateTypes { // Of type
5327 893 ASTDataTypeNode* dataTypeNode = node.templateTypeLst.dataTypes.front();
5328 893 return dataTypeNode.node.getEvaluatedSymbolType(this.manIdx);
5329 }
5330 116 return this.getArgType(node, 0l); // Of value
5331 }
5332
5333 // Evaluated symbol type of the template type with the given index of a function call
5334 1207 f<QualType> TypeChecker.getTemplateType(ASTFctCallNode* node, unsigned long templateTypeIdx) {
5335 1207 ASTDataTypeNode* dataTypeNode = node.templateTypeLst.dataTypes.get(templateTypeIdx);
5336 1207 return dataTypeNode.node.getEvaluatedSymbolType(this.manIdx);
5337 }
5338
5339 // Set the evaluated symbol type of a builtin call and return it as expression result
5340 9837 f<Any> TypeChecker.builtinResult(ASTFctCallNode* node, const QualType& qualType) {
5341 9837 return Any(ExprResult{node.node.setEvaluatedSymbolType(qualType, this.manIdx), nil<SymbolTableEntry*>});
5342 }
5343
5344 // Set the compile time value of a function call for the current manifestation
5345 1495 p TypeChecker.setCompileTimeValue(ASTFctCallNode* node, const CompileTimeValue& value) {
5346 1495 FctCallData& data = node.data.get(this.manIdx);
5347 1495 data.setCompileTimeValue(value);
5348 1495 }
5349
5350 // Whether the given char is a printf flag
5351 2119 f<bool> isPrintfFlag(char c) {
5352
8/8
✓ Branch 0 taken 2118 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2117 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2116 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 2115 times.
2119 return c == '-' || c == '+' || c == ' ' || c == '#' || c == '0';
5353 }
5354
5355 // Whether the given char is a decimal digit
5356 2145 f<bool> isDecimalDigit(char c) {
5357
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2135 times.
2145 return c >= '0' && c <= '9';
5358 }
5359
5360 2407 f<Any> TypeChecker.visitBuiltinPrintfCall(ASTFctCallNode* node) {
5361 2407 assert node.fqFunctionName == BUILTIN_FCT_NAME_PRINTF;
5362 2407 ASTNode* baseNode = cast<ASTNode*>(node);
5363
5364 // Retrieve templated string
5365 2407 assert node.hasArgs;
5366 2407 ASTNode* firstArg = getASTNode(node.argLst.args.front());
5367 2407 const QualType firstArgType = this.getArgType(node, 0l);
5368 2407 assert firstArgType.is(SuperType::TY_STRING) && firstArg.hasCompileTimeValue(this.manIdx);
5369 2407 const CompileTimeValue firstArgValue = firstArg.getCompileTimeValue(this.manIdx);
5370 2407 const unsigned long stringOffset = firstArgValue.getStringValueOffset();
5371 2407 String templatedString = String(this.resourceManager.getCompileTimeStringValue(stringOffset));
5372 2407 const long templatedStringSize = cast<long>(templatedString.getLength());
5373 2407 const unsigned long argCount = node.argLst.args.getSize();
5374
5375 // Check if assignment types match placeholder types
5376 2407 unsigned long placeholderCount = 0l;
5377 2407 long index = templatedString.find('%');
5378
4/4
✓ Branch 0 taken 2398 times.
✓ Branch 1 taken 2106 times.
✓ Branch 2 taken 2106 times.
✓ Branch 3 taken 2398 times.
4504 while index != -1l && index != templatedStringSize - 1l {
5379 // Handle escaped percent signs (%%), which do not consume an argument
5380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2106 times.
2106 if templatedString[cast<unsigned long>(index + 1l)] == '%' {
5381 ✗ index = templatedString.find('%', cast<unsigned long>(index + 2l));
5382 ✗ continue;
5383 }
5384
5385 // Determine the position of the conversion specifier by skipping the optional
5386 // flags, field width and precision (e.g. the "04" in "%04d"). These are forwarded
5387 // verbatim to the underlying C printf, so we only need to skip over them here.
5388 2106 long specifierIndex = index + 1l;
5389
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2119 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 2106 times.
2119 while specifierIndex < templatedStringSize && isPrintfFlag(templatedString[cast<unsigned long>(specifierIndex)]) {
5390 13 specifierIndex++; // Skip flags
5391 13 }
5392
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2117 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 2106 times.
2117 while specifierIndex < templatedStringSize && isDecimalDigit(templatedString[cast<unsigned long>(specifierIndex)]) {
5393 11 specifierIndex++; // Skip field width
5394 11 }
5395
3/4
✓ Branch 0 taken 2106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 2096 times.
2106 if specifierIndex < templatedStringSize && templatedString[cast<unsigned long>(specifierIndex)] == '.' {
5396 10 specifierIndex++; // Skip precision dot
5397
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
20 while specifierIndex < templatedStringSize && isDecimalDigit(templatedString[cast<unsigned long>(specifierIndex)]) {
5398 10 specifierIndex++; // Skip precision
5399 10 }
5400 10 }
5401
1/2
✓ Branch 0 taken 2106 times.
✗ Branch 1 not taken.
2106 if specifierIndex >= templatedStringSize {
5402 ✗ return this.softErrorER(baseNode, SemanticErrorType::PRINTF_TYPE_ERROR, String("The placeholder string contains an invalid placeholder"));
5403 ✗ }
5404
5405 // Check if there is another assignExpr
5406
2/2
✓ Branch 0 taken 2105 times.
✓ Branch 1 taken 1 time.
2106 if argCount - 1l <= placeholderCount {
5407 2 return this.softErrorER(baseNode, SemanticErrorType::PRINTF_ARG_COUNT_ERROR, String("The placeholder string contains more placeholders than arguments"));
5408 1 }
5409
5410 // Get next assignment
5411 2105 const ASTNode* assignment = getASTNode(node.argLst.args.get(placeholderCount + 1l));
5412 // Visit assignment
5413 2105 QualType argType = this.getArgType(node, placeholderCount + 1l);
5414
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2105 times.
2105 if argType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
5415 2105 argType = argType.removeReferenceWrapper();
5416
5417 2105 const char specifier = templatedString[cast<unsigned long>(specifierIndex)];
5418
6/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 1381 times.
✓ Branch 3 taken 66 times.
✓ Branch 4 taken 500 times.
✓ Branch 5 taken 9 times.
2105 switch specifier {
5419 case 'c': {
5420
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 1 time.
66 if !argType.is(SuperType::TY_CHAR) {
5421 2 return this.softErrorER(assignment, SemanticErrorType::PRINTF_TYPE_ERROR, "The placeholder string expects char, but got " + argType.getName(false));
5422 1 }
5423 65 placeholderCount++;
5424 65 }
5425 case 'd', 'i', 'l', 'o', 'u', 'x', 'X': {
5426 1381 const dyn intLikeTypes = [SuperType::TY_INT, SuperType::TY_SHORT, SuperType::TY_LONG, SuperType::TY_BYTE, SuperType::TY_BOOL];
5427
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1379 times.
1381 if !argType.isOneOf(intLikeTypes, len(intLikeTypes)) {
5428 4 return this.softErrorER(assignment, SemanticErrorType::PRINTF_TYPE_ERROR, "The placeholder string expects int, short, long, byte or bool, but got " + argType.getName(false));
5429 2 }
5430 1379 placeholderCount++;
5431 1379 }
5432 case 'a', 'A', 'f', 'F', 'e', 'E', 'g', 'G': {
5433
2/2
✓ Branch 0 taken 146 times.
✓ Branch 1 taken 1 time.
147 if !argType.is(SuperType::TY_DOUBLE) {
5434 2 return this.softErrorER(assignment, SemanticErrorType::PRINTF_TYPE_ERROR, "The placeholder string expects double, but got " + argType.getName(false));
5435 1 }
5436 146 placeholderCount++;
5437 146 }
5438 case 's': {
5439
8/8
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 389 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 108 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 499 times.
✓ Branch 7 taken 1 time.
500 if !argType.is(SuperType::TY_STRING) && !argType.isStringObj() && !argType.isPtrTo(SuperType::TY_CHAR) && !argType.isArrayOf(SuperType::TY_CHAR) {
5440 2 return this.softErrorER(assignment, SemanticErrorType::PRINTF_TYPE_ERROR, "The placeholder string expects string, String, char* or char[], but got " + argType.getName(false));
5441 1 }
5442 499 placeholderCount++;
5443 499 }
5444 case 'p': {
5445
5/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 time.
9 if !argType.isPtr() && !argType.isArray() && !argType.is(SuperType::TY_STRING) {
5446 2 return this.softErrorER(assignment, SemanticErrorType::PRINTF_TYPE_ERROR, "The placeholder string expects pointer, array or string, but got " + argType.getName(false));
5447 1 }
5448 8 placeholderCount++;
5449 8 }
5450 default: {
5451 4 return this.softErrorER(baseNode, SemanticErrorType::PRINTF_TYPE_ERROR, String("The placeholder string contains an invalid placeholder"));
5452 2 }
5453 }
5454 2097 index = templatedString.find('%', cast<unsigned long>(specifierIndex + 1l)); // We can also skip the conversion specifier
5455 2097 }
5456
5457 // Check if the number of placeholders matches the number of args
5458
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2397 times.
2398 if placeholderCount < argCount - 1l {
5459 2 return this.softErrorER(baseNode, SemanticErrorType::PRINTF_ARG_COUNT_ERROR, String("The placeholder string contains less placeholders than arguments"));
5460 1 }
5461
5462 4794 return this.builtinResult(node, QualType(SuperType::TY_INT));
5463 2407 }
5464
5465 916 f<Any> TypeChecker.visitBuiltinSizeOfCall(ASTFctCallNode* node) {
5466 916 assert node.fqFunctionName == BUILTIN_FCT_NAME_SIZEOF;
5467
5468 // Directly set compile time value here, so that compile time ifs can be evaluated.
5469 916 const QualType qualType = this.getTemplateTypeOrArgType(node);
5470
5471 916 const dyn dynOrUnresolved = [SuperType::TY_UNRESOLVED, SuperType::TY_DYN];
5472
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 914 times.
916 if qualType.isOneOf(dynOrUnresolved, len(dynOrUnresolved)) {
5473 4 return this.softErrorER(cast<ASTNode*>(node), SemanticErrorType::UNEXPECTED_DYN_TYPE, String("Cannot use sizeof on a dyn or unresolved type"));
5474 2 }
5475
5476 914 const Type* rawType = qualType.getType();
5477 914 CompileTimeValue compileTimeValue;
5478 914 compileTimeValue.setLongValue(cast<long>(rawType.getTypeAllocSize(this.sourceFile)));
5479 914 this.setCompileTimeValue(node, compileTimeValue);
5480
5481 914 return this.builtinResult(node, QualType(SuperType::TY_LONG));
5482 }
5483
5484 35 f<Any> TypeChecker.visitBuiltinAlignOfCall(ASTFctCallNode* node) {
5485 35 assert node.fqFunctionName == BUILTIN_FCT_NAME_ALIGNOF;
5486
5487 // Directly set compile time value here, so that compile time ifs can be evaluated.
5488 35 const QualType qualType = this.getTemplateTypeOrArgType(node);
5489
5490 35 const dyn dynOrUnresolved = [SuperType::TY_UNRESOLVED, SuperType::TY_DYN];
5491
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 33 times.
35 if qualType.isOneOf(dynOrUnresolved, len(dynOrUnresolved)) {
5492 4 return this.softErrorER(cast<ASTNode*>(node), SemanticErrorType::UNEXPECTED_DYN_TYPE, String("Cannot use alignof on a dyn or unresolved type"));
5493 2 }
5494
5495 33 const Type* rawType = qualType.getType();
5496 33 CompileTimeValue compileTimeValue;
5497 33 compileTimeValue.setLongValue(cast<long>(rawType.getABITypeAlign(this.sourceFile)));
5498 33 this.setCompileTimeValue(node, compileTimeValue);
5499
5500 33 return this.builtinResult(node, QualType(SuperType::TY_LONG));
5501 }
5502
5503 40 f<Any> TypeChecker.visitBuiltinOffsetOfCall(ASTFctCallNode* node) {
5504 40 assert node.fqFunctionName == BUILTIN_FCT_NAME_OFFSETOF;
5505 40 assert node.hasArgs && node.argLst.args.getSize() == 2l;
5506
5507 // The first argument denotes the struct to compute the offset within
5508 40 const ASTNode* structArg = getASTNode(node.argLst.args.get(0l));
5509 40 QualType structType = this.getArgType(node, 0l);
5510 40 structType = structType.removeReferenceWrapper();
5511 40 structType = structType.autoDereference();
5512
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 39 times.
40 if !structType.is(SuperType::TY_STRUCT) {
5513 2 return this.softErrorER(structArg, SemanticErrorType::EXPECTED_STRUCT_TYPE, String("The offsetof builtin expects a struct as its first argument"));
5514 1 }
5515
5516 // The second argument must be a (possibly chained) member access, denoting the field to compute the offset of
5517 39 ASTNode* memberArg = getASTNode(node.argLst.args.get(1l));
5518 39 ASTPostfixUnaryExprNode* leafAccess = nil<ASTPostfixUnaryExprNode*>;
5519 39 ASTNode* cur = memberArg;
5520
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 39 times.
40 while cur != nil<ASTNode*> {
5521
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 1 time.
39 if cur.kind == ASTNodeKind::POSTFIX_UNARY_EXPR {
5522 38 leafAccess = downcastMut<ASTPostfixUnaryExprNode>(cur);
5523 38 break;
5524 }
5525 1 const Vector<ASTNode*>& children = cur.getChildren();
5526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 cur = children.getSize() == 1l ? children.front() : nil<ASTNode*>;
5527 1 }
5528
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 38 times.
39 if leafAccess == nil<ASTPostfixUnaryExprNode*> || leafAccess.op != PostfixUnaryOp::OP_MEMBER_ACCESS {
5529 2 return this.softErrorER(memberArg, SemanticErrorType::INVALID_MEMBER_ACCESS, String("The offsetof builtin expects a struct member access as its second argument"));
5530 1 }
5531
5532 // Collect the chain of member accesses (from leaf to base). Only plain member accesses are supported.
5533 38 Vector<ASTPostfixUnaryExprNode*> accessChain;
5534 38 ASTPostfixUnaryExprNode* access = leafAccess;
5535
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 37 times.
84 while access != nil<ASTPostfixUnaryExprNode*> {
5536
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 46 times.
47 if access.op != PostfixUnaryOp::OP_MEMBER_ACCESS {
5537 2 return this.softErrorER(memberArg, SemanticErrorType::INVALID_MEMBER_ACCESS, String("The offsetof builtin only supports plain member accesses"));
5538 1 }
5539 46 accessChain.pushBack(access);
5540 // Continue with the operand of the member access, if it is a postfix unary expression itself
5541 46 access = nil<ASTPostfixUnaryExprNode*>;
5542 46 IVisitable* operand = accessChain.back().postfixUnaryExpr;
5543
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 37 times.
✓ Branch 3 taken 9 times.
46 if operand != nil<IVisitable*> && getASTNode(operand).kind == ASTNodeKind::POSTFIX_UNARY_EXPR {
5544 9 access = downcast<ASTPostfixUnaryExprNode>(operand);
5545 9 }
5546 46 }
5547
5548 // Ensure the base of the member access chain is the struct that was passed as first argument
5549 37 QualType chainBaseType = this.getOperandType(accessChain.back());
5550 37 chainBaseType = chainBaseType.removeReferenceWrapper();
5551 37 chainBaseType = chainBaseType.autoDereference();
5552
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 36 times.
37 if !chainBaseType.matches(structType, false, true, false) {
5553 2 return this.softErrorER(memberArg, SemanticErrorType::INVALID_MEMBER_ACCESS, String("The member access must be rooted in the struct passed as first argument"));
5554 1 }
5555
5556 // Accumulate the byte offset by walking each access in the chain through its struct layout
5557 36 long offset = 0l;
5558
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 44 times.
80 foreach ASTPostfixUnaryExprNode* chainAccess : accessChain {
5559 44 QualType baseType = this.getOperandType(chainAccess);
5560 44 baseType = baseType.removeReferenceWrapper();
5561 44 baseType = baseType.autoDereference();
5562 44 assert baseType.is(SuperType::TY_STRUCT);
5563
5564 // Resolve the struct manifestation
5565 44 Struct* spiceStruct = baseType.getStruct(cast<ASTNode*>(node));
5566 44 assert spiceStruct != nil<Struct*> && !spiceStruct.scope.isGenericScope;
5567
5568 // Look up the accessed field to retrieve its index path within the LLVM struct type
5569 44 Vector<unsigned long> indexPath;
5570 44 const SymbolTableEntry* fieldEntry = spiceStruct.lookupInComposedFields(chainAccess.identifier, indexPath);
5571
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if fieldEntry == nil<SymbolTableEntry*> {
5572 ✗ return this.softErrorER(memberArg, SemanticErrorType::REFERENCED_UNDEFINED_FIELD, "Field '" + chainAccess.identifier + "' not found in struct " + baseType.getSubType());
5573 ✗ }
5574
5575 // Walk the index path through the struct layout to accumulate the byte offset of the field
5576 44 const Type* baseRawType = baseType.getType();
5577 44 offset += cast<long>(baseRawType.getFieldOffset(this.sourceFile, indexPath));
5578 44 }
5579 36 CompileTimeValue compileTimeValue;
5580 36 compileTimeValue.setLongValue(offset);
5581 36 this.setCompileTimeValue(node, compileTimeValue);
5582
5583 72 return this.builtinResult(node, QualType(SuperType::TY_LONG));
5584 38 }
5585
5586 // Evaluated symbol type of the operand of a postfix unary expression
5587 81 f<QualType> TypeChecker.getOperandType(ASTPostfixUnaryExprNode* node) {
5588 81 ASTExprNode* operandNode = asExprNode(getASTNode(node.postfixUnaryExpr));
5589 81 return operandNode.getEvaluatedSymbolType(this.manIdx);
5590 }
5591
5592 35 f<Any> TypeChecker.visitBuiltinTypeIdCall(ASTFctCallNode* node) {
5593 35 assert node.fqFunctionName == BUILTIN_FCT_NAME_TYPEID;
5594
5595 // Directly set compile time value here, so that compile time ifs can be evaluated.
5596 35 const QualType qualType = this.getTemplateTypeOrArgType(node);
5597 35 const Hash typeId = this.resourceManager.typeRegistry.getTypeHash(*qualType.getType());
5598 35 CompileTimeValue compileTimeValue;
5599 35 compileTimeValue.setLongValue(cast<long>(typeId));
5600 35 this.setCompileTimeValue(node, compileTimeValue);
5601
5602 35 return this.builtinResult(node, QualType(SuperType::TY_LONG));
5603 }
5604
5605 23 f<Any> TypeChecker.visitBuiltinTypeNameCall(ASTFctCallNode* node) {
5606 23 assert node.fqFunctionName == BUILTIN_FCT_NAME_TYPENAME;
5607
5608 // Directly set compile time value here, so that compile time ifs can be evaluated.
5609 23 const QualType qualType = this.getTemplateTypeOrArgType(node);
5610 23 CompileTimeValue compileTimeValue;
5611 23 compileTimeValue.setStringValueOffset(this.resourceManager.compileTimeStringValues.getSize());
5612 23 this.resourceManager.compileTimeStringValues.pushBack(qualType.getName());
5613 23 this.setCompileTimeValue(node, compileTimeValue);
5614
5615 46 return this.builtinResult(node, QualType(SuperType::TY_STRING));
5616 23 }
5617
5618 150 f<Any> TypeChecker.visitBuiltinLenCall(ASTFctCallNode* node) {
5619 150 assert node.fqFunctionName == BUILTIN_FCT_NAME_LEN;
5620
5621 // Directly set compile time value here, so that compile time ifs can be evaluated.
5622 150 QualType argType = this.getArgType(node, 0l);
5623 150 argType = argType.removeReferenceWrapper();
5624
5625 // Check if arg is of type array
5626
4/4
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 149 times.
150 if !argType.isArray() && !argType.is(SuperType::TY_STRING) {
5627 2 return this.softErrorER(getASTNode(node.argLst.args.front()), SemanticErrorType::EXPECTED_ARRAY_TYPE, String("The len builtin can only work on arrays or strings"));
5628 1 }
5629
5630
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 99 times.
149 if argType.is(SuperType::TY_ARRAY) {
5631 50 CompileTimeValue compileTimeValue;
5632 50 compileTimeValue.setLongValue(cast<long>(argType.getArraySize()));
5633 50 this.setCompileTimeValue(node, compileTimeValue);
5634 50 } else {
5635 // If we want to use the len builtin on a string, we need to import the string runtime module
5636
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 98 times.
99 if !this.sourceFile.isStringRT() {
5637 98 this.sourceFile.requestRuntimeModule(RuntimeModule::STRING_RT);
5638 98 }
5639 99 }
5640
5641 149 return this.builtinResult(node, QualType(SuperType::TY_LONG));
5642 }
5643
5644 3033 f<Any> TypeChecker.visitBuiltinPanicCall(ASTFctCallNode* node) {
5645 3033 assert node.fqFunctionName == BUILTIN_FCT_NAME_PANIC;
5646
5647 3033 assert node.hasArgs;
5648 3033 const ASTNode* assignExpr = getASTNode(node.argLst.args.front());
5649 3033 QualType argType = this.getArgType(node, 0l);
5650
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3033 times.
3033 if argType.is(SuperType::TY_UNRESOLVED) { return unresolvedER(); }
5651 3033 argType = argType.removeReferenceWrapper();
5652
5653 // Check if arg is of type array
5654
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3032 times.
3033 if !argType.isErrorObj() {
5655 2 return this.softErrorER(assignExpr, SemanticErrorType::EXPECTED_ERROR_TYPE, String("The panic builtin can only work with errors"));
5656 1 }
5657
5658 // The panic prints the stack trace, so the runtime that does that has to be loaded
5659
4/4
✓ Branch 0 taken 3020 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 3021 times.
✓ Branch 3 taken 11 times.
3032 if this.cliOptions.printsStackTraceOnAbort() && !this.sourceFile.isRT(RuntimeModule::STACK_TRACE_RT) {
5660 11 this.sourceFile.requestRuntimeModule(RuntimeModule::STACK_TRACE_RT);
5661 11 }
5662
5663 3032 return this.builtinResult(node, QualType(SuperType::TY_DYN));
5664 }
5665
5666 8 f<Any> TypeChecker.visitBuiltinSyscallCall(ASTFctCallNode* node) {
5667 8 assert node.fqFunctionName == BUILTIN_FCT_NAME_SYSCALL;
5668
5669 // Check if the syscall number if of type short
5670 8 ASTNode* sysCallNumberExpr = getASTNode(node.argLst.args.front());
5671 8 const QualType sysCallNumberType = this.getArgType(node, 0l);
5672
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7 times.
8 if !sysCallNumberType.is(SuperType::TY_SHORT) {
5673 2 return this.softErrorER(sysCallNumberExpr, SemanticErrorType::INVALID_SYSCALL_NUMBER_TYPE, String("Syscall number must be of type short"));
5674 1 }
5675
5676 // Check if the syscall number is out of range
5677 // According to https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/
5678 7 ASTNode* baseNode = cast<ASTNode*>(node);
5679
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if sysCallNumberExpr.hasCompileTimeValue(this.manIdx) {
5680 2 const CompileTimeValue sysCallNumberValue = sysCallNumberExpr.getCompileTimeValue(this.manIdx);
5681 2 const short sysCallNumber = sysCallNumberValue.getShortValue();
5682
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if sysCallNumber < 0s || sysCallNumber > 439s {
5683 4 return this.softErrorER(baseNode, SemanticErrorType::SYSCALL_NUMBER_OUT_OF_RANGE, String("Only syscall numbers between 0 and 439 are supported"));
5684 2 }
5685 ✗ }
5686
5687 5 return this.builtinResult(node, QualType(SuperType::TY_LONG));
5688 }
5689
5690 18 f<Any> TypeChecker.visitBuiltinIsSameCall(ASTFctCallNode* node) {
5691 18 assert node.fqFunctionName == BUILTIN_FCT_NAME_IS_SAME;
5692
5693 // Directly set compile time value here, so that compile time ifs can be evaluated.
5694 18 CompileTimeValue compileTimeValue;
5695 18 compileTimeValue.setBoolValue(true);
5696 18 const QualType firstType = this.getTemplateType(node, 0l);
5697
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 18 times.
24 for unsigned long i = 1l; i < node.templateTypeLst.dataTypes.getSize(); i++ {
5698 18 const QualType qualType = this.getTemplateType(node, i);
5699
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 if !qualType.matches(firstType, false, true, false) {
5700 12 compileTimeValue.setBoolValue(false);
5701 12 break;
5702 }
5703 6 }
5704 18 this.setCompileTimeValue(node, compileTimeValue);
5705
5706 18 return this.builtinResult(node, QualType(SuperType::TY_BOOL));
5707 }
5708
5709 3 f<Any> TypeChecker.visitBuiltinImplementsInterfaceCall(ASTFctCallNode* node) {
5710 3 assert node.fqFunctionName == BUILTIN_FCT_NAME_IMPLEMENTS_INTERFACE;
5711
5712 3 const QualType interfaceType = this.getTemplateType(node, 0l);
5713 3 const QualType structType = this.getTemplateType(node, node.templateTypeLst.dataTypes.getSize() - 1l);
5714 3 CompileTimeValue compileTimeValue;
5715
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 compileTimeValue.setBoolValue(interfaceType.is(SuperType::TY_INTERFACE) && structType.is(SuperType::TY_STRUCT) && structType.doesImplement(interfaceType, cast<ASTNode*>(node)));
5716 3 this.setCompileTimeValue(node, compileTimeValue);
5717
5718 3 return this.builtinResult(node, QualType(SuperType::TY_BOOL));
5719 }
5720
5721 /**
5722 * Parse the given string to an int with the semantics of std::stoi: Leading whitespace and trailing garbage are ignored, but
5723 * at least one digit has to be present and the value has to fit into an int.
5724 *
5725 * @param input Input string
5726 * @param output Parsed int value
5727 * @return Successfully parsed or not
5728 */
5729 5 f<bool> parseIntLikeStoi(const String& input, int& output) {
5730 5 const unsigned long length = input.getLength();
5731 5 unsigned long idx = 0l;
5732 5 char c = '\0';
5733 // Skip leading whitespace
5734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 while idx < length {
5735 5 c = input[idx];
5736
6/12
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 5 times.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
5 if c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != '\f' && c != '\v' { break; }
5737 ✗ idx++;
5738 ✗ }
5739 // Sign
5740 5 bool isNegative = false;
5741
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if idx < length {
5742 5 c = input[idx];
5743
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 if c == '+' || c == '-' {
5744 ✗ isNegative = c == '-';
5745 ✗ idx++;
5746 ✗ }
5747 5 }
5748 // Digits
5749 5 long value = 0l;
5750 5 unsigned long digitCount = 0l;
5751
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 8 times.
11 while idx < length {
5752 8 c = input[idx];
5753
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if !isDecimalDigit(c) { break; }
5754 6 value = value * 10l + cast<long>(toInt(c) - toInt('0'));
5755
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if value > 2147483648l { return false; } // Out of range
5756 6 digitCount++;
5757 6 idx++;
5758 6 }
5759
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if digitCount == 0l { return false; } // Invalid argument
5760
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if isNegative { value = -value; }
5761
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if value > 2147483647l { return false; } // Out of range
5762 3 output = cast<int>(value);
5763 3 return true;
5764 }
5765
5766 26 f<Any> TypeChecker.visitBuiltinGetBuildVarCall(ASTFctCallNode* node) {
5767 26 assert node.fqFunctionName == BUILTIN_FCT_NAME_GET_BUILD_VAR;
5768 26 ASTNode* baseNode = cast<ASTNode*>(node);
5769
5770 26 const ASTNode* requestedTypeNode = cast<ASTNode*>(node.templateTypeLst.dataTypes.front());
5771 26 const QualType requestedType = this.getTemplateType(node, 0l);
5772 26 const dyn buildVarTypes = [SuperType::TY_STRING, SuperType::TY_INT, SuperType::TY_BOOL];
5773
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 25 times.
26 if !requestedType.isOneOf(buildVarTypes, len(buildVarTypes)) {
5774 2 return this.softErrorER(requestedTypeNode, SemanticErrorType::BUILTIN_SIGNATURE_MISMATCH, String("Build var type must be string, int or bool"));
5775 1 }
5776
5777 25 ASTNode* varNameNode = getASTNode(node.argLst.args.get(0l));
5778 25 const QualType varNameType = this.getArgType(node, 0l);
5779
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 24 times.
25 if !varNameType.is(SuperType::TY_STRING) {
5780 2 return this.softErrorER(varNameNode, SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, String("Build var name must be a string"));
5781 1 }
5782
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 23 times.
24 if !varNameNode.hasCompileTimeValue(this.manIdx) {
5783 2 return this.softErrorER(varNameNode, SemanticErrorType::EXPECTED_COMPILE_TIME_VALUE, String("Var name must be known at compile time"));
5784 1 }
5785
5786 23 const bool hasDefaultValue = node.argLst.args.getSize() == 2l;
5787 23 CompileTimeValue defaultValue;
5788
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 11 times.
23 if hasDefaultValue {
5789 11 ASTNode* defaultValueNode = getASTNode(node.argLst.args.get(1l));
5790 11 const QualType defaultValueType = this.getArgType(node, 1l);
5791
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 10 times.
11 if !defaultValueType.matches(requestedType, false, true, true) {
5792 2 return this.softErrorER(defaultValueNode, SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, String("Default value type must be the same as the requested type"));
5793 1 }
5794
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 9 times.
10 if !defaultValueNode.hasCompileTimeValue(this.manIdx) {
5795 2 return this.softErrorER(defaultValueNode, SemanticErrorType::EXPECTED_COMPILE_TIME_VALUE, String("Default value must be known at compile time"));
5796 1 }
5797 9 defaultValue = defaultValueNode.getCompileTimeValue(this.manIdx);
5798 9 }
5799
5800 21 FctCallData& data = node.data.get(this.manIdx);
5801 21 const CompileTimeValue varNameValue = varNameNode.getCompileTimeValue(this.manIdx);
5802 21 const unsigned long stringValueOffset = varNameValue.getStringValueOffset();
5803 21 const String varName = String(this.resourceManager.getCompileTimeStringValue(stringValueOffset));
5804
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 4 times.
21 if this.cliOptions.buildVars.contains(varName) {
5805 17 const String buildVarValue = String(this.cliOptions.buildVars.get(varName));
5806 17 CompileTimeValue compileTimeValue;
5807
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 8 times.
17 if requestedType.is(SuperType::TY_STRING) {
5808 9 compileTimeValue.setStringValueOffset(this.resourceManager.compileTimeStringValues.getSize());
5809 9 this.resourceManager.compileTimeStringValues.pushBack(buildVarValue);
5810
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
17 } else if requestedType.is(SuperType::TY_INT) {
5811 // Invalid values are rejected
5812 5 int value = 0;
5813
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if !parseIntLikeStoi(buildVarValue, value) {
5814 4 return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, String("Error while parsing the provided value to the requested type"));
5815 2 }
5816 3 compileTimeValue.setIntValue(value);
5817
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
6 } else if requestedType.is(SuperType::TY_BOOL) {
5818 3 compileTimeValue.setBoolValue(buildVarValue == "true");
5819 6 }
5820 15 data.setCompileTimeValue(compileTimeValue);
5821 17 } else {
5822
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 if !hasDefaultValue {
5823 2 return this.softErrorER(varNameNode, SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, String("Build var with this name was not provided"));
5824 1 }
5825 3 data.setCompileTimeValue(defaultValue);
5826 3 }
5827
5828 36 return this.builtinResult(node, requestedType);
5829 21 }
5830
5831 8 f<Any> TypeChecker.visitBuiltinIsTriviallyConstructible(ASTFctCallNode* node) {
5832 8 assert node.fqFunctionName == BUILTIN_FCT_NAME_IS_TRIVIALLY_CONSTRUCTIBLE;
5833
5834 8 const QualType qualType = this.getTemplateType(node, 0l);
5835 8 CompileTimeValue compileTimeValue;
5836 8 compileTimeValue.setBoolValue(qualType.isTriviallyConstructible(cast<ASTNode*>(node)));
5837 8 this.setCompileTimeValue(node, compileTimeValue);
5838
5839 8 return this.builtinResult(node, QualType(SuperType::TY_BOOL));
5840 }
5841
5842 14 f<Any> TypeChecker.visitBuiltinIsTriviallyCopyable(ASTFctCallNode* node) {
5843 14 assert node.fqFunctionName == BUILTIN_FCT_NAME_IS_TRIVIALLY_COPYABLE;
5844
5845 14 const QualType qualType = this.getTemplateType(node, 0l);
5846 14 CompileTimeValue compileTimeValue;
5847 14 compileTimeValue.setBoolValue(qualType.isTriviallyCopyable(cast<ASTNode*>(node)));
5848 14 this.setCompileTimeValue(node, compileTimeValue);
5849
5850 14 return this.builtinResult(node, QualType(SuperType::TY_BOOL));
5851 }
5852
5853 338 f<Any> TypeChecker.visitBuiltinIsTriviallyDestructible(ASTFctCallNode* node) {
5854 338 assert node.fqFunctionName == BUILTIN_FCT_NAME_IS_TRIVIALLY_DESTRUCTIBLE;
5855
5856 338 const QualType qualType = this.getTemplateType(node, 0l);
5857 338 CompileTimeValue compileTimeValue;
5858 338 compileTimeValue.setBoolValue(qualType.isTriviallyDestructible(cast<ASTNode*>(node)));
5859 338 this.setCompileTimeValue(node, compileTimeValue);
5860
5861 338 return this.builtinResult(node, QualType(SuperType::TY_BOOL));
5862 }
5863
5864 8 f<Any> TypeChecker.visitBuiltinIsHeap(ASTFctCallNode* node) {
5865 8 assert node.fqFunctionName == BUILTIN_FCT_NAME_IS_HEAP;
5866
5867 8 const QualType qualType = this.getTemplateType(node, 0l);
5868 8 CompileTimeValue compileTimeValue;
5869 8 compileTimeValue.setBoolValue(qualType.isHeap());
5870 8 this.setCompileTimeValue(node, compileTimeValue);
5871
5872 8 return this.builtinResult(node, QualType(SuperType::TY_BOOL));
5873 }
5874
5875 /**
5876 * Check the constructor of a struct for __new and __placement_new. Shared part of visitBuiltinNewCall and
5877 * visitBuiltinPlacementNewCall.
5878 *
5879 * @param node Builtin call node
5880 * @param templateType Struct type to construct
5881 * @param ctorArgs Constructor arguments
5882 * @return Whether the check was successful. If not, a soft error was recorded
5883 */
5884 534 f<bool> TypeChecker.checkBuiltinNewCtor(ASTFctCallNode* node, const QualType& templateType, const ArgList& ctorArgs) {
5885 534 ASTNode* baseNode = cast<ASTNode*>(node);
5886 534 FctCallData& data = node.data.get(this.manIdx);
5887 534 Scope* bodyScope = templateType.getBodyScope();
5888 534 const QualTypeList noTemplateTypeHints;
5889 534 Function* ctor = matchFunction(bodyScope, String(CTOR_FUNCTION_NAME), templateType, ctorArgs, noTemplateTypeHints, false, baseNode);
5890
2/2
✓ Branch 0 taken 508 times.
✓ Branch 1 taken 26 times.
534 if ctor == nil<Function*> {
5891 26 const bool copyCtorCall = this.isCopyCtorCall(ctorArgs, templateType);
5892
4/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 24 times.
26 if !copyCtorCall && !templateType.isTriviallyConstructible(baseNode) {
5893 2 this.softError(baseNode, SemanticErrorType::NO_MATCHING_CTOR_FOUND, "No matching constructor found for type '" + templateType.getName(false) + "'");
5894 2 return false;
5895 2 }
5896
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
24 if copyCtorCall && !templateType.isTriviallyCopyable(baseNode) {
5897 ✗ this.softError(baseNode, SemanticErrorType::NO_MATCHING_CTOR_FOUND, "No matching copy constructor found for type '" + templateType.getName(false) + "'");
5898 ✗ return false;
5899 ✗ }
5900 24 }
5901
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 508 times.
532 if ctor != nil<Function*> {
5902 508 this.matchCopyCtorsForOwnedParams(ctor, baseNode);
5903 508 }
5904 532 data.callee = ctor;
5905 532 return true;
5906 534 }
5907
5908 241 f<Any> TypeChecker.visitBuiltinNewCall(ASTFctCallNode* node) {
5909 241 assert node.fqFunctionName == BUILTIN_FCT_NAME_NEW;
5910 241 ASTNode* baseNode = cast<ASTNode*>(node);
5911
5912 241 FctCallData& data = node.data.get(this.manIdx);
5913 241 const QualType templateType = this.getTemplateType(node, 0l);
5914
5915
2/2
✓ Branch 0 taken 231 times.
✓ Branch 1 taken 10 times.
241 if templateType.is(SuperType::TY_STRUCT) {
5916
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 230 times.
231 if !this.checkBuiltinNewCtor(node, templateType, data.args) { return unresolvedER(); }
5917
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 9 times.
240 } else if data.args.getSize() > 1l {
5918 1 const String msg = "Expected no or 1 argument for __new on primitive type, got " + toString(data.args.getSize());
5919 2 return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, msg);
5920
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
10 } else if data.args.getSize() == 1l {
5921 4 const Pair<QualType, bool>& arg = data.args.front();
5922 4 QualType argType = arg.getFirst();
5923 4 argType = argType.removeReferenceWrapper();
5924 4 argType = argType.toNonConst();
5925
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if !templateType.matches(argType, false, false, true) {
5926 ✗ const String msg = "Argument type '" + argType.getName() + "' does not match template type '" + templateType.getName() + "'";
5927 ✗ return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, msg);
5928 ✗ }
5929 9 }
5930
5931 239 QualType returnType = templateType.toPtr(baseNode);
5932 239 returnType.makeHeap();
5933 239 return this.builtinResult(node, returnType);
5934 }
5935
5936 530 f<Any> TypeChecker.visitBuiltinPlacementNewCall(ASTFctCallNode* node) {
5937 530 assert node.fqFunctionName == BUILTIN_FCT_NAME_PLACEMENT_NEW;
5938 530 ASTNode* baseNode = cast<ASTNode*>(node);
5939
5940 530 FctCallData& data = node.data.get(this.manIdx);
5941 530 const QualType templateType = this.getTemplateType(node, 0l);
5942
5943 // Validate first arg is a byte pointer or a heap pointer to the template type
5944 530 const Pair<QualType, bool>& ptrArg = data.args.front();
5945 530 QualType ptrArgType = ptrArg.getFirst();
5946 530 ptrArgType = ptrArgType.removeReferenceWrapper();
5947 530 const bool isBytePtr = ptrArgType.isPtrTo(SuperType::TY_BYTE);
5948
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 530 times.
530 const bool isStructPtr = ptrArgType.isPtr() && ptrArgType.getContained() == templateType;
5949
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 529 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 529 times.
530 if !isBytePtr && !isStructPtr {
5950 2 return this.softErrorER(getASTNode(node.argLst.args.front()), SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, String("__placement_new expects a 'byte*' or 'T*' as its first argument"));
5951 1 }
5952
5953
2/2
✓ Branch 0 taken 303 times.
✓ Branch 1 taken 226 times.
529 if templateType.is(SuperType::TY_STRUCT) {
5954 303 ArgList ctorArgs;
5955
2/2
✓ Branch 0 taken 305 times.
✓ Branch 1 taken 303 times.
608 for unsigned long i = 1l; i < data.args.getSize(); i++ {
5956 305 ctorArgs.pushBack(data.args.get(i));
5957 305 }
5958
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 302 times.
303 if !this.checkBuiltinNewCtor(node, templateType, ctorArgs) { return unresolvedER(); }
5959
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 225 times.
529 } else if data.args.getSize() > 2l {
5960 1 const String msg = "Expected 1 or 2 arguments for __placement_new, got " + toString(data.args.getSize());
5961 2 return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, msg);
5962
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
226 } else if data.args.getSize() == 2l {
5963 225 const Pair<QualType, bool>& arg = data.args.back();
5964 225 QualType argType = arg.getFirst();
5965 225 argType = argType.removeReferenceWrapper();
5966 225 argType = argType.toNonConst();
5967
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
225 if !templateType.matches(argType, false, false, true) {
5968 ✗ const String msg = "Argument type '" + argType.getName() + "' does not match template type '" + templateType.getName() + "'";
5969 ✗ return this.softErrorER(baseNode, SemanticErrorType::BUILTIN_ARG_TYPE_MISMATCH, msg);
5970 ✗ }
5971 225 }
5972
5973 527 return this.builtinResult(node, templateType.toPtr(baseNode));
5974 }
5975
5976 3 f<Any> TypeChecker.visitBuiltinSourceFileCall(ASTFctCallNode* node) {
5977 3 assert node.fqFunctionName == BUILTIN_FCT_NAME_SOURCE_FILE;
5978
5979 3 CompileTimeValue compileTimeValue;
5980 3 compileTimeValue.setStringValueOffset(this.resourceManager.compileTimeStringValues.getSize());
5981 3 this.resourceManager.compileTimeStringValues.pushBack(node.codeLoc.toPrettyFilePath());
5982 3 this.setCompileTimeValue(node, compileTimeValue);
5983
5984 6 return this.builtinResult(node, QualType(SuperType::TY_STRING));
5985 3 }
5986
5987 6 f<Any> TypeChecker.visitBuiltinSourceLineCall(ASTFctCallNode* node) {
5988 6 assert node.fqFunctionName == BUILTIN_FCT_NAME_SOURCE_LINE;
5989
5990 6 CompileTimeValue compileTimeValue;
5991 6 compileTimeValue.setLongValue(cast<long>(node.codeLoc.line));
5992 6 this.setCompileTimeValue(node, compileTimeValue);
5993
5994 6 return this.builtinResult(node, QualType(SuperType::TY_LONG));
5995 }
5996
5997 6 f<Any> TypeChecker.visitBuiltinSourceColumnCall(ASTFctCallNode* node) {
5998 6 assert node.fqFunctionName == BUILTIN_FCT_NAME_SOURCE_COLUMN;
5999
6000 6 CompileTimeValue compileTimeValue;
6001 6 compileTimeValue.setLongValue(cast<long>(node.codeLoc.col));
6002 6 this.setCompileTimeValue(node, compileTimeValue);
6003
6004 6 return this.builtinResult(node, QualType(SuperType::TY_LONG));
6005 }
6006
6007 /**
6008 * Resolves to a reference to the calling thread's ErrorTraceBuffer (std/runtime/error_trace_rt.spice). Only ever
6009 * called from within that same file, where the struct is always in scope by name.
6010 */
6011 996 f<Any> TypeChecker.visitBuiltinErrTraceBufferCall(ASTFctCallNode* node) {
6012 996 assert node.fqFunctionName == BUILTIN_FCT_NAME_ERR_TRACE_BUFFER;
6013
6014 996 const SymbolTableEntry* structEntry = this.rootScope.lookupStrict(String("ErrorTraceBuffer"));
6015 996 assert structEntry != nil<SymbolTableEntry*>;
6016 996 const QualType structType = structEntry.getQualType();
6017 996 const QualType bufferType = structType.toRef(cast<ASTNode*>(node));
6018
6019 1992 return this.builtinResult(node, bufferType);
6020 996 }
6021
6022 /**
6023 * Resolves to a pointer to the process's stderr stream. Spice source has no syntax for declaring an external
6024 * *variable* (only functions/procedures via 'ext'), so this exists as a builtin that reaches the real libc
6025 * symbol directly in IR generation, the same way panic()'s own codegen already did before this builtin existed.
6026 */
6027 1029 f<Any> TypeChecker.visitBuiltinStdErrCall(ASTFctCallNode* node) {
6028 1029 assert node.fqFunctionName == BUILTIN_FCT_NAME_STDERR;
6029
6030 1029 const QualType byteType = QualType(SuperType::TY_BYTE);
6031 1029 return this.builtinResult(node, byteType.toPtr(cast<ASTNode*>(node)));
6032 }
6033
6034 /**
6035 * Resolves to the frame address (saved frame-pointer slot) of the calling function's own stack frame, i.e. the
6036 * same value as C's `__builtin_frame_address(0)`. IRGenerator keeps frame pointers for every function it emits, so
6037 * the value is always a real frame address - but see the IR generator side of this builtin for why that alone does
6038 * not make the surrounding frame chain walkable on every target.
6039 */
6040 ✗ f<Any> TypeChecker.visitBuiltinFrameAddressCall(ASTFctCallNode* node) {
6041 ✗ assert node.fqFunctionName == BUILTIN_FCT_NAME_FRAME_ADDRESS;
6042
6043 ✗ const QualType byteType = QualType(SuperType::TY_BYTE);
6044 ✗ return this.builtinResult(node, byteType.toPtr(cast<ASTNode*>(node)));
6045 }
6046
6047 // ===================================================== Implicit functions ======================================================
6048
6049 /**
6050 * Check whether the given struct and all of its by-value struct fields are manifested, transitively, and have already
6051 * decided their own implicit default members. While a circular import is still being prepared, a by-value struct field
6052 * may (transitively) reference a struct that is not manifested yet; generating implicit special members for such a
6053 * struct recurses through isTriviallyConstructible and would dereference a null struct lookup. A genuine infinite-size
6054 * containment cycle among these structs is reported separately by the infinite-size check during struct preparation,
6055 * so a containment cycle here is treated as manifested.
6056 *
6057 * A field struct can also be manifested (the Struct object exists) but not decided yet: a generic struct like
6058 * Result<T> can be substantiated for T = Url while checking a function body during Url's own file's one-shot sweep
6059 * (TypeChecker.visitEntry), before that sweep has reached Url's struct declaration and decided that Url needs a
6060 * dtor/copy ctor. Deciding Result<Url> right then would see Url as trivial (no dtor found yet) and permanently skip
6061 * generating Result<Url>'s dtor, since createImplicitDefaultMembers only ever runs once per manifestation. Requiring
6062 * the field struct to have decided its own members first defers Result<Url> the same way as the circular-import case
6063 * above, to be picked up again by the fallback sweep in visitStructDefCheck once Url has decided.
6064 */
6065 7382 f<bool> structFullyManifested(const Struct* spiceStruct, const ASTNode* node, Vector<const Scope*>& visited) {
6066
2/2
✓ Branch 0 taken 7230 times.
✓ Branch 1 taken 17704 times.
24934 foreach const QualType& fieldType : spiceStruct.fieldTypes {
6067
2/2
✓ Branch 0 taken 15448 times.
✓ Branch 1 taken 2256 times.
17704 if !fieldType.is(SuperType::TY_STRUCT) {
6068 15448 continue;
6069 }
6070 2256 const Struct* fieldStruct = fieldType.getStruct(node);
6071
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2255 times.
✓ Branch 2 taken 2104 times.
✓ Branch 3 taken 152 times.
2256 if fieldStruct == nil<const Struct*> || !fieldStruct.structBase.implicitDefaultMembersDecided {
6072 152 return false;
6073 }
6074 // Recurse into each field struct once. A containment cycle (already visited) is fine here - it is reported
6075 // separately as an infinite-size error. Leaf structs never touch the list, so the common case stays allocation-free.
6076 2104 bool alreadyVisited = false;
6077
2/2
✓ Branch 0 taken 2247 times.
✓ Branch 1 taken 2104 times.
4351 foreach const Scope* visitedScope : visited {
6078
2/2
✓ Branch 0 taken 2019 times.
✓ Branch 1 taken 228 times.
2247 if visitedScope == fieldStruct.structBase.scope {
6079 228 alreadyVisited = true;
6080 228 }
6081 2247 }
6082
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 1876 times.
2104 if !alreadyVisited {
6083 1876 visited.pushBack(fieldStruct.structBase.scope);
6084
1/2
✓ Branch 0 taken 1876 times.
✗ Branch 1 not taken.
1876 if !structFullyManifested(fieldStruct, node, visited) {
6085 ✗ return false;
6086 }
6087 1876 }
6088 2104 }
6089 7230 return true;
6090 }
6091
6092 /**
6093 * Decide which compiler-generated default members (ctor, copy ctor, move ctor, dtor) the given struct manifestation
6094 * needs and create them.
6095 *
6096 * This decision depends on the concrete field types, so it has to be taken once per manifestation and not once per
6097 * struct declaration: whether e.g. HashEntry<K, V> needs a dtor is unanswerable while K and V are still generic, but
6098 * well-defined for HashEntry<int, String>. It is therefore taken as soon as a manifestation comes into existence -
6099 * either by the one-shot sweep over the manifestations that already exist when a source file is prepared
6100 * (TypeChecker.visitEntry), or, for manifestations that are substantiated later on, right at the point where
6101 * matchStruct creates them. Deciding any later would be too late for all the reactive lookups that ask a
6102 * struct for its default members (matchFunction/lookupFunction, doScopeCleanup, ...), since a function body is
6103 * type-checked exactly once and would not pick up a dtor that appears afterward.
6104 *
6105 * The default move ctor is decided about only when the caller asks for it. A manifestation that is substantiated after
6106 * its file was prepared does not get one, which keeps it at the behavior it had before this decision was taken per
6107 * manifestation at all. Synthesizing one would require the move ctor of each struct-typed field to be resolvable for
6108 * the concrete manifestation, which the raw manifestation scan behind it (findMoveCtor) cannot do for
6109 * a generic field type - it returns the generic preset that the manifestation scope inherited. Without a move ctor,
6110 * moving such a struct falls back to copying it, which is correct, just less efficient.
6111 *
6112 * @param spiceStruct Struct manifestation to decide the default members for
6113 * @param node AST node for error messages
6114 * @param withMoveCtor Also decide about the default move ctor
6115 */
6116 13433 public p createImplicitDefaultMembers(Struct& spiceStruct, const ASTNode* node, bool withMoveCtor = true) {
6117 // Take the decision only once per manifestation
6118
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3239 times.
✓ Branch 2 taken 8078 times.
✓ Branch 3 taken 2116 times.
13433 if spiceStruct.structBase.implicitDefaultMembersDecided {
6119 8078 return;
6120 }
6121
6122 5355 SourceFile* structSourceFile = spiceStruct.structBase.scope.sourceFile;
6123 5355 GlobalResourceManager& resourceManager = structSourceFile.resourceManager;
6124
6125 // Skip while a by-value struct field is (transitively) not manifested yet, or has not decided its own implicit
6126 // default members yet (circular import still in progress, or a sibling struct's one-shot sweep has not reached it
6127 // yet - see structFullyManifested). Relying solely on the fallback sweep in visitStructDefCheck to pick this back up
6128 // can be too late: that sweep only runs during the type-checker POST pass, by which point some other function body
6129 // elsewhere may already have looked up (and missed) this manifestation's dtor for good, since a function body is
6130 // type-checked exactly once. Queue the manifestation instead, so it gets retried the moment something else's
6131 // decision completes (see drainPendingImplicitDefaultMemberDecisions below) - typically still within the same PRE
6132 // pass that first substantiated it.
6133 5355 Vector<const Scope*> visitedScopes;
6134
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3238 times.
✓ Branch 2 taken 35 times.
✓ Branch 3 taken 2081 times.
5355 if !structFullyManifested(&spiceStruct, node, visitedScopes) {
6135 36 Vector<Pair<Struct*, bool>>& pending = resourceManager.pendingStructDefaultMemberDecisions;
6136 36 bool found = false;
6137
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 35 times.
43 foreach Pair<Struct*, bool>& entry : pending {
6138
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 if entry.getFirst() == &spiceStruct {
6139 // Upgrade to deciding the move ctor too if any deferred caller asked for it
6140 ✗ bool& pendingWithMoveCtor = entry.getSecond();
6141
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ pendingWithMoveCtor = pendingWithMoveCtor || withMoveCtor;
6142 ✗ found = true;
6143 ✗ }
6144 7 }
6145
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 35 times.
36 if !found {
6146 36 pending.pushBack(Pair<Struct*, bool>(&spiceStruct, withMoveCtor));
6147 36 }
6148 36 return;
6149 }
6150
6151 5319 decideDefaultMembers(spiceStruct, withMoveCtor);
6152
6153 // This struct just became decided - drain the pending list so it can pick up anything that was blocked on it, directly or
6154 // transitively (a chain of Result<Outer<Inner>>-shaped dependencies).
6155 5319 drainPendingImplicitDefaultMemberDecisions(resourceManager);
6156 5355 }
6157
6158 /**
6159 * Set a struct manifestation's default members as decided and create whichever of them (ctor, copy ctor, move ctor,
6160 * dtor) it actually needs. Only called once structFullyManifested has confirmed the struct is ready.
6161 *
6162 * @param spiceStruct Struct manifestation to decide the default members for
6163 * @param withMoveCtor Also decide about the default move ctor
6164 */
6165 5354 p decideDefaultMembers(Struct& spiceStruct, bool withMoveCtor) {
6166 5354 spiceStruct.structBase.implicitDefaultMembersDecided = true;
6167
6168 5354 createDefaultCtorIfRequired(spiceStruct, spiceStruct.structBase.scope);
6169 5354 createDefaultCopyCtorIfRequired(spiceStruct, spiceStruct.structBase.scope);
6170
2/2
✓ Branch 0 taken 2116 times.
✓ Branch 1 taken 3238 times.
5354 if withMoveCtor {
6171 3238 createDefaultMoveCtorIfRequired(spiceStruct, spiceStruct.structBase.scope);
6172 3238 }
6173 5354 createDefaultDtorIfRequired(spiceStruct, spiceStruct.structBase.scope);
6174 5354 inheritDefaultMethods(spiceStruct);
6175 5354 }
6176
6177 /**
6178 * Let a struct manifestation inherit the default methods of its interfaces for all interface methods, that the struct does
6179 * not implement itself. This happens as early as possible, so that calls to inherited methods can be type-checked before
6180 * the struct itself is checked to implement all interface methods. Default methods of interfaces in a circular import
6181 * might not be prepared yet at this point. Those are inherited when checking the struct (see visitStructDefCheck).
6182 *
6183 * @param spiceStruct Struct manifestation
6184 */
6185 5354 p inheritDefaultMethods(Struct& spiceStruct) {
6186
2/2
✓ Branch 0 taken 1124 times.
✓ Branch 1 taken 4230 times.
5354 if !spiceStruct.structBase.isFullySubstantiated() {
6187 1124 return;
6188 }
6189
6190 4230 const QualType structType = spiceStruct.structBase.entry.getQualType();
6191
2/2
✓ Branch 0 taken 733 times.
✓ Branch 1 taken 4230 times.
4963 foreach const QualType& interfaceType : spiceStruct.interfaceTypes {
6192 // Generic interfaces cannot have default methods
6193 733 const QualTypeList interfaceTemplateTypes = interfaceType.getTemplateTypes();
6194
2/2
✓ Branch 0 taken 685 times.
✓ Branch 1 taken 48 times.
733 if !interfaceTemplateTypes.isEmpty() {
6195 685 continue;
6196 }
6197 48 const Interface* spiceInterface = interfaceType.getInterface(spiceStruct.structBase.declNode);
6198 48 assert spiceInterface != nil<const Interface*>;
6199
6200
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 48 times.
114 foreach const Function* method : spiceInterface.methods {
6201 // Check if the struct implements the method itself
6202 66 const QualTypeList paramTypes = method.getParamTypes();
6203 66 ArgList args;
6204 66 args.reserve(paramTypes.getSize());
6205
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 66 times.
85 foreach const QualType& paramType : paramTypes {
6206 19 args.pushBack(Pair<QualType, bool>(paramType, false));
6207 19 }
6208 66 const QualTypeList noTemplateTypeHints;
6209
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 15 times.
66 if matchFunction(spiceStruct.structBase.scope, method.name, structType, args, noTemplateTypeHints, true, spiceStruct.structBase.declNode) != nil<Function*> {
6210 51 continue;
6211 }
6212
6213 15 const Function* _ = inheritDefaultMethod(spiceStruct.structBase.scope, structType, spiceInterface, method.name, paramTypes, method.returnType);
6214 66 }
6215 733 }
6216 4230 }
6217
6218 /**
6219 * Let a struct inherit the default method of an interface for the given method signature, if the interface provides one.
6220 * The inherited method is inserted into the struct scope, but has no body on its own. It refers to the default method,
6221 * which is emitted only once and gets called with a pointer to the interface part of the struct.
6222 *
6223 * @param structScope Scope of the struct manifestation
6224 * @param structType Struct type, which implements the interface
6225 * @param spiceInterface Interface, which might provide the default method
6226 * @param methodName Name of the requested method
6227 * @param paramTypes Parameter types of the requested method
6228 * @param returnType Return type of the requested method
6229 * @return Inherited method or nil if the interface has no default method for the signature
6230 */
6231 20 f<Function*> inheritDefaultMethod(Scope* structScope, const QualType& structType, const Interface* spiceInterface, const String& methodName, const QualTypeList& paramTypes, const QualType& returnType) {
6232 // Search for a default method with the exact signature
6233 20 Function* defaultMethod = lookupDefaultMethod(spiceInterface.structBase.scope, methodName, paramTypes, returnType);
6234
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 8 times.
20 if defaultMethod == nil<Function*> {
6235 8 return nil<Function*>;
6236 }
6237
6238 // If the struct declares a method with this name, it most likely tries to implement the interface method, but with a
6239 // signature that does not match. Do not silently inherit the default method in this case, but report the missing method
6240
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
12 if hasUserMethodWithName(structScope, methodName) {
6241 2 return nil<Function*>;
6242 }
6243
6244 // Create the inherited method in the struct scope
6245 10 Function inheritedMethod = *defaultMethod;
6246 10 inheritedMethod.thisType = structType;
6247 10 inheritedMethod.bodyScope = nil<Scope*>;
6248 10 inheritedMethod.isInterfaceDefaultMethod = false;
6249 10 inheritedMethod.defaultMethod = defaultMethod;
6250 10 inheritedMethod.alreadyTypeChecked = true; // The body is type-checked as part of the default method
6251 10 inheritedMethod.used = false;
6252
6253 // The default method is referenced by the vtable of the struct, so it has to be emitted
6254 10 defaultMethod.used = true;
6255 10 defaultMethod.entry.used = true;
6256
6257 20 return insertFunction(structScope, inheritedMethod);
6258 10 }
6259
6260 /**
6261 * Drain every struct manifestation that createImplicitDefaultMembers previously had to defer because a by-value
6262 * field struct had not decided its own default members yet. Called whenever a struct's decision completes, since
6263 * that can unblock others - directly (a struct that was waiting on exactly this one) or transitively (a chain of
6264 * Result<Outer<Inner>>-shaped dependencies).
6265 *
6266 * This calls decideDefaultMembers directly rather than createImplicitDefaultMembers, so draining one entry can never
6267 * recurse back into this function - no reentrancy guard is needed, the loop below just keeps going until a full pass
6268 * makes no further progress.
6269 *
6270 * @param resourceManager Resource manager that owns the pending list for this compilation
6271 */
6272 5319 p drainPendingImplicitDefaultMemberDecisions(GlobalResourceManager& resourceManager) {
6273 5319 bool progressed = true;
6274
2/2
✓ Branch 0 taken 5353 times.
✓ Branch 1 taken 5319 times.
10672 while progressed {
6275 5353 progressed = false;
6276 // Snapshot before iterating: deciding one pending manifestation can append newly-deferred entries to the very
6277 // list being iterated (a struct that unblocks can itself be a field of another, still-blocked struct).
6278 5353 const Vector<Pair<Struct*, bool>> snapshot = resourceManager.pendingStructDefaultMemberDecisions;
6279
2/2
✓ Branch 0 taken 186 times.
✓ Branch 1 taken 5353 times.
5539 foreach const Pair<Struct*, bool>& pendingEntry : snapshot {
6280 186 Struct* pendingStruct = pendingEntry.getFirst();
6281
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 151 times.
186 if pendingStruct.structBase.implicitDefaultMembersDecided {
6282 35 continue; // Already resolved by an earlier entry in this same pass
6283 }
6284
6285 151 Vector<const Scope*> visitedScopes;
6286
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 35 times.
151 if !structFullyManifested(pendingStruct, pendingStruct.structBase.declNode, visitedScopes) {
6287 116 continue; // Still blocked on something else; leave it queued
6288 }
6289
6290 35 decideDefaultMembers(*pendingStruct, pendingEntry.getSecond());
6291 35 progressed = true;
6292 151 }
6293 5353 }
6294
6295 // Drop everything that got resolved. Whatever remains is still genuinely blocked (e.g. a circular import still in
6296 // progress) and stays queued to be retried again the next time some other struct's decision completes.
6297 5319 Vector<Pair<Struct*, bool>> stillPending;
6298
2/2
✓ Branch 0 taken 145 times.
✓ Branch 1 taken 5319 times.
5464 foreach const Pair<Struct*, bool>& pendingEntry : resourceManager.pendingStructDefaultMemberDecisions {
6299 145 const Struct* pendingStruct = pendingEntry.getFirst();
6300
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 110 times.
145 if !pendingStruct.structBase.implicitDefaultMembersDecided {
6301 110 stillPending.pushBack(pendingEntry);
6302 110 }
6303 145 }
6304 5319 resourceManager.pendingStructDefaultMemberDecisions = stillPending;
6305 5319 }
6306
6307 /**
6308 * Create a default struct method
6309 * Checks if the given struct scope already has a user-defined constructor and creates a default one if not.
6310 *
6311 * @param spiceStruct Struct instance
6312 * @param entryName Name of the symbol table entry
6313 * @param name Name of the method to create
6314 * @param params Parameter types of the method
6315 */
6316 1731 p createDefaultStructMethod(const Struct& spiceStruct, const String& entryName, const String& name, const ParamList& params) {
6317 1731 Scope* structScope = spiceStruct.structBase.scope;
6318 1731 SourceFile* sourceFile = structScope.sourceFile;
6319 1731 ASTNode* node = spiceStruct.structBase.declNode;
6320 1731 const SymbolTableEntry* structEntry = spiceStruct.structBase.entry;
6321 1731 const QualType structType = structEntry.getQualType();
6322 1731 const String fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + name;
6323
6324 // Procedure type
6325 1731 QualType procedureType = QualType(SuperType::TY_PROCEDURE);
6326 1731 procedureType.makePublic(); // Always public
6327
6328 // Insert symbol for function into the symbol table
6329 1731 SymbolTableEntry* procEntry = structScope.insert(entryName, structEntry.declNode);
6330 1731 procEntry.updateType(procedureType, true);
6331
6332 // Add to external name registry. Only the generic preset does this: the registry is keyed by the plain struct name,
6333 // so an entry added for one manifestation would be indistinguishable from a user-defined member and would wrongly
6334 // suppress the default member of every sibling manifestation (e.g. HashEntry<int, String> vs. HashEntry<int, int>).
6335 // Manifestation-level default members are only ever looked up through their struct scope anyway.
6336
2/2
✓ Branch 0 taken 537 times.
✓ Branch 1 taken 1194 times.
1731 if !spiceStruct.structBase.isGenericSubstantiation() {
6337 1194 sourceFile.addNameRegistryEntry(fqFctName, cast<unsigned long>(SuperType::TY_PROCEDURE), procEntry, structScope, true, nil<SymbolTableEntry*>);
6338 1194 }
6339
6340 // Create the default method
6341 1731 const Vector<GenericType> templateTypes = spiceStruct.structBase.templateTypes;
6342 1731 const QualType returnType = QualType(SuperType::TY_DYN);
6343 1731 Function defaultMethod = Function(name, procEntry, structType, returnType, params, templateTypes, structEntry.declNode);
6344 1731 defaultMethod.implicitDefault = true;
6345
6346 // Fill type mapping for the case, that the template type list contains non-generic types. Only struct, interface
6347 // and enum types carry a sub type usable as a key here; a non-generic primitive template argument (e.g. the 'int'
6348 // in BlockAllocator<int>) has no sub type, so getSubType() would assert on it - skip those.
6349 1731 const dyn subTypeOptions = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_ENUM];
6350
2/2
✓ Branch 0 taken 1731 times.
✓ Branch 1 taken 1149 times.
2880 foreach const GenericType& templateType : templateTypes {
6351 1149 const QualType& templateQualType = templateType.qualType;
6352
4/4
✓ Branch 0 taken 544 times.
✓ Branch 1 taken 605 times.
✓ Branch 2 taken 948 times.
✓ Branch 3 taken 201 times.
1149 if !templateQualType.is(SuperType::TY_GENERIC) && templateQualType.isOneOf(subTypeOptions, len(subTypeOptions)) {
6353 201 defaultMethod.typeMapping.upsert(templateQualType.getSubType(), templateQualType);
6354 201 }
6355 1149 }
6356
6357 // Create function scope
6358 1731 Scope* procScope = structScope.createChildScope(defaultMethod.getScopeName(), ScopeType::FUNC_PROC_BODY, &node.codeLoc);
6359 1731 defaultMethod.bodyScope = procScope;
6360
6361 // Create 'this' symbol in the function scope
6362 1731 SymbolTableEntry* thisEntry = procScope.insert(String(THIS_VARIABLE_NAME), node);
6363 1731 thisEntry.updateType(structType.toPtr(node), true);
6364 1731 thisEntry.used = true; // Always set to used to not print warnings for non-existing code
6365
6366 // Hand it off to the function manager to register the function
6367 1731 ASTNode* structDeclNode = structEntry.declNode;
6368 1731 insertFunction(structScope, defaultMethod, structDeclNode.getFctManifestations(name));
6369
6370 // The body of a default member is not prepared here, but in visitStructDefCheck, which runs over all manifestations
6371 // of the struct. For a manifestation that was substantiated after its file was already checked, that run is long
6372 // over, so ask for another one - otherwise the member would end up without a body preamble, and the IR generator
6373 // would e.g. not find the copy ctor of a field it is supposed to copy. During the prepare stage the flag is set
6374 // anyway, so this is a no-op there.
6375 1731 sourceFile.reVisitRequested = true;
6376 1731 }
6377
6378 /**
6379 * Checks if the given struct scope already has a user-defined constructor and creates a default one if not.
6380 *
6381 * For generating a default ctor, the following conditions need to be met:
6382 * - No user-defined constructors (incl. copy/move ctors)
6383 *
6384 * @param spiceStruct Struct instance
6385 * @param structScope Scope of the struct
6386 */
6387 5354 p createDefaultCtorIfRequired(const Struct& spiceStruct, Scope* structScope) {
6388 5354 const ASTStructDefNode* node = downcast<ASTStructDefNode>(spiceStruct.structBase.declNode);
6389 5354 ASTNode* baseNode = cast<ASTNode*>(node);
6390 5354 assert structScope != nil<Scope*> && structScope.scopeType == ScopeType::STRUCT;
6391 5354 SourceFile* sourceFile = structScope.sourceFile;
6392
6393 // Abort if the struct already has a constructor. The name registry is the authoritative source here: it is filled by
6394 // the symbol table builder, so it already knows about every user-defined ctor, even one that is declared further down
6395 // in the file and therefore has not been registered in the struct scope yet. It also covers a ctor that the generic
6396 // preset received, which every manifestation inherits through its copied scope. The scope scan on top of it only
6397 // guards against creating a second ctor into a scope that visibly has one already.
6398 // A registry entry that stems from a default ctor of the generic preset does not count for a manifestation, that has no
6399 // ctor in its scope: Such a manifestation was substantiated before the preset got its default ctors, so it could not
6400 // inherit them through its copied scope and has to create its own. The registry entry itself cannot tell this apart,
6401 // since all ctors share the registry name, and a default copy or move ctor of the preset replaces the entry of a
6402 // user-defined ctor. Ask the scope of the preset instead, which knows all user-defined ctors at this point.
6403 5354 const SymbolTableEntry* structEntry = spiceStruct.structBase.entry;
6404 5354 const QualType structType = structEntry.getQualType();
6405 5354 const String fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + CTOR_FUNCTION_NAME;
6406 5354 const NameRegistryEntry* registryEntry = sourceFile.getNameRegistryEntry(fqFctName);
6407
2/2
✓ Branch 0 taken 3955 times.
✓ Branch 1 taken 1399 times.
5354 if hasAnyCtor(structScope) {
6408 3955 return;
6409 }
6410
2/2
✓ Branch 0 taken 1396 times.
✓ Branch 1 taken 3 times.
1399 if registryEntry != nil<const NameRegistryEntry*> {
6411
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 const bool hasOnlyPresetDefaultCtors = spiceStruct.structBase.isGenericSubstantiation() && !hasUserMethodWithName(spiceStruct.structBase.genericPreset.scope, String(CTOR_FUNCTION_NAME));
6412
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 if !hasOnlyPresetDefaultCtors {
6413 1 return;
6414 }
6415 2 }
6416
6417 // Check if we have fields, that require us to do anything in the ctor
6418 1398 const unsigned long fieldCount = structScope.getFieldCount();
6419 1398 bool hasFieldsWithDefaultValue = false;
6420 1398 bool hasFieldsToConstruct = false;
6421
2/2
✓ Branch 0 taken 3520 times.
✓ Branch 1 taken 1371 times.
4891 for unsigned long i = 0l; i < fieldCount; i++ {
6422 3520 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(i));
6423 3520 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.declNode != nil<ASTNode*>;
6424
6425 3520 QualType fieldType = fieldSymbol.getQualType();
6426
4/4
✓ Branch 0 taken 3334 times.
✓ Branch 1 taken 186 times.
✓ Branch 2 taken 3505 times.
✓ Branch 3 taken 15 times.
3520 if fieldType.hasAnyGenericParts() && !spiceStruct.structBase.typeMapping.isEmpty() {
6427 15 substantiateTypeWithTypeMapping(fieldType, spiceStruct.structBase.typeMapping, fieldSymbol.declNode);
6428 15 }
6429
6430 // Abort if we have a field, that is a reference
6431
2/2
✓ Branch 0 taken 3504 times.
✓ Branch 1 taken 16 times.
3520 if fieldType.isRef() {
6432 16 return;
6433 }
6434
6435 3504 ASTNode* fieldDeclNode = fieldSymbol.declNode;
6436
2/2
✓ Branch 0 taken 3443 times.
✓ Branch 1 taken 61 times.
3504 if fieldDeclNode.kind == ASTNodeKind::FIELD {
6437 3443 const ASTFieldNode* fieldNode = downcast<ASTFieldNode>(fieldDeclNode);
6438
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 3331 times.
3443 hasFieldsWithDefaultValue = hasFieldsWithDefaultValue || (fieldNode.defaultValue != nil<IVisitable*>);
6439 3443 } else {
6440 61 assert fieldDeclNode.kind == ASTNodeKind::DATA_TYPE;
6441 61 }
6442
6443
2/2
✓ Branch 0 taken 3325 times.
✓ Branch 1 taken 179 times.
3504 if fieldType.is(SuperType::TY_STRUCT) {
6444 179 Scope* bodyScope = fieldType.getBodyScope();
6445 // Check if we are required to call a ctor
6446 179 const bool isCtorCallRequired = !fieldType.isTriviallyConstructible(baseNode);
6447 // While the outer struct is still a generic preset, a field type that is itself generic (e.g. Inner<T, bool>,
6448 // the internal field of Outer<T>) cannot be matched to a concrete ctor yet. Since a default ctor takes no
6449 // arguments, matching it against the still-generic 'this' type would partially substantiate a bogus
6450 // manifestation (e.g. Inner<K, bool>, with K left generic) that is wrongly treated as fully substantiated
6451 // later on and crashes name mangling during IR generation (see #1255). In that case we only record that the
6452 // field has a default ctor via a direct scan; the concrete ctor is matched later, per manifestation, in
6453 // createCtorBodyPreamble.
6454
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 165 times.
179 if fieldType.hasAnyGenericParts() {
6455 14 const bool hasDefaultCtor = hasDefaultCtor(bodyScope);
6456
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 if !hasDefaultCtor && isCtorCallRequired {
6457 ✗ return;
6458 }
6459
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 hasFieldsToConstruct = hasFieldsToConstruct || hasDefaultCtor;
6460 14 } else {
6461 // Lookup ctor function
6462 165 const ArgList noArgs;
6463 165 const QualTypeList noTemplateTypeHints;
6464 165 const Function* ctorFct = matchFunction(bodyScope, String(CTOR_FUNCTION_NAME), fieldType, noArgs, noTemplateTypeHints, true, baseNode);
6465 // If we are required to construct, but no constructor is found, we can't generate a default ctor for the outer struct
6466
4/4
✓ Branch 0 taken 119 times.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 154 times.
165 if ctorFct == nil<const Function*> && isCtorCallRequired {
6467 11 return;
6468 }
6469
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 124 times.
154 hasFieldsToConstruct = hasFieldsToConstruct || (ctorFct != nil<const Function*>);
6470 165 }
6471 168 }
6472 3493 }
6473
6474 // If we don't have any fields, that require us to do anything in the ctor, we can skip it
6475
6/6
✓ Branch 0 taken 1285 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 85 times.
✓ Branch 3 taken 1200 times.
✓ Branch 4 taken 1170 times.
✓ Branch 5 taken 201 times.
1371 if !hasFieldsWithDefaultValue && !hasFieldsToConstruct && !node.emitVTable {
6476 1170 return;
6477 }
6478
6479 // Create the default ctor function
6480 201 const String entryName = getSymbolTableEntryNameDefaultCtor(baseNode.codeLoc);
6481 201 const ParamList noParams;
6482 201 createDefaultStructMethod(spiceStruct, entryName, String(CTOR_FUNCTION_NAME), noParams);
6483 5354 }
6484
6485 /**
6486 * Checks if the given struct scope already has a user-defined constructor and creates a default one if not.
6487 *
6488 * For generating a default copy ctor, the following conditions need to be met:
6489 * - No user-defined copy ctor
6490 *
6491 * @param spiceStruct Struct instance
6492 * @param structScope Scope of the struct
6493 */
6494 5354 p createDefaultCopyCtorIfRequired(const Struct& spiceStruct, Scope* structScope) {
6495 5354 const ASTStructDefNode* node = downcast<ASTStructDefNode>(spiceStruct.structBase.declNode);
6496 5354 ASTNode* baseNode = cast<ASTNode*>(node);
6497 5354 assert structScope != nil<Scope*> && structScope.scopeType == ScopeType::STRUCT;
6498
6499 // Abort if the struct already has a user-defined copy constructor
6500 5354 const SymbolTableEntry* structEntry = spiceStruct.structBase.entry;
6501 5354 const QualType structType = structEntry.getQualType();
6502
2/2
✓ Branch 0 taken 1442 times.
✓ Branch 1 taken 3912 times.
5354 if hasCopyCtor(structScope) {
6503 1442 return;
6504 }
6505
6506 // Check if we have fields, that require us to do anything in the ctor
6507 3912 const unsigned long fieldCount = structScope.getFieldCount();
6508 3912 bool copyCtorRequired = false;
6509
2/2
✓ Branch 0 taken 8931 times.
✓ Branch 1 taken 3912 times.
12843 for unsigned long i = 0l; i < fieldCount; i++ {
6510 8931 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(i));
6511 8931 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.declNode != nil<ASTNode*>;
6512
6513 8931 QualType fieldType = fieldSymbol.getQualType();
6514
4/4
✓ Branch 0 taken 7519 times.
✓ Branch 1 taken 1412 times.
✓ Branch 2 taken 8760 times.
✓ Branch 3 taken 171 times.
8931 if fieldType.hasAnyGenericParts() && !spiceStruct.structBase.typeMapping.isEmpty() {
6515 171 substantiateTypeWithTypeMapping(fieldType, spiceStruct.structBase.typeMapping, fieldSymbol.declNode);
6516 171 }
6517
6518 // If the field is of type struct, check if this struct has a copy ctor that has to be called
6519
2/2
✓ Branch 0 taken 7475 times.
✓ Branch 1 taken 1456 times.
8931 if fieldType.is(SuperType::TY_STRUCT) {
6520 // A field whose copy is non-trivial forces the outer struct to have a copy ctor as well.
6521 1456 const bool fieldRequiresCopyCtor = !fieldType.isTriviallyCopyable(baseNode);
6522 // While the outer struct is still a generic preset, a field type that is itself generic (e.g. Inner<T, bool>,
6523 // the internal field of Outer<T>) cannot be matched to a concrete copy ctor yet. Matching it via the
6524 // substantiating matchFunction() below can partially substantiate a bogus manifestation (e.g.
6525 // Inner<K, bool>, with K left generic) that is wrongly treated as fully substantiated later on and crashes
6526 // name mangling during IR generation (see #1255). This can happen even though the copy ctor call carries an
6527 // argument of the same (still-generic) field type, whenever the field struct's own generic type name
6528 // collides with the outer struct's generic type name (e.g. both named 'V', as in Set<V> wrapping
6529 // RedBlackTree<K,V>). The concrete copy ctor is matched later, per manifestation, in
6530 // createCopyCtorBodyPreamble, and its result here is not otherwise consulted while the field type is still
6531 // generic (fieldRequiresCopyCtor already reflects copy-ctor existence via the non-substantiating
6532 // isTriviallyCopyable check above) - so skip the substantiating match entirely in that case.
6533
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 1333 times.
1456 if !fieldType.hasAnyGenericParts() {
6534 1333 Scope* bodyScope = fieldType.getBodyScope();
6535 1333 ArgList args;
6536 1333 args.pushBack(Pair<QualType, bool>(fieldType.toConstRef(baseNode), false /* we always have the field as storage */));
6537 1333 const QualTypeList noTemplateTypeHints;
6538 1333 const Function* ctorFct = matchFunction(bodyScope, String(CTOR_FUNCTION_NAME), fieldType, args, noTemplateTypeHints, true, baseNode);
6539 // If the field requires a copy ctor, but we proved none exists, we cannot synthesize one for the outer struct.
6540
3/4
✓ Branch 0 taken 538 times.
✓ Branch 1 taken 795 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1333 times.
1333 if ctorFct == nil<const Function*> && fieldRequiresCopyCtor {
6541 ✗ return;
6542 }
6543 1333 }
6544
2/2
✓ Branch 0 taken 476 times.
✓ Branch 1 taken 980 times.
1456 copyCtorRequired = copyCtorRequired || fieldRequiresCopyCtor;
6545 1456 }
6546
6547 // If we have an owning heap pointer, we need to do a memcpy of the heap storage and therefore need a default copy ctor
6548
2/2
✓ Branch 0 taken 8711 times.
✓ Branch 1 taken 220 times.
8931 if fieldType.isHeap() {
6549 220 assert fieldType.isPtr();
6550 220 copyCtorRequired = true;
6551 220 }
6552 8931 }
6553
6554 // If we don't have any fields, that require us to do anything in the copy ctor, we can skip it
6555
4/4
✓ Branch 0 taken 575 times.
✓ Branch 1 taken 3337 times.
✓ Branch 2 taken 2929 times.
✓ Branch 3 taken 983 times.
3912 if !copyCtorRequired && !node.emitVTable {
6556 2929 return;
6557 }
6558
6559 // Create the default copy ctor function
6560 983 const String entryName = getSymbolTableEntryNameDefaultCopyCtor(baseNode.codeLoc);
6561 983 ParamList paramTypes;
6562 983 paramTypes.pushBack(Param{structType.toConstRef(baseNode), false});
6563 983 createDefaultStructMethod(spiceStruct, entryName, String(CTOR_FUNCTION_NAME), paramTypes);
6564 983 }
6565
6566 /**
6567 * Checks if the given struct scope already has a user-defined move constructor and creates a default one if not.
6568 *
6569 * For generating a default move ctor, the following conditions need to be met:
6570 * - No user-defined move ctor
6571 * - At least one field requires non-trivial moving (heap pointer or struct field with move ctor)
6572 *
6573 * @param spiceStruct Struct instance
6574 * @param structScope Scope of the struct
6575 */
6576 3238 p createDefaultMoveCtorIfRequired(const Struct& spiceStruct, Scope* structScope) {
6577 3238 ASTNode* node = spiceStruct.structBase.declNode;
6578 3238 assert structScope != nil<Scope*> && structScope.scopeType == ScopeType::STRUCT;
6579
6580 // Skip generic struct presets - we only synthesize a default move ctor for fully substantiated structs.
6581 // Reason: each manifestation gets its own scope (deep-copied from the generic), and the implicit move ctor
6582 // body's preamble writes to field lifecycle state via bodyScope.parent. If we created the move ctor on the
6583 // generic struct, the deep-copied Function in the manifestation's scope would still carry a bodyScope pointer
6584 // into the generic struct's scope, causing the preamble to update fields in the wrong scope.
6585
2/2
✓ Branch 0 taken 925 times.
✓ Branch 1 taken 2313 times.
3238 if !spiceStruct.structBase.isFullySubstantiated() {
6586 925 return;
6587 }
6588
6589 // Abort if the struct already has a user-defined move constructor.
6590 // We can't just call lookupFunction with a non-const ref arg here, since the lookup permits
6591 // const-param-to-non-const-arg matching ("constify") and would return the copy ctor (if one exists) as
6592 // a false positive. Instead, check the function manifestations directly for a single-self-non-const-ref ctor.
6593
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2309 times.
2313 if hasMoveCtor(structScope) {
6594 4 return;
6595 }
6596
6597 // Abort if the struct has a user-defined copy ctor. Reason: in Spice the move-vs-copy tie-breaker picks the
6598 // non-const-ref (move) candidate for non-const lvalue arguments, so silently auto-generating a move ctor on
6599 // top of a user-defined copy ctor would change the behavior of existing `T b = T(a)` call sites (a's heap
6600 // contents would be stolen instead of deep-copied). Users that want a default move ctor in addition to their
6601 // own copy ctor can write an empty `p T.ctor(T& other) {}` to opt in. We still synthesize the default move
6602 // ctor when only the auto-generated copy ctor exists (heap-owning struct with no user ctors) - the user did
6603 // not write any binding semantics in that case, so picking move for non-const lvalues is fine.
6604
2/2
✓ Branch 0 taken 329 times.
✓ Branch 1 taken 1980 times.
2309 if hasUserCopyCtor(structScope) {
6605 329 return;
6606 }
6607 1980 const SymbolTableEntry* structEntry = spiceStruct.structBase.entry;
6608 1980 const QualType structType = structEntry.getQualType();
6609
6610 // Check if we have fields, that require us to do anything in the move ctor
6611 1980 const unsigned long fieldCount = structScope.getFieldCount();
6612 1980 bool moveCtorRequired = false;
6613
2/2
✓ Branch 0 taken 4760 times.
✓ Branch 1 taken 1980 times.
6740 for unsigned long i = 0l; i < fieldCount; i++ {
6614 4760 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(i));
6615 4760 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.declNode != nil<ASTNode*>;
6616
6617 4760 QualType fieldType = fieldSymbol.getQualType();
6618
2/4
✓ Branch 0 taken 4760 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4760 times.
✗ Branch 3 not taken.
4760 if fieldType.hasAnyGenericParts() && !spiceStruct.structBase.typeMapping.isEmpty() {
6619 ✗ substantiateTypeWithTypeMapping(fieldType, spiceStruct.structBase.typeMapping, fieldSymbol.declNode);
6620 ✗ }
6621
6622 // If the field is of type struct, check whether the field's struct has its own move ctor. We use
6623 // findMoveCtor (a direct scan of the manifestations) rather than matchFunction here for two
6624 // reasons: (1) match permits const-param-to-non-const-arg "constify" matching and would return the
6625 // field's copy ctor as a false positive, and (2) match populates the lookup cache, which would inflate
6626 // the per-struct cache-miss counts even for structs that have nothing to do with move ctors.
6627
2/2
✓ Branch 0 taken 4295 times.
✓ Branch 1 taken 465 times.
4760 if fieldType.is(SuperType::TY_STRUCT) {
6628 465 Scope* bodyScope = fieldType.getBodyScope();
6629
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 463 times.
465 moveCtorRequired = moveCtorRequired || (findMoveCtor(bodyScope) != nil<Function*>);
6630 465 }
6631
6632 // If we have an owning heap pointer, we transfer ownership of the heap storage
6633
2/2
✓ Branch 0 taken 4746 times.
✓ Branch 1 taken 14 times.
4760 if fieldType.isHeap() {
6634 14 assert fieldType.isPtr();
6635 14 moveCtorRequired = true;
6636 14 }
6637 4760 }
6638
6639 // If we don't have any fields that require us to do anything special in the move ctor, we can skip it.
6640 // Unlike the copy ctor we do NOT generate a default move ctor just because the struct emits a vtable - the
6641 // vtable pointer is part of the struct layout and gets shallow-copied along with everything else, no
6642 // special move handling needed. Users that want to move a vtable-bearing struct fall back to the default
6643 // copy ctor.
6644
2/2
✓ Branch 0 taken 1959 times.
✓ Branch 1 taken 21 times.
1980 if !moveCtorRequired {
6645 1959 return;
6646 }
6647
6648 // Create the default move ctor function
6649 21 const String entryName = getSymbolTableEntryNameDefaultMoveCtor(node.codeLoc);
6650 21 const QualType nonConstStructType = structType.toNonConst();
6651 21 ParamList paramTypes;
6652 21 paramTypes.pushBack(Param{nonConstStructType.toRef(node), false});
6653 21 createDefaultStructMethod(spiceStruct, entryName, String(CTOR_FUNCTION_NAME), paramTypes);
6654 21 }
6655
6656 /**
6657 * Checks if the given struct scope already has a user-defined destructor and creates a default one if not.
6658 *
6659 * For generating a default dtor, the following conditions need to be met:
6660 * - No user-defined dtor
6661 *
6662 * @param spiceStruct Struct instance
6663 * @param structScope Scope of the struct
6664 */
6665 5354 p createDefaultDtorIfRequired(const Struct& spiceStruct, Scope* structScope) {
6666 5354 const ASTNode* node = spiceStruct.structBase.declNode;
6667 5354 assert structScope != nil<Scope*> && structScope.scopeType == ScopeType::STRUCT;
6668 5354 SourceFile* sourceFile = structScope.sourceFile;
6669
6670 // Abort if the struct already has a destructor. See createDefaultCtorIfRequired for why the name registry is asked
6671 // first and the struct scope only on top of it. A registry entry that stems from the default dtor of the generic
6672 // preset does not count for a manifestation, that has no dtor in its scope: Such a manifestation was substantiated
6673 // before the preset got its default dtor, so it could not inherit it through its copied scope and has to create its own.
6674 5354 const SymbolTableEntry* structEntry = spiceStruct.structBase.entry;
6675 5354 const QualType structType = structEntry.getQualType();
6676 5354 const String fqFctName = structType.getSubType() + MEMBER_ACCESS_TOKEN + DTOR_FUNCTION_NAME;
6677 5354 const NameRegistryEntry* registryEntry = sourceFile.getNameRegistryEntry(fqFctName);
6678
2/2
✓ Branch 0 taken 1137 times.
✓ Branch 1 taken 4217 times.
5354 if hasDtor(structScope) {
6679 1137 return;
6680 }
6681
2/2
✓ Branch 0 taken 4211 times.
✓ Branch 1 taken 6 times.
4217 if registryEntry != nil<const NameRegistryEntry*> {
6682
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
6 const bool isPresetDefaultDtor = spiceStruct.structBase.isGenericSubstantiation() && registryEntry.targetEntry.name == getSymbolTableEntryNameDefaultDtor(node.codeLoc);
6683
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
6 if !isPresetDefaultDtor {
6684 1 return;
6685 }
6686 5 }
6687
6688 // Check we have field types, that require use to do anything in the destructor
6689 4216 const unsigned long fieldCount = structScope.getFieldCount();
6690 4216 bool hasFieldsToDeAllocate = false;
6691 4216 bool hasFieldsToDestruct = false;
6692
2/2
✓ Branch 0 taken 10010 times.
✓ Branch 1 taken 4216 times.
14226 for unsigned long i = 0l; i < fieldCount; i++ {
6693 10010 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(i));
6694 10010 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.declNode != nil<ASTNode*>;
6695
6696 10010 QualType fieldType = fieldSymbol.getQualType();
6697
4/4
✓ Branch 0 taken 8500 times.
✓ Branch 1 taken 1510 times.
✓ Branch 2 taken 9804 times.
✓ Branch 3 taken 206 times.
10010 if fieldType.hasAnyGenericParts() && !spiceStruct.structBase.typeMapping.isEmpty() {
6698 206 substantiateTypeWithTypeMapping(fieldType, spiceStruct.structBase.typeMapping, fieldSymbol.declNode);
6699 206 }
6700
6701
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 9814 times.
10010 hasFieldsToDeAllocate = hasFieldsToDeAllocate || fieldType.needsDeAllocation();
6702
2/2
✓ Branch 0 taken 8515 times.
✓ Branch 1 taken 1495 times.
10010 if fieldType.is(SuperType::TY_STRUCT) {
6703 1495 Scope* fieldScope = fieldType.getBodyScope();
6704 // While the outer struct is still a generic preset, a field type that is itself generic (e.g.
6705 // RedBlackTree<V, bool>, the internal field of Set<V>) cannot be matched to a concrete dtor yet. Since a
6706 // dtor takes no arguments, matching it against the still-generic 'this' type would partially substantiate a
6707 // bogus manifestation (e.g. RedBlackTree<K, bool>, with K left generic) that is wrongly treated as fully
6708 // substantiated later on and crashes name mangling during IR generation. In that case we only record that
6709 // the field has a dtor via a direct scan; the concrete dtor is matched later, per manifestation, in
6710 // createDtorBodyPreamble.
6711
2/2
✓ Branch 0 taken 133 times.
✓ Branch 1 taken 1362 times.
1495 if fieldType.hasAnyGenericParts() {
6712
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 116 times.
133 hasFieldsToDestruct = hasFieldsToDestruct || hasDtor(fieldScope);
6713 133 } else {
6714 // Lookup dtor function
6715 1362 const ArgList noArgs;
6716 1362 const QualTypeList noTemplateTypeHints;
6717 1362 const Function* dtorFct = matchFunction(fieldScope, String(DTOR_FUNCTION_NAME), fieldType, noArgs, noTemplateTypeHints, true, node);
6718
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 1111 times.
1362 hasFieldsToDestruct = hasFieldsToDestruct || (dtorFct != nil<const Function*>);
6719 1362 requestRevisitIfRequired(dtorFct);
6720 1362 }
6721 1495 }
6722 10010 }
6723
6724 // If we don't have any fields, that require us to do anything in the dtor, we can skip it
6725
4/4
✓ Branch 0 taken 229 times.
✓ Branch 1 taken 3987 times.
✓ Branch 2 taken 3690 times.
✓ Branch 3 taken 526 times.
4216 if !hasFieldsToDeAllocate && !hasFieldsToDestruct {
6726 3690 return;
6727 }
6728
6729 // Create the default dtor function. The memory runtime that the generated body needs for its de-allocations is not
6730 // requested here, but only when that body is prepared (see createDtorBodyPreamble) - see the comment there.
6731 526 const String entryName = getSymbolTableEntryNameDefaultDtor(node.codeLoc);
6732 526 const ParamList noParams;
6733 526 createDefaultStructMethod(spiceStruct, entryName, String(DTOR_FUNCTION_NAME), noParams);
6734 5354 }
6735
6736 /**
6737 * Prepare the generation of the ctor body preamble. This preamble is used to initialize the VTable, construct or initialize
6738 * fields.
6739 */
6740 5895 p TypeChecker.createCtorBodyPreamble(const Scope* bodyScope) {
6741 // Retrieve struct scope
6742 5895 Scope* structScope = bodyScope.parent;
6743 5895 assert structScope != nil<Scope*>;
6744
6745 5895 const unsigned long fieldCount = structScope.getFieldCount();
6746
2/2
✓ Branch 0 taken 15560 times.
✓ Branch 1 taken 5895 times.
21455 for unsigned long i = 0l; i < fieldCount; i++ {
6747 15560 SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(i));
6748 15560 assert fieldSymbol != nil<SymbolTableEntry*> && fieldSymbol.isField();
6749
2/2
✓ Branch 0 taken 1079 times.
✓ Branch 1 taken 14481 times.
15560 if fieldSymbol.isImplicitField {
6750 1079 continue;
6751 }
6752
6753 14481 QualType fieldType = fieldSymbol.getQualType();
6754
1/2
✓ Branch 0 taken 14481 times.
✗ Branch 1 not taken.
14481 if fieldType.hasAnyGenericParts() {
6755 ✗ substantiateTypeWithTypeMapping(fieldType, this.typeMapping, fieldSymbol.declNode);
6756 ✗ }
6757
6758 // A reference field has no default value to construct - it can only be bound by an explicit assignment in the
6759 // ctor body (e.g. Pair<K, V&>'s 'this.second = second;'), and that assignment is what should mark the field's
6760 // lifecycle as initialized (see the isDecl / initial-field-ref-assign logic in TypeChecker.visitAssignExpr and
6761 // IRGenerator.doAssignment). Marking it initialized here, before that binding assignment runs, would make the
6762 // compiler treat the binding as a reassignment through the reference instead of the initial binding - illegal
6763 // for a const reference field, and wrong even for a non-const one.
6764
2/2
✓ Branch 0 taken 723 times.
✓ Branch 1 taken 13758 times.
14481 if fieldType.isRef() {
6765 723 continue;
6766 }
6767
6768
2/2
✓ Branch 0 taken 11296 times.
✓ Branch 1 taken 2462 times.
13758 if fieldType.is(SuperType::TY_STRUCT) {
6769 2462 ASTNode* fieldNode = fieldSymbol.declNode;
6770 2462 assert fieldNode.kind == ASTNodeKind::FIELD;
6771 // Match ctor function, create the concrete manifestation and set it to used
6772 2462 Scope* matchScope = fieldType.getBodyScope();
6773 2462 const ArgList noArgs;
6774 2462 const QualTypeList noTemplateTypeHints;
6775 2462 const Function* spiceFunc = matchFunction(matchScope, String(CTOR_FUNCTION_NAME), fieldType, noArgs, noTemplateTypeHints, false, fieldNode);
6776
2/2
✓ Branch 0 taken 2280 times.
✓ Branch 1 taken 182 times.
2462 if spiceFunc != nil<const Function*> {
6777 2280 const QualType& ctorThisType = spiceFunc.thisType;
6778 2280 fieldSymbol.updateType(fieldType.getWithBodyScope(ctorThisType.getBodyScope()), true);
6779
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 55 times.
2462 } else if !fieldType.isTriviallyConstructible(fieldNode) {
6780 127 continue;
6781 55 }
6782 2462 }
6783
6784 13631 fieldSymbol.updateState(LifecycleState::INITIALIZED);
6785 13631 }
6786 5895 }
6787
6788 /**
6789 * Prepare the generation of the copy ctor body preamble. This preamble is used to initialize the VTable, construct or initialize
6790 * fields.
6791 */
6792 1272 p TypeChecker.createCopyCtorBodyPreamble(const Scope* bodyScope) {
6793 // Retrieve struct scope
6794 1272 Scope* structScope = bodyScope.parent;
6795 1272 assert structScope != nil<Scope*>;
6796
6797 1272 const unsigned long fieldCount = structScope.getFieldCount();
6798
2/2
✓ Branch 0 taken 3123 times.
✓ Branch 1 taken 1272 times.
4395 for unsigned long i = 0l; i < fieldCount; i++ {
6799 3123 SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(i));
6800 3123 assert fieldSymbol != nil<SymbolTableEntry*> && fieldSymbol.isField();
6801
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 3002 times.
3123 if fieldSymbol.isImplicitField {
6802 121 continue;
6803 }
6804
6805 3002 QualType fieldType = fieldSymbol.getQualType();
6806
1/2
✓ Branch 0 taken 3002 times.
✗ Branch 1 not taken.
3002 if fieldType.hasAnyGenericParts() {
6807 ✗ substantiateTypeWithTypeMapping(fieldType, this.typeMapping, fieldSymbol.declNode);
6808 ✗ }
6809
6810
2/2
✓ Branch 0 taken 1323 times.
✓ Branch 1 taken 1679 times.
3002 if fieldType.is(SuperType::TY_STRUCT) {
6811 1679 ASTNode* fieldNode = fieldSymbol.declNode;
6812 1679 assert fieldNode.kind == ASTNodeKind::FIELD;
6813 // Match ctor function, create the concrete manifestation and set it to used
6814 1679 Scope* matchScope = fieldType.getBodyScope();
6815 1679 ArgList args;
6816 1679 args.pushBack(Pair<QualType, bool>(fieldType.toConstRef(fieldNode), false /* we always have the field as storage */));
6817 1679 const QualTypeList noTemplateTypeHints;
6818 1679 const Function* copyCtorFct = matchFunction(matchScope, String(CTOR_FUNCTION_NAME), fieldType, args, noTemplateTypeHints, false, fieldNode);
6819
2/2
✓ Branch 0 taken 1173 times.
✓ Branch 1 taken 506 times.
1679 if copyCtorFct != nil<const Function*> {
6820 1173 const QualType& ctorThisType = copyCtorFct.thisType;
6821 1173 fieldSymbol.updateType(fieldType.getWithBodyScope(ctorThisType.getBodyScope()), true);
6822
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 506 times.
1679 } else if !fieldType.isTriviallyCopyable(fieldNode) {
6823 ✗ continue;
6824 506 }
6825 1679 }
6826
6827 3002 fieldSymbol.updateState(LifecycleState::INITIALIZED);
6828 3002 }
6829 1272 }
6830
6831 /**
6832 * Prepare the generation of the move ctor body preamble. This preamble is used to initialize the VTable, move-construct or
6833 * shallow-copy fields and transfer ownership of heap allocations.
6834 */
6835 36 p TypeChecker.createMoveCtorBodyPreamble(const Scope* bodyScope) {
6836 // Retrieve struct scope
6837 36 Scope* structScope = bodyScope.parent;
6838 36 assert structScope != nil<Scope*>;
6839
6840 36 const unsigned long fieldCount = structScope.getFieldCount();
6841
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 36 times.
94 for unsigned long i = 0l; i < fieldCount; i++ {
6842 58 SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(i));
6843 58 assert fieldSymbol != nil<SymbolTableEntry*> && fieldSymbol.isField();
6844
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 if fieldSymbol.isImplicitField {
6845 ✗ continue;
6846 }
6847
6848 58 QualType fieldType = fieldSymbol.getQualType();
6849
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 if fieldType.hasAnyGenericParts() {
6850 ✗ substantiateTypeWithTypeMapping(fieldType, this.typeMapping, fieldSymbol.declNode);
6851 ✗ }
6852
6853
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 18 times.
58 if fieldType.is(SuperType::TY_STRUCT) {
6854 // Find a move ctor on the field's struct. We use findMoveCtor rather than matchFunction to
6855 // avoid a constify-based false positive returning the copy ctor. findMoveCtor doesn't mark the
6856 // function as used, so do that explicitly to ensure the inner move ctor's body is actually emitted.
6857 18 Scope* matchScope = fieldType.getBodyScope();
6858 18 Function* moveCtorFct = findMoveCtor(matchScope);
6859
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if moveCtorFct != nil<Function*> {
6860 18 moveCtorFct.used = true;
6861
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if moveCtorFct.entry != nil<SymbolTableEntry*> {
6862 18 moveCtorFct.entry.used = true;
6863 18 }
6864 18 const QualType& ctorThisType = moveCtorFct.thisType;
6865 18 fieldSymbol.updateType(fieldType.getWithBodyScope(ctorThisType.getBodyScope()), true);
6866 18 }
6867 18 }
6868
6869 58 fieldSymbol.updateState(LifecycleState::INITIALIZED);
6870 58 }
6871 36 }
6872
6873 /**
6874 * Prepare the generation of the dtor body preamble. This preamble is used to destruct all fields and to free all heap fields.
6875 *
6876 * @param bodyScope Body scope of the dtor
6877 * @param node Struct definition node for error messages
6878 */
6879 1146 p TypeChecker.createDtorBodyPreamble(const Scope* bodyScope, const ASTNode* node) {
6880 // Retrieve struct scope
6881 1146 Scope* structScope = bodyScope.parent;
6882 1146 assert structScope != nil<Scope*>;
6883
6884 1146 const unsigned long fieldCount = structScope.getFieldCount();
6885 1146 bool hasFieldsToDeAllocate = false;
6886
2/2
✓ Branch 0 taken 3334 times.
✓ Branch 1 taken 1146 times.
4480 for unsigned long i = 0l; i < fieldCount; i++ {
6887 3334 const unsigned long fieldIdx = fieldCount - 1l - i; // Destruct fields in reverse order
6888 3334 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx));
6889 3334 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField();
6890
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 3222 times.
3334 if fieldSymbol.isImplicitField {
6891 112 continue;
6892 }
6893
6894 3222 QualType fieldType = fieldSymbol.getQualType();
6895
1/2
✓ Branch 0 taken 3222 times.
✗ Branch 1 not taken.
3222 if fieldType.hasAnyGenericParts() {
6896 ✗ substantiateTypeWithTypeMapping(fieldType, this.typeMapping, fieldSymbol.declNode);
6897 ✗ }
6898
6899
2/2
✓ Branch 0 taken 309 times.
✓ Branch 1 taken 2913 times.
3222 hasFieldsToDeAllocate = hasFieldsToDeAllocate || fieldType.needsDeAllocation();
6900
6901
2/2
✓ Branch 0 taken 1407 times.
✓ Branch 1 taken 1815 times.
3222 if fieldType.is(SuperType::TY_STRUCT) {
6902 1815 ASTNode* fieldNode = fieldSymbol.declNode;
6903 1815 assert fieldNode.kind == ASTNodeKind::FIELD;
6904 // Match ctor function, create the concrete manifestation and set it to used
6905 1815 Scope* matchScope = fieldType.getBodyScope();
6906 1815 const ArgList noArgs;
6907 1815 const QualTypeList noTemplateTypeHints;
6908 1815 matchFunction(matchScope, String(DTOR_FUNCTION_NAME), fieldType, noArgs, noTemplateTypeHints, false, fieldNode);
6909 1815 }
6910 3222 }
6911
6912 // Request the memory runtime for the de-allocations that the generated body will contain, and mark its sDealloc as
6913 // used so that a definition is emitted for it. This deliberately happens here and not where the dtor is created
6914 // (createDefaultDtorIfRequired): a manifestation can come into existence while memory_rt.spice is itself still being
6915 // pre-checked - sAlloc() returns Result<heap byte*>, so the very first Result manifestation is substantiated from
6916 // memory_rt's own signature list. Asking that half-prepared file for sDealloc, which is declared further down in it,
6917 // would come up empty. By the time a default member's body is prepared, every runtime module is fully pre-checked.
6918 // The string runtime does not use sDealloc, but frees manually to avoid a circular dependency.
6919 1146 SourceFile* structSourceFile = structScope.sourceFile;
6920
4/4
✓ Branch 0 taken 649 times.
✓ Branch 1 taken 497 times.
✓ Branch 2 taken 649 times.
✓ Branch 3 taken 497 times.
1146 if hasFieldsToDeAllocate && !structSourceFile.isStringRT() {
6921 497 SourceFile* memoryRT = structSourceFile.requestRuntimeModule(RuntimeModule::MEMORY_RT);
6922 497 assert memoryRT != nil<SourceFile*>;
6923 497 Scope* matchScope = memoryRT.getGlobalScope();
6924 // Set dealloc function to used
6925 497 const QualType thisType = QualType(SuperType::TY_DYN);
6926 497 const QualType byteType = QualType(SuperType::TY_BYTE);
6927 497 const QualType bytePtrType = byteType.toPtr(node);
6928 497 QualType bytePtrRefType = bytePtrType.toRef(node);
6929 497 bytePtrRefType.makeHeap();
6930 497 ArgList args;
6931 497 args.pushBack(Pair<QualType, bool>(bytePtrRefType, false /* we always have the field as storage */));
6932 497 const QualTypeList noTemplateTypeHints;
6933 497 Function* deallocFct = matchFunction(matchScope, String(FCT_NAME_DEALLOC), thisType, args, noTemplateTypeHints, true, node);
6934 497 assert deallocFct != nil<Function*>;
6935 497 deallocFct.used = true;
6936 497 }
6937 1146 }
6938
6939 /**
6940 * Prepare the generation of a call to a method of a given struct
6941 *
6942 * @param entry Symbol entry to use as 'this' pointer for the method call
6943 * @param methodName Name of the method to call
6944 * @param args Provided arguments by the caller
6945 * @param node AST node
6946 */
6947 7015 public f<Function*> TypeChecker.implicitlyCallStructMethod(const SymbolTableEntry* entry, const String& methodName, const ArgList& args, const ASTNode* node) {
6948 7015 const QualType entryType = entry.getQualType();
6949 7015 const QualType unwrappedType = entryType.removeReferenceWrapper();
6950 7015 const QualType thisType = unwrappedType.toNonConst();
6951 7015 return this.implicitlyCallStructMethod(thisType, methodName, args, node);
6952 }
6953
6954 /**
6955 * Prepare the generation of a call to a method of a given struct
6956 *
6957 * @param thisType Struct type to call the method on
6958 * @param methodName Name of the method to call
6959 * @param args Provided arguments by the caller
6960 * @param node AST node
6961 */
6962 8340 public f<Function*> TypeChecker.implicitlyCallStructMethod(const QualType& origThisType, const String& methodName, const ArgList& args, const ASTNode* node) {
6963 8340 assert origThisType.is(SuperType::TY_STRUCT);
6964 8340 Scope* matchScope = origThisType.getBodyScope();
6965 8340 assert matchScope.scopeType == ScopeType::STRUCT;
6966
6967 // Search for dtor
6968 8340 QualType thisType = origThisType;
6969
2/2
✓ Branch 0 taken 2204 times.
✓ Branch 1 taken 6136 times.
8340 if matchScope.isImportedBy(this.rootScope) {
6970 6136 thisType = this.mapLocalTypeToImportedScopeType(matchScope, thisType);
6971 6136 }
6972 8340 const QualTypeList noTemplateTypeHints;
6973 16680 return matchFunction(matchScope, methodName, thisType, args, noTemplateTypeHints, true, node);
6974 8340 }
6975
6976 /**
6977 * Prepare the generation of a call to the copy ctor of a given struct
6978 *
6979 * @param thisType Struct type to call the copy ctor on
6980 * @param node Current AST node
6981 */
6982 1281 public f<Function*> TypeChecker.implicitlyCallStructCopyCtor(const QualType& thisType, const ASTNode* node) {
6983 1281 const QualType unwrappedType = thisType.removeReferenceWrapper();
6984 1281 const QualType argType = unwrappedType.toConstRef(node);
6985 1281 ArgList args;
6986 1281 args.pushBack(Pair<QualType, bool>(argType, false /* we always have an entry here */));
6987 // The new instance being constructed is never const, regardless of whether the copy source is const
6988 1281 const QualType nonConstThisType = thisType.toNonConst();
6989 2562 return this.implicitlyCallStructMethod(nonConstThisType, String(CTOR_FUNCTION_NAME), args, node);
6990 1281 }
6991
6992 /**
6993 * Prepare the generation of a call to the dtor of a given struct
6994 *
6995 * @param entry Symbol entry to use as 'this' pointer for the dtor call
6996 * @param resources Resources to clean up at the end of the current scope
6997 * @param node Current AST node for error messages
6998 */
6999 6982 p TypeChecker.implicitlyCallStructDtor(SymbolTableEntry* entry, ResourcesForManifestationToCleanup& resources, const ASTNode* node) {
7000 // Add the dtor to the resources to clean up to call it later in codegen
7001 6982 const ArgList noArgs;
7002 6982 Function* dtor = this.implicitlyCallStructMethod(entry, String(DTOR_FUNCTION_NAME), noArgs, node);
7003
2/2
✓ Branch 0 taken 1075 times.
✓ Branch 1 taken 5907 times.
6982 if dtor != nil<Function*> {
7004 5907 resources.dtorFunctionsToCall.pushBack(Pair<SymbolTableEntry*, Function*>(entry, dtor));
7005 5907 }
7006 6982 }
7007
7008 /**
7009 * Prepare the generation of a call to the deallocate function for a heap-allocated variable
7010 *
7011 * @param node Current AST node for error messages
7012 */
7013 494 p TypeChecker.implicitlyCallDeallocate(const ASTNode* node) {
7014 494 SourceFile* memoryRT = this.sourceFile.requestRuntimeModule(RuntimeModule::MEMORY_RT);
7015 494 assert memoryRT != nil<SourceFile*>;
7016 494 Scope* matchScope = memoryRT.getGlobalScope();
7017 // Set dealloc function to used
7018 494 const QualType thisType = QualType(SuperType::TY_DYN);
7019 494 const QualType byteType = QualType(SuperType::TY_BYTE);
7020 494 const QualType bytePtrType = byteType.toPtr(node);
7021 494 QualType bytePtrRefType = bytePtrType.toRef(node);
7022 494 bytePtrRefType.makeHeap();
7023 494 ArgList args;
7024 494 args.pushBack(Pair<QualType, bool>(bytePtrRefType, false /* we always have the field as storage */));
7025 494 const QualTypeList noTemplateTypeHints;
7026 494 Function* deallocFct = matchFunction(matchScope, String(FCT_NAME_DEALLOC), thisType, args, noTemplateTypeHints, true, node);
7027 494 assert deallocFct != nil<Function*>;
7028 494 deallocFct.used = true;
7029 494 }
7030
7031 /**
7032 * Sort the given variables in reverse declaration order. This is the order in which their dtors are called.
7033 *
7034 * @param vars Variables to sort
7035 */
7036 151617 p TypeChecker.sortByReverseDeclarationOrder(Vector<SymbolTableEntry*>& vars) {
7037 // Stable insertion sort
7038
2/2
✓ Branch 0 taken 13671 times.
✓ Branch 1 taken 151617 times.
165288 for unsigned long i = 1l; i < vars.getSize(); i++ {
7039 13671 SymbolTableEntry* current = vars.get(i);
7040 13671 unsigned long j = i;
7041
4/4
✓ Branch 0 taken 4696 times.
✓ Branch 1 taken 73255 times.
✓ Branch 2 taken 64280 times.
✓ Branch 3 taken 13671 times.
77951 while j > 0l && this.isDeclaredAfter(current, vars.get(j - 1l)) {
7042 64280 SymbolTableEntry*& slot = vars.get(j);
7043 64280 slot = vars.get(j - 1l);
7044 64280 j--;
7045 64280 }
7046 13671 SymbolTableEntry*& targetSlot = vars.get(j);
7047 13671 targetSlot = current;
7048 13671 }
7049 151617 }
7050
7051 // Comparator of sortByReverseDeclarationOrder
7052 73255 f<bool> TypeChecker.isDeclaredAfter(const SymbolTableEntry* a, const SymbolTableEntry* b) {
7053 73255 const ASTNode* aDeclNode = a.declNode;
7054 73255 const ASTNode* bDeclNode = b.declNode;
7055 // Primary sort criteria is the code location
7056
2/2
✓ Branch 0 taken 73204 times.
✓ Branch 1 taken 51 times.
73255 if !(aDeclNode.codeLoc == bDeclNode.codeLoc) {
7057 73204 return aDeclNode.codeLoc.isAfter(bDeclNode.codeLoc);
7058 }
7059 // Secondary sort criteria is the node id
7060 51 return this.resourceManager.getNodeId(aDeclNode) > this.resourceManager.getNodeId(bDeclNode);
7061 }
7062
7063 /**
7064 * Consider calls to destructors for the current scope
7065 *
7066 * @param resources Resources to clean up at the end of the current scope
7067 * @param node Node the current scope belongs to (a StmtLstNode or the head of a loop)
7068 */
7069 100419 p TypeChecker.doScopeCleanup(ResourcesForManifestationToCleanup& resources, const ASTNode* node) {
7070 // Get all variables, that are approved for de-allocation
7071 100419 Vector<SymbolTableEntry*> vars = this.currentScope.getVarsGoingOutOfScope();
7072 // Sort by reverse declaration order
7073 100419 this.sortByReverseDeclarationOrder(vars);
7074 // Call the dtor of each variable. We call the dtor in reverse declaration order
7075
2/2
✓ Branch 0 taken 38804 times.
✓ Branch 1 taken 100419 times.
139223 foreach SymbolTableEntry* varEntry : vars {
7076 38804 const QualType varType = varEntry.getQualType();
7077 // Check if we have a heap-allocated pointer
7078 38804 const dyn heapOptions = [SuperType::TY_PTR, SuperType::TY_STRING, SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
7079
4/4
✓ Branch 0 taken 34121 times.
✓ Branch 1 taken 4683 times.
✓ Branch 2 taken 34853 times.
✓ Branch 3 taken 3951 times.
38804 if varType.isHeap() && varType.isOneOf(heapOptions, len(heapOptions)) {
7080 // The memory runtime is ignored, because it manually allocates to avoid circular dependencies.
7081 // Same goes for the string runtime.
7082
4/4
✓ Branch 0 taken 688 times.
✓ Branch 1 taken 3263 times.
✓ Branch 2 taken 3331 times.
✓ Branch 3 taken 620 times.
3951 if this.sourceFile.isMemoryRT() || this.sourceFile.isStringRT() {
7083 3331 continue;
7084 }
7085 // If the local variable currently does not have the ownership, we must not deallocate its memory
7086 620 const Lifecycle& lifecycle = varEntry.getLifecycle();
7087
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 494 times.
620 if !lifecycle.isInOwningState() {
7088 126 continue;
7089 }
7090
7091 494 this.implicitlyCallDeallocate(node); // Required to request the memory runtime
7092 494 resources.heapVarsToFree.pushBack(varEntry);
7093 494 }
7094 // Only generate dtor call for structs
7095
2/2
✓ Branch 0 taken 28365 times.
✓ Branch 1 taken 6982 times.
35347 if !varType.is(SuperType::TY_STRUCT) {
7096 28365 continue;
7097 }
7098 // Variable must be either initialized or a struct field
7099
3/4
✓ Branch 0 taken 6980 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6982 times.
6982 if !varEntry.isInitialized() && varEntry.scope.scopeType != ScopeType::STRUCT {
7100 ✗ continue;
7101 }
7102 // Call dtor
7103 6982 this.implicitlyCallStructDtor(varEntry, resources, node);
7104 6982 }
7105 100419 }
7106
7107 /**
7108 * Consider calls to the destructors of the temporaries of the current expression scope
7109 *
7110 * @param node Expression, that owns the current expression scope
7111 */
7112 51198 p TypeChecker.doExprScopeCleanup(IVisitable* node) {
7113 51198 assert this.currentScope.scopeType == ScopeType::EXPR_BODY;
7114
7115 // Get all temporaries that go out of scope. This is executed again if the expression is type-checked again
7116 51198 Vector<SymbolTableEntry*> temporaries = this.currentScope.getVarsGoingOutOfScope();
7117 51198 this.sortByReverseDeclarationOrder(temporaries);
7118 51198 this.currentScope.temporaryDtorsToCall.clear();
7119
7120 // Call the dtor of each temporary. We call the dtor in reverse declaration order
7121 51198 ASTNode* baseNode = getASTNode(node);
7122
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 51198 times.
51231 foreach SymbolTableEntry* temporary : temporaries {
7123 // Only generate dtor call for initialized structs
7124 33 const QualType temporaryType = temporary.getQualType();
7125
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
33 if !temporaryType.is(SuperType::TY_STRUCT) || !temporary.isInitialized() {
7126 ✗ continue;
7127 }
7128 33 const ArgList noArgs;
7129 33 Function* dtor = this.implicitlyCallStructMethod(temporary, String(DTOR_FUNCTION_NAME), noArgs, baseNode);
7130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 if dtor != nil<Function*> {
7131 33 this.currentScope.temporaryDtorsToCall.pushBack(Pair<SymbolTableEntry*, Function*>(temporary, dtor));
7132 33 }
7133 33 }
7134 51198 }
7135
7136 /**
7137 * Visit an expression in its expression scope, if it has one, and consider calls to the destructors of its temporaries
7138 *
7139 * @param expr Expression to visit
7140 * @return Result of the expression
7141 */
7142 46909 #[ignoreUnusedReturnValue]
7143 46909 f<ExprResult> TypeChecker.visitInExprScope(IVisitable* expr) {
7144 46909 ExprScopeHandle exprScopeHandle = ExprScopeHandle(&this.compilerPass, expr);
7145 46909 const ExprResult exprResult = this.visitExpr(expr);
7146 // If the expression is not resolved yet, it is visited again later on, which is when the temporaries are known
7147
4/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 46873 times.
✓ Branch 2 taken 43 times.
✓ Branch 3 taken 46866 times.
46909 if exprScopeHandle.getExprScope() != nil<Scope*> && !exprResult.ty.is(SuperType::TY_UNRESOLVED) {
7148 46866 this.doExprScopeCleanup(expr);
7149 46866 }
7150 93818 return exprResult;
7151 46909 }
7152