model/function.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/text/stringstream"; | ||
| 6 | |||
| 7 | // Own imports | ||
| 8 | import "@/ast/ast-nodes"; | ||
| 9 | import "@/reader/code-loc"; | ||
| 10 | import "@/symboltablebuilder/super-type"; | ||
| 11 | import "@/symboltablebuilder/qual-type"; | ||
| 12 | import "@/symboltablebuilder/symbol-table-entry"; | ||
| 13 | import "@/symboltablebuilder/scope"; | ||
| 14 | import "@/model/generic-type"; | ||
| 15 | import "@/irgenerator/name-mangling"; | ||
| 16 | |||
| 17 | // Helper structs | ||
| 18 | 962992 | public type Param struct { | |
| 19 | public QualType qualType | ||
| 20 | public bool isOptional = false | ||
| 21 | } | ||
| 22 | |||
| 23 | // Type aliases | ||
| 24 | public type ParamList alias Vector<Param>; | ||
| 25 | |||
| 26 | 749814 | public type Function struct { | |
| 27 | public String name | ||
| 28 | public QualType thisType | ||
| 29 | public QualType returnType | ||
| 30 | public ParamList paramList | ||
| 31 | public Vector<GenericType> templateTypes | ||
| 32 | public TypeMapping typeMapping | ||
| 33 | 57852 | public SymbolTableEntry* entry = nil<SymbolTableEntry*> | |
| 34 | 57852 | public ASTNode* declNode = nil<ASTNode*> | |
| 35 | 57852 | public Scope* bodyScope = nil<Scope*> | |
| 36 | public String predefinedMangledName | ||
| 37 | public String mangleSuffix | ||
| 38 | 57852 | public Function* genericPreset = nil<Function*> | |
| 39 | // Interface default method, that implements this struct method, if the struct does not implement it itself | ||
| 40 | 57852 | public Function* defaultMethod = nil<Function*> | |
| 41 | 57852 | public bool isVararg = false | |
| 42 | 57852 | public bool mangleFunctionName = true | |
| 43 | 57852 | public bool alreadyTypeChecked = false | |
| 44 | 57852 | public bool used = false | |
| 45 | 57852 | public bool implicitDefault = false | |
| 46 | 57852 | public bool isVirtual = false | |
| 47 | 57852 | public bool isNewlyInserted = false | |
| 48 | 57852 | public bool isInterfaceDefaultMethod = false // Default implementation of an interface method, defined as method of the interface | |
| 49 | 57852 | public unsigned long vtableIndex = 0ul | |
| 50 | } | ||
| 51 | |||
| 52 | 57852 | public p Function.ctor(const String& name, SymbolTableEntry* entry, const QualType& thisType, const QualType& returnType, const ParamList& paramList, const Vector<GenericType>& templateTypes, ASTNode* declNode) { | |
| 53 | 57852 | this.name = name; | |
| 54 | 57852 | this.entry = entry; | |
| 55 | 57852 | this.thisType = thisType; | |
| 56 | 57852 | this.returnType = returnType; | |
| 57 | 57852 | this.paramList = paramList; | |
| 58 | 57852 | this.templateTypes = templateTypes; | |
| 59 | 57852 | this.declNode = declNode; | |
| 60 | 57852 | } | |
| 61 | |||
| 62 | /** | ||
| 63 | * Retrieve the parameter types of the current function | ||
| 64 | * | ||
| 65 | * @return Vector of parameter types | ||
| 66 | */ | ||
| 67 | 255601 | public f<QualTypeList> Function.getParamTypes() { | |
| 68 | 255601 | result = Vector<QualType>(); | |
| 69 | 647643 | foreach const Param& param : this.paramList { | |
| 70 | 392042 | result.pushBack(param.qualType); | |
| 71 | 392042 | } | |
| 72 | 255601 | } | |
| 73 | |||
| 74 | /** | ||
| 75 | * Get a string representation of the current function. | ||
| 76 | * | ||
| 77 | * Example: | ||
| 78 | * string Vector<double>.setData<double>(double) | ||
| 79 | * | ||
| 80 | * @param withThisType Include 'this' type in signature | ||
| 81 | * @param withTemplateTypes Include concrete template types in the signature | ||
| 82 | * @param withTypeAliases Print type aliases as is and not decompose them | ||
| 83 | * @param withSize Print array sizes | ||
| 84 | * @return String representation as function signature | ||
| 85 | */ | ||
| 86 | 248398 | public f<String> Function.getSignature(bool withThisType = true, bool withTemplateTypes = true, bool withTypeAliases = true, bool withSize = false) { | |
| 87 | 248398 | QualTypeList concreteTemplateTypes; | |
| 88 | 248398 | if withTemplateTypes { | |
| 89 | 248396 | concreteTemplateTypes.reserve(this.templateTypes.getSize()); | |
| 90 | 378577 | foreach const GenericType& genericType : this.templateTypes { | |
| 91 | 130181 | if genericType.qualType.is(SuperType::TY_GENERIC) && !this.typeMapping.isEmpty() { | |
| 92 | 74370 | const String& subType = genericType.qualType.getSubType(); | |
| 93 | 74370 | assert this.typeMapping.contains(subType); | |
| 94 | 74370 | concreteTemplateTypes.pushBack(this.typeMapping.get(subType)); | |
| 95 | 74370 | } else { | |
| 96 | 55811 | concreteTemplateTypes.pushBack(genericType.qualType); | |
| 97 | 55811 | } | |
| 98 | 130181 | } | |
| 99 | 248396 | } | |
| 100 | |||
| 101 | 496796 | return getFunctionSignature(this.name, this.thisType, this.returnType, this.paramList, concreteTemplateTypes, true, withThisType, true, withTypeAliases, withSize); | |
| 102 | 248398 | } | |
| 103 | |||
| 104 | /** | ||
| 105 | * Get a string representation of a function. This is a free function, since Spice has no static methods. | ||
| 106 | * | ||
| 107 | * @param name Function name | ||
| 108 | * @param thisType This symbol type | ||
| 109 | * @param returnType Return symbol type | ||
| 110 | * @param paramList Param symbol types | ||
| 111 | * @param concreteTemplateTypes Concrete template symbol types | ||
| 112 | * @param withReturnType Include return type in signature | ||
| 113 | * @param withThisType Include 'this' type in signature | ||
| 114 | * @param ignorePublic Not include public modifiers in signature | ||
| 115 | * @param withTypeAliases Print type aliases as is and not decompose them | ||
| 116 | * @param withSize Print array sizes | ||
| 117 | * @return Function signature | ||
| 118 | */ | ||
| 119 | 248419 | public f<String> getFunctionSignature(const String& name, const QualType& thisType, const QualType& returnType, const ParamList& paramList, const QualTypeList& concreteTemplateTypes, bool withReturnType = true, bool withThisType = true, bool ignorePublic = true, bool withTypeAliases = true, bool withSize = false) { | |
| 120 | 248419 | StringStream signature; | |
| 121 | |||
| 122 | // Build return type string | ||
| 123 | 248419 | if withReturnType { | |
| 124 | 248398 | if returnType.is(SuperType::TY_DYN) { | |
| 125 | 100289 | signature << "p "; | |
| 126 | 100289 | } else { | |
| 127 | 148109 | signature << "f<"; | |
| 128 | 148109 | returnType.getName(signature, withSize, ignorePublic, withTypeAliases); | |
| 129 | 148109 | signature << "> "; | |
| 130 | 148109 | } | |
| 131 | 248398 | } | |
| 132 | |||
| 133 | // Build this type string | ||
| 134 | 248419 | if withThisType && !thisType.is(SuperType::TY_DYN) { | |
| 135 | 73639 | const QualType thisBaseType = thisType.getBase(); | |
| 136 | 73639 | signature << thisBaseType.getSubType(); | |
| 137 | 73639 | const QualTypeList& thisTemplateTypes = thisType.getTemplateTypes(); | |
| 138 | 73639 | if !thisTemplateTypes.isEmpty() { | |
| 139 | 35925 | signature << "<"; | |
| 140 | 79218 | foreach unsigned long i, const QualType& templateType : thisTemplateTypes { | |
| 141 | 43293 | if i > 0ul { signature << ","; } | |
| 142 | 43293 | templateType.getName(signature, withSize, ignorePublic, withTypeAliases); | |
| 143 | 43293 | } | |
| 144 | 35925 | signature << ">"; | |
| 145 | 35925 | } | |
| 146 | 73639 | signature << MEMBER_ACCESS_TOKEN; | |
| 147 | 73639 | } | |
| 148 | |||
| 149 | // Name | ||
| 150 | 248419 | signature << name; | |
| 151 | |||
| 152 | // Build template type string | ||
| 153 | 248419 | if !concreteTemplateTypes.isEmpty() { | |
| 154 | 107369 | signature << "<"; | |
| 155 | 237550 | foreach unsigned long i, const QualType& templateType : concreteTemplateTypes { | |
| 156 | 130181 | if i > 0ul { signature << ","; } | |
| 157 | 130181 | templateType.getName(signature, withSize, ignorePublic, withTypeAliases); | |
| 158 | 130181 | } | |
| 159 | 107369 | signature << ">"; | |
| 160 | 107369 | } | |
| 161 | |||
| 162 | // Parameter type string | ||
| 163 | 248419 | signature << "("; | |
| 164 | 538853 | foreach unsigned long i, const Param& param : paramList { | |
| 165 | 290434 | if i > 0ul { signature << ","; } | |
| 166 | 290434 | param.qualType.getName(signature, withSize, ignorePublic, withTypeAliases); | |
| 167 | 290434 | if param.isOptional { signature << "?"; } | |
| 168 | 290434 | } | |
| 169 | 248419 | signature << ")"; | |
| 170 | |||
| 171 | 496838 | return signature.str(); | |
| 172 | 248419 | } | |
| 173 | |||
| 174 | /** | ||
| 175 | * Get the name of the scope, where members and the body of the function live | ||
| 176 | * | ||
| 177 | * @return Scope name | ||
| 178 | */ | ||
| 179 | 125922 | public f<String> Function.getScopeName() { | |
| 180 | 125922 | return this.getSignature(false, true, false, true); | |
| 181 | } | ||
| 182 | |||
| 183 | /** | ||
| 184 | * Get the mangled name of the function | ||
| 185 | * | ||
| 186 | * @return Mangled name | ||
| 187 | */ | ||
| 188 | 184732 | public f<String> Function.getMangledName() { | |
| 189 | // Note: members are returned via an explicit `String(...)` copy, so that the copy ctor is invoked for sure. A shallow | ||
| 190 | // alias of the member buffer leads to a double free once both the returned value and the owning struct are destructed. | ||
| 191 | // A struct method, which is inherited from an interface default method, is implemented by the default method | ||
| 192 | 184732 | if this.defaultMethod != nil<Function*> { | |
| 193 | 9 | return this.defaultMethod.getMangledName(); | |
| 194 | } | ||
| 195 | // Use predefined mangled name if available | ||
| 196 | 184723 | if !this.predefinedMangledName.isEmpty() { | |
| 197 | 2 | return String(this.predefinedMangledName); | |
| 198 | } | ||
| 199 | // Use function name if mangling is disabled | ||
| 200 | 184721 | if !this.mangleFunctionName { | |
| 201 | 16540 | return String(this.name); | |
| 202 | } | ||
| 203 | // Use normal name mangling | ||
| 204 | 168181 | return mangleFunction(*this); | |
| 205 | } | ||
| 206 | |||
| 207 | /** | ||
| 208 | * Get the name of the symbol table entry of the default constructor of a struct | ||
| 209 | * | ||
| 210 | * @param structCodeLoc Code location of the struct | ||
| 211 | * @return Symbol table entry name | ||
| 212 | */ | ||
| 213 | 201 | public f<String> getSymbolTableEntryNameDefaultCtor(const CodeLoc& structCodeLoc) { | |
| 214 | 402 | return "default_" + CTOR_FUNCTION_NAME + ":" + structCodeLoc.toString(); | |
| 215 | 201 | } | |
| 216 | |||
| 217 | /** | ||
| 218 | * Get the name of the symbol table entry of the default copy constructor of a struct | ||
| 219 | * | ||
| 220 | * @param structCodeLoc Code location of the struct | ||
| 221 | * @return Symbol table entry name | ||
| 222 | */ | ||
| 223 | 983 | public f<String> getSymbolTableEntryNameDefaultCopyCtor(const CodeLoc& structCodeLoc) { | |
| 224 | 1966 | return "default_copy" + CTOR_FUNCTION_NAME + ":" + structCodeLoc.toString(); | |
| 225 | 983 | } | |
| 226 | |||
| 227 | /** | ||
| 228 | * Get the name of the symbol table entry of the default move constructor of a struct | ||
| 229 | * | ||
| 230 | * @param structCodeLoc Code location of the struct | ||
| 231 | * @return Symbol table entry name | ||
| 232 | */ | ||
| 233 | 21 | public f<String> getSymbolTableEntryNameDefaultMoveCtor(const CodeLoc& structCodeLoc) { | |
| 234 | 42 | return "default_move" + CTOR_FUNCTION_NAME + ":" + structCodeLoc.toString(); | |
| 235 | 21 | } | |
| 236 | |||
| 237 | /** | ||
| 238 | * Get the name of the symbol table entry of the default destructor of a struct | ||
| 239 | * | ||
| 240 | * @param structCodeLoc Code location of the struct | ||
| 241 | * @return Symbol table entry name | ||
| 242 | */ | ||
| 243 | 531 | public f<String> getSymbolTableEntryNameDefaultDtor(const CodeLoc& structCodeLoc) { | |
| 244 | 1062 | return "default_" + DTOR_FUNCTION_NAME + ":" + structCodeLoc.toString(); | |
| 245 | 531 | } | |
| 246 | |||
| 247 | /** | ||
| 248 | * Checks if a function contains optional parameters. | ||
| 249 | * This would imply that the function is not substantiated by its optional parameters yet. | ||
| 250 | * | ||
| 251 | * @return Substantiated params or not | ||
| 252 | */ | ||
| 253 | 3427413 | public f<bool> Function.hasSubstantiatedParams() { | |
| 254 | 9161727 | foreach const Param& param : this.paramList { | |
| 255 | 5734314 | if param.isOptional { | |
| 256 | ✗ | return false; | |
| 257 | } | ||
| 258 | 5734314 | } | |
| 259 | 3427413 | return true; | |
| 260 | } | ||
| 261 | |||
| 262 | /** | ||
| 263 | * Checks if a function contains template types. | ||
| 264 | * This would imply that the function is not substantiated by its generic types yet. | ||
| 265 | * | ||
| 266 | * @return Substantiated generics or not | ||
| 267 | */ | ||
| 268 | 493233 | public f<bool> Function.hasSubstantiatedGenerics() { | |
| 269 | 615233 | foreach const GenericType& genericType : this.templateTypes { | |
| 270 | // A template slot that is not generic (anymore) is already substantiated. This is the case for the default members | ||
| 271 | // of an already-concrete struct manifestation (e.g. HashEntry<int, String>.dtor()), whose template type list holds | ||
| 272 | // the concrete types instead of generic placeholders. Only generic types carry a sub type usable as a mapping key. | ||
| 273 | 299298 | if !genericType.qualType.is(SuperType::TY_GENERIC) { | |
| 274 | 4652 | continue; | |
| 275 | } | ||
| 276 | 294646 | if !this.typeMapping.contains(genericType.qualType.getSubType()) { | |
| 277 | 177298 | return false; | |
| 278 | } | ||
| 279 | 117348 | } | |
| 280 | 315935 | return true; | |
| 281 | } | ||
| 282 | |||
| 283 | /** | ||
| 284 | * Checks if a function contains optional parameters or has generic types present. | ||
| 285 | * This would imply that the function is not fully substantiated yet. | ||
| 286 | * | ||
| 287 | * @return Fully substantiated or not | ||
| 288 | */ | ||
| 289 | 493233 | public f<bool> Function.isFullySubstantiated() { | |
| 290 | 493233 | return this.hasSubstantiatedParams() && this.hasSubstantiatedGenerics(); | |
| 291 | } | ||
| 292 | |||
| 293 | /** | ||
| 294 | * Returns, if this function is a substantiation of a generic one. | ||
| 295 | * | ||
| 296 | * @return Generic substantiation or not | ||
| 297 | */ | ||
| 298 | 2624350 | public f<bool> Function.isGenericSubstantiation() { | |
| 299 | 2624350 | return this.genericPreset != nil<Function*>; | |
| 300 | } | ||
| 301 | |||
| 302 | /** | ||
| 303 | * Retrieve the declaration code location of this function | ||
| 304 | * | ||
| 305 | * @return Declaration code location | ||
| 306 | */ | ||
| 307 | 1643 | public f<CodeLoc&> Function.getDeclCodeLoc() { | |
| 308 | 1643 | return this.declNode.codeLoc; | |
| 309 | } | ||
| 310 | |||
| 311 | /** | ||
| 312 | * Check if the current function is a method (has a 'this' type) | ||
| 313 | * | ||
| 314 | * @return Method or not | ||
| 315 | */ | ||
| 316 | 677777 | public f<bool> Function.isMethod() { | |
| 317 | 677777 | return !this.thisType.is(SuperType::TY_DYN); | |
| 318 | } | ||
| 319 | |||
| 320 | /** | ||
| 321 | * Check if the current function is a function (has a return type) | ||
| 322 | * | ||
| 323 | * @return Function or not | ||
| 324 | */ | ||
| 325 | 84684 | public f<bool> Function.isFunction() { | |
| 326 | 84684 | return !this.returnType.is(SuperType::TY_DYN); | |
| 327 | } | ||
| 328 | |||
| 329 | /** | ||
| 330 | * Check if the current function is a procedure (has no return type) | ||
| 331 | * | ||
| 332 | * @return Procedure or not | ||
| 333 | */ | ||
| 334 | 80097 | public f<bool> Function.isProcedure() { | |
| 335 | 80097 | return this.returnType.is(SuperType::TY_DYN); | |
| 336 | } | ||
| 337 | |||
| 338 | /** | ||
| 339 | * Check if the current function is a normal function (no method) | ||
| 340 | * | ||
| 341 | * @return Normal function or not | ||
| 342 | */ | ||
| 343 | 2637 | public f<bool> Function.isNormalFunction() { | |
| 344 | 2637 | return this.isFunction() && !this.isMethod(); | |
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * Check if the current function is a virtual method | ||
| 349 | * | ||
| 350 | * @return Virtual method or not | ||
| 351 | */ | ||
| 352 | 34702 | public f<bool> Function.isVirtualMethod() { | |
| 353 | 34702 | return this.isMethod() && this.isVirtual; | |
| 354 | } | ||
| 355 | |||
| 356 | 35303 | public f<bool> Function.isInheritedDefaultMethod() { | |
| 357 | 35303 | return this.defaultMethod != nil<Function*>; | |
| 358 | } | ||
| 359 |