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 | 2596 | 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 2596 times.
|
2596 | 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 3138 times.
✓ Branch 1 (14→15) taken 2592 times.
|
5730 | for (size_t i = 0; i < candidateTypes.size(); i++) { |
18 | 3138 | const QualType &reqType = reqTypes.at(i); | |
19 | 3138 | const QualType &candidateType = candidateTypes.at(i); | |
20 | |||
21 | // Match the pair of template types | ||
22 |
2/2✓ Branch 0 (10→11) taken 4 times.
✓ Branch 1 (10→12) taken 3134 times.
|
3138 | if (!matchRequestedToCandidateType(candidateType, reqType, typeMapping, resolverFct, strictQualifiers)) |
23 | 4 | return false; | |
24 | } | ||
25 | 2592 | return true; | |
26 | } | ||
27 | |||
28 | 66420 | 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 | 66420 | 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 49092 times.
✓ Branch 1 (4→10) taken 17328 times.
|
66420 | 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 2 times.
✓ Branch 1 (6→8) taken 49090 times.
|
49092 | if (candidateType.matchesInterfaceImplementedByStruct(requestedType)) |
37 | 2 | return true; | |
38 | // Normal equality check | ||
39 | 49090 | 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 8515 times.
✓ Branch 1 (11→57) taken 8813 times.
|
17328 | if (candidateType.isBase(TY_GENERIC)) { // The candidate type itself is generic |
44 |
3/6✓ Branch 0 (12→13) taken 8515 times.
✗ Branch 1 (12→84) not taken.
✓ Branch 2 (13→14) taken 8515 times.
✗ Branch 3 (13→84) not taken.
✓ Branch 4 (14→15) taken 8515 times.
✗ Branch 5 (14→84) not taken.
|
8515 | 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 8515 times.
✗ Branch 1 (15→91) not taken.
✓ Branch 2 (16→17) taken 1405 times.
✓ Branch 3 (16→29) taken 7110 times.
|
8515 | if (typeMapping.contains(genericTypeName)) { // This is a known generic type |
48 |
1/2✓ Branch 0 (17→18) taken 1405 times.
✗ Branch 1 (17→86) not taken.
|
1405 | 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 1405 times.
✗ Branch 1 (20→86) not taken.
|
1405 | const TypeQualifiers mergedQualifiers = knownConcreteType.getQualifiers().merge(candidateType.getQualifiers()); |
52 | 1405 | knownConcreteType.setQualifiers(mergedQualifiers); | |
53 | |||
54 | // Remove reference wrapper of candidate type if required | ||
55 |
3/4✓ Branch 0 (22→23) taken 1405 times.
✗ Branch 1 (22→86) not taken.
✓ Branch 2 (23→24) taken 1279 times.
✓ Branch 3 (23→26) taken 126 times.
|
1405 | if (!requestedType.isRef()) |
56 |
1/2✓ Branch 0 (24→25) taken 1279 times.
✗ Branch 1 (24→85) not taken.
|
1279 | knownConcreteType = knownConcreteType.removeReferenceWrapper(); |
57 | |||
58 | // Check if the known concrete type matches the requested type | ||
59 |
1/2✓ Branch 0 (26→27) taken 1405 times.
✗ Branch 1 (26→86) not taken.
|
1405 | 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 7110 times.
✗ Branch 1 (29→90) not taken.
|
7110 | const GenericType *genericCandidateType = resolverFct(genericTypeName); |
63 |
1/2✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 7110 times.
|
7110 | assert(genericCandidateType != nullptr); |
64 | |||
65 | // Check if the requested type fulfills all conditions of the generic candidate type | ||
66 | 7110 | QualType substantiation; | |
67 |
3/4✓ Branch 0 (32→33) taken 7110 times.
✗ Branch 1 (32→90) not taken.
✓ Branch 2 (33→34) taken 2723 times.
✓ Branch 3 (33→35) taken 4387 times.
|
7110 | if (!genericCandidateType->checkConditionsOf(requestedType, substantiation, true, !strictQualifierMatching)) |
68 | 2723 | return false; | |
69 | |||
70 | // Zero out all qualifiers in the requested type, that are present in the candidate type | ||
71 | // This is to set all qualifiers that are not present in the candidate type to the generic type replacement | ||
72 |
1/2✓ Branch 0 (37→38) taken 4387 times.
✗ Branch 1 (37→90) not taken.
|
4387 | requestedType.getQualifiers().eraseWithMask(candidateType.getQualifiers()); |
73 | |||
74 | // Add to type mapping | ||
75 |
3/4✓ Branch 0 (38→39) taken 4387 times.
✗ Branch 1 (38→90) not taken.
✓ Branch 2 (39→40) taken 907 times.
✓ Branch 3 (39→41) taken 3480 times.
|
4387 | const QualType newMappingType = requestedType.hasAnyGenericParts() ? candidateType : substantiation; |
76 |
6/10✓ Branch 0 (42→43) taken 4387 times.
✗ Branch 1 (42→90) not taken.
✓ Branch 2 (43→44) taken 3480 times.
✓ Branch 3 (43→50) taken 907 times.
✓ Branch 4 (44→45) taken 3480 times.
✗ Branch 5 (44→90) not taken.
✓ Branch 6 (45→46) taken 3480 times.
✗ Branch 7 (45→50) not taken.
✗ Branch 8 (48→49) not taken.
✓ Branch 9 (48→50) taken 3480 times.
|
4387 | assert(newMappingType.is(TY_GENERIC) || newMappingType.is(TY_INVALID) || |
77 | newMappingType.getQualifiers().isSigned != newMappingType.getQualifiers().isUnsigned); | ||
78 |
2/4✓ Branch 0 (50→51) taken 4387 times.
✗ Branch 1 (50→89) not taken.
✓ Branch 2 (51→52) taken 4387 times.
✗ Branch 3 (51→87) not taken.
|
4387 | typeMapping.insert({genericTypeName, newMappingType}); |
79 | |||
80 | 4387 | return true; // The type was successfully matched, by enriching the type mapping | |
81 | } | ||
82 | 8515 | } else { // The candidate type itself is non-generic, but one or several template or param types are | |
83 | // Check if supertype and subtype are equal | ||
84 |
2/2✓ Branch 0 (59→60) taken 6073 times.
✓ Branch 1 (59→61) taken 2740 times.
|
8813 | if (requestedType.getSuperType() != candidateType.getSuperType()) |
85 | 6073 | return false; | |
86 | |||
87 | // If we have a function/procedure type, check the param and return types. Otherwise, check the template types | ||
88 |
3/4✓ Branch 0 (61→62) taken 2740 times.
✗ Branch 1 (61→94) not taken.
✓ Branch 2 (62→63) taken 1 times.
✓ Branch 3 (62→68) taken 2739 times.
|
2740 | if (candidateType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) { |
89 | // Check param and return types | ||
90 | 1 | const QualTypeList &candidatePRTypes = candidateType.getFunctionParamAndReturnTypes(); | |
91 | 1 | const QualTypeList &requestedPRTypes = requestedType.getFunctionParamAndReturnTypes(); | |
92 |
1/2✗ Branch 0 (66→67) not taken.
✓ Branch 1 (66→82) taken 1 times.
|
1 | if (!matchRequestedToCandidateTypes(candidatePRTypes, requestedPRTypes, typeMapping, resolverFct, strictQualifierMatching)) |
93 | ✗ | return false; | |
94 | } else { | ||
95 |
2/2✓ Branch 0 (71→72) taken 144 times.
✓ Branch 1 (71→73) taken 2595 times.
|
2739 | if (requestedType.getSubType() != candidateType.getSubType()) |
96 | 144 | return false; | |
97 |
1/2✗ Branch 0 (75→76) not taken.
✓ Branch 1 (75→77) taken 2595 times.
|
2595 | if (requestedType.getBodyScope()->parent != candidateType.getBodyScope()->parent) |
98 | ✗ | return false; | |
99 | |||
100 | // Check template types | ||
101 | 2595 | const QualTypeList &candidateTTypes = candidateType.getTemplateTypes(); | |
102 | 2595 | const QualTypeList &requestedTTypes = requestedType.getTemplateTypes(); | |
103 |
2/2✓ Branch 0 (80→81) taken 4 times.
✓ Branch 1 (80→82) taken 2591 times.
|
2595 | if (!matchRequestedToCandidateTypes(candidateTTypes, requestedTTypes, typeMapping, resolverFct, strictQualifierMatching)) |
104 | 4 | return false; | |
105 | } | ||
106 | |||
107 | 2592 | return true; // All requested template types match to their respective candidate template type -> successfully matched | |
108 | } | ||
109 | } | ||
110 | |||
111 | 840 | void TypeMatcher::substantiateTypesWithTypeMapping(QualTypeList &qualTypes, const TypeMapping &typeMapping, const ASTNode *node) { | |
112 |
2/2✓ Branch 0 (10→4) taken 134 times.
✓ Branch 1 (10→11) taken 840 times.
|
974 | for (QualType &qualType : qualTypes) |
113 |
3/4✓ Branch 0 (5→6) taken 134 times.
✗ Branch 1 (5→12) not taken.
✓ Branch 2 (6→7) taken 120 times.
✓ Branch 3 (6→8) taken 14 times.
|
134 | if (qualType.hasAnyGenericParts()) |
114 |
1/2✓ Branch 0 (7→8) taken 120 times.
✗ Branch 1 (7→12) not taken.
|
120 | substantiateTypeWithTypeMapping(qualType, typeMapping, node); |
115 | 840 | } | |
116 | |||
117 | 12278 | void TypeMatcher::substantiateTypeWithTypeMapping(QualType &type, const TypeMapping &typeMapping, // NOLINT(*-no-recursion) | |
118 | const ASTNode *node) { | ||
119 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 12278 times.
|
12278 | assert(type.hasAnyGenericParts()); |
120 | |||
121 | // Check if the type itself is generic | ||
122 |
2/2✓ Branch 0 (6→7) taken 8520 times.
✓ Branch 1 (6→17) taken 3758 times.
|
12278 | if (type.isBase(TY_GENERIC)) { // The symbol type itself is generic |
123 |
3/6✓ Branch 0 (7→8) taken 8520 times.
✗ Branch 1 (7→88) not taken.
✓ Branch 2 (8→9) taken 8520 times.
✗ Branch 3 (8→88) not taken.
✓ Branch 4 (9→10) taken 8520 times.
✗ Branch 5 (9→88) not taken.
|
8520 | const std::string genericTypeName = type.getBase().getSubType(); |
124 |
2/4✓ Branch 0 (10→11) taken 8520 times.
✗ Branch 1 (10→90) not taken.
✗ Branch 2 (11→12) not taken.
✓ Branch 3 (11→13) taken 8520 times.
|
8520 | assert(typeMapping.contains(genericTypeName)); |
125 |
1/2✓ Branch 0 (13→14) taken 8520 times.
✗ Branch 1 (13→90) not taken.
|
8520 | const QualType &replacementType = typeMapping.at(genericTypeName); |
126 |
1/2✓ Branch 0 (14→15) taken 8520 times.
✗ Branch 1 (14→89) not taken.
|
8520 | type = type.replaceBaseType(replacementType); |
127 |
4/6✓ Branch 0 (17→18) taken 3758 times.
✗ Branch 1 (17→94) not taken.
✓ Branch 2 (18→19) taken 3758 times.
✗ Branch 3 (18→93) not taken.
✓ Branch 4 (19→20) taken 19 times.
✓ Branch 5 (19→34) taken 3739 times.
|
12278 | } else if (type.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE})) { // The base type is a function or procedure |
128 | // Substantiate each param or return type | ||
129 |
2/4✓ Branch 0 (20→21) taken 19 times.
✗ Branch 1 (20→99) not taken.
✓ Branch 2 (21→22) taken 19 times.
✗ Branch 3 (21→99) not taken.
|
19 | QualTypeList paramAndReturnTypes = type.getFunctionParamAndReturnTypes(); |
130 |
2/2✓ Branch 0 (30→24) taken 38 times.
✓ Branch 1 (30→31) taken 19 times.
|
57 | for (QualType ¶mOrReturnType : paramAndReturnTypes) |
131 |
3/4✓ Branch 0 (25→26) taken 38 times.
✗ Branch 1 (25→95) not taken.
✓ Branch 2 (26→27) taken 19 times.
✓ Branch 3 (26→28) taken 19 times.
|
38 | if (paramOrReturnType.hasAnyGenericParts()) |
132 |
1/2✓ Branch 0 (27→28) taken 19 times.
✗ Branch 1 (27→95) not taken.
|
19 | substantiateTypeWithTypeMapping(paramOrReturnType, typeMapping, node); |
133 | // Attach the list of concrete param types to the symbol type | ||
134 |
1/2✓ Branch 0 (31→32) taken 19 times.
✗ Branch 1 (31→96) not taken.
|
19 | type = type.getWithFunctionParamAndReturnTypes(paramAndReturnTypes); |
135 | 19 | } else { // The base type is a struct or interface | |
136 |
3/6✓ Branch 0 (34→35) taken 3739 times.
✗ Branch 1 (34→101) not taken.
✓ Branch 2 (35→36) taken 3739 times.
✗ Branch 3 (35→100) not taken.
✗ Branch 4 (36→37) not taken.
✓ Branch 5 (36→38) taken 3739 times.
|
3739 | assert(type.getBase().isOneOf({TY_STRUCT, TY_INTERFACE})); |
137 | // Substantiate each template type | ||
138 |
1/2✓ Branch 0 (38→39) taken 3739 times.
✗ Branch 1 (38→132) not taken.
|
3739 | const QualType baseType = type.getBase(); |
139 |
2/4✓ Branch 0 (39→40) taken 3739 times.
✗ Branch 1 (39→132) not taken.
✓ Branch 2 (40→41) taken 3739 times.
✗ Branch 3 (40→132) not taken.
|
3739 | QualTypeList templateTypes = baseType.getTemplateTypes(); |
140 |
2/2✓ Branch 0 (49→43) taken 4730 times.
✓ Branch 1 (49→50) taken 3739 times.
|
8469 | for (QualType &templateType : templateTypes) |
141 |
3/4✓ Branch 0 (44→45) taken 4730 times.
✗ Branch 1 (44→102) not taken.
✓ Branch 2 (45→46) taken 4523 times.
✓ Branch 3 (45→47) taken 207 times.
|
4730 | if (templateType.hasAnyGenericParts()) |
142 |
1/2✓ Branch 0 (46→47) taken 4523 times.
✗ Branch 1 (46→102) not taken.
|
4523 | substantiateTypeWithTypeMapping(templateType, typeMapping, node); |
143 | // Attach the list of concrete template types to the symbol type | ||
144 |
1/2✓ Branch 0 (50→51) taken 3739 times.
✗ Branch 1 (50→103) not taken.
|
3739 | type = type.getWithBaseTemplateTypes(templateTypes); |
145 | // Lookup the scope of the concrete struct or interface type | ||
146 | // Only do this, if the struct or interface is not self-referencing, because in that case we'd end up in an infinite recursion | ||
147 |
3/4✓ Branch 0 (51→52) taken 3739 times.
✗ Branch 1 (51→130) not taken.
✓ Branch 2 (52→53) taken 3539 times.
✓ Branch 3 (52→85) taken 200 times.
|
3739 | if (!baseType.isSelfReferencingStructType()) { |
148 |
3/4✓ Branch 0 (53→54) taken 3539 times.
✗ Branch 1 (53→130) not taken.
✓ Branch 2 (54→55) taken 3247 times.
✓ Branch 3 (54→70) taken 292 times.
|
3539 | if (baseType.is(TY_STRUCT)) { // Struct |
149 |
1/2✓ Branch 0 (55→56) taken 3247 times.
✗ Branch 1 (55→130) not taken.
|
3247 | const Struct *spiceStruct = baseType.getStruct(node, templateTypes); |
150 |
1/2✗ Branch 0 (56→57) not taken.
✓ Branch 1 (56→68) taken 3247 times.
|
3247 | 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 (68→69) taken 3247 times.
✗ Branch 1 (68→116) not taken.
|
3247 | type = type.getWithBodyScope(spiceStruct->scope); |
156 | } else { // Interface | ||
157 |
1/2✓ Branch 0 (70→71) taken 292 times.
✗ Branch 1 (70→130) not taken.
|
292 | const Interface *spiceInterface = baseType.getInterface(node, templateTypes); |
158 |
1/2✗ Branch 0 (71→72) not taken.
✓ Branch 1 (71→83) taken 292 times.
|
292 | 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 (83→84) taken 292 times.
✗ Branch 1 (83→129) not taken.
|
292 | type = type.getWithBodyScope(spiceInterface->scope); |
164 | } | ||
165 | } | ||
166 | 3739 | } | |
167 | 12278 | } | |
168 | |||
169 | } // namespace spice::compiler | ||
170 |