GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
73 / 74
98.6%
Branches:
33 / 34, 6 excl
97.1%
Functions:
12 / 12
100.0%

model/struct.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 "@/model/struct-base";
10 import "@/model/generic-type";
11 import "@/symboltablebuilder/qual-type";
12 import "@/symboltablebuilder/symbol-table-entry";
13 import "@/symboltablebuilder/scope";
14 import "@/symboltablebuilder/lifecycle";
15 import "@/util/common-util";
16
17 // Constants
18 const string STRUCT_SCOPE_PREFIX = "struct:";
19
20 33679 public type Struct struct {
21 public compose StructBase structBase
22 public QualTypeList fieldTypes
23 public QualTypeList interfaceTypes
24 }
25
26 3218 public p Struct.ctor(const String& name, SymbolTableEntry* entry, Scope* scope, const QualTypeList& fieldTypes, const Vector<GenericType>& templateTypes, const QualTypeList& interfaceTypes, ASTNode* declNode) {
27 3218 this.structBase = StructBase(name, entry, scope, templateTypes, declNode);
28 3218 this.fieldTypes = fieldTypes;
29 3218 this.interfaceTypes = interfaceTypes;
30 3218 }
31
32 /**
33 * Retrieve the name of the scope, where members and methods are placed. This is used to navigate to the scope of the struct
34 * from the parent scope.
35 *
36 * @return Name of the struct scope
37 */
38 4280 public f<String> Struct.getScopeName() {
39
2/2
✓ Branch 0 taken 2140 times.
✓ Branch 1 taken 2140 times.
4280 const String &appendix = this.structBase.isGenericSubstantiation() ? this.structBase.getSignature() : this.name;
40 8560 return String(STRUCT_SCOPE_PREFIX) + appendix;
41 4280 }
42
43 /**
44 * Retrieve the name of the scope, where members and methods are placed, from just a name and concrete
45 * template types — usable without a live Struct instance (e.g. from a plain AST node, before any Struct
46 * model object exists, such as during symbol-table building). Mirrors StructBase.getSignature's 2-arg
47 * overload, which is a pure function of its params and does not read 'this'.
48 *
49 * @param name Struct name
50 * @param concreteTemplateTypes Concrete template types
51 * @return Name of the struct scope
52 */
53 83244 public f<String> getStructScopeName(const String& name, const QualTypeList& concreteTemplateTypes) {
54 83244 StringStream templateTyStr;
55
2/2
✓ Branch 0 taken 66082 times.
✓ Branch 1 taken 17162 times.
83244 if !concreteTemplateTypes.isEmpty() {
56 17162 templateTyStr << "<";
57
2/2
✓ Branch 0 taken 20689 times.
✓ Branch 1 taken 17162 times.
37851 foreach unsigned long i, const QualType& templateType : concreteTemplateTypes {
58
2/2
✓ Branch 0 taken 17162 times.
✓ Branch 1 taken 3527 times.
20689 if i > 0ul { templateTyStr << ","; }
59 20689 templateType.getName(templateTyStr, false, true);
60 20689 }
61 17162 templateTyStr << ">";
62 17162 }
63 166488 return String(STRUCT_SCOPE_PREFIX) + getLastFragment(name, SCOPE_ACCESS_TOKEN) + templateTyStr.str();
64 83244 }
65
66 /**
67 * Checks at least one field is a reference.
68 * This is used to prohibit constant instantiations.
69 *
70 * @return Has reference as field type or not
71 */
72 1662 public f<bool> Struct.hasReferenceFields() {
73
2/2
✓ Branch 0 taken 3981 times.
✓ Branch 1 taken 1653 times.
5634 foreach const QualType& fieldType : this.fieldTypes {
74
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3972 times.
3981 if fieldType.isRef() {
75 9 return true;
76 }
77 3972 }
78 1653 return false;
79 }
80
81 /**
82 * Checks if the LLVM struct type of this struct carries a synthesized vtable pointer as its first element, which
83 * shifts all fields by one element. Structs that implement interfaces receive their vtable pointer through their
84 * leading implicit interface field instead, so only structs that request a vtable without implementing any
85 * interface carry the extra element.
86 *
87 * @return Carries a synthesized vtable pointer or not
88 */
89 203456 public f<bool> Struct.hasSynthesizedVTablePtr() {
90 203456 assert this.declNode.kind == ASTNodeKind::STRUCT_DEF;
91 203456 const ASTStructDefNode* structDeclNode = downcast<ASTStructDefNode>(this.declNode);
92
2/2
✓ Branch 0 taken 28816 times.
✓ Branch 1 taken 174640 times.
203456 return !structDeclNode.hasInterfaces && structDeclNode.emitVTable;
93 }
94
95 /**
96 * Map the order index of a field to the index of the corresponding element in the LLVM struct type. Those differ by one
97 * if the struct carries a synthesized vtable pointer as its first element.
98 *
99 * @param orderIndex Order index of the field
100 * @return Index of the field in the LLVM struct type
101 */
102 192282 public f<unsigned long> Struct.getFieldElementIndex(unsigned long orderIndex) {
103
2/2
✓ Branch 0 taken 1653 times.
✓ Branch 1 taken 190629 times.
192282 return this.hasSynthesizedVTablePtr() ? orderIndex + 1l : orderIndex;
104 }
105
106 /**
107 * Check if a field exists in this struct or in one of its composed fields and return it if possible.
108 *
109 * @param name Name of the desired field
110 * @param indexPath How to index the found field using LLVM struct element indices (e.g. for GEP)
111 * @return Desired field / nullptr if the field was not found
112 */
113 172279 public f<SymbolTableEntry*> Struct.lookupInComposedFields(const String& name, Vector<unsigned long>& indexPath) {
114 // Check if we have a field with this name in the current struct
115 172279 SymbolTableEntry* strictResult = this.scope.lookupStrict(name);
116
2/2
✓ Branch 0 taken 172207 times.
✓ Branch 1 taken 72 times.
172279 if strictResult != nil<SymbolTableEntry*> {
117 172207 indexPath.pushBack(this.getFieldElementIndex(strictResult.orderIndex));
118 172207 return strictResult;
119 }
120
121 // If it was not found in the current struct, loop through all composed fields
122 72 const unsigned long fieldCount = this.scope.getFieldCount();
123
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 22 times.
116 for unsigned long i = 0l; i < fieldCount; i++ {
124 94 const SymbolTableEntry* fieldEntry = this.scope.lookupField(cast<unsigned int>(i));
125 94 const QualType fieldType = fieldEntry.getQualType();
126
127 // Skip all fields that are not composition fields
128
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 69 times.
94 if !fieldType.isComposition() {
129 25 continue;
130 }
131
132 // Add the current field's element index to the index path
133 69 indexPath.pushBack(this.getFieldElementIndex(fieldEntry.orderIndex));
134
135 // Search in the composed struct
136 69 Struct* composedStruct = fieldType.getStruct(fieldEntry.declNode);
137 69 assert composedStruct != nil<Struct*>;
138 69 SymbolTableEntry* composedResult = composedStruct.lookupInComposedFields(name, indexPath);
139
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 19 times.
69 if composedResult != nil<SymbolTableEntry*> {
140 50 return composedResult;
141 }
142
143 // Remove the current field's element index from the index path
144 19 indexPath.popBack();
145 19 }
146
147 // Field was not found in the current struct, return nullptr
148 22 return nil<SymbolTableEntry*>;
149 }
150
151 /**
152 * Check that all fields are in a certain lifecycle state.
153 *
154 * @param state Lifecycle state to check for
155 * @return nullptr if all fields are in this state, otherwise the first mismatched field symbol
156 */
157 1640 public f<SymbolTableEntry*> Struct.areAllFieldsInState(const LifecycleState state) {
158
2/2
✓ Branch 0 taken 3777 times.
✓ Branch 1 taken 1640 times.
5417 for unsigned long i = 0l; i < this.fieldTypes.getSize(); i++ {
159 3777 SymbolTableEntry* fieldSymbol = this.scope.lookupField(cast<unsigned int>(i));
160 3777 assert fieldSymbol != nil<SymbolTableEntry*>;
161 3777 const Lifecycle& lifecycle = fieldSymbol.getLifecycle();
162
3/4
✓ Branch 0 taken 150 times.
✓ Branch 1 taken 3627 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3777 times.
3777 if !fieldSymbol.isImplicitField && lifecycle.getCurrentState() != state {
163 ✗ return fieldSymbol;
164 }
165 3777 }
166 1640 return nil<SymbolTableEntry*>;
167 }
168
169 /**
170 * Check that all fields are initialized.
171 *
172 * @return nullptr if all fields are initialized, otherwise the first uninitialized field symbol
173 */
174 1640 public f<SymbolTableEntry*> Struct.areAllFieldsInitialized() {
175 1640 return this.areAllFieldsInState(LifecycleState::INITIALIZED);
176 }
177
178 /**
179 * Reset the initialization state of all fields to DECLARED.
180 */
181 8078 public p Struct.resetFieldSymbolsToDeclared() {
182 // Reset every explicit field
183 8078 const unsigned long fieldCount = this.scope.getFieldCount();
184
2/2
✓ Branch 0 taken 22325 times.
✓ Branch 1 taken 8078 times.
30403 for unsigned long i = 0l; i < fieldCount; i++ {
185 22325 SymbolTableEntry* fieldSymbol = this.scope.lookupField(cast<unsigned int>(i));
186
2/2
✓ Branch 0 taken 1664 times.
✓ Branch 1 taken 20661 times.
22325 if !fieldSymbol.isImplicitField {
187 20661 fieldSymbol.updateState(LifecycleState::DECLARED);
188 20661 }
189 22325 }
190 8078 }
191