Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2025 ChilliBits. All rights reserved. | ||
2 | |||
3 | #include "TypeMatcher.h" | ||
4 | |||
5 | #include <exception/SemanticError.h> | ||
6 | #include <symboltablebuilder/Scope.h> | ||
7 | |||
8 | namespace spice::compiler { | ||
9 | |||
10 | 2624 | bool TypeMatcher::matchRequestedToCandidateTypes(const QualTypeList &candidateTypes, const QualTypeList &reqTypes, | |
11 | TypeMapping &typeMapping, ResolverFct &resolverFct, bool strictQualifiers) { | ||
12 | // Check if the size of template types matches | ||
13 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 2624 times.
|
2624 | if (reqTypes.size() != candidateTypes.size()) |
14 | ✗ | return false; | |
15 | |||
16 | // Loop through both lists at the same time and match each pair of template types individually | ||
17 |
2/2✓ Branch 0 (14→7) taken 3115 times.
✓ Branch 1 (14→15) taken 2621 times.
|
5736 | for (size_t i = 0; i < candidateTypes.size(); i++) { |
18 | 3115 | const QualType &reqType = reqTypes.at(i); | |
19 | 3115 | const QualType &candidateType = candidateTypes.at(i); | |
20 | |||
21 | // Match the pair of template types | ||
22 |
2/2✓ Branch 0 (10→11) taken 3 times.
✓ Branch 1 (10→12) taken 3112 times.
|
3115 | if (!matchRequestedToCandidateType(candidateType, reqType, typeMapping, resolverFct, strictQualifiers)) |
23 | 3 | return false; | |
24 | } | ||
25 | 2621 | return true; | |
26 | } | ||
27 | |||
28 | 64646 | bool TypeMatcher::matchRequestedToCandidateType(QualType candidateType, QualType requestedType, TypeMapping &typeMapping, | |
29 | ResolverFct &resolverFct, bool strictQualifierMatching) { | ||
30 | // Unwrap as far as possible and remove reference wrappers if possible | ||
31 | 64646 | QualType::unwrapBoth(candidateType, requestedType); | |
32 | |||
33 | // If the candidate does not contain any generic parts, we can simply check for type equality | ||
34 |
2/2✓ Branch 0 (4→5) taken 47839 times.
✓ Branch 1 (4→10) taken 16807 times.
|
64646 | if (!candidateType.hasAnyGenericParts()) { |
35 | // Check if the right one is a struct that implements the interface on the left | ||
36 |
2/2✓ Branch 0 (6→7) taken 1 times.
✓ Branch 1 (6→8) taken 47838 times.
|
47839 | if (candidateType.matchesInterfaceImplementedByStruct(requestedType)) |
37 | 1 | return true; | |
38 | // Normal equality check | ||
39 | 47838 | return candidateType.matches(requestedType, true, !strictQualifierMatching, true); | |
40 | } | ||
41 | |||
42 | // Check if the candidate type itself is generic | ||
43 |
2/2✓ Branch 0 (11→12) taken 8203 times.
✓ Branch 1 (11→57) taken 8604 times.
|
16807 | if (candidateType.isBase(TY_GENERIC)) { // The candidate type itself is generic |
44 |
3/6✓ Branch 0 (12→13) taken 8203 times.
✗ Branch 1 (12→84) not taken.
✓ Branch 2 (13→14) taken 8203 times.
✗ Branch 3 (13→84) not taken.
✓ Branch 4 (14→15) taken 8203 times.
✗ Branch 5 (14→84) not taken.
|
8203 | const std::string genericTypeName = candidateType.getBase().getSubType(); |
45 | |||
46 | // Check if we know the concrete type for that generic type name already | ||
47 |
3/4✓ Branch 0 (15→16) taken 8203 times.
✗ Branch 1 (15→91) not taken.
✓ Branch 2 (16→17) taken 1466 times.
✓ Branch 3 (16→29) taken 6737 times.
|
8203 | if (typeMapping.contains(genericTypeName)) { // This is a known generic type |
48 |
1/2✓ Branch 0 (17→18) taken 1466 times.
✗ Branch 1 (17→86) not taken.
|
1466 | QualType knownConcreteType = typeMapping.at(genericTypeName); |
49 | |||
50 | // Merge qualifiers of candidate type and known concrete type together | ||
51 |
1/2✓ Branch 0 (20→21) taken 1466 times.
✗ Branch 1 (20→86) not taken.
|
1466 | const TypeQualifiers mergedQualifiers = knownConcreteType.getQualifiers().merge(candidateType.getQualifiers()); |
52 | 1466 | knownConcreteType.setQualifiers(mergedQualifiers); | |
53 | |||
54 | // Remove reference wrapper of candidate type if required | ||
55 |
3/4✓ Branch 0 (22→23) taken 1466 times.
✗ Branch 1 (22→86) not taken.
✓ Branch 2 (23→24) taken 1348 times.
✓ Branch 3 (23→26) taken 118 times.
|
1466 | if (!requestedType.isRef()) |
56 |
1/2✓ Branch 0 (24→25) taken 1348 times.
✗ Branch 1 (24→85) not taken.
|
1348 | knownConcreteType = knownConcreteType.removeReferenceWrapper(); |
57 | |||
58 | // Check if the known concrete type matches the requested type | ||
59 |
1/2✓ Branch 0 (26→27) taken 1466 times.
✗ Branch 1 (26→86) not taken.
|
1466 | return knownConcreteType.matches(requestedType, true, !strictQualifierMatching, true); |
60 | } else { // This is an unknown generic type | ||
61 | // Retrieve generic candidate type by its name | ||
62 |
1/2✓ Branch 0 (29→30) taken 6737 times.
✗ Branch 1 (29→90) not taken.
|
6737 | const GenericType *genericCandidateType = resolverFct(genericTypeName); |
63 |
1/2✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 6737 times.
|
6737 | assert(genericCandidateType != nullptr); |
64 | |||
65 | // Check if the requested type fulfills all conditions of the generic candidate type | ||
66 |
3/4✓ Branch 0 (32→33) taken 6737 times.
✗ Branch 1 (32→90) not taken.
✓ Branch 2 (33→34) taken 2602 times.
✓ Branch 3 (33→35) taken 4135 times.
|
6737 | if (!genericCandidateType->checkConditionsOf(requestedType, true, !strictQualifierMatching)) |
67 | 2602 | return false; | |
68 | |||
69 | // Zero out all qualifiers in the requested type, that are present in the candidate type | ||
70 | // This is to set all qualifiers that are not present in the candidate type to the generic type replacement | ||
71 |
1/2✓ Branch 0 (37→38) taken 4135 times.
✗ Branch 1 (37→90) not taken.
|
4135 | requestedType.getQualifiers().eraseWithMask(candidateType.getQualifiers()); |
72 | |||
73 | // Add to type mapping | ||
74 |
3/4✓ Branch 0 (38→39) taken 4135 times.
✗ Branch 1 (38→90) not taken.
✓ Branch 2 (39→40) taken 827 times.
✓ Branch 3 (39→41) taken 3308 times.
|
4135 | const QualType newMappingType = requestedType.hasAnyGenericParts() ? candidateType : requestedType; |
75 |
6/10✓ Branch 0 (42→43) taken 4135 times.
✗ Branch 1 (42→90) not taken.
✓ Branch 2 (43→44) taken 3308 times.
✓ Branch 3 (43→50) taken 827 times.
✓ Branch 4 (44→45) taken 3308 times.
✗ Branch 5 (44→90) not taken.
✓ Branch 6 (45→46) taken 3308 times.
✗ Branch 7 (45→50) not taken.
✗ Branch 8 (48→49) not taken.
✓ Branch 9 (48→50) taken 3308 times.
|
4135 | assert(newMappingType.is(TY_GENERIC) || newMappingType.is(TY_INVALID) || |
76 | newMappingType.getQualifiers().isSigned != newMappingType.getQualifiers().isUnsigned); | ||
77 |
2/4✓ Branch 0 (50→51) taken 4135 times.
✗ Branch 1 (50→89) not taken.
✓ Branch 2 (51→52) taken 4135 times.
✗ Branch 3 (51→87) not taken.
|
4135 | typeMapping.insert({genericTypeName, newMappingType}); |
78 | |||
79 | 4135 | return true; // The type was successfully matched, by enriching the type mapping | |
80 | } | ||
81 | 8203 | } else { // The candidate type itself is non-generic, but one or several template or param types are | |
82 | // Check if supertype and subtype are equal | ||
83 |
2/2✓ Branch 0 (59→60) taken 5854 times.
✓ Branch 1 (59→61) taken 2750 times.
|
8604 | if (requestedType.getSuperType() != candidateType.getSuperType()) |
84 | 5854 | return false; | |
85 | |||
86 | // If we have a function/procedure type, check the param and return types. Otherwise, check the template types | ||
87 |
3/4✓ Branch 0 (61→62) taken 2750 times.
✗ Branch 1 (61→94) not taken.
✓ Branch 2 (62→63) taken 20 times.
✓ Branch 3 (62→68) taken 2730 times.
|
2750 | if (candidateType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) { |
88 | // Check param and return types | ||
89 | 20 | const QualTypeList &candidatePRTypes = candidateType.getFunctionParamAndReturnTypes(); | |
90 | 20 | const QualTypeList &requestedPRTypes = requestedType.getFunctionParamAndReturnTypes(); | |
91 |
1/2✗ Branch 0 (66→67) not taken.
✓ Branch 1 (66→82) taken 20 times.
|
20 | if (!matchRequestedToCandidateTypes(candidatePRTypes, requestedPRTypes, typeMapping, resolverFct, strictQualifierMatching)) |
92 | ✗ | return false; | |
93 | } else { | ||
94 |
2/2✓ Branch 0 (71→72) taken 126 times.
✓ Branch 1 (71→73) taken 2604 times.
|
2730 | if (requestedType.getSubType() != candidateType.getSubType()) |
95 | 126 | return false; | |
96 |
1/2✗ Branch 0 (75→76) not taken.
✓ Branch 1 (75→77) taken 2604 times.
|
2604 | if (requestedType.getBodyScope()->parent != candidateType.getBodyScope()->parent) |
97 | ✗ | return false; | |
98 | |||
99 | // Check template types | ||
100 | 2604 | const QualTypeList &candidateTTypes = candidateType.getTemplateTypes(); | |
101 | 2604 | const QualTypeList &requestedTTypes = requestedType.getTemplateTypes(); | |
102 |
2/2✓ Branch 0 (80→81) taken 3 times.
✓ Branch 1 (80→82) taken 2601 times.
|
2604 | if (!matchRequestedToCandidateTypes(candidateTTypes, requestedTTypes, typeMapping, resolverFct, strictQualifierMatching)) |
103 | 3 | return false; | |
104 | } | ||
105 | |||
106 | 2621 | return true; // All requested template types match to their respective candidate template type -> successfully matched | |
107 | } | ||
108 | } | ||
109 | |||
110 | 780 | void TypeMatcher::substantiateTypesWithTypeMapping(QualTypeList &qualTypes, const TypeMapping &typeMapping, const ASTNode *node) { | |
111 |
2/2✓ Branch 0 (10→4) taken 126 times.
✓ Branch 1 (10→11) taken 780 times.
|
906 | for (QualType &qualType : qualTypes) |
112 |
3/4✓ Branch 0 (5→6) taken 126 times.
✗ Branch 1 (5→12) not taken.
✓ Branch 2 (6→7) taken 112 times.
✓ Branch 3 (6→8) taken 14 times.
|
126 | if (qualType.hasAnyGenericParts()) |
113 |
1/2✓ Branch 0 (7→8) taken 112 times.
✗ Branch 1 (7→12) not taken.
|
112 | substantiateTypeWithTypeMapping(qualType, typeMapping, node); |
114 | 780 | } | |
115 | |||
116 | 11845 | void TypeMatcher::substantiateTypeWithTypeMapping(QualType &type, const TypeMapping &typeMapping, // NOLINT(*-no-recursion) | |
117 | const ASTNode *node) { | ||
118 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 11845 times.
|
11845 | assert(type.hasAnyGenericParts()); |
119 | |||
120 | // Check if the type itself is generic | ||
121 |
2/2✓ Branch 0 (6→7) taken 8151 times.
✓ Branch 1 (6→17) taken 3694 times.
|
11845 | if (type.isBase(TY_GENERIC)) { // The symbol type itself is generic |
122 |
3/6✓ Branch 0 (7→8) taken 8151 times.
✗ Branch 1 (7→91) not taken.
✓ Branch 2 (8→9) taken 8151 times.
✗ Branch 3 (8→91) not taken.
✓ Branch 4 (9→10) taken 8151 times.
✗ Branch 5 (9→91) not taken.
|
8151 | const std::string genericTypeName = type.getBase().getSubType(); |
123 |
2/4✓ Branch 0 (10→11) taken 8151 times.
✗ Branch 1 (10→93) not taken.
✗ Branch 2 (11→12) not taken.
✓ Branch 3 (11→13) taken 8151 times.
|
8151 | assert(typeMapping.contains(genericTypeName)); |
124 |
1/2✓ Branch 0 (13→14) taken 8151 times.
✗ Branch 1 (13→93) not taken.
|
8151 | const QualType &replacementType = typeMapping.at(genericTypeName); |
125 |
1/2✓ Branch 0 (14→15) taken 8151 times.
✗ Branch 1 (14→92) not taken.
|
8151 | type = type.replaceBaseType(replacementType); |
126 |
4/6✓ Branch 0 (17→18) taken 3694 times.
✗ Branch 1 (17→97) not taken.
✓ Branch 2 (18→19) taken 3694 times.
✗ Branch 3 (18→96) not taken.
✓ Branch 4 (19→20) taken 52 times.
✓ Branch 5 (19→34) taken 3642 times.
|
11845 | } else if (type.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE})) { // The base type is a function or procedure |
127 | // Substantiate each param or return type | ||
128 |
2/4✓ Branch 0 (20→21) taken 52 times.
✗ Branch 1 (20→102) not taken.
✓ Branch 2 (21→22) taken 52 times.
✗ Branch 3 (21→102) not taken.
|
52 | QualTypeList paramAndReturnTypes = type.getFunctionParamAndReturnTypes(); |
129 |
2/2✓ Branch 0 (30→24) taken 104 times.
✓ Branch 1 (30→31) taken 52 times.
|
156 | for (QualType ¶mOrReturnType : paramAndReturnTypes) |
130 |
3/4✓ Branch 0 (25→26) taken 104 times.
✗ Branch 1 (25→98) not taken.
✓ Branch 2 (26→27) taken 52 times.
✓ Branch 3 (26→28) taken 52 times.
|
104 | if (paramOrReturnType.hasAnyGenericParts()) |
131 |
1/2✓ Branch 0 (27→28) taken 52 times.
✗ Branch 1 (27→98) not taken.
|
52 | substantiateTypeWithTypeMapping(paramOrReturnType, typeMapping, node); |
132 | // Attach the list of concrete param types to the symbol type | ||
133 |
1/2✓ Branch 0 (31→32) taken 52 times.
✗ Branch 1 (31→99) not taken.
|
52 | type = type.getWithFunctionParamAndReturnTypes(paramAndReturnTypes); |
134 | 52 | } else { // The base type is a struct or interface | |
135 |
3/6✓ Branch 0 (34→35) taken 3642 times.
✗ Branch 1 (34→104) not taken.
✓ Branch 2 (35→36) taken 3642 times.
✗ Branch 3 (35→103) not taken.
✗ Branch 4 (36→37) not taken.
✓ Branch 5 (36→38) taken 3642 times.
|
3642 | assert(type.getBase().isOneOf({TY_STRUCT, TY_INTERFACE})); |
136 | // Substantiate each template type | ||
137 |
1/2✓ Branch 0 (38→39) taken 3642 times.
✗ Branch 1 (38→135) not taken.
|
3642 | const QualType baseType = type.getBase(); |
138 |
2/4✓ Branch 0 (39→40) taken 3642 times.
✗ Branch 1 (39→135) not taken.
✓ Branch 2 (40→41) taken 3642 times.
✗ Branch 3 (40→135) not taken.
|
3642 | QualTypeList templateTypes = baseType.getTemplateTypes(); |
139 |
2/2✓ Branch 0 (49→43) taken 4515 times.
✓ Branch 1 (49→50) taken 3642 times.
|
8157 | for (QualType &templateType : templateTypes) |
140 |
3/4✓ Branch 0 (44→45) taken 4515 times.
✗ Branch 1 (44→105) not taken.
✓ Branch 2 (45→46) taken 4322 times.
✓ Branch 3 (45→47) taken 193 times.
|
4515 | if (templateType.hasAnyGenericParts()) |
141 |
1/2✓ Branch 0 (46→47) taken 4322 times.
✗ Branch 1 (46→105) not taken.
|
4322 | substantiateTypeWithTypeMapping(templateType, typeMapping, node); |
142 | // Attach the list of concrete template types to the symbol type | ||
143 |
1/2✓ Branch 0 (50→51) taken 3642 times.
✗ Branch 1 (50→106) not taken.
|
3642 | type = type.getWithBaseTemplateTypes(templateTypes); |
144 | // Lookup the scope of the concrete struct or interface type | ||
145 | // Only do this, if the struct or interface is not self-referencing, because in that case we'd end up in an infinite recursion | ||
146 |
3/4✓ Branch 0 (51→52) taken 3642 times.
✗ Branch 1 (51→133) not taken.
✓ Branch 2 (52→53) taken 3453 times.
✓ Branch 3 (52→88) taken 189 times.
|
3642 | if (!baseType.isSelfReferencingStructType()) { |
147 |
1/2✓ Branch 0 (53→54) taken 3453 times.
✗ Branch 1 (53→133) not taken.
|
3453 | Scope *matchScope = baseType.getBodyScope()->parent; |
148 |
3/4✓ Branch 0 (54→55) taken 3453 times.
✗ Branch 1 (54→133) not taken.
✓ Branch 2 (55→56) taken 3189 times.
✓ Branch 3 (55→72) taken 264 times.
|
3453 | if (baseType.is(TY_STRUCT)) { // Struct |
149 |
2/4✓ Branch 0 (56→57) taken 3189 times.
✗ Branch 1 (56→133) not taken.
✓ Branch 2 (57→58) taken 3189 times.
✗ Branch 3 (57→133) not taken.
|
3189 | const Struct *spiceStruct = StructManager::match(matchScope, baseType.getSubType(), templateTypes, node); |
150 |
1/2✗ Branch 0 (58→59) not taken.
✓ Branch 1 (58→70) taken 3189 times.
|
3189 | if (!spiceStruct) { |
151 | ✗ | assert(node != nullptr); | |
152 | ✗ | const std::string signature = Struct::getSignature(baseType.getSubType(), templateTypes); | |
153 | ✗ | throw SemanticError(node, UNKNOWN_DATATYPE, "Could not find struct '" + signature + "'"); | |
154 | ✗ | } | |
155 |
1/2✓ Branch 0 (70→71) taken 3189 times.
✗ Branch 1 (70→119) not taken.
|
3189 | type = type.getWithBodyScope(spiceStruct->scope); |
156 | } else { // Interface | ||
157 |
2/4✓ Branch 0 (72→73) taken 264 times.
✗ Branch 1 (72→133) not taken.
✓ Branch 2 (73→74) taken 264 times.
✗ Branch 3 (73→133) not taken.
|
264 | const Interface *spiceInterface = InterfaceManager::match(matchScope, baseType.getSubType(), templateTypes, node); |
158 |
1/2✗ Branch 0 (74→75) not taken.
✓ Branch 1 (74→86) taken 264 times.
|
264 | if (!spiceInterface) { |
159 | ✗ | assert(node != nullptr); | |
160 | ✗ | const std::string signature = Interface::getSignature(baseType.getSubType(), templateTypes); | |
161 | ✗ | throw SemanticError(node, UNKNOWN_DATATYPE, "Could not find interface '" + signature + "'"); | |
162 | ✗ | } | |
163 |
1/2✓ Branch 0 (86→87) taken 264 times.
✗ Branch 1 (86→132) not taken.
|
264 | type = type.getWithBodyScope(spiceInterface->scope); |
164 | } | ||
165 | } | ||
166 | 3642 | } | |
167 | 11845 | } | |
168 | |||
169 | } // namespace spice::compiler | ||
170 |