Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2025 ChilliBits. All rights reserved. | ||
2 | |||
3 | #include "TypeChecker.h" | ||
4 | |||
5 | #include <SourceFile.h> | ||
6 | #include <ast/Attributes.h> | ||
7 | #include <global/GlobalResourceManager.h> | ||
8 | #include <symboltablebuilder/SymbolTableBuilder.h> | ||
9 | |||
10 | namespace spice::compiler { | ||
11 | |||
12 | 2801 | TypeChecker::TypeChecker(GlobalResourceManager &resourceManager, SourceFile *sourceFile, TypeCheckerMode typeCheckerMode) | |
13 |
1/2✓ Branch 0 (4→5) taken 2801 times.
✗ Branch 1 (4→7) not taken.
|
2801 | : CompilerPass(resourceManager, sourceFile), typeCheckerMode(typeCheckerMode), warnings(sourceFile->compilerOutput.warnings) { |
14 | 2801 | } | |
15 | |||
16 | 2302 | std::any TypeChecker::visitEntry(EntryNode *node) { | |
17 | // Initialize | ||
18 | 2302 | currentScope = rootScope; | |
19 | |||
20 | // Initialize AST nodes with size of 1 | ||
21 | 2302 | const bool isPrepare = typeCheckerMode == TC_MODE_PRE; | |
22 |
2/2✓ Branch 0 (2→3) taken 989 times.
✓ Branch 1 (2→4) taken 1313 times.
|
2302 | if (isPrepare) |
23 | 989 | node->resizeToNumberOfManifestations(1); | |
24 | |||
25 | // Visit children | ||
26 |
2/2✓ Branch 0 (4→5) taken 2258 times.
✓ Branch 1 (4→22) taken 44 times.
|
2302 | visitChildren(node); |
27 | |||
28 | // Check which implicit structures we need for each struct, defined in this source file | ||
29 |
2/2✓ Branch 0 (6→7) taken 975 times.
✓ Branch 1 (6→19) taken 1283 times.
|
2258 | if (isPrepare) { |
30 |
1/2✓ Branch 0 (7→8) taken 975 times.
✗ Branch 1 (7→26) not taken.
|
975 | const std::vector<const Struct *> manifestations = rootScope->getAllStructManifestationsInDeclarationOrder(); |
31 |
2/2✓ Branch 0 (16→10) taken 580 times.
✓ Branch 1 (16→17) taken 975 times.
|
1555 | for (const Struct *manifestation : manifestations) { |
32 | // Check if we need to create a default ctor, copy ctor or dtor | ||
33 |
1/2✓ Branch 0 (11→12) taken 580 times.
✗ Branch 1 (11→23) not taken.
|
580 | createDefaultCtorIfRequired(*manifestation, manifestation->scope); |
34 |
1/2✓ Branch 0 (12→13) taken 580 times.
✗ Branch 1 (12→23) not taken.
|
580 | createDefaultCopyCtorIfRequired(*manifestation, manifestation->scope); |
35 |
1/2✓ Branch 0 (13→14) taken 580 times.
✗ Branch 1 (13→23) not taken.
|
580 | createDefaultDtorIfRequired(*manifestation, manifestation->scope); |
36 | } | ||
37 | 975 | } | |
38 | |||
39 |
1/2✓ Branch 0 (19→20) taken 2258 times.
✗ Branch 1 (19→27) not taken.
|
2258 | return nullptr; |
40 | } | ||
41 | |||
42 | /** | ||
43 | * Check if the capture rules for async lambdas are enforced if the async attribute is set | ||
44 | * | ||
45 | * Only one capture with pointer type, pass-by-val is allowed, since only then we can store it in the second field of the | ||
46 | * fat pointer and can ensure, that no stack variable is referenced inside the lambda. | ||
47 | * | ||
48 | * @param node Lambda base node | ||
49 | * @param attrs Lambda attributes | ||
50 | * @return False if the rules are violated, true otherwise | ||
51 | */ | ||
52 | 37 | bool TypeChecker::checkAsyncLambdaCaptureRules(const LambdaBaseNode *node, const LambdaAttrNode *attrs) const { | |
53 | // If the async attribute is not set, we can return early | ||
54 |
18/32✓ Branch 0 (2→3) taken 16 times.
✓ Branch 1 (2→13) taken 21 times.
✓ Branch 2 (5→6) taken 16 times.
✗ Branch 3 (5→53) not taken.
✓ Branch 4 (6→7) taken 16 times.
✗ Branch 5 (6→53) not taken.
✓ Branch 6 (7→8) taken 16 times.
✗ Branch 7 (7→13) not taken.
✓ Branch 8 (10→11) taken 16 times.
✗ Branch 9 (10→53) not taken.
✓ Branch 10 (11→12) taken 16 times.
✗ Branch 11 (11→53) not taken.
✗ Branch 12 (12→13) not taken.
✓ Branch 13 (12→14) taken 16 times.
✓ Branch 14 (15→16) taken 16 times.
✓ Branch 15 (15→17) taken 21 times.
✓ Branch 16 (17→18) taken 16 times.
✓ Branch 17 (17→20) taken 21 times.
✓ Branch 18 (20→21) taken 16 times.
✓ Branch 19 (20→22) taken 21 times.
✓ Branch 20 (22→23) taken 16 times.
✓ Branch 21 (22→25) taken 21 times.
✓ Branch 22 (25→26) taken 21 times.
✓ Branch 23 (25→27) taken 16 times.
✗ Branch 24 (53→54) not taken.
✗ Branch 25 (53→55) not taken.
✗ Branch 26 (57→58) not taken.
✗ Branch 27 (57→60) not taken.
✗ Branch 28 (62→63) not taken.
✗ Branch 29 (62→64) not taken.
✗ Branch 30 (66→67) not taken.
✗ Branch 31 (66→69) not taken.
|
101 | if (!attrs || !attrs->attrLst->hasAttr(ATTR_ASYNC) || !attrs->attrLst->getAttrValueByName(ATTR_ASYNC)->boolValue) |
55 | 21 | return true; // Not violated | |
56 | |||
57 | // If we don't have any captures, we can return early | ||
58 | 16 | const CaptureMap &captures = node->bodyScope->symbolTable.captures; | |
59 |
2/2✓ Branch 0 (28→29) taken 10 times.
✓ Branch 1 (28→30) taken 6 times.
|
16 | if (captures.empty()) |
60 | 10 | return true; // Not violated | |
61 | |||
62 | // Check for the capture rules | ||
63 | 6 | if (const Capture &capture = captures.begin()->second; | |
64 |
8/8✓ Branch 0 (33→34) taken 4 times.
✓ Branch 1 (33→39) taken 2 times.
✓ Branch 2 (36→37) taken 2 times.
✓ Branch 3 (36→39) taken 2 times.
✓ Branch 4 (38→39) taken 1 times.
✓ Branch 5 (38→40) taken 1 times.
✓ Branch 6 (41→42) taken 5 times.
✓ Branch 7 (41→51) taken 1 times.
|
6 | captures.size() > 1 || !capture.capturedSymbol->getQualType().isPtr() || capture.getMode() != BY_VALUE) { |
65 | 5 | const auto warningMessage = | |
66 | "Async lambdas can only capture one pointer by value without storing captures in the caller stack frame, which can lead " | ||
67 | "to bugs due to references, outliving the validity scope of the referenced variable."; | ||
68 |
2/4✓ Branch 0 (44→45) taken 5 times.
✗ Branch 1 (44→73) not taken.
✓ Branch 2 (45→46) taken 5 times.
✗ Branch 3 (45→71) not taken.
|
5 | const CompilerWarning warning(node->codeLoc, ASYNC_LAMBDA_CAPTURE_RULE_VIOLATION, warningMessage); |
69 |
1/2✓ Branch 0 (48→49) taken 5 times.
✗ Branch 1 (48→77) not taken.
|
5 | currentScope->sourceFile->compilerOutput.warnings.push_back(warning); |
70 | 5 | } | |
71 | |||
72 | 6 | return false; // Violated | |
73 | } | ||
74 | |||
75 | 32 | Function *TypeChecker::matchCopyCtor(const QualType &thisType, const ASTNode *node) { | |
76 |
1/2✓ Branch 0 (2→3) taken 32 times.
✗ Branch 1 (2→40) not taken.
|
32 | Scope *matchScope = thisType.getBodyScope(); |
77 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 32 times.
|
32 | assert(matchScope != nullptr); |
78 |
2/4✓ Branch 0 (5→6) taken 32 times.
✗ Branch 1 (5→27) not taken.
✓ Branch 2 (9→10) taken 32 times.
✗ Branch 3 (9→23) not taken.
|
64 | const ArgList args = {{thisType.toConstRef(node), false}}; |
79 |
2/4✓ Branch 0 (14→15) taken 32 times.
✗ Branch 1 (14→31) not taken.
✓ Branch 2 (15→16) taken 32 times.
✗ Branch 3 (15→29) not taken.
|
128 | return FunctionManager::match(this, matchScope, CTOR_FUNCTION_NAME, thisType, args, {}, true, node); |
80 | 32 | } | |
81 | |||
82 | 109632 | QualType TypeChecker::mapLocalTypeToImportedScopeType(const Scope *targetScope, const QualType &symbolType) const { | |
83 | // Skip all types, except structs | ||
84 |
3/4✓ Branch 0 (2→3) taken 109632 times.
✗ Branch 1 (2→50) not taken.
✓ Branch 2 (3→4) taken 98798 times.
✓ Branch 3 (3→5) taken 10834 times.
|
109632 | if (!symbolType.isBase(TY_STRUCT)) |
85 | 98798 | return symbolType; | |
86 | |||
87 | // If the target scope is in the current source file, we can return the symbol type as is | ||
88 | 10834 | SourceFile *targetSourceFile = targetScope->sourceFile; | |
89 |
2/2✓ Branch 0 (5→6) taken 4820 times.
✓ Branch 1 (5→7) taken 6014 times.
|
10834 | if (targetSourceFile == sourceFile) |
90 | 4820 | return symbolType; | |
91 | |||
92 | // Match the scope of the symbol type against all scopes in the name registry of the target file | ||
93 |
5/8✓ Branch 0 (7→8) taken 6014 times.
✗ Branch 1 (7→46) not taken.
✓ Branch 2 (8→9) taken 6014 times.
✗ Branch 3 (8→46) not taken.
✓ Branch 4 (9→10) taken 6014 times.
✗ Branch 5 (9→46) not taken.
✓ Branch 6 (33→11) taken 264936 times.
✓ Branch 7 (33→34) taken 213 times.
|
265149 | for (const auto &entry : targetSourceFile->exportedNameRegistry | std::views::values) |
94 |
7/10✓ Branch 0 (12→13) taken 264936 times.
✗ Branch 1 (12→17) not taken.
✓ Branch 2 (13→14) taken 264936 times.
✗ Branch 3 (13→46) not taken.
✓ Branch 4 (14→15) taken 264936 times.
✗ Branch 5 (14→46) not taken.
✓ Branch 6 (15→16) taken 18229 times.
✓ Branch 7 (15→17) taken 246707 times.
✓ Branch 8 (18→19) taken 18229 times.
✓ Branch 9 (18→31) taken 246707 times.
|
264936 | if (entry.targetEntry != nullptr && entry.targetEntry->getQualType().isBase(TY_STRUCT)) |
95 |
3/4✓ Branch 0 (19→20) taken 18229 times.
✗ Branch 1 (19→45) not taken.
✓ Branch 2 (29→22) taken 38236 times.
✓ Branch 3 (29→30) taken 12428 times.
|
50664 | for (const Struct *manifestation : *entry.targetEntry->declNode->getStructManifestations()) |
96 |
4/6✓ Branch 0 (23→24) taken 38236 times.
✗ Branch 1 (23→44) not taken.
✓ Branch 2 (24→25) taken 38236 times.
✗ Branch 3 (24→44) not taken.
✓ Branch 4 (25→26) taken 5801 times.
✓ Branch 5 (25→27) taken 32435 times.
|
38236 | if (manifestation->scope == symbolType.getBase().getBodyScope()) |
97 | 5801 | return symbolType; | |
98 | |||
99 | // The target file does not know about the struct at all | ||
100 | // -> show it how to find the struct | ||
101 |
3/6✓ Branch 0 (34→35) taken 213 times.
✗ Branch 1 (34→47) not taken.
✓ Branch 2 (35→36) taken 213 times.
✗ Branch 3 (35→47) not taken.
✓ Branch 4 (36→37) taken 213 times.
✗ Branch 5 (36→47) not taken.
|
213 | const std::string structName = symbolType.getBase().getSubType(); |
102 |
1/2✓ Branch 0 (37→38) taken 213 times.
✗ Branch 1 (37→48) not taken.
|
213 | const NameRegistryEntry *origRegistryEntry = sourceFile->getNameRegistryEntry(structName); |
103 |
1/2✗ Branch 0 (38→39) not taken.
✓ Branch 1 (38→40) taken 213 times.
|
213 | assert(origRegistryEntry != nullptr); |
104 | 213 | const uint64_t targetTypeId = origRegistryEntry->typeId; | |
105 | 213 | SymbolTableEntry *targetEntry = origRegistryEntry->targetEntry; | |
106 |
1/2✓ Branch 0 (40→41) taken 213 times.
✗ Branch 1 (40→48) not taken.
|
213 | targetSourceFile->addNameRegistryEntry(structName, targetTypeId, targetEntry, origRegistryEntry->targetScope, false); |
107 | |||
108 | 213 | return symbolType; | |
109 | 213 | } | |
110 | |||
111 | 3744 | QualType TypeChecker::mapImportedScopeTypeToLocalType(const Scope *sourceScope, const QualType &symbolType) const { | |
112 | // Skip all types, except structs | ||
113 |
3/4✓ Branch 0 (2→3) taken 3744 times.
✗ Branch 1 (2→45) not taken.
✓ Branch 2 (3→4) taken 488 times.
✓ Branch 3 (3→5) taken 3256 times.
|
3744 | if (!symbolType.isBase(TY_STRUCT)) |
114 | 488 | return symbolType; | |
115 | |||
116 | // If the source scope is in the current source file, we can return the symbol type as is | ||
117 | 3256 | const SourceFile *sourceSourceFile = sourceScope->sourceFile; | |
118 |
2/2✓ Branch 0 (5→6) taken 1114 times.
✓ Branch 1 (5→7) taken 2142 times.
|
3256 | if (sourceSourceFile == sourceFile) |
119 | 1114 | return symbolType; | |
120 | |||
121 | // Match the scope of the symbol type against all scopes in the name registry of this source file | ||
122 |
1/2✓ Branch 0 (7→8) taken 2142 times.
✗ Branch 1 (7→45) not taken.
|
2142 | const QualType baseType = symbolType.getBase(); |
123 |
5/8✓ Branch 0 (8→9) taken 2142 times.
✗ Branch 1 (8→44) not taken.
✓ Branch 2 (9→10) taken 2142 times.
✗ Branch 3 (9→44) not taken.
✓ Branch 4 (10→11) taken 2142 times.
✗ Branch 5 (10→44) not taken.
✓ Branch 6 (33→12) taken 116323 times.
✓ Branch 7 (33→34) taken 44 times.
|
116367 | for (const auto &entry : sourceFile->exportedNameRegistry | std::views::values) |
124 |
7/10✓ Branch 0 (13→14) taken 116323 times.
✗ Branch 1 (13→18) not taken.
✓ Branch 2 (14→15) taken 116323 times.
✗ Branch 3 (14→44) not taken.
✓ Branch 4 (15→16) taken 116323 times.
✗ Branch 5 (15→44) not taken.
✓ Branch 6 (16→17) taken 6964 times.
✓ Branch 7 (16→18) taken 109359 times.
✓ Branch 8 (19→20) taken 6964 times.
✓ Branch 9 (19→31) taken 109359 times.
|
116323 | if (entry.targetEntry != nullptr && entry.targetEntry->getQualType().isBase(TY_STRUCT)) |
125 |
3/4✓ Branch 0 (20→21) taken 6964 times.
✗ Branch 1 (20→43) not taken.
✓ Branch 2 (29→23) taken 15656 times.
✓ Branch 3 (29→30) taken 4866 times.
|
20522 | for (const Struct *manifestation : *entry.targetEntry->declNode->getStructManifestations()) |
126 |
3/4✓ Branch 0 (24→25) taken 15656 times.
✗ Branch 1 (24→43) not taken.
✓ Branch 2 (25→26) taken 2098 times.
✓ Branch 3 (25→27) taken 13558 times.
|
15656 | if (manifestation->scope == baseType.getBodyScope()) |
127 | 2098 | return symbolType; | |
128 | |||
129 | // This source file does not know about the struct at all | ||
130 | // -> show it how to find the struct | ||
131 |
2/4✓ Branch 0 (34→35) taken 44 times.
✗ Branch 1 (34→45) not taken.
✓ Branch 2 (35→36) taken 44 times.
✗ Branch 3 (35→45) not taken.
|
44 | const NameRegistryEntry *origRegistryEntry = sourceSourceFile->getNameRegistryEntry(baseType.getSubType()); |
132 |
1/2✗ Branch 0 (36→37) not taken.
✓ Branch 1 (36→38) taken 44 times.
|
44 | assert(origRegistryEntry != nullptr); |
133 | 44 | const uint64_t typeId = origRegistryEntry->typeId; | |
134 | 44 | SymbolTableEntry *targetEntry = origRegistryEntry->targetEntry; | |
135 |
2/4✓ Branch 0 (38→39) taken 44 times.
✗ Branch 1 (38→45) not taken.
✓ Branch 2 (39→40) taken 44 times.
✗ Branch 3 (39→45) not taken.
|
44 | sourceFile->addNameRegistryEntry(baseType.getSubType(), typeId, targetEntry, origRegistryEntry->targetScope, false); |
136 | |||
137 | 44 | return symbolType; | |
138 | } | ||
139 | |||
140 | /** | ||
141 | * Returns the operator function list for the current manifestation and the given node | ||
142 | * | ||
143 | * @param node Node to retrieve the op fct pointer list from | ||
144 | * @return Op fct pointer list | ||
145 | */ | ||
146 | 743 | std::vector<const Function *> &TypeChecker::getOpFctPointers(ASTNode *node) const { | |
147 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 743 times.
|
743 | assert(node->getOpFctPointers()->size() > manIdx); |
148 | 743 | return node->getOpFctPointers()->at(manIdx); | |
149 | } | ||
150 | |||
151 | /** | ||
152 | * Check if a function has been type-checked already. If not, request a revisit | ||
153 | * | ||
154 | * @param fct Function to check | ||
155 | */ | ||
156 | 20675 | void TypeChecker::requestRevisitIfRequired(const Function *fct) { | |
157 |
4/4✓ Branch 0 (2→3) taken 20605 times.
✓ Branch 1 (2→5) taken 70 times.
✓ Branch 2 (3→4) taken 11320 times.
✓ Branch 3 (3→5) taken 9285 times.
|
20675 | if (fct && !fct->alreadyTypeChecked) |
158 | 11320 | fct->entry->scope->sourceFile->reVisitRequested = true; | |
159 | 20675 | } | |
160 | |||
161 | /** | ||
162 | * Check type name against well-known type names that require a runtime import. If found one, auto-import the runtime module. | ||
163 | * | ||
164 | * @param typeName Given type name | ||
165 | */ | ||
166 | 24389 | void TypeChecker::ensureLoadedRuntimeForTypeName(const std::string &typeName) const { | |
167 |
2/2✓ Branch 0 (18→4) taken 71422 times.
✓ Branch 1 (18→19) taken 22397 times.
|
93819 | for (const auto &[wellKnownTypeName, runtimeModule] : TYPE_NAME_TO_RT_MODULE_MAPPING) { |
168 |
8/10✓ Branch 0 (7→8) taken 71422 times.
✗ Branch 1 (7→20) not taken.
✓ Branch 2 (8→9) taken 6397 times.
✓ Branch 3 (8→12) taken 65025 times.
✓ Branch 4 (9→10) taken 6397 times.
✗ Branch 5 (9→20) not taken.
✓ Branch 6 (10→11) taken 1992 times.
✓ Branch 7 (10→12) taken 4405 times.
✓ Branch 8 (13→14) taken 1992 times.
✓ Branch 9 (13→16) taken 69430 times.
|
71422 | if (typeName == wellKnownTypeName && !sourceFile->isRT(runtimeModule)) { |
169 |
1/2✓ Branch 0 (14→15) taken 1992 times.
✗ Branch 1 (14→20) not taken.
|
1992 | sourceFile->requestRuntimeModule(runtimeModule); |
170 | 1992 | break; | |
171 | } | ||
172 | } | ||
173 | 24389 | } | |
174 | |||
175 | /** | ||
176 | * Check type name against well-known function names that require a runtime import. If found one, auto-import the runtime module. | ||
177 | * | ||
178 | * @param functionName Given function name | ||
179 | */ | ||
180 | 7730 | void TypeChecker::ensureLoadedRuntimeForFunctionName(const std::string &functionName) const { | |
181 |
2/2✓ Branch 0 (18→4) taken 104569 times.
✓ Branch 1 (18→19) taken 7283 times.
|
111852 | for (const auto &[wellKnownFunctionName, runtimeModule] : FCT_NAME_TO_RT_MODULE_MAPPING) { |
182 |
8/10✓ Branch 0 (7→8) taken 104569 times.
✗ Branch 1 (7→20) not taken.
✓ Branch 2 (8→9) taken 455 times.
✓ Branch 3 (8→12) taken 104114 times.
✓ Branch 4 (9→10) taken 455 times.
✗ Branch 5 (9→20) not taken.
✓ Branch 6 (10→11) taken 447 times.
✓ Branch 7 (10→12) taken 8 times.
✓ Branch 8 (13→14) taken 447 times.
✓ Branch 9 (13→16) taken 104122 times.
|
104569 | if (functionName == wellKnownFunctionName && !sourceFile->isRT(runtimeModule)) { |
183 |
1/2✓ Branch 0 (14→15) taken 447 times.
✗ Branch 1 (14→20) not taken.
|
447 | sourceFile->requestRuntimeModule(runtimeModule); |
184 | 447 | break; | |
185 | } | ||
186 | } | ||
187 | 7730 | } | |
188 | |||
189 | /** | ||
190 | * Add a soft error to the error list | ||
191 | */ | ||
192 | 25 | void TypeChecker::softError(const ASTNode *node, const SemanticErrorType errorType, const std::string &message) const { | |
193 | 25 | resourceManager.errorManager.addSoftError(node, errorType, message); | |
194 | 25 | } | |
195 | |||
196 | } // namespace spice::compiler | ||
197 |