GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
496 / 506
98.0%
Branches:
282 / 300, 42 excl
94.0%
Functions:
35 / 35
100.0%

typechecker/function-manager.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/data/vector";
5 import "std/data/map";
6 import "std/data/unordered-map";
7 import "std/data/pair";
8 import "std/math/hash";
9
10 // Own imports
11 import "@/exception/error-manager";
12 import "@/exception/semantic-error";
13 import "@/ast/ast-nodes";
14 import "@/model/function";
15 import "@/model/generic-type";
16 import "@/model/struct";
17 import "@/symboltablebuilder/qual-type";
18 import "@/symboltablebuilder/super-type";
19 import "@/symboltablebuilder/scope";
20 import "@/symboltablebuilder/symbol-table-entry";
21 import "@/typechecker/type-matcher";
22 import "@/typechecker/type-checker";
23 import "@/util/custom-hash-functions";
24 import "@/util/lookup-cache";
25 import "@/util/concurrency";
26
27 // Aliases
28 public type FunctionManifestationList alias UnorderedMap</*mangledName=*/String, Function>;
29 public type FunctionRegistry alias Map</*fctId=*/String, /*manifestaionList=*/FunctionManifestationList>;
30 public type Arg alias Pair</*type=*/QualType, /*isTemporary=*/bool>;
31 public type ArgList alias Vector<Arg>;
32
33 type MatchResult enum {
34 MATCHED,
35 SKIP_MANIFESTATION,
36 SKIP_FUNCTION
37 }
38
39 public const string MAIN_FUNCTION_NAME = "main";
40
41 // Active function lookup cache. It is owned by the global resource manager.
42 LookupCache<Function>* ACTIVE_FUNCTION_LOOKUP_CACHE;
43
44 /**
45 * Make the given lookup cache the active one. The function lookup operates on it.
46 *
47 * @param cache Lookup cache
48 */
49 1055 public p setActiveFunctionLookupCache(LookupCache<Function>* cache) {
50 1055 ACTIVE_FUNCTION_LOOKUP_CACHE = cache;
51 1055 }
52
53 /**
54 * Get the active function lookup cache
55 *
56 * @return Active lookup cache
57 */
58 429370 f<LookupCache<Function>*> getActiveFunctionLookupCache() {
59 429370 assert ACTIVE_FUNCTION_LOOKUP_CACHE != nil<LookupCache<Function>*>;
60 429370 return ACTIVE_FUNCTION_LOOKUP_CACHE;
61 }
62
63 /**
64 * Insert a function declaration (which may still carry optional parameters) into the given scope, substantiating
65 * it into one manifestation per optional-parameter arity.
66 *
67 * @param insertScope Scope to insert the function into
68 * @param baseFunction Function to insert
69 * @param nodeFunctionList Optional list to collect all inserted manifestations into (owned by the declaration node)
70 * @return The first manifestation if nodeFunctionList is given, otherwise the last inserted one
71 */
72 53394 #[ignoreUnusedReturnValue]
73 53394 public f<Function*> insertFunction(Scope* insertScope, const Function& baseFunction, Vector<Function*>* nodeFunctionList = nil<Vector<Function*>*>) {
74 // Open a new manifestation list for the function definition
75 53394 const String fctId = baseFunction.name + ":" + baseFunction.declNode.codeLoc.toString();
76 // Keep an existing list (e.g. of the implicit ctor and copy ctor, which share a decl node)
77
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 180 times.
✓ Branch 3 taken 53204 times.
53394 if !insertScope.functions.contains(fctId) {
78 53214 insertScope.functions.upsert(fctId, FunctionManifestationList());
79 53214 }
80 53394 getActiveFunctionLookupCache().registryVersion++; // A new function might match requests that did not match anything before
81
82 // Collect substantiations
83 53394 Vector<Function> manifestations;
84 53394 substantiateOptionalParams(baseFunction, manifestations);
85 53394 assert !manifestations.isEmpty();
86
87 // Save substantiations in declaration node
88 53394 Function* manifestationPtr = nil<Function*>;
89
4/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 56069 times.
✓ Branch 3 taken 53384 times.
109473 foreach const Function& manifestation : manifestations {
90 56079 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
91 56079 assert manifestationPtr != nil<Function*>;
92
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 56069 times.
56079 if nodeFunctionList != nil<Vector<Function*>*> {
93 56069 nodeFunctionList.pushBack(manifestationPtr);
94 56069 }
95 56079 }
96
97
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 53384 times.
✗ Branch 3 not taken.
53394 if nodeFunctionList == nil<Vector<Function*>*> {
98 20 return manifestationPtr;
99 }
100
101 53384 assert !nodeFunctionList.isEmpty();
102 106768 return nodeFunctionList.front();
103 53394 }
104
105 /**
106 * Create definite functions from ambiguous ones, in regard to optional arguments.
107 *
108 * Example:
109 * int testFunc(string, int?, double?)
110 * gets
111 * int testFunc(string)
112 * int testFunc(string, int)
113 * int testFunc(string, int, double)
114 *
115 * This function also accepts functions, that are already definite, but does nothing to them.
116 *
117 * @param baseFunction Ambiguous base function
118 * @param manifestations Vector to store the definite manifestations
119 */
120 53394 public p substantiateOptionalParams(const Function& baseFunction, Vector<Function>& manifestations) {
121 // Handle the case of no parameters -> simply return the base function
122
2/2
✓ Branch 0 taken 14187 times.
✓ Branch 1 taken 39207 times.
53394 if baseFunction.paramList.isEmpty() {
123 14187 manifestations.pushBack(baseFunction);
124 14187 return;
125 }
126
127 39207 ParamList currentFunctionParamTypes;
128 39207 currentFunctionParamTypes.reserve(baseFunction.paramList.getSize());
129 39207 bool metFirstOptionalParam = false;
130 39207 Function manifestation = baseFunction;
131
132 // Loop over all parameters
133
2/2
✓ Branch 0 taken 66063 times.
✓ Branch 1 taken 39207 times.
105270 foreach const Param& param : baseFunction.paramList {
134 // Check if we have a mandatory parameter
135
2/2
✓ Branch 0 taken 63378 times.
✓ Branch 1 taken 2685 times.
66063 if !param.isOptional {
136 63378 currentFunctionParamTypes.pushBack(Param{param.qualType, false});
137 63378 continue;
138 }
139
140 // Add substantiation without the optional parameter
141
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 2649 times.
2685 if !metFirstOptionalParam {
142 2649 manifestation.paramList = currentFunctionParamTypes;
143 2649 manifestations.pushBack(manifestation);
144 // Now we cannot accept mandatory parameters anymore
145 2649 metFirstOptionalParam = true;
146 2649 }
147
148 // Add substantiation with the optional parameter
149 2685 currentFunctionParamTypes.pushBack(Param{param.qualType, false});
150 2685 manifestation.paramList = currentFunctionParamTypes;
151 2685 manifestations.pushBack(manifestation);
152 2685 }
153
154 // Ensure at least once manifestation
155
2/2
✓ Branch 0 taken 2649 times.
✓ Branch 1 taken 36558 times.
39207 if manifestations.isEmpty() {
156 36558 manifestations.pushBack(baseFunction);
157 36558 }
158 39207 }
159
160 /**
161 * Build the implicit main function, based on the given parameter types
162 *
163 * @param entry Symbol table entry of the main function
164 * @param paramTypes Parameter types of the main function
165 * @param declNode Declaration AST node
166 * @return The main function
167 */
168 22 public f<Function> createMainFunction(SymbolTableEntry* entry, const QualTypeList& paramTypes, ASTNode* declNode) {
169 22 ParamList paramList;
170
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 22 times.
26 foreach const QualType& paramType : paramTypes {
171 4 paramList.pushBack(Param{paramType, false});
172 4 }
173 22 Vector<GenericType> noTemplates;
174 44 return Function(String(MAIN_FUNCTION_NAME), entry, QualType(SuperType::TY_DYN), QualType(SuperType::TY_INT), paramList, noTemplates, declNode);
175 22 }
176
177 /**
178 * Search all manifestation lists of the given scope for a function with the given signature
179 *
180 * @param scope Scope to search in
181 * @param signature Signature to search for
182 * @return Found function or nil
183 */
184 14839 public f<Function*> findManifestationBySignature(Scope* scope, const String& signature) {
185
2/2
✓ Branch 0 taken 267496 times.
✓ Branch 1 taken 13365 times.
280861 foreach Pair<const String&, FunctionManifestationList&> entry : scope.functions {
186 267496 FunctionManifestationList& manifestations = entry.getSecond();
187
2/2
✓ Branch 0 taken 1474 times.
✓ Branch 1 taken 266022 times.
267496 if manifestations.contains(signature) {
188 1474 return &manifestations.get(signature);
189 }
190 266022 }
191 13365 return nil<Function*>;
192 }
193
194 /**
195 * Insert one already-substantiated function manifestation into the given scope
196 *
197 * @param insertScope Scope to insert the function into
198 * @param newManifestation Substantiated function manifestation
199 * @param declNode Declaration AST node
200 * @return Inserted function manifestation
201 */
202 69444 public f<Function*> insertSubstantiation(Scope* insertScope, const Function& newManifestation, const ASTNode* declNode) {
203 69444 assert newManifestation.hasSubstantiatedParams();
204
205 69444 const String signature = newManifestation.getSignature(true, true, false, true);
206
207 // Check if the function exists already
208
2/2
✓ Branch 0 taken 1055686 times.
✓ Branch 1 taken 69442 times.
1125128 foreach Pair<const String&, FunctionManifestationList&> entry : insertScope.functions {
209 1055686 FunctionManifestationList& manifestations = entry.getSecond();
210
2/2
✓ Branch 0 taken 1055684 times.
✓ Branch 1 taken 2 times.
1055686 if manifestations.contains(signature) {
211
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 const SemanticErrorType errorType = newManifestation.isFunction() ? SemanticErrorType::FUNCTION_DECLARED_TWICE : SemanticErrorType::PROCEDURE_DECLARED_TWICE;
212 2 throwSemanticError(declNode, errorType, "'" + newManifestation.getSignature(true, false) + "' is declared twice");
213 // The compilation is aborted after this stage. Until then, keep the first declaration
214 4 return &manifestations.get(signature);
215 2 }
216 1055684 }
217
218 // Retrieve the matching manifestation list of the scope
219 69442 const String fctId = newManifestation.name + ":" + declNode.codeLoc.toString();
220 69442 assert insertScope.functions.contains(fctId);
221 69442 FunctionManifestationList& manifestationList = insertScope.functions.get(fctId);
222
223 // Add substantiated function
224 69442 manifestationList.upsert(signature, newManifestation);
225 138884 return &manifestationList.get(signature);
226 69444 }
227
228 /**
229 * Search the given interface scope for a default method with exactly the given signature
230 *
231 * @param interfaceScope Scope of the interface
232 * @param name Method name
233 * @param paramTypes Parameter types
234 * @param returnType Return type
235 * @return Found default method or nil
236 */
237 20 public f<Function*> lookupDefaultMethod(Scope* interfaceScope, const String& name, const QualTypeList& paramTypes, const QualType& returnType) {
238 20 assert interfaceScope.scopeType == ScopeType::INTERFACE;
239
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 48 times.
56 foreach Pair<const String&, FunctionManifestationList&> defEntry : interfaceScope.functions {
240
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 36 times.
84 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
241 48 Function& function = manEntry.getSecond();
242
7/8
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 36 times.
✓ Branch 7 taken 12 times.
48 if function.isInterfaceDefaultMethod && function.name == name && function.getParamTypes() == paramTypes && function.returnType == returnType {
243 12 return &function;
244 }
245 36 }
246 36 }
247 8 return nil<Function*>;
248 }
249
250 /**
251 * Check if the given struct scope contains a method with the given name, that was declared by the user. Methods, that
252 * are generated by the compiler or inherited from interface default methods, do not count.
253 *
254 * @param structScope Scope of the struct
255 * @param name Method name
256 * @return Method with the name exists or not
257 */
258 15 public f<bool> hasUserMethodWithName(Scope* structScope, const String& name) {
259
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 40 times.
52 foreach Pair<const String&, FunctionManifestationList&> defEntry : structScope.functions {
260
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 37 times.
87 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
261 50 const Function& function = manEntry.getSecond();
262
6/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 47 times.
✓ Branch 5 taken 3 times.
50 if function.name == name && !function.implicitDefault && !function.isInheritedDefaultMethod() {
263 3 return true;
264 }
265 47 }
266 37 }
267 12 return false;
268 }
269
270 /**
271 * Checks if a function exists by matching it, but not setting it to used
272 *
273 * @param matchScope Scope to match against
274 * @param reqName Function name requirement
275 * @param reqThisType This type requirement
276 * @param reqArgs Argument requirement
277 * @param strictQualifierMatching Match argument and this type qualifiers strictly
278 * @return Found function or nil
279 */
280 40176 public f<const Function*> lookupFunction(Scope* matchScope, const String& reqName, const QualType& reqThisType, const ArgList& reqArgs, bool strictQualifierMatching) {
281 40176 const dyn thisTypeOptions = [SuperType::TY_DYN, SuperType::TY_STRUCT];
282 40176 assert reqThisType.isOneOf(thisTypeOptions, len(thisTypeOptions));
283
284 // The IR generator reaches this from the worker threads of the parallel back end
285 40176 ConditionalLock _lock = ConditionalLock(getSymbolRegistryMutex());
286
287 // Do cache lookup
288 40176 LookupCache<Function>* cache = getActiveFunctionLookupCache();
289 40176 const QualTypeList noTemplateTypes;
290 40176 const Hash cacheKey = getFunctionCacheKey(matchScope, reqName, reqThisType, reqArgs, noTemplateTypes);
291
2/2
✓ Branch 0 taken 10959 times.
✓ Branch 1 taken 29217 times.
40176 if cache.lookupCache.contains(cacheKey) {
292 10959 cache.lookupCacheHits++;
293 21918 return cache.lookupCache.get(cacheKey);
294 }
295 29217 cache.lookupCacheMisses++;
296
297 // Loop over function registry to find functions, that match the requirements of the call
298 29217 Vector<const Function*> matches;
299
2/2
✓ Branch 0 taken 206005 times.
✓ Branch 1 taken 29217 times.
235222 foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions {
300
2/2
✓ Branch 0 taken 93193 times.
✓ Branch 1 taken 240775 times.
333968 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
301 240775 const Function& presetFunction = manEntry.getSecond();
302 240775 assert presetFunction.hasSubstantiatedParams(); // No optional params are allowed at this point
303
304 240775 const bool anyArgGeneric = argListHasAnyGenericParts(reqArgs);
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 240775 times.
240775 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && !anyArgGeneric;
306
307 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
308 // - search for generic fct: Only match against generic preset functions
309
2/2
✓ Branch 0 taken 107966 times.
✓ Branch 1 taken 132809 times.
240775 if presetFunction.isFullySubstantiated() != requestedFullySubstantiated {
310 107966 continue;
311 }
312
313 // Check the name before copying the function, because most functions in the scope have a different name
314
2/2
✓ Branch 0 taken 108241 times.
✓ Branch 1 taken 24568 times.
132809 if !matchName(presetFunction, reqName) {
315 108241 break; // Leave the whole manifestation list, because all have the same name
316 }
317
318 // Copy the function to be able to substantiate types. Unlike in matchFunction, the type mapping is kept: a fully
319 // substantiated manifestation of a generic function still has generic template type slots, which are resolved by it
320 24568 Function candidate = presetFunction;
321
322 24568 bool forceSubstantiation = false;
323 24568 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, candidate.typeMapping, strictQualifierMatching, forceSubstantiation, nil<const ASTNode*>);
324
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24568 times.
24568 if matchResult == MatchResult::SKIP_FUNCTION {
325 ✗ break; // Leave the whole function
326 }
327
2/2
✓ Branch 0 taken 19997 times.
✓ Branch 1 taken 4571 times.
24568 if matchResult == MatchResult::SKIP_MANIFESTATION {
328 19997 continue; // Leave this manifestation and try the next one
329 }
330
331 // Add to matches
332 4571 FunctionManifestationList& matchManifestations = matchScope.functions.get(defEntry.getFirst());
333 4571 matches.pushBack(&matchManifestations.get(manEntry.getFirst()));
334
335 4571 break; // Leave the whole manifestation list to not double-match the manifestation
336 24568 }
337 206005 }
338
339 // Return the very match or a nil
340
2/2
✓ Branch 0 taken 24646 times.
✓ Branch 1 taken 4571 times.
29217 return matches.isEmpty() ? nil<const Function*> : matches.front();
341 40176 }
342
343 /**
344 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
345 * If more than one function matches the requirement, this is reported as an ambiguity error.
346 *
347 * @param matchScope Scope to match against
348 * @param reqName Function name requirement
349 * @param reqThisType This type requirement
350 * @param reqArgs Argument requirement
351 * @param templateTypeHints Template type requirement
352 * @param strictQualifierMatching Match argument and this type qualifiers strictly
353 * @param callNode Call AST node for printing error messages
354 * @return Matched function or nil
355 */
356 239396 #[ignoreUnusedReturnValue]
357 239396 public f<Function*> matchFunction(Scope* matchScope, const String& reqName, const QualType& reqThisType, const ArgList& reqArgs, const QualTypeList& templateTypeHints, bool strictQualifierMatching, const ASTNode* callNode) {
358 239396 const dyn thisTypeOptions = [SuperType::TY_DYN, SuperType::TY_STRUCT, SuperType::TY_INTERFACE];
359 239396 assert reqThisType.isOneOf(thisTypeOptions, len(thisTypeOptions));
360
361 // Do cache lookup
362 239396 LookupCache<Function>* cache = getActiveFunctionLookupCache();
363 239396 const Hash cacheKey = getFunctionCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
364
2/2
✓ Branch 0 taken 56311 times.
✓ Branch 1 taken 183085 times.
239396 if cache.lookupCache.contains(cacheKey) {
365 56311 cache.lookupCacheHits++;
366 56311 return cache.lookupCache.get(cacheKey);
367 }
368 // Requests that did not match anything do not match anything as long as no function was inserted in the meantime
369
4/4
✓ Branch 0 taken 145329 times.
✓ Branch 1 taken 37756 times.
✓ Branch 2 taken 26773 times.
✓ Branch 3 taken 156312 times.
183085 if cache.noMatchCache.contains(cacheKey) && cache.noMatchCache.get(cacheKey) == cache.registryVersion {
370 26773 cache.lookupCacheHits++;
371 26773 return nil<Function*>;
372 }
373 156312 cache.lookupCacheMisses++;
374
375 // Loop over function registry to find functions, that match the requirements of the call
376 156312 Vector<Function*> matches;
377
2/2
✓ Branch 0 taken 2329164 times.
✓ Branch 1 taken 156312 times.
2485476 foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions {
378 2329164 const String& fctId = defEntry.getFirst();
379
2/2
✓ Branch 0 taken 115730 times.
✓ Branch 1 taken 2623961 times.
2739691 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
380 2623961 const String& signature = manEntry.getFirst();
381 2623961 const Function& presetFunction = manEntry.getSecond();
382 2623961 assert presetFunction.hasSubstantiatedParams(); // No optional params are allowed at this point
383
384 // Skip generic and newly inserted substantiations to prevent double matching of a function
385
4/4
✓ Branch 0 taken 290206 times.
✓ Branch 1 taken 2333755 times.
✓ Branch 2 taken 290206 times.
✓ Branch 3 taken 2333755 times.
2623961 if presetFunction.isGenericSubstantiation() || presetFunction.isNewlyInserted {
386 290206 continue;
387 }
388
389 // Default methods are not called directly, but through the method signature of the interface
390
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 2333734 times.
2333755 if presetFunction.isInterfaceDefaultMethod {
391 21 break;
392 }
393
394 // Check the name before copying the function, because most functions in the scope have a different name
395
2/2
✓ Branch 0 taken 2198574 times.
✓ Branch 1 taken 135160 times.
2333734 if !matchName(presetFunction, reqName) {
396 2198574 break; // Leave the whole manifestation list, because all have the same name
397 }
398
399 // Copy the function to be able to substantiate types
400 135160 Function candidate = presetFunction;
401
402 // Prepare type mapping, based on the given initial type mapping
403 135160 candidate.typeMapping.clear();
404
2/2
✓ Branch 0 taken 96510 times.
✓ Branch 1 taken 46396 times.
142906 foreach unsigned long i, const GenericType& candidateTemplateType : candidate.templateTypes {
405
2/2
✓ Branch 0 taken 38650 times.
✓ Branch 1 taken 7746 times.
46396 if i >= templateTypeHints.getSize() {
406 38650 break;
407 }
408 // Skip template slots that are not generic (anymore)
409
2/2
✓ Branch 0 taken 454 times.
✓ Branch 1 taken 7292 times.
7746 if !candidateTemplateType.qualType.is(SuperType::TY_GENERIC) {
410 454 continue;
411 }
412 7292 const String& typeName = candidateTemplateType.qualType.getSubType();
413 7292 const QualType& templateType = templateTypeHints.get(i);
414 7292 candidate.typeMapping.upsert(typeName, templateType);
415 7292 }
416
417 135160 bool forceSubstantiation = false;
418 135160 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, candidate.typeMapping, strictQualifierMatching, forceSubstantiation, callNode);
419
1/2
✓ Branch 0 taken 135160 times.
✗ Branch 1 not taken.
135160 if matchResult == MatchResult::SKIP_FUNCTION {
420 ✗ break; // Leave the whole function
421 }
422
2/2
✓ Branch 0 taken 95433 times.
✓ Branch 1 taken 39727 times.
135160 if matchResult == MatchResult::SKIP_MANIFESTATION {
423 95433 continue; // Leave this manifestation and try the next one
424 }
425
426 // We found a match! -> Set the actual candidate and its entry to used
427 39727 candidate.used = true;
428 39727 candidate.entry.used = true;
429
430 // Check if the function is generic and needs to be substantiated
431
4/4
✓ Branch 0 taken 13883 times.
✓ Branch 1 taken 25844 times.
✓ Branch 2 taken 24888 times.
✓ Branch 3 taken 14839 times.
39727 if presetFunction.templateTypes.isEmpty() && !forceSubstantiation {
432 24888 assert matchScope.functions.contains(fctId);
433 24888 FunctionManifestationList& fctManifestations = matchScope.functions.get(fctId);
434 24888 assert fctManifestations.contains(signature);
435 24888 Function* fctMatch = &fctManifestations.get(signature);
436 24888 fctMatch.used = true;
437 24888 matches.pushBack(fctMatch);
438 24888 continue; // Match was successful -> match the next function
439 }
440
441 // Check if we already have this manifestation and can simply re-use it. matchManifestation may have
442 // redirected matchScope from the generic struct scope to the concrete manifestation scope.
443 14839 const String newSignature = candidate.getSignature(true, true, false, true);
444 14839 Function* existingManifestation = findManifestationBySignature(matchScope, newSignature);
445
2/2
✓ Branch 0 taken 1474 times.
✓ Branch 1 taken 13365 times.
14839 if existingManifestation != nil<Function*> {
446 1474 existingManifestation.used = true;
447 1474 matches.pushBack(existingManifestation);
448 1474 break; // Leave the whole manifestation list to not double-match the manifestation
449 }
450
451 // Insert the substantiated version if required
452 13365 Function* substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
453 13365 FunctionManifestationList& presetManifestations = matchScope.functions.get(fctId);
454 13365 substantiatedFunction.genericPreset = &presetManifestations.get(signature);
455 13365 substantiatedFunction.alreadyTypeChecked = false;
456 13365 Vector<Function*>* declManifestations = substantiatedFunction.declNode.getFctManifestations(reqName);
457 13365 declManifestations.pushBack(substantiatedFunction);
458 13365 substantiatedFunction.isNewlyInserted = true; // To not iterate over it in the same matching
459
460 // Copy function entry
461 13365 const String newScopeName = substantiatedFunction.getScopeName();
462 13365 matchScope.lookupStrict(presetFunction.entry.name).used = true;
463 13365 substantiatedFunction.entry = matchScope.symbolTable.copySymbol(presetFunction.entry.name, newScopeName);
464 13365 assert substantiatedFunction.entry != nil<SymbolTableEntry*>;
465
466 // Copy function scope. Interface method declarations have no body and therefore no child scope to copy.
467 13365 const String oldScopeName = presetFunction.getScopeName();
468
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 13358 times.
13365 if matchScope.children.contains(oldScopeName) {
469 13358 matchScope.copyChildScope(oldScopeName, newScopeName);
470 13358 Scope* childScope = matchScope.getChildScope(newScopeName);
471 13358 assert childScope != nil<Scope*>;
472 13358 childScope.isGenericScope = false;
473 13358 substantiatedFunction.bodyScope = childScope;
474
475 // Insert symbols for generic type names with concrete types into the child block
476
2/2
✓ Branch 0 taken 15230 times.
✓ Branch 1 taken 13358 times.
28588 foreach Pair<const String&, QualType&> mapping : substantiatedFunction.typeMapping {
477 15230 childScope.insertGenericType(mapping.getFirst(), GenericType(mapping.getSecond()));
478 15230 }
479
480 // Substantiate the 'this' entry in the new function scope
481
4/4
✓ Branch 0 taken 3827 times.
✓ Branch 1 taken 9531 times.
✓ Branch 2 taken 3837 times.
✓ Branch 3 taken 9521 times.
13358 if presetFunction.isMethod() && !presetFunction.templateTypes.isEmpty() {
482 9521 SymbolTableEntry* thisEntry = childScope.lookupStrict(String(THIS_VARIABLE_NAME));
483 9521 assert thisEntry != nil<SymbolTableEntry*>;
484 9521 thisEntry.updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
485 9521 }
486 13358 }
487
488 // Add to matched functions
489 13365 matches.pushBack(substantiatedFunction);
490
491 13365 break; // Leave the whole manifestation list to not double-match the manifestation
492 135160 }
493 2329164 }
494
495 // If no matches were found, remember that and return a nil
496
2/2
✓ Branch 0 taken 116948 times.
✓ Branch 1 taken 39364 times.
156312 if matches.isEmpty() {
497 116948 cache.noMatchCache.upsert(cacheKey, cache.registryVersion);
498 116948 return nil<Function*>;
499 }
500
501 // Tie-breaking: if multiple candidates match, narrow them by qualifier specificity and by preferring
502 // explicitly declared overloads over generic substitutions (see breakOverloadTie).
503 39364 breakOverloadTie(matches, reqArgs);
504
505 // Check if more than one function matches the requirements
506
2/2
✓ Branch 0 taken 39363 times.
✓ Branch 1 taken 1 time.
39364 if matches.getSize() > 1l {
507 1 String errorMessage = "The function/procedure '" + reqName + "' is ambiguous. All of the following match the requested criteria:";
508
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 foreach const Function* match : matches {
509 2 errorMessage += "\n ";
510 2 errorMessage += match.getSignature();
511 2 }
512 1 throwSemanticError(callNode, SemanticErrorType::FUNCTION_AMBIGUITY, errorMessage);
513 // The compilation is aborted after this stage. Until then, deterministically pick the first candidate
514 1 }
515 39364 Function* matchedFunction = matches.front();
516 39364 matchedFunction.isNewlyInserted = false;
517
518 // Insert into cache
519 39364 cache.lookupCache.upsert(cacheKey, matchedFunction);
520
521 // Trigger revisit in type checker if required
522 39364 requestRevisitIfRequired(matchedFunction);
523
524 // Return the very match
525 78728 return matchedFunction;
526 156312 }
527
528 240775 f<bool> argListHasAnyGenericParts(const ArgList& args) {
529
2/2
✓ Branch 0 taken 99639 times.
✓ Branch 1 taken 240775 times.
340414 foreach const Arg& arg : args {
530 99639 const QualType& argType = arg.getFirst();
531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 99639 times.
99639 if argType.hasAnyGenericParts() {
532 ✗ return true;
533 }
534 99639 }
535 240775 return false;
536 }
537
538 159728 f<MatchResult> matchManifestation(Function& candidate, Scope*& matchScope, const String& reqName, const QualType& reqThisType, const ArgList& reqArgs, TypeMapping& typeMapping, bool strictQualifierMatching, bool& forceSubstantiation, const ASTNode* callNode) {
539 // Check name requirement
540
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159728 times.
159728 if !matchName(candidate, reqName) {
541 ✗ return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
542 }
543
544 // Check 'this' type requirement
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159728 times.
159728 if !matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode) {
546 ✗ return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
547 }
548
549 // Check arg types requirement
550
2/2
✓ Branch 0 taken 115428 times.
✓ Branch 1 taken 44300 times.
159728 if !matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode) {
551 115428 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
552 }
553
554 // Check if there are unresolved generic types
555 44300 unsigned long genericCount = 0ul;
556
2/2
✓ Branch 0 taken 19206 times.
✓ Branch 1 taken 44300 times.
63506 foreach const GenericType& templateType : candidate.templateTypes {
557
2/2
✓ Branch 0 taken 1602 times.
✓ Branch 1 taken 17604 times.
19206 if templateType.qualType.is(SuperType::TY_GENERIC) {
558 17604 genericCount++;
559 17604 }
560 19206 }
561
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 44298 times.
44300 if typeMapping.getSize() < genericCount {
562 2 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
563 }
564
565 // Substantiate return type
566 44298 substantiateReturnType(candidate, typeMapping, callNode);
567
568 // Set the match scope to the scope of the concrete substantiation
569
2/2
✓ Branch 0 taken 19415 times.
✓ Branch 1 taken 24883 times.
44298 if !candidate.thisType.is(SuperType::TY_DYN) {
570 // If we only have the generic struct scope, lookup the concrete manifestation scope
571
2/2
✓ Branch 0 taken 24844 times.
✓ Branch 1 taken 39 times.
24883 if matchScope.isGenericScope {
572 39 Struct* spiceStruct = candidate.thisType.getStruct(candidate.declNode);
573 39 assert spiceStruct != nil<Struct*>;
574 39 matchScope = spiceStruct.scope;
575 39 }
576 24883 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
577 24883 }
578
579 44298 return MatchResult::MATCHED;
580 }
581
582 /**
583 * Checks if the matching candidate fulfills the name requirement
584 */
585 2626271 f<bool> matchName(const Function& candidate, const String& reqName) {
586 2626271 return candidate.name == reqName;
587 }
588
589 /**
590 * Checks if the matching candidate fulfills the 'this' type requirement
591 */
592 159728 f<bool> matchThisType(Function& candidate, const QualType& reqThisType, TypeMapping& typeMapping, bool strictQualifierMatching, const ASTNode* callNode) {
593 // Shortcut for procedures
594
4/4
✓ Branch 0 taken 65639 times.
✓ Branch 1 taken 94089 times.
✓ Branch 2 taken 94089 times.
✓ Branch 3 taken 65639 times.
159728 if candidate.thisType.is(SuperType::TY_DYN) && reqThisType.is(SuperType::TY_DYN) {
595 94089 return true;
596 }
597
598
599 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
600
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65639 times.
65639 if !matchRequestedToCandidateType(candidate.thisType, reqThisType, typeMapping, candidate.templateTypes, false, strictQualifierMatching) {
601 ✗ return false;
602 }
603
604 // Substantiate the candidate param type, based on the type mapping
605
2/2
✓ Branch 0 taken 50810 times.
✓ Branch 1 taken 14829 times.
65639 if candidate.thisType.hasAnyGenericParts() {
606 14829 substantiateTypeWithTypeMapping(candidate.thisType, typeMapping, callNode);
607 14829 }
608
609 65639 return true;
610 }
611
612 /**
613 * Checks if the matching candidate fulfills the argument types requirement
614 */
615 159728 f<bool> matchArgTypes(Function& candidate, const ArgList& reqArgs, TypeMapping& typeMapping, bool strictQualifierMatching, bool& needsSubstantiation, const ASTNode* callNode) {
616 // If the number of arguments does not match with the number of params, the matching fails
617
4/4
✓ Branch 0 taken 1283 times.
✓ Branch 1 taken 158445 times.
✓ Branch 2 taken 26503 times.
✓ Branch 3 taken 133225 times.
159728 if !candidate.isVararg && reqArgs.getSize() != candidate.paramList.getSize() {
618 26503 return false;
619 }
620 // In the case of a vararg function, we only disallow fewer arguments than parameters
621
3/4
✓ Branch 0 taken 131942 times.
✓ Branch 1 taken 1283 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 133225 times.
133225 if candidate.isVararg && reqArgs.getSize() < candidate.paramList.getSize() {
622 ✗ return false;
623 }
624
625
626 // Loop over all parameters
627
2/2
✓ Branch 0 taken 44300 times.
✓ Branch 1 taken 151183 times.
195483 foreach unsigned long i, const Arg& arg : reqArgs {
628 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in
629 // the candidate, but we need to modify the candidate param list to accept them
630
4/4
✓ Branch 0 taken 145709 times.
✓ Branch 1 taken 5474 times.
✓ Branch 2 taken 2657 times.
✓ Branch 3 taken 148526 times.
151183 if candidate.isVararg && i >= candidate.paramList.getSize() {
631 2657 const QualType varargType = arg.getFirst();
632 2657 candidate.paramList.pushBack(Param{varargType, false});
633 2657 needsSubstantiation = true; // We need to modify the candidate param types
634 2657 continue;
635 }
636
637 // Retrieve actual and requested types
638 148526 assert !candidate.paramList.get(i).isOptional;
639 148526 QualType& candidateType = candidate.paramList.get(i).qualType;
640 148526 const QualType& requestedType = arg.getFirst();
641 148526 const bool isArgTemporary = arg.getSecond();
642
643 // Check if the requested param type matches the candidate param type. The type mapping may be extended
644
2/2
✓ Branch 0 taken 59602 times.
✓ Branch 1 taken 88924 times.
148526 if !matchRequestedToCandidateType(candidateType, requestedType, typeMapping, candidate.templateTypes, false, strictQualifierMatching) {
645 88924 return false;
646 }
647
648 // Substantiate the candidate param type, based on the type mapping
649
2/2
✓ Branch 0 taken 49406 times.
✓ Branch 1 taken 10196 times.
59602 if candidateType.hasAnyGenericParts() {
650 10196 substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
651 10196 }
652
653 // Check if we try to bind a non-ref temporary to a non-const ref parameter
654
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 59601 times.
59602 if !candidateType.canBind(requestedType, isArgTemporary) {
655
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if callNode != nil<ASTNode*> {
656 1 throwSemanticError(callNode, SemanticErrorType::TEMP_TO_NON_CONST_REF, String("Temporary values can only be bound to const reference parameters"));
657 1 }
658 1 return false;
659 }
660
661 // If we have a function/procedure type we need to take care of the information, if it takes captures
662 59601 const dyn fctOptions = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
663 59601 const QualType requestedBaseType = requestedType.getBase();
664
4/4
✓ Branch 0 taken 59465 times.
✓ Branch 1 taken 136 times.
✓ Branch 2 taken 59558 times.
✓ Branch 3 taken 43 times.
59601 if requestedBaseType.isOneOf(fctOptions, len(fctOptions)) && requestedType.hasLambdaCaptures() {
665 43 candidateType = candidateType.getWithLambdaCaptures();
666 43 needsSubstantiation = true;
667 43 }
668 59601 }
669
670 44300 return true;
671 }
672
673 /**
674 * Substantiates the candidate return type, based on the given type mapping
675 */
676 44298 public p substantiateReturnType(Function& candidate, const TypeMapping& typeMapping, const ASTNode* callNode) {
677
2/2
✓ Branch 0 taken 40590 times.
✓ Branch 1 taken 3708 times.
44298 if candidate.returnType.hasAnyGenericParts() {
678 3708 substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
679 3708 }
680 44298 }
681
682 /**
683 * Narrow a multi-match overload set by preferring the candidate whose parameter qualifiers most closely match
684 * the argument qualifiers. This resolves the typical copy-vs-move ctor ambiguity where both a `const T&`
685 * (copy) and a `T&` (move) ctor match a non-const lvalue argument - we prefer the non-const-ref candidate
686 * (move) since it requires no constification. When the argument is const, we prefer the const-ref candidate
687 * (copy) since binding to a non-const ref would require const-loss. As a secondary criterion, an explicitly
688 * declared (non-generic) overload is preferred over a generic substitution that matches equally well.
689 *
690 * Modifies `matches` in place, removing any candidate that scores worse than the best one. A no-op if there
691 * are fewer than two candidates.
692 */
693 39364 public p breakOverloadTie(Vector<Function*>& matches, const ArgList& reqArgs) {
694
2/2
✓ Branch 0 taken 39001 times.
✓ Branch 1 taken 363 times.
39364 if matches.getSize() < 2l {
695 39001 return;
696 }
697
698 363 const int constifyPenalty = 1;
699 363 const int constLossPenalty = 100;
700 // An exact type match is always preferred over a match that required an implicit upcast (struct to
701 // implemented interface, or struct to composed base struct). This keeps e.g. 'f(Derived)' preferred
702 // over 'f(Base)' when called with a Derived, instead of reporting an ambiguity.
703 363 const int upcastPenalty = 1000;
704
705 363 int bestScore = 2147483647;
706
2/2
✓ Branch 0 taken 726 times.
✓ Branch 1 taken 363 times.
1089 foreach const Function* m : matches {
707 726 const int score = scoreSpecificity(*m, reqArgs, constifyPenalty, constLossPenalty, upcastPenalty);
708
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 368 times.
726 if score < bestScore {
709 368 bestScore = score;
710 368 }
711 726 }
712
713 363 Vector<Function*> filtered;
714 363 filtered.reserve(matches.getSize());
715
2/2
✓ Branch 0 taken 726 times.
✓ Branch 1 taken 363 times.
1089 foreach Function* m : matches {
716
2/2
✓ Branch 0 taken 357 times.
✓ Branch 1 taken 369 times.
726 if scoreSpecificity(*m, reqArgs, constifyPenalty, constLossPenalty, upcastPenalty) == bestScore {
717 369 filtered.pushBack(m);
718 369 }
719 726 }
720 363 matches = filtered;
721
722 // Secondary tie-break: prefer an explicitly declared (non-generic) overload over a generic substitution
723 // when both match equally well, mirroring C++ overload resolution where a non-template wins over a
724 // template specialization.
725 // A generic substitution that loses here and was only inserted for this very match is removed from its
726 // declaration's manifestation list again, so the IR generator never emits a manifestation that was never
727 // type-checked (and so we leave no dead code behind).
728 363 bool anyNonGeneric = false;
729
2/2
✓ Branch 0 taken 369 times.
✓ Branch 1 taken 363 times.
732 foreach const Function* m : matches {
730
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 346 times.
369 if !m.isGenericSubstantiation() {
731 346 anyNonGeneric = true;
732 346 }
733 369 }
734
4/4
✓ Branch 0 taken 357 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 358 times.
✓ Branch 3 taken 5 times.
363 if matches.getSize() > 1l && anyNonGeneric {
735
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
15 foreach Function* m : matches {
736
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
10 if m.isGenericSubstantiation() && m.isNewlyInserted {
737 5 eraseFunction(m.declNode.getFctManifestations(m.name), m);
738 5 }
739 10 }
740 5 Vector<Function*> nonGenericMatches;
741
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
15 foreach Function* m : matches {
742
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if !m.isGenericSubstantiation() {
743 5 nonGenericMatches.pushBack(m);
744 5 }
745 10 }
746 5 matches = nonGenericMatches;
747 5 }
748 363 }
749
750 /**
751 * Remove all occurrences of a function from a function list
752 *
753 * @param functions Function list
754 * @param fct Function to remove
755 */
756 5 p eraseFunction(Vector<Function*>* functions, const Function* fct) {
757 5 unsigned long i = 0ul;
758
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 5 times.
17 while i < functions.getSize() {
759
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
12 if functions.get(i) == fct {
760 4 functions.removeAt(i);
761 4 } else {
762 8 i++;
763 8 }
764 12 }
765 5 }
766
767 1452 f<int> scoreSpecificity(const Function& fct, const ArgList& reqArgs, int constifyPenalty, int constLossPenalty, int upcastPenalty) {
768 1452 int penalty = 0;
769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1452 times.
1452 unsigned long limit = fct.paramList.getSize() < reqArgs.getSize() ? fct.paramList.getSize() : reqArgs.getSize();
770 1452 unsigned long i = 0ul;
771
2/2
✓ Branch 0 taken 2872 times.
✓ Branch 1 taken 1452 times.
4324 while i < limit {
772 2872 const QualType& paramType = fct.paramList.get(i).qualType;
773 2872 const Arg& arg = reqArgs.get(i);
774 2872 const QualType& argType = arg.getFirst();
775 2872 const QualType unwrappedParamType = paramType.removeReferenceWrapper();
776 2872 const QualType unwrappedArgType = argType.removeReferenceWrapper();
777 // Compare the qualifier instead of isConst(), so that the const of a pointee counts as well
778 2872 const bool paramIsConst = unwrappedParamType.qualifiers.isConst;
779 2872 const bool argIsConst = unwrappedArgType.qualifiers.isConst;
780
4/4
✓ Branch 0 taken 2134 times.
✓ Branch 1 taken 738 times.
✓ Branch 2 taken 726 times.
✓ Branch 3 taken 2146 times.
2872 if paramIsConst && !argIsConst {
781 726 penalty += constifyPenalty;
782
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2134 times.
✓ Branch 2 taken 2070 times.
✓ Branch 3 taken 76 times.
2872 } else if !paramIsConst && argIsConst {
783 76 penalty += constLossPenalty;
784 2146 }
785 // Penalize matches that were only possible through an implicit upcast
786 2872 QualType paramBase = paramType;
787 2872 QualType argBase = argType;
788 2872 unwrapBothWithRefWrappers(paramBase, argBase);
789
1/2
✓ Branch 0 taken 2872 times.
✗ Branch 1 not taken.
2872 if !paramBase.matches(argBase, true, true, true) {
790 ✗ penalty += upcastPenalty;
791 ✗ }
792 2872 i++;
793 2872 }
794 1452 return penalty;
795 }
796
797 type CtorKind enum {
798 ANY_NON_COPY_NON_MOVE,
799 COPY,
800 MOVE
801 }
802
803 /**
804 * Checks if the given function takes exactly one parameter, which is a reference to its own 'this' type
805 */
806 185144 f<bool> hasSingleSelfRefParam(const Function& function) {
807
2/2
✓ Branch 0 taken 55296 times.
✓ Branch 1 taken 129848 times.
185144 if function.paramList.getSize() != 1l {
808 55296 return false;
809 }
810 129848 const QualType& paramType = function.paramList.get(0ul).qualType;
811 129848 const QualType paramBaseType = paramType.getBase();
812 // Compare without qualifiers: getBase() keeps the ref's qualifiers, so e.g. the base of 'const Foo&' is a const Foo
813
2/2
✓ Branch 0 taken 56368 times.
✓ Branch 1 taken 73480 times.
129848 return paramType.isRef() && paramBaseType.matches(function.thisType, false, true, true);
814 }
815
816 58373 f<bool> isFirstParamConstRef(const Function& function) {
817 58373 const QualType& paramType = function.paramList.get(0ul).qualType;
818 58373 return paramType.isConstRef();
819 }
820
821 56997 f<bool> hasCtor(Scope* matchScope, CtorKind kind) {
822
2/2
✓ Branch 0 taken 474090 times.
✓ Branch 1 taken 31497 times.
505587 foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions {
823
2/2
✓ Branch 0 taken 448590 times.
✓ Branch 1 taken 524279 times.
972869 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
824 524279 const Function& function = manEntry.getSecond();
825
2/2
✓ Branch 0 taken 377234 times.
✓ Branch 1 taken 147045 times.
524279 if function.name != String(CTOR_FUNCTION_NAME) {
826 377234 continue;
827 }
828 147045 const bool singleSelfRefParam = hasSingleSelfRefParam(function);
829
2/2
✓ Branch 0 taken 121845 times.
✓ Branch 1 taken 25200 times.
147045 const bool isCopyCtor = singleSelfRefParam && isFirstParamConstRef(function);
830
2/2
✓ Branch 0 taken 121845 times.
✓ Branch 1 taken 25200 times.
147045 const bool isMoveCtor = singleSelfRefParam && !isFirstParamConstRef(function);
831
4/4
✓ Branch 0 taken 5238 times.
✓ Branch 1 taken 141807 times.
✓ Branch 2 taken 122844 times.
✓ Branch 3 taken 24201 times.
147045 if kind == CtorKind::COPY && isCopyCtor {
832 24201 return true;
833 }
834
4/4
✓ Branch 0 taken 119115 times.
✓ Branch 1 taken 3729 times.
✓ Branch 2 taken 122840 times.
✓ Branch 3 taken 4 times.
122844 if kind == CtorKind::MOVE && isMoveCtor {
835 4 return true;
836 }
837
6/6
✓ Branch 0 taken 1509 times.
✓ Branch 1 taken 121331 times.
✓ Branch 2 taken 197 times.
✓ Branch 3 taken 1312 times.
✓ Branch 4 taken 1295 times.
✓ Branch 5 taken 121545 times.
122840 if kind == CtorKind::ANY_NON_COPY_NON_MOVE && !isCopyCtor && !isMoveCtor {
838 1295 return true;
839 }
840 121545 }
841 448590 }
842 31497 return false;
843 }
844
845 /**
846 * Check if the given scope contains any constructor at all
847 *
848 * @param matchScope Scope to search in
849 * @return Has any constructor or not
850 */
851 /**
852 * Calculate the cache key for the function lookup cache
853 *
854 * @param scope Scope to match against
855 * @param name Function name requirement
856 * @param thisType This type requirement
857 * @param args Argument requirement
858 * @param templateTypes Template type requirement
859 * @return Cache key
860 */
861 279572 f<Hash> getFunctionCacheKey(const Scope* scope, const String& name, const QualType& thisType, const ArgList& args, const QualTypeList& templateTypes) {
862 279572 Hash hash = 0ul;
863 279572 hashCombine64(hash, hashPointer(scope));
864 279572 hashCombine64(hash, hash(name));
865 279572 hashCombine64(hash, hashQualType(thisType));
866
2/2
✓ Branch 0 taken 392162 times.
✓ Branch 1 taken 279572 times.
671734 foreach const Arg& arg : args {
867 392162 hashCombine64(hash, hashQualType(arg.getFirst()));
868 392162 hashCombine64(hash, hash(arg.getSecond()));
869 392162 }
870 279572 hashCombine64(hash, hashQualTypeList(templateTypes));
871 279572 return hash;
872 }
873
874 5354 public f<bool> hasAnyCtor(Scope* matchScope) {
875
2/2
✓ Branch 0 taken 1399 times.
✓ Branch 1 taken 9756 times.
11155 foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions {
876
2/2
✓ Branch 0 taken 9978 times.
✓ Branch 1 taken 5801 times.
15779 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
877
2/2
✓ Branch 0 taken 6023 times.
✓ Branch 1 taken 3955 times.
9978 if manEntry.getSecond().name == String(CTOR_FUNCTION_NAME) {
878 3955 return true;
879 }
880 6023 }
881 5801 }
882 1399 return false;
883 }
884
885 /**
886 * Check if the given scope contains a constructor, that is neither a copy nor a move constructor
887 *
888 * @param matchScope Scope to search in
889 * @return Has such a constructor or not
890 */
891 1466 public f<bool> hasAnyNonCopyCtor(Scope* matchScope) {
892 1466 return hasCtor(matchScope, CtorKind::ANY_NON_COPY_NON_MOVE);
893 }
894
895 /**
896 * Check if the given scope contains a copy constructor (taking a single 'const T&' parameter)
897 *
898 * @param matchScope Scope to search in
899 * @return Has a copy constructor or not
900 */
901 53218 public f<bool> hasCopyCtor(Scope* matchScope) {
902 53218 return hasCtor(matchScope, CtorKind::COPY);
903 }
904
905 /**
906 * Check if the given scope contains a user-defined (not implicitly generated) copy constructor
907 *
908 * @param matchScope Scope to search in
909 * @return Has a user-defined copy constructor or not
910 */
911 2309 public f<bool> hasUserCopyCtor(Scope* matchScope) {
912
2/2
✓ Branch 0 taken 11174 times.
✓ Branch 1 taken 1980 times.
13154 foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions {
913
2/2
✓ Branch 0 taken 11874 times.
✓ Branch 1 taken 10845 times.
22719 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
914 11874 const Function& function = manEntry.getSecond();
915
4/4
✓ Branch 0 taken 8161 times.
✓ Branch 1 taken 3713 times.
✓ Branch 2 taken 8749 times.
✓ Branch 3 taken 3125 times.
11874 if function.name != String(CTOR_FUNCTION_NAME) || function.implicitDefault {
916 8749 continue;
917 }
918
2/2
✓ Branch 0 taken 2796 times.
✓ Branch 1 taken 329 times.
3125 const bool isCopyCtor = hasSingleSelfRefParam(function) && isFirstParamConstRef(function);
919
2/2
✓ Branch 0 taken 2796 times.
✓ Branch 1 taken 329 times.
3125 if isCopyCtor {
920 329 return true;
921 }
922 2796 }
923 10845 }
924 1980 return false;
925 }
926
927 /**
928 * Check if the given scope contains a move constructor (taking a single non-const 'T&' parameter)
929 *
930 * @param matchScope Scope to search in
931 * @return Has a move constructor or not
932 */
933 2313 public f<bool> hasMoveCtor(Scope* matchScope) {
934 2313 return hasCtor(matchScope, CtorKind::MOVE);
935 }
936
937 /**
938 * Find the move constructor (taking a single non-const 'T&' parameter) in the given scope
939 *
940 * @param matchScope Scope to search in
941 * @return Move constructor, or nil if the scope has none
942 */
943 12558 public f<Function*> findMoveCtor(Scope* matchScope) {
944
2/2
✓ Branch 0 taken 132255 times.
✓ Branch 1 taken 12289 times.
144544 foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions {
945
2/2
✓ Branch 0 taken 168992 times.
✓ Branch 1 taken 131986 times.
300978 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
946 168992 Function& function = manEntry.getSecond();
947
2/2
✓ Branch 0 taken 134018 times.
✓ Branch 1 taken 34974 times.
168992 if function.name != String(CTOR_FUNCTION_NAME) {
948 134018 continue;
949 }
950
2/2
✓ Branch 0 taken 27330 times.
✓ Branch 1 taken 7644 times.
34974 const bool isMoveCtor = hasSingleSelfRefParam(function) && !isFirstParamConstRef(function);
951
2/2
✓ Branch 0 taken 34705 times.
✓ Branch 1 taken 269 times.
34974 if isMoveCtor {
952 269 return &function;
953 }
954 34705 }
955 131986 }
956 12289 return nil<Function*>;
957 }
958
959 /**
960 * Check if the given scope contains a default constructor (taking no parameters)
961 *
962 * @param matchScope Scope to search in
963 * @return Has a default constructor or not
964 */
965 14 public f<bool> hasDefaultCtor(Scope* matchScope) {
966
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
67 foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions {
967
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 53 times.
120 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
968 67 const Function& function = manEntry.getSecond();
969
4/4
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 53 times.
✓ Branch 3 taken 14 times.
67 if function.name == String(CTOR_FUNCTION_NAME) && function.paramList.isEmpty() {
970 14 return true;
971 }
972 53 }
973 53 }
974 ✗ return false;
975 }
976
977 /**
978 * Check if the given scope contains a destructor
979 *
980 * @param matchScope Scope to search in
981 * @return Has a destructor or not
982 */
983 46841 public f<bool> hasDtor(Scope* matchScope) {
984
2/2
✓ Branch 0 taken 36115 times.
✓ Branch 1 taken 442073 times.
478188 foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions {
985
2/2
✓ Branch 0 taken 488725 times.
✓ Branch 1 taken 431347 times.
920072 foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() {
986
2/2
✓ Branch 0 taken 477999 times.
✓ Branch 1 taken 10726 times.
488725 if manEntry.getSecond().name == String(DTOR_FUNCTION_NAME) {
987 10726 return true;
988 }
989 477999 }
990 431347 }
991 36115 return false;
992 }
993
994 /**
995 * Get the version of the function registry, which is increased whenever a new function is inserted
996 *
997 * @return Registry version
998 */
999 95644 public f<unsigned long> getFunctionRegistryVersion() {
1000 95644 return getActiveFunctionLookupCache().registryVersion;
1001 }
1002
1003 /**
1004 * Dump usage statistics for the lookup cache
1005 */
1006 760 public f<String> dumpFunctionLookupCacheStatistics() {
1007 760 LookupCache<Function>* cache = getActiveFunctionLookupCache();
1008 760 return cache.dumpLookupCacheStatistics("FunctionManager");
1009 }
1010