GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
366 / 371
98.7%
Branches:
126 / 130, 62 excl
96.9%
Functions:
48 / 48
100.0%

symboltablebuilder/type.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 import "std/bindings/llvm/llvm" as llvm;
7
8 // Own imports
9 import "@/ast/ast-nodes";
10 import "@/symboltablebuilder/type-chain";
11 import "@/symboltablebuilder/qual-type";
12 import "@/symboltablebuilder/super-type";
13 import "@/symboltablebuilder/scope";
14 import "@/source-file";
15 import "@/global/type-registry";
16 import "@/ast/attributes";
17 import "@/exception/semantic-error";
18 import "@/irgenerator/name-mangling";
19 import "@/model/interface";
20 import "@/model/struct";
21 import "@/model/struct-base";
22 import "@/model/union";
23 import "@/symboltablebuilder/symbol-table-entry";
24
25 6547544 public type Type struct {
26 Vector<TypeChainElement> typeChain
27 // Non-owning back-reference to the registry that interns this type. Set by the registry on creation.
28 // It is intentionally excluded from hashing and equality (which are defined over the type chain only).
29 6551868 TypeRegistry* registry = nil<TypeRegistry*>
30 }
31
32 4150519 public p Type.ctor(SuperType superType) {
33 4150519 this.typeChain.pushBack(TypeChainElement(superType));
34 4150519 }
35
36 6445 public p Type.ctor(SuperType superType, const String& subType) {
37 6445 this.typeChain.pushBack(TypeChainElement(superType, subType));
38 6445 }
39
40 5079 public p Type.ctor(SuperType superType, const String& subType, unsigned long typeId, const TypeChainElementData& data, const QualTypeList& templateTypes) {
41 5079 this.typeChain.pushBack(TypeChainElement(superType, subType, typeId, data, templateTypes));
42 5079 }
43
44 2320061 public p Type.ctor(const TypeChain& typeChain) {
45 2320061 this.typeChain = typeChain;
46 2320061 }
47
48 69764 public p Type.ctor(const Type& other) {
49 69764 this.typeChain = other.typeChain;
50 69764 this.registry = other.registry;
51 69764 }
52
53 /**
54 * Get the super type of the current type
55 *
56 * @return Super type
57 */
58 38263956 public f<SuperType> Type.getSuperType() {
59 38263956 assert !this.typeChain.isEmpty();
60 38263956 return this.typeChain.back().superType;
61 }
62
63 /**
64 * Get the sub type of the current type
65 *
66 * @return Sub type
67 */
68 1299959 public f<const String&> Type.getSubType() {
69 1299959 assert !this.typeChain.isEmpty();
70 1299959 const dyn options = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_UNION, SuperType::TY_ENUM, SuperType::TY_GENERIC];
71 1299959 assert this.isOneOf(options, len(options));
72 1299959 return this.typeChain.back().subType;
73 }
74
75 /**
76 * Get the array size of the current type
77 *
78 * @return Array size
79 */
80 7500 public f<unsigned int> Type.getArraySize() {
81 7500 assert this.isArray();
82 7500 return this.typeChain.back().data.arraySize;
83 }
84
85 /**
86 * Get the body scope of the current type
87 *
88 * @return Body scope
89 */
90 574394 public f<Scope*> Type.getBodyScope() {
91 574394 const dyn options = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_UNION];
92 574394 assert this.isOneOf(options, len(options));
93 574394 return this.typeChain.back().data.bodyScope;
94 }
95
96 /**
97 * Get the return type of function type
98 *
99 * @return Function return type
100 */
101 142 public f<QualType> Type.getFunctionReturnType() {
102 142 assert this.is(SuperType::TY_FUNCTION);
103 142 const TypeChainElement& front = this.typeChain.front();
104 142 assert !front.paramTypes.isEmpty();
105 142 return front.paramTypes.front();
106 }
107
108 /**
109 * Get the param types of a function or procedure type
110 *
111 * @return Function param types
112 */
113 570 public f<QualTypeList> Type.getFunctionParamTypes() {
114 570 const dyn options = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
115 570 assert this.isOneOf(options, len(options));
116 570 const TypeChainElement& front = this.typeChain.front();
117 570 QualTypeList paramTypes;
118 // The first element is the return type, the params follow
119
2/2
✓ Branch 0 taken 540 times.
✓ Branch 1 taken 570 times.
1110 for unsigned long i = 1ul; i < front.paramTypes.getSize(); i++ {
120 540 paramTypes.pushBack(front.paramTypes.get(i));
121 540 }
122 570 return paramTypes;
123 }
124
125 /**
126 * Get the param and return types of a function or procedure base type
127 *
128 * @return Function param and return types (first is return type, rest are param types)
129 */
130 598 public f<QualTypeList> Type.getFunctionParamAndReturnTypes() {
131 598 const Type* baseType = this.getBase();
132 598 const dyn options = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
133 598 assert baseType.isOneOf(options, len(options));
134 // Copy explicitly: returning the member directly would hand out a list that shares the storage of the type chain
135 598 const TypeChainElement& front = this.typeChain.front();
136 598 QualTypeList paramAndReturnTypes = front.paramTypes;
137 598 return paramAndReturnTypes;
138 }
139
140 /**
141 * Check if a function or procedure type has captures
142 *
143 * @return Has captures
144 */
145 533 public f<bool> Type.hasLambdaCaptures() {
146 533 const Type* baseType = this.getBase();
147 533 const dyn options = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
148 533 assert baseType.isOneOf(options, len(options));
149 533 return this.typeChain.front().data.hasCaptures;
150 }
151
152 /**
153 * Retrieve template types of the current type
154 *
155 * @return Vector of template types
156 */
157 748459 public f<QualTypeList> Type.getTemplateTypes() {
158 // Copy explicitly: returning the member directly would hand out a list that shares the storage of the type chain
159 748459 const TypeChainElement& back = this.typeChain.back();
160 748459 QualTypeList templateTypes = back.templateTypes;
161 748459 return templateTypes;
162 }
163
164 /**
165 * Get the type chain depth of the current type
166 *
167 * @return Type chain depth
168 */
169 338454 public const f<unsigned long> Type.getTypeChainDepth() {
170 338454 return this.typeChain.getSize();
171 }
172
173 /**
174 * Check if the current type is of a certain super type
175 *
176 * @return Applicable or not
177 */
178 19403873 public f<bool> Type.is(SuperType superType) {
179 19403873 return this.getSuperType() == superType;
180 }
181
182 /**
183 * Check if the current type is one of a list of super types
184 *
185 * @return Applicable or not
186 */
187 7138809 public f<bool> Type.isOneOf(SuperType[] superTypes, unsigned long superTypesSize) {
188 7138809 const SuperType currentSuperType = this.getSuperType();
189
2/2
✓ Branch 0 taken 16890510 times.
✓ Branch 1 taken 2993568 times.
19884078 for unsigned long i = 0ul; i < superTypesSize; i++ {
190
2/2
✓ Branch 0 taken 4145241 times.
✓ Branch 1 taken 12745269 times.
16890510 if currentSuperType == superTypes[i] { return true; }
191 12745269 }
192 2993568 return false;
193 }
194
195 /**
196 * Check if the base type of the current type chain is of a certain super type
197 *
198 * @param superType Super type to check for
199 * @return Applicable or not
200 */
201 782419 public f<bool> Type.isBase(SuperType superType) {
202 782419 assert !this.typeChain.isEmpty();
203 782419 return this.typeChain.front().superType == superType;
204 }
205
206 /**
207 * Check if the current type is a primitive type
208 *
209 * @return Primitive type or not
210 */
211 649038 public f<bool> Type.isPrimitive() {
212 649038 const dyn options = [SuperType::TY_DOUBLE, SuperType::TY_INT, SuperType::TY_SHORT, SuperType::TY_LONG, SuperType::TY_BYTE, SuperType::TY_CHAR, SuperType::TY_STRING, SuperType::TY_BOOL];
213 649038 return this.isOneOf(options, len(options));
214 }
215
216 /**
217 * Check if the type is an extended primitive type
218 * The definition of extended primitive types contains all primitive types plus the following:
219 * - structs
220 * - interfaces
221 * - functions/procedures
222 *
223 * @return Extended primitive or not
224 */
225 494659 public f<bool> Type.isExtendedPrimitive() {
226 494659 const dyn options = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_UNION, SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
227
2/2
✓ Branch 0 taken 249134 times.
✓ Branch 1 taken 245525 times.
494659 return this.isPrimitive() || this.isOneOf(options, len(options));
228 }
229
230 /**
231 * Check if the current type is a pointer type
232 *
233 * @return Pointer type or not
234 */
235 2351554 public f<bool> Type.isPtr() {
236 2351554 return this.getSuperType() == SuperType::TY_PTR;
237 }
238
239 /**
240 * Check if the current type is a reference type
241 *
242 * @return Reference type or not
243 */
244 4634501 public f<bool> Type.isRef() {
245 4634501 return this.getSuperType() == SuperType::TY_REF;
246 }
247
248 /**
249 * Check if the current type is an array type
250 *
251 * @return Array type or not
252 */
253 1039937 public f<bool> Type.isArray() {
254 1039937 return this.getSuperType() == SuperType::TY_ARRAY;
255 }
256
257 /**
258 * Checks if the base type is generic itself or has generic parts in its template types
259 *
260 * @return Contains generic parts or not
261 */
262 2048490 public f<bool> Type.hasAnyGenericParts() {
263 2048490 const Type* baseType = this.getBase();
264
265 // Check if the type itself is generic
266
2/2
✓ Branch 0 taken 225877 times.
✓ Branch 1 taken 1822613 times.
2048490 if baseType.is(SuperType::TY_GENERIC) { return true; }
267
268 // Check if the type has generic template types
269
2/2
✓ Branch 0 taken 538455 times.
✓ Branch 1 taken 1739914 times.
2278369 foreach const QualType& templateType : baseType.typeChain.back().templateTypes {
270
2/2
✓ Branch 0 taken 82699 times.
✓ Branch 1 taken 455756 times.
538455 if templateType.rawType.hasAnyGenericParts() { return true; }
271 455756 }
272
273 // Check param and return types of functions/procedures
274 1739914 const dyn fctOptions = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
275
2/2
✓ Branch 0 taken 1726184 times.
✓ Branch 1 taken 13730 times.
1739914 if baseType.isOneOf(fctOptions, len(fctOptions)) {
276
2/2
✓ Branch 0 taken 22750 times.
✓ Branch 1 taken 13369 times.
36119 foreach const QualType& paramType : baseType.typeChain.front().paramTypes {
277
2/2
✓ Branch 0 taken 361 times.
✓ Branch 1 taken 22389 times.
22750 if paramType.rawType.hasAnyGenericParts() { return true; }
278 22389 }
279 13369 }
280
281 1739553 return false; // Does not have generic parts
282 }
283
284 /**
285 * Check if the current type is of the same container type like the other type.
286 * Only TY_PTR, TY_REF and TY_ARRAY are considered as container types.
287 *
288 * @param other Other symbol type
289 * @return Same container type or not
290 */
291 303604 public f<bool> Type.isSameContainerTypeAs(const Type* other) {
292
2/2
✓ Branch 0 taken 288234 times.
✓ Branch 1 taken 15370 times.
303604 const bool bothPtr = this.isPtr() && other.isPtr();
293
2/2
✓ Branch 0 taken 277978 times.
✓ Branch 1 taken 25626 times.
303604 const bool bothRef = this.isRef() && other.isRef();
294
2/2
✓ Branch 0 taken 303371 times.
✓ Branch 1 taken 233 times.
303604 const bool bothArray = this.isArray() && other.isArray();
295
4/4
✓ Branch 0 taken 289812 times.
✓ Branch 1 taken 13792 times.
✓ Branch 2 taken 18845 times.
✓ Branch 3 taken 270967 times.
303604 return bothPtr || bothRef || bothArray;
296 }
297
298 /**
299 * Check for the matching compatibility of two types.
300 * Useful for struct and function matching as well as assignment type validation and function arg matching.
301 *
302 * @param otherType Type to compare against
303 * @param ignoreArraySize Ignore array sizes
304 * @return Matching or not
305 */
306 6911613 public f<bool> Type.matches(const Type* otherType, bool ignoreArraySize) {
307 // If the size does not match, it is not equal
308
2/2
✓ Branch 0 taken 49462 times.
✓ Branch 1 taken 6862151 times.
6911613 if this.typeChain.getSize() != otherType.typeChain.getSize() { return false; }
309
310 // Compare the elements
311
2/2
✓ Branch 0 taken 7119577 times.
✓ Branch 1 taken 6742498 times.
13862075 for unsigned long i = 0ul; i < this.typeChain.getSize(); i++ {
312 7119577 const TypeChainElement& lhsElement = this.typeChain.get(i);
313 7119577 const TypeChainElement& rhsElement = otherType.typeChain.get(i);
314
315 // Ignore differences in array size
316
6/6
✓ Branch 0 taken 219100 times.
✓ Branch 1 taken 6900477 times.
✓ Branch 2 taken 219093 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 7119570 times.
7119577 if ignoreArraySize && lhsElement.superType == SuperType::TY_ARRAY && rhsElement.superType == SuperType::TY_ARRAY {
317 7 continue;
318 }
319
320 // Not both types are arrays -> compare them as usual
321
2/2
✓ Branch 0 taken 119653 times.
✓ Branch 1 taken 6999917 times.
7119570 if lhsElement != rhsElement { return false; }
322 6999917 }
323
324 6742498 return true;
325 }
326
327 /**
328 * Get the name of the symbol type as a string
329 *
330 * @param name Get name of type
331 * @param withSize Include the array size for sized types
332 * @param ignorePublic Ignore any potential public qualifier
333 * @param withAliases Print aliases as is and not decompose them
334 * @return Symbol type name
335 */
336 1534495 public p Type.getName(StringStream& name, bool withSize, bool ignorePublic, bool withAliases) {
337 // Loop through all chain elements
338
2/2
✓ Branch 0 taken 1949865 times.
✓ Branch 1 taken 1534495 times.
3484360 foreach const TypeChainElement& chainElement : this.typeChain {
339 1949865 chainElement.getName(name, withSize, ignorePublic, withAliases);
340 1949865 }
341 1534495 }
342
343 /**
344 * Get the name of the symbol type as a string
345 *
346 * @param withSize Include the array size for sized types
347 * @param ignorePublic Ignore any potential public qualifier
348 * @param withAliases Print aliases as is and not decompose them
349 * @return Symbol type name
350 */
351 68095 public f<String> Type.getName(bool withSize, bool ignorePublic, bool withAliases) {
352 68095 StringStream name;
353 68095 this.getName(name, withSize, ignorePublic, withAliases);
354 136190 return name.str();
355 68095 }
356
357 /**
358 * Get the pointer type of the current type as a new type
359 *
360 * @param node AST node for error messages
361 * @return Pointer type of the current type
362 */
363 117140 public f<const Type*> Type.toPtr(const ASTNode* node) {
364
2/2
✓ Branch 0 taken 117138 times.
✓ Branch 1 taken 2 times.
117140 if this.is(SuperType::TY_DYN) { abortWithSemanticError(node, SemanticErrorType::DYN_POINTERS_NOT_ALLOWED, "Just use the dyn type without '*' instead"); }
365
2/2
✓ Branch 0 taken 117139 times.
✓ Branch 1 taken 1 time.
117140 if this.isRef() { abortWithSemanticError(node, SemanticErrorType::REF_POINTERS_ARE_NOT_ALLOWED, "Pointers to references are not allowed. Use pointer instead"); }
366
367 // Create new type chain
368 117140 TypeChain newTypeChain = this.typeChain;
369 117140 newTypeChain.pushBack(TypeChainElement(SuperType::TY_PTR));
370
371 // Register new type or return if already registered
372 234280 return this.registry.getOrInsertChain(newTypeChain);
373 117140 }
374
375 /**
376 * Get the reference type of the current type as a new type
377 *
378 * @param node AST node for error messages
379 * @return Reference type of the current type
380 */
381 56652 public f<const Type*> Type.toRef(const ASTNode* node) {
382
2/2
✓ Branch 0 taken 56651 times.
✓ Branch 1 taken 1 time.
56652 if this.is(SuperType::TY_DYN) { abortWithSemanticError(node, SemanticErrorType::DYN_REFERENCES_NOT_ALLOWED, "Just use the dyn type without '&' instead"); }
383
2/2
✓ Branch 0 taken 56651 times.
✓ Branch 1 taken 1 time.
56652 if this.isRef() { abortWithSemanticError(node, SemanticErrorType::MULTI_REF_NOT_ALLOWED, "References to references are not allowed"); }
384
385 // Create new type chain
386 56652 TypeChain newTypeChain = this.typeChain;
387 56652 newTypeChain.pushBack(TypeChainElement(SuperType::TY_REF));
388
389 // Register new type or return if already registered
390 113304 return this.registry.getOrInsertChain(newTypeChain);
391 56652 }
392
393 /**
394 * Get the array type of the current type as a new type
395 *
396 * @param node AST node for error messages
397 * @param size Size of the array
398 * @param skipDynCheck Skip check if array base type is dyn
399 * @return Array type of the current type
400 */
401 1032 public f<const Type*> Type.toArr(const ASTNode* node, unsigned int size, bool skipDynCheck) {
402
4/4
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 850 times.
✓ Branch 2 taken 1031 times.
✓ Branch 3 taken 1 time.
1032 if !skipDynCheck && this.typeChain.back().superType == SuperType::TY_DYN {
403 1 abortWithSemanticError(node, SemanticErrorType::DYN_ARRAYS_NOT_ALLOWED, "Just use the dyn type without '[]' instead");
404 1 }
405
406 // Create new type chain
407 1032 TypeChain newTypeChain = this.typeChain;
408 1032 TypeChainElementData data = TypeChainElementData{/*arraySize=*/ size, /*bodyScope=*/ nil<Scope*>, /*hasCaptures*/ false};
409 1032 newTypeChain.pushBack(TypeChainElement(SuperType::TY_ARRAY, data));
410
411 // Register new type or return if already registered
412 2064 return this.registry.getOrInsertChain(newTypeChain);
413 1032 }
414
415 /**
416 * Retrieve the base type of an array or a pointer
417 *
418 * @return Base type
419 */
420 622826 public f<const Type*> Type.getContained() {
421
2/2
✓ Branch 0 taken 2465 times.
✓ Branch 1 taken 620361 times.
622826 if this.is(SuperType::TY_STRING) {
422 2465 return this.registry.getOrInsert(SuperType::TY_CHAR);
423 }
424
425 // Create new type chain
426 620361 TypeChain newTypeChain = this.typeChain;
427 620361 assert newTypeChain.getSize() > 1l;
428 620361 newTypeChain.removeAt(newTypeChain.getSize() - 1ul);
429
430 // Register new type or return if already registered
431 1240722 return this.registry.getOrInsertChain(newTypeChain);
432 620361 }
433
434 /**
435 * Replace the base type with another one
436 *
437 * @param newBaseType New base type
438 * @return The new type
439 */
440 70541 public f<const Type*> Type.replaceBase(const Type* newBaseType) {
441 70541 assert !this.typeChain.isEmpty();
442
443 // Create new type
444 70541 TypeChain newTypeChain = newBaseType.typeChain;
445
2/2
✓ Branch 0 taken 67612 times.
✓ Branch 1 taken 2929 times.
70541 const bool doubleRef = newTypeChain.back().superType == SuperType::TY_REF && this.typeChain.back().superType == SuperType::TY_REF;
446 70541 const unsigned long size = this.typeChain.getSize();
447
2/2
✓ Branch 0 taken 8374 times.
✓ Branch 1 taken 70541 times.
78915 for unsigned long i = 1ul; i < size; i++ {
448
4/4
✓ Branch 0 taken 8280 times.
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 94 times.
✓ Branch 3 taken 8280 times.
8374 if !doubleRef || i > 1ul {
449 8280 newTypeChain.pushBack(this.typeChain[i]);
450 8280 }
451 8374 }
452
453 // Register new type or return if already registered
454 141082 return this.registry.getOrInsertChain(newTypeChain);
455 70541 }
456
457 /**
458 * Remove reference wrapper from the current type
459 *
460 * @return Type without reference wrapper
461 */
462 74848 public f<const Type*> Type.removeReferenceWrapper() {
463
1/2
✓ Branch 0 taken 74848 times.
✗ Branch 1 not taken.
74848 if this.isRef() { return this.getContained(); }
464 ✗ const Type* self = this;
465 ✗ return self;
466 }
467
468 /**
469 * Retrieve the base type of the current type
470 *
471 * @return Base type
472 */
473 5084863 public f<const Type*> Type.getBase() {
474 5084863 assert !this.typeChain.isEmpty();
475
476 // A type chain with a single element is the base type already. All types are interned in the type registry, so there is
477 // no need to look it up again
478
2/2
✓ Branch 0 taken 3832802 times.
✓ Branch 1 taken 1252061 times.
5084863 if this.typeChain.getSize() == 1l {
479 3832802 const Type* self = this;
480 3832802 return self;
481 }
482
483 // Create new type chain
484 1252061 TypeChain newTypeChain;
485 1252061 newTypeChain.pushBack(this.typeChain.front());
486
487 // Register new type or return if already registered
488 2504122 return this.registry.getOrInsertChain(newTypeChain);
489 1252061 }
490
491 /**
492 * Retrieve the same type, but with lambda captures
493 *
494 * @return Type with lambda captures
495 */
496 223 public f<const Type*> Type.getWithLambdaCaptures(bool enabled) {
497 223 const Type* baseType = this.getBase();
498 223 const dyn options = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
499 223 assert baseType.isOneOf(options, len(options));
500
501 // Create new type chain
502 223 TypeChain newTypeChain = this.typeChain;
503 223 newTypeChain.front().data.hasCaptures = enabled;
504
505 // Register new type or return if already registered
506 446 return this.registry.getOrInsertChain(newTypeChain);
507 223 }
508
509 /**
510 * Retrieve the same type, but with the body scope removed
511 *
512 * @return Type with body scope removed
513 */
514 106416 public f<const Type*> Type.getWithBodyScope(Scope* bodyScope) {
515 106416 const Type* baseType = this.getBase();
516 106416 const dyn options = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_UNION];
517 106416 assert baseType.isOneOf(options, len(options));
518
519 // Create new type chain
520 106416 TypeChain newTypeChain = this.typeChain;
521 106416 newTypeChain.front().data.bodyScope = bodyScope;
522
523 // Register new type or return if already registered
524 212832 return this.registry.getOrInsertChain(newTypeChain);
525 106416 }
526
527 /**
528 * Retrieve the same type, but with the given base template types
529 *
530 * @return Type with new base template types
531 */
532 19460 public f<const Type*> Type.getWithTemplateTypes(const QualTypeList& templateTypes) {
533 19460 const dyn options = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_UNION];
534 19460 assert this.isOneOf(options, len(options));
535 19460 return this.getWithBaseTemplateTypes(templateTypes);
536 }
537
538 /**
539 * Retrieve the same type, but with the given base template types
540 *
541 * @return Type with new base template types
542 */
543 41918 public f<const Type*> Type.getWithBaseTemplateTypes(const QualTypeList& templateTypes) {
544 41918 const Type* baseType = this.getBase();
545 41918 const dyn options = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_UNION];
546 41918 assert baseType.isOneOf(options, len(options));
547
548 // Create new type chain
549 41918 TypeChain newTypeChain = this.typeChain;
550
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41918 times.
41918 newTypeChain.front().templateTypes = templateTypes;
551
552 // Register new type or return if already registered
553 83836 return this.registry.getOrInsertChain(newTypeChain);
554 41918 }
555
556 /**
557 * Retrieve the same type, but with the param and return types removed
558 *
559 * @return Type with param and return types removed
560 */
561 53723 public f<const Type*> Type.getWithFunctionParamAndReturnTypes(const QualTypeList& paramAndReturnTypes) {
562 53723 const Type* baseType = this.getBase();
563 53723 const dyn options = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
564 53723 assert baseType.isOneOf(options, len(options));
565
566 // Create new type chain
567 53723 TypeChain newTypeChain = this.typeChain;
568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53723 times.
53723 newTypeChain.front().paramTypes = paramAndReturnTypes;
569
570 // Register new type or return if already registered
571 107446 return this.registry.getOrInsertChain(newTypeChain);
572 53723 }
573
574 /**
575 * Return the LLVM type for this symbol type
576 *
577 * @param sourceFile Referenced source file
578 * @return Corresponding LLVM type
579 */
580 37090 public f<llvm::Type> Type.toLLVMType(SourceFile* sourceFile) {
581 37090 assert !this.typeChain.isEmpty() && !this.is(SuperType::TY_INVALID);
582 37090 const llvm::LLVMContext& context = sourceFile.getLLVMContext();
583
584 37090 const dyn ptrLikeTypes = [SuperType::TY_PTR, SuperType::TY_REF, SuperType::TY_STRING];
585
6/6
✓ Branch 0 taken 18239 times.
✓ Branch 1 taken 18851 times.
✓ Branch 2 taken 18248 times.
✓ Branch 3 taken 603 times.
✓ Branch 4 taken 18301 times.
✓ Branch 5 taken 18789 times.
37090 if this.isOneOf(ptrLikeTypes, len(ptrLikeTypes)) || (this.isArray() && this.getArraySize() == 0u) {
586 18301 return llvm::getPtrTy(context);
587 }
588
589
2/2
✓ Branch 0 taken 541 times.
✓ Branch 1 taken 18248 times.
18789 if this.isArray() {
590 541 assert this.getArraySize() > 0u;
591 541 const llvm::Type containedType = sourceFile.getLLVMType(this.getContained());
592 541 return llvm::getArrayType(containedType, cast<unsigned long>(this.getArraySize()));
593 }
594
595 18248 assert !this.hasAnyGenericParts();
596
597
2/2
✓ Branch 0 taken 221 times.
✓ Branch 1 taken 18027 times.
18248 if this.is(SuperType::TY_DOUBLE) {
598 221 return llvm::getDoubleTy(context);
599 }
600
601 18027 const dyn int32Types = [SuperType::TY_INT, SuperType::TY_ENUM];
602
2/2
✓ Branch 0 taken 2407 times.
✓ Branch 1 taken 15620 times.
18027 if this.isOneOf(int32Types, len(int32Types)) {
603 2407 return llvm::getInt32Ty(context);
604 }
605
606
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 15458 times.
15620 if this.is(SuperType::TY_SHORT) {
607 162 return llvm::getInt16Ty(context);
608 }
609
610
2/2
✓ Branch 0 taken 1478 times.
✓ Branch 1 taken 13980 times.
15458 if this.is(SuperType::TY_LONG) {
611 1478 return llvm::getInt64Ty(context);
612 }
613
614 13980 const dyn int8Types = [SuperType::TY_CHAR, SuperType::TY_BYTE];
615
2/2
✓ Branch 0 taken 1933 times.
✓ Branch 1 taken 12047 times.
13980 if this.isOneOf(int8Types, len(int8Types)) {
616 1933 return llvm::getInt8Ty(context);
617 }
618
619
2/2
✓ Branch 0 taken 2003 times.
✓ Branch 1 taken 10044 times.
12047 if this.is(SuperType::TY_BOOL) {
620 2003 return llvm::getInt1Ty(context);
621 }
622
623 10044 const dyn structOrInterface = [SuperType::TY_STRUCT, SuperType::TY_INTERFACE];
624
2/2
✓ Branch 0 taken 9568 times.
✓ Branch 1 taken 476 times.
10044 if this.isOneOf(structOrInterface, len(structOrInterface)) {
625 9568 const Scope* structBodyScope = this.getBodyScope();
626 9568 const String structSignature = getStructSignature(this.getSubType(), this.getTemplateTypes());
627 9568 const SymbolTableEntry* structSymbol = structBodyScope.parent.lookupStrict(structSignature);
628 9568 assert structSymbol != nil<SymbolTableEntry*>;
629 9568 const QualType structSymbolType = structSymbol.getQualType();
630
631 // Collect concrete field types
632 9568 String mangledName;
633 9568 Vector<llvm::Type> fieldTypes;
634 9568 bool isPacked = false;
635
2/2
✓ Branch 0 taken 8746 times.
✓ Branch 1 taken 822 times.
9568 if this.is(SuperType::TY_STRUCT) { // Struct
636 8746 const Struct* spiceStruct = structSymbolType.getStruct(structSymbol.declNode);
637 8746 assert spiceStruct != nil<Struct*>;
638 8746 mangledName = mangleStruct(*spiceStruct);
639
640 8746 const unsigned long totalFieldCount = spiceStruct.scope.getFieldCount();
641 8746 fieldTypes.reserve(totalFieldCount);
642
643 // If the struct implements interfaces, the first implicit field (added below via lookupField) is an interface
644 // type, which already lowers to a { ptr } struct carrying the vtable pointer. Only add an explicit ptr field
645 // here for structs without interfaces that still need a vtable (e.g. RTTI root types), to avoid duplicating it.
646 8746 assert structSymbol.declNode.kind == ASTNodeKind::STRUCT_DEF;
647 8746 const ASTStructDefNode* structDeclNode = downcast<ASTStructDefNode>(structSymbol.declNode);
648
2/2
✓ Branch 0 taken 8578 times.
✓ Branch 1 taken 168 times.
8746 if spiceStruct.hasSynthesizedVTablePtr() {
649 168 fieldTypes.pushBack(llvm::getPtrTy(context));
650 168 }
651
652 // Collect all field types
653
2/2
✓ Branch 0 taken 8746 times.
✓ Branch 1 taken 24347 times.
33093 for unsigned long i = 0l; i < totalFieldCount; i++ {
654 24347 const SymbolTableEntry* fieldSymbol = spiceStruct.scope.lookupField(cast<unsigned int>(i));
655 24347 assert fieldSymbol != nil<SymbolTableEntry*>;
656 24347 const QualType fieldType = fieldSymbol.getQualType();
657 24347 fieldTypes.pushBack(sourceFile.getLLVMType(fieldType.getType()));
658 24347 }
659
660 // Check if the struct is declared as packed
661
2/2
✓ Branch 0 taken 8568 times.
✓ Branch 1 taken 178 times.
8746 if structDeclNode.attrs != nil<IVisitable*> {
662 178 const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(structDeclNode.attrs);
663 178 const String packedAttrName = String(ATTR_CORE_COMPILER_PACKED);
664
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 10 times.
178 if attrs.attrLst.hasAttr(packedAttrName) {
665 10 const CompileTimeValue packedAttrValue = attrs.attrLst.getAttrValueByName(packedAttrName);
666 10 isPacked = packedAttrValue.getBoolValue();
667 10 }
668 178 }
669 8746 } else { // Interface
670 822 const Interface* spiceInterface = structSymbolType.getInterface(structSymbol.declNode);
671 822 assert spiceInterface != nil<Interface*>;
672 822 mangledName = mangleInterface(*spiceInterface);
673
674 // vtable pointer
675 822 fieldTypes.pushBack(llvm::getPtrTy(context));
676 822 }
677
678 19136 return llvm::createStructType(context, fieldTypes, mangledName.getRaw(), isPacked);
679 9568 }
680
681
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 457 times.
476 if this.is(SuperType::TY_UNION) {
682 // A union is lowered to { i32 tag, [0 x MaxAlignTy] alignPad, [maxSize x i8] payload }. The tag tracks which field
683 // is currently active. The zero-length array contributes no bytes but forces the whole struct's (and therefore the
684 // payload's) ABI alignment to be at least as strict as the most-aligned field, so that every field can be safely
685 // loaded/stored at the payload's address regardless of its own alignment requirement. The payload itself is always
686 // a raw byte buffer (not a typed array), so that any field's value can be stored/loaded there directly, and any
687 // compile-time constant for it can be built uniformly as packed raw bytes, without needing a legal LLVM constant
688 // conversion between the payload's declared element type and a field's unrelated type.
689 19 const Scope* unionBodyScope = this.getBodyScope();
690 19 const String unionSignature = getStructSignature(this.getSubType(), this.getTemplateTypes());
691 19 const SymbolTableEntry* unionSymbol = unionBodyScope.parent.lookupStrict(unionSignature);
692 19 assert unionSymbol != nil<SymbolTableEntry*>;
693
694 19 const QualType unionSymbolType = unionSymbol.getQualType();
695 19 const Union* spiceUnion = unionSymbolType.getUnion(unionSymbol.declNode);
696 19 assert spiceUnion != nil<Union*>;
697 19 const String mangledName = mangleUnion(*spiceUnion);
698
699 19 llvm::DataLayout dataLayout = sourceFile.targetMachine.createDataLayout();
700 19 const unsigned long totalFieldCount = spiceUnion.scope.getFieldCount();
701
702 19 unsigned long maxSize = 0l;
703 19 unsigned long maxAlign = 1l;
704 19 llvm::Type maxAlignFieldType = llvm::getInt8Ty(context);
705
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 42 times.
61 for unsigned long i = 0l; i < totalFieldCount; i++ {
706 42 const SymbolTableEntry* fieldSymbol = spiceUnion.scope.lookupField(cast<unsigned int>(i));
707 42 assert fieldSymbol != nil<SymbolTableEntry*>;
708 42 const QualType fieldQualType = fieldSymbol.getQualType();
709 42 const llvm::Type fieldType = sourceFile.getLLVMType(fieldQualType.getType());
710 42 const unsigned long fieldSize = dataLayout.getTypeAllocSize(fieldType);
711 42 const unsigned long fieldAlign = cast<unsigned long>(dataLayout.getABITypeAlign(fieldType));
712
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 33 times.
42 if fieldSize > maxSize {
713 33 maxSize = fieldSize;
714 33 }
715
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 32 times.
42 if fieldAlign > maxAlign {
716 32 maxAlign = fieldAlign;
717 32 maxAlignFieldType = fieldType;
718 32 }
719 42 }
720
721 19 Vector<llvm::Type> elementTypes;
722 19 elementTypes.pushBack(llvm::getInt32Ty(context)); // Tag type
723 19 elementTypes.pushBack(llvm::getArrayType(maxAlignFieldType, 0l)); // Align pad type
724 19 elementTypes.pushBack(llvm::getArrayType(llvm::getInt8Ty(context), maxSize)); // Payload type
725 38 return llvm::createStructType(context, elementTypes, mangledName.getRaw());
726 19 }
727
728 457 const dyn fctOrProc = [SuperType::TY_FUNCTION, SuperType::TY_PROCEDURE];
729
1/2
✓ Branch 0 taken 457 times.
✗ Branch 1 not taken.
457 if this.isOneOf(fctOrProc, len(fctOrProc)) {
730 // Lambda/function values are represented as a fat pointer with three slots:
731 // { fctPtr, capturePtr, captureSize }. The capture size (in bytes) travels with
732 // the value so that the std Lambda type can take ownership of the captures on the
733 // heap regardless of where the lambda came from. It is 0 if there is no owned
734 // capture struct (no captures, or a single capture stored inline in capturePtr).
735 457 const llvm::Type ptrTy = llvm::getPtrTy(context);
736 457 Vector<llvm::Type> elementTypes;
737 457 elementTypes.pushBack(ptrTy);
738 457 elementTypes.pushBack(ptrTy);
739 457 elementTypes.pushBack(llvm::getInt64Ty(context));
740 914 return llvm::getStructType(context, elementTypes);
741 457 }
742
743 ✗ const String message = "Cannot determine LLVM type of " + this.getName(true, true, true);
744 ✗ panic(Error(message.getRaw())); // Unhandled branch
745 ✗ }
746
747 // The following helpers query the data layout of the target for the LLVM type of this type, for places that need it (e.g. the
748 // type checker for the sizeof builtin). The LLVM bindings cannot be imported there, because their type names (e.g. Function,
749 // Type) clash with the ones of the compiler.
750
751 /**
752 * Get the allocation size of this type in bytes, according to the data layout of the target
753 *
754 * @param sourceFile Source file
755 * @return Allocation size in bytes
756 */
757 914 public f<unsigned long> Type.getTypeAllocSize(SourceFile* sourceFile) {
758 914 const llvm::Type llvmType = sourceFile.getLLVMType(this);
759 914 llvm::DataLayout dataLayout = sourceFile.targetMachine.createDataLayout();
760 1828 return dataLayout.getTypeAllocSize(llvmType);
761 914 }
762
763 /**
764 * Get the ABI alignment of this type in bytes, according to the data layout of the target
765 *
766 * @param sourceFile Source file
767 * @return ABI alignment in bytes
768 */
769 33 public f<unsigned long> Type.getABITypeAlign(SourceFile* sourceFile) {
770 33 const llvm::Type llvmType = sourceFile.getLLVMType(this);
771 33 llvm::DataLayout dataLayout = sourceFile.targetMachine.createDataLayout();
772 66 return cast<unsigned long>(dataLayout.getABITypeAlign(llvmType));
773 33 }
774
775 /**
776 * Get the byte offset of a (possibly composed) field of this struct type, by walking the given index path through the struct
777 * layout of the target
778 *
779 * @param sourceFile Source file
780 * @param indexPath Index path of the field, like Struct.lookupInComposedFields returns it
781 * @return Byte offset of the field
782 */
783 44 public f<unsigned long> Type.getFieldOffset(SourceFile* sourceFile, const Vector<unsigned long>& indexPath) {
784 44 llvm::DataLayout dataLayout = sourceFile.targetMachine.createDataLayout();
785 44 unsigned long offset = 0l;
786 44 llvm::Type currentType = sourceFile.getLLVMType(this);
787
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 44 times.
90 foreach const unsigned long index : indexPath {
788 46 const unsigned int elementIndex = cast<unsigned int>(index);
789 46 offset += dataLayout.getStructElementOffset(currentType, elementIndex);
790 46 currentType = currentType.getStructElementType(elementIndex);
791 46 }
792 88 return offset;
793 44 }
794
795 /**
796 * Remove pointers / arrays / references if both types have them as far as possible.
797 *
798 * @param typeA Candidate type
799 * @param typeB Requested type
800 */
801 // Named unwrapBothTypes instead of unwrapBoth, because the QualType overload in
802 // qual-type.spice would shadow it there
803 260989 public p unwrapBothTypes(const Type*& typeA, const Type*& typeB) {
804 // Unwrap both types as far as possible
805
2/2
✓ Branch 0 taken 30440 times.
✓ Branch 1 taken 260989 times.
291429 while typeA.isSameContainerTypeAs(typeB) {
806 30440 typeA = typeA.getContained();
807 30440 typeB = typeB.getContained();
808 30440 }
809 260989 }
810
811 /**
812 * Remove pointers / arrays / references if both types have them as far as possible.
813 * Furthermore, remove reference wrappers if possible.
814 *
815 * @param typeA Candidate type
816 * @param typeB Requested type
817 */
818 253544 public p unwrapBothWithReferenceWrappers(const Type*& typeA, const Type*& typeB) {
819 // Remove reference wrapper of front type if required
820
4/4
✓ Branch 0 taken 167264 times.
✓ Branch 1 taken 86280 times.
✓ Branch 2 taken 186046 times.
✓ Branch 3 taken 67498 times.
253544 if typeA.isRef() && !typeB.isRef() {
821 67498 typeA = typeA.removeReferenceWrapper();
822 67498 }
823
6/6
✓ Branch 0 taken 234762 times.
✓ Branch 1 taken 18782 times.
✓ Branch 2 taken 225420 times.
✓ Branch 3 taken 9342 times.
✓ Branch 4 taken 246194 times.
✓ Branch 5 taken 7350 times.
253544 if !typeA.isRef() && typeB.isRef() && !typeA.isBase(SuperType::TY_GENERIC) {
824 7350 typeB = typeB.removeReferenceWrapper();
825 7350 }
826
827 // Unwrap both types as far as possible
828 253544 unwrapBothTypes(typeA, typeB);
829 253544 }
830
831 /**
832 * Check if two types have the same type chain depth
833 *
834 * @param typeA First type
835 * @param typeB Second type
836 * @return Same depth or not
837 */
838 78881 public f<bool> hasSameTypeChainDepth(const Type* typeA, const Type* typeB) {
839 78881 return typeA.getTypeChainDepth() == typeB.getTypeChainDepth();
840 }
841