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 | 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 | 109473 | foreach const Function& manifestation : manifestations { | |
| 90 | 56079 | manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode); | |
| 91 | 56079 | assert manifestationPtr != nil<Function*>; | |
| 92 | 56079 | if nodeFunctionList != nil<Vector<Function*>*> { | |
| 93 | 56069 | nodeFunctionList.pushBack(manifestationPtr); | |
| 94 | 56069 | } | |
| 95 | 56079 | } | |
| 96 | |||
| 97 | 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 | 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 | 105270 | foreach const Param& param : baseFunction.paramList { | |
| 134 | // Check if we have a mandatory parameter | ||
| 135 | 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 | 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 | 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 | 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 | 280861 | foreach Pair<const String&, FunctionManifestationList&> entry : scope.functions { | |
| 186 | 267496 | FunctionManifestationList& manifestations = entry.getSecond(); | |
| 187 | 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 | 1125128 | foreach Pair<const String&, FunctionManifestationList&> entry : insertScope.functions { | |
| 209 | 1055686 | FunctionManifestationList& manifestations = entry.getSecond(); | |
| 210 | 1055686 | if manifestations.contains(signature) { | |
| 211 | 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 | 56 | foreach Pair<const String&, FunctionManifestationList&> defEntry : interfaceScope.functions { | |
| 240 | 84 | foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() { | |
| 241 | 48 | Function& function = manEntry.getSecond(); | |
| 242 | 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 | 52 | foreach Pair<const String&, FunctionManifestationList&> defEntry : structScope.functions { | |
| 260 | 87 | foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() { | |
| 261 | 50 | const Function& function = manEntry.getSecond(); | |
| 262 | 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 | 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 | 235222 | foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions { | |
| 300 | 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 | 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 | 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 | 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 | 24568 | if matchResult == MatchResult::SKIP_FUNCTION { | |
| 325 | ✗ | break; // Leave the whole function | |
| 326 | } | ||
| 327 | 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 | 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 | 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 | 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 | 2485476 | foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions { | |
| 378 | 2329164 | const String& fctId = defEntry.getFirst(); | |
| 379 | 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 | 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 | 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 | 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 | 142906 | foreach unsigned long i, const GenericType& candidateTemplateType : candidate.templateTypes { | |
| 405 | 46396 | if i >= templateTypeHints.getSize() { | |
| 406 | 38650 | break; | |
| 407 | } | ||
| 408 | // Skip template slots that are not generic (anymore) | ||
| 409 | 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 | 135160 | if matchResult == MatchResult::SKIP_FUNCTION { | |
| 420 | ✗ | break; // Leave the whole function | |
| 421 | } | ||
| 422 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 39364 | if matches.getSize() > 1l { | |
| 507 | 1 | String errorMessage = "The function/procedure '" + reqName + "' is ambiguous. All of the following match the requested criteria:"; | |
| 508 | 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 | 340414 | foreach const Arg& arg : args { | |
| 530 | 99639 | const QualType& argType = arg.getFirst(); | |
| 531 | 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 | 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 | 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 | 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 | 63506 | foreach const GenericType& templateType : candidate.templateTypes { | |
| 557 | 19206 | if templateType.qualType.is(SuperType::TY_GENERIC) { | |
| 558 | 17604 | genericCount++; | |
| 559 | 17604 | } | |
| 560 | 19206 | } | |
| 561 | 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 | 44298 | if !candidate.thisType.is(SuperType::TY_DYN) { | |
| 570 | // If we only have the generic struct scope, lookup the concrete manifestation scope | ||
| 571 | 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 | 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 | 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 | 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 | 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 | 133225 | if candidate.isVararg && reqArgs.getSize() < candidate.paramList.getSize() { | |
| 622 | ✗ | return false; | |
| 623 | } | ||
| 624 | |||
| 625 | |||
| 626 | // Loop over all parameters | ||
| 627 | 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 | 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 | 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 | 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 | 59602 | if !candidateType.canBind(requestedType, isArgTemporary) { | |
| 655 | 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 | 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 | 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 | 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 | 1089 | foreach const Function* m : matches { | |
| 707 | 726 | const int score = scoreSpecificity(*m, reqArgs, constifyPenalty, constLossPenalty, upcastPenalty); | |
| 708 | 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 | 1089 | foreach Function* m : matches { | |
| 716 | 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 | 732 | foreach const Function* m : matches { | |
| 730 | 369 | if !m.isGenericSubstantiation() { | |
| 731 | 346 | anyNonGeneric = true; | |
| 732 | 346 | } | |
| 733 | 369 | } | |
| 734 | 363 | if matches.getSize() > 1l && anyNonGeneric { | |
| 735 | 15 | foreach Function* m : matches { | |
| 736 | 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 | 15 | foreach Function* m : matches { | |
| 742 | 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 | 17 | while i < functions.getSize() { | |
| 759 | 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 | 1452 | unsigned long limit = fct.paramList.getSize() < reqArgs.getSize() ? fct.paramList.getSize() : reqArgs.getSize(); | |
| 770 | 1452 | unsigned long i = 0ul; | |
| 771 | 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 | 2872 | if paramIsConst && !argIsConst { | |
| 781 | 726 | penalty += constifyPenalty; | |
| 782 | 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 | 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 | 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 | 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 | 505587 | foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions { | |
| 823 | 972869 | foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() { | |
| 824 | 524279 | const Function& function = manEntry.getSecond(); | |
| 825 | 524279 | if function.name != String(CTOR_FUNCTION_NAME) { | |
| 826 | 377234 | continue; | |
| 827 | } | ||
| 828 | 147045 | const bool singleSelfRefParam = hasSingleSelfRefParam(function); | |
| 829 | 147045 | const bool isCopyCtor = singleSelfRefParam && isFirstParamConstRef(function); | |
| 830 | 147045 | const bool isMoveCtor = singleSelfRefParam && !isFirstParamConstRef(function); | |
| 831 | 147045 | if kind == CtorKind::COPY && isCopyCtor { | |
| 832 | 24201 | return true; | |
| 833 | } | ||
| 834 | 122844 | if kind == CtorKind::MOVE && isMoveCtor { | |
| 835 | 4 | return true; | |
| 836 | } | ||
| 837 | 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 | 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 | 11155 | foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions { | |
| 876 | 15779 | foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() { | |
| 877 | 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 | 13154 | foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions { | |
| 913 | 22719 | foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() { | |
| 914 | 11874 | const Function& function = manEntry.getSecond(); | |
| 915 | 11874 | if function.name != String(CTOR_FUNCTION_NAME) || function.implicitDefault { | |
| 916 | 8749 | continue; | |
| 917 | } | ||
| 918 | 3125 | const bool isCopyCtor = hasSingleSelfRefParam(function) && isFirstParamConstRef(function); | |
| 919 | 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 | 144544 | foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions { | |
| 945 | 300978 | foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() { | |
| 946 | 168992 | Function& function = manEntry.getSecond(); | |
| 947 | 168992 | if function.name != String(CTOR_FUNCTION_NAME) { | |
| 948 | 134018 | continue; | |
| 949 | } | ||
| 950 | 34974 | const bool isMoveCtor = hasSingleSelfRefParam(function) && !isFirstParamConstRef(function); | |
| 951 | 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 | 67 | foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions { | |
| 967 | 120 | foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() { | |
| 968 | 67 | const Function& function = manEntry.getSecond(); | |
| 969 | 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 | 478188 | foreach Pair<const String&, FunctionManifestationList&> defEntry : matchScope.functions { | |
| 985 | 920072 | foreach Pair<const String&, Function&> manEntry : defEntry.getSecond() { | |
| 986 | 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 |