Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2024 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 | 2390 | bool TypeMatcher::matchRequestedToCandidateTypes(const QualTypeList &candidateTypes, const QualTypeList &reqTypes, | |
11 | TypeMapping &typeMapping, ResolverFct &resolverFct, bool strictSpecifiers) { | ||
12 | // Check if the size of template types matches | ||
13 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2390 times.
|
2390 | 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 1 taken 2850 times.
✓ Branch 2 taken 2387 times.
|
5237 | for (size_t i = 0; i < candidateTypes.size(); i++) { |
18 | 2850 | const QualType &reqType = reqTypes.at(i); | |
19 | 2850 | const QualType &candidateType = candidateTypes.at(i); | |
20 | |||
21 | // Match the pair of template types | ||
22 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2847 times.
|
2850 | if (!matchRequestedToCandidateType(candidateType, reqType, typeMapping, resolverFct, strictSpecifiers)) |
23 | 3 | return false; | |
24 | } | ||
25 | 2387 | return true; | |
26 | } | ||
27 | |||
28 | 62026 | bool TypeMatcher::matchRequestedToCandidateType(QualType candidateType, QualType requestedType, TypeMapping &typeMapping, | |
29 | ResolverFct &resolverFct, bool strictSpecifierMatching) { | ||
30 | // Unwrap as far as possible and remove reference wrappers if possible | ||
31 | 62026 | 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 1 taken 48811 times.
✓ Branch 2 taken 13215 times.
|
62026 | if (!candidateType.hasAnyGenericParts()) { |
35 | // Check if the right one is a struct that implements the interface on the left | ||
36 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 48810 times.
|
48811 | if (candidateType.matchesInterfaceImplementedByStruct(requestedType)) |
37 | 1 | return true; | |
38 | // Normal equality check | ||
39 | 48810 | return candidateType.matches(requestedType, true, !strictSpecifierMatching, true); | |
40 | } | ||
41 | |||
42 | // Check if the candidate type itself is generic | ||
43 |
2/2✓ Branch 1 taken 7256 times.
✓ Branch 2 taken 5959 times.
|
13215 | if (candidateType.isBase(TY_GENERIC)) { // The candidate type itself is generic |
44 |
3/6✓ Branch 1 taken 7256 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7256 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7256 times.
✗ Branch 8 not taken.
|
7256 | 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 1 taken 7256 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1314 times.
✓ Branch 4 taken 5942 times.
|
7256 | if (typeMapping.contains(genericTypeName)) { // This is a known generic type |
48 |
1/2✓ Branch 1 taken 1314 times.
✗ Branch 2 not taken.
|
1314 | QualType knownConcreteType = typeMapping.at(genericTypeName); |
49 | |||
50 | // Merge specifiers of candidate type and known concrete type together | ||
51 |
1/2✓ Branch 3 taken 1314 times.
✗ Branch 4 not taken.
|
1314 | const TypeSpecifiers mergedSpecifiers = knownConcreteType.getSpecifiers().merge(candidateType.getSpecifiers()); |
52 | 1314 | knownConcreteType.setSpecifiers(mergedSpecifiers); | |
53 | |||
54 | // Remove reference wrapper of candidate type if required | ||
55 |
3/4✓ Branch 1 taken 1314 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1206 times.
✓ Branch 4 taken 108 times.
|
1314 | if (!requestedType.isRef()) |
56 |
1/2✓ Branch 1 taken 1206 times.
✗ Branch 2 not taken.
|
1206 | knownConcreteType = knownConcreteType.removeReferenceWrapper(); |
57 | |||
58 | // Check if the known concrete type matches the requested type | ||
59 |
1/2✓ Branch 1 taken 1314 times.
✗ Branch 2 not taken.
|
1314 | return knownConcreteType.matches(requestedType, true, !strictSpecifierMatching, true); |
60 | } else { // This is an unknown generic type | ||
61 | // Retrieve generic candidate type by its name | ||
62 |
1/2✓ Branch 1 taken 5942 times.
✗ Branch 2 not taken.
|
5942 | const GenericType *genericCandidateType = resolverFct(genericTypeName); |
63 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5942 times.
|
5942 | assert(genericCandidateType != nullptr); |
64 | |||
65 | // Check if the requested type fulfills all conditions of the generic candidate type | ||
66 |
3/4✓ Branch 1 taken 5942 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2212 times.
✓ Branch 4 taken 3730 times.
|
5942 | if (!genericCandidateType->checkConditionsOf(requestedType, true, !strictSpecifierMatching)) |
67 | 2212 | return false; | |
68 | |||
69 | // Zero out all specifiers in the requested type, that are present in the candidate type | ||
70 | // This is to set all specifiers that are not present in the candidate type to the generic type replacement | ||
71 |
1/2✓ Branch 3 taken 3730 times.
✗ Branch 4 not taken.
|
3730 | requestedType.getSpecifiers().eraseWithMask(candidateType.getSpecifiers()); |
72 | |||
73 | // Add to type mapping | ||
74 |
3/4✓ Branch 1 taken 3730 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 791 times.
✓ Branch 4 taken 2939 times.
|
3730 | const QualType newMappingType = requestedType.hasAnyGenericParts() ? candidateType : requestedType; |
75 |
4/6✓ Branch 1 taken 3730 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2939 times.
✓ Branch 4 taken 791 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2939 times.
|
3730 | assert(newMappingType.is(TY_GENERIC) || |
76 | newMappingType.getSpecifiers().isSigned != newMappingType.getSpecifiers().isUnsigned); | ||
77 |
2/4✓ Branch 1 taken 3730 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3730 times.
✗ Branch 5 not taken.
|
3730 | typeMapping.insert({genericTypeName, newMappingType}); |
78 | |||
79 | 3730 | return true; // The type was successfully matched, by enriching the type mapping | |
80 | } | ||
81 | 7256 | } 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 2 taken 3471 times.
✓ Branch 3 taken 2488 times.
|
5959 | if (requestedType.getSuperType() != candidateType.getSuperType()) |
84 | 3471 | 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 1 taken 2488 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 2478 times.
|
2488 | if (candidateType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) { |
88 | // Check param and return types | ||
89 | 10 | const QualTypeList &candidatePRTypes = candidateType.getFunctionParamAndReturnTypes(); | |
90 | 10 | const QualTypeList &requestedPRTypes = requestedType.getFunctionParamAndReturnTypes(); | |
91 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
|
10 | if (!matchRequestedToCandidateTypes(candidatePRTypes, requestedPRTypes, typeMapping, resolverFct, strictSpecifierMatching)) |
92 | ✗ | return false; | |
93 | } else { | ||
94 |
2/2✓ Branch 3 taken 98 times.
✓ Branch 4 taken 2380 times.
|
2478 | if (requestedType.getSubType() != candidateType.getSubType()) |
95 | 98 | return false; | |
96 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2380 times.
|
2380 | if (requestedType.getBodyScope()->parent != candidateType.getBodyScope()->parent) |
97 | ✗ | return false; | |
98 | |||
99 | // Check template types | ||
100 | 2380 | const QualTypeList &candidateTTypes = candidateType.getTemplateTypes(); | |
101 | 2380 | const QualTypeList &requestedTTypes = requestedType.getTemplateTypes(); | |
102 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2377 times.
|
2380 | if (!matchRequestedToCandidateTypes(candidateTTypes, requestedTTypes, typeMapping, resolverFct, strictSpecifierMatching)) |
103 | 3 | return false; | |
104 | } | ||
105 | |||
106 | 2387 | return true; // All requested template types match to their respective candidate template type -> successfully matched | |
107 | } | ||
108 | } | ||
109 | |||
110 | 1372 | void TypeMatcher::substantiateTypesWithTypeMapping(QualTypeList &qualTypes, const TypeMapping &typeMapping, const ASTNode *node) { | |
111 |
2/2✓ Branch 5 taken 116 times.
✓ Branch 6 taken 1372 times.
|
1488 | for (QualType &qualType : qualTypes) |
112 |
3/4✓ Branch 1 taken 116 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 102 times.
✓ Branch 4 taken 14 times.
|
116 | if (qualType.hasAnyGenericParts()) |
113 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | substantiateTypeWithTypeMapping(qualType, typeMapping, node); |
114 | 1372 | } | |
115 | |||
116 | 11527 | void TypeMatcher::substantiateTypeWithTypeMapping(QualType &type, const TypeMapping &typeMapping, // NOLINT(*-no-recursion) | |
117 | const ASTNode *node) { | ||
118 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11527 times.
|
11527 | assert(type.hasAnyGenericParts()); |
119 | |||
120 | // Check if the type itself is generic | ||
121 |
2/2✓ Branch 1 taken 7857 times.
✓ Branch 2 taken 3670 times.
|
11527 | if (type.isBase(TY_GENERIC)) { // The symbol type itself is generic |
122 |
3/6✓ Branch 1 taken 7857 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7857 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7857 times.
✗ Branch 8 not taken.
|
7857 | const std::string genericTypeName = type.getBase().getSubType(); |
123 |
2/4✓ Branch 1 taken 7857 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7857 times.
|
7857 | assert(typeMapping.contains(genericTypeName)); |
124 |
1/2✓ Branch 1 taken 7857 times.
✗ Branch 2 not taken.
|
7857 | const QualType &replacementType = typeMapping.at(genericTypeName); |
125 |
1/2✓ Branch 1 taken 7857 times.
✗ Branch 2 not taken.
|
7857 | type = type.replaceBaseType(replacementType); |
126 |
4/6✓ Branch 2 taken 3670 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3670 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 42 times.
✓ Branch 8 taken 3628 times.
|
11527 | } 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 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
|
42 | QualTypeList paramAndReturnTypes = type.getFunctionParamAndReturnTypes(); |
129 |
2/2✓ Branch 5 taken 84 times.
✓ Branch 6 taken 42 times.
|
126 | for (QualType ¶mOrReturnType : paramAndReturnTypes) |
130 |
3/4✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 42 times.
|
84 | if (paramOrReturnType.hasAnyGenericParts()) |
131 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | substantiateTypeWithTypeMapping(paramOrReturnType, typeMapping, node); |
132 | // Attach the list of concrete param types to the symbol type | ||
133 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | type = type.getWithFunctionParamAndReturnTypes(paramAndReturnTypes); |
134 | 42 | } else { // The base type is a struct or interface | |
135 |
3/6✓ Branch 1 taken 3628 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3628 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3628 times.
|
3628 | assert(type.getBase().isOneOf({TY_STRUCT, TY_INTERFACE})); |
136 | // Substantiate each template type | ||
137 |
1/2✓ Branch 1 taken 3628 times.
✗ Branch 2 not taken.
|
3628 | const QualType baseType = type.getBase(); |
138 |
2/4✓ Branch 1 taken 3628 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3628 times.
✗ Branch 5 not taken.
|
3628 | QualTypeList templateTypes = baseType.getTemplateTypes(); |
139 |
2/2✓ Branch 5 taken 4597 times.
✓ Branch 6 taken 3628 times.
|
8225 | for (QualType &templateType : templateTypes) |
140 |
3/4✓ Branch 1 taken 4597 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4287 times.
✓ Branch 4 taken 310 times.
|
4597 | if (templateType.hasAnyGenericParts()) |
141 |
1/2✓ Branch 1 taken 4287 times.
✗ Branch 2 not taken.
|
4287 | substantiateTypeWithTypeMapping(templateType, typeMapping, node); |
142 | // Attach the list of concrete template types to the symbol type | ||
143 |
1/2✓ Branch 1 taken 3628 times.
✗ Branch 2 not taken.
|
3628 | 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 1 taken 3628 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3439 times.
✓ Branch 4 taken 189 times.
|
3628 | if (!baseType.isSelfReferencingStructType()) { |
147 |
1/2✓ Branch 1 taken 3439 times.
✗ Branch 2 not taken.
|
3439 | Scope *matchScope = baseType.getBodyScope()->parent; |
148 |
3/4✓ Branch 1 taken 3439 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3061 times.
✓ Branch 4 taken 378 times.
|
3439 | if (baseType.is(TY_STRUCT)) { // Struct |
149 |
2/4✓ Branch 1 taken 3061 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3061 times.
✗ Branch 5 not taken.
|
3061 | const Struct *spiceStruct = StructManager::match(matchScope, baseType.getSubType(), templateTypes, node); |
150 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3061 times.
|
3061 | 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 1 taken 3061 times.
✗ Branch 2 not taken.
|
3061 | type = type.getWithBodyScope(spiceStruct->scope); |
156 | } else { // Interface | ||
157 |
2/4✓ Branch 1 taken 378 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 378 times.
✗ Branch 5 not taken.
|
378 | const Interface *spiceInterface = InterfaceManager::match(matchScope, baseType.getSubType(), templateTypes, node); |
158 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 378 times.
|
378 | 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 1 taken 378 times.
✗ Branch 2 not taken.
|
378 | type = type.getWithBodyScope(spiceInterface->scope); |
164 | } | ||
165 | } | ||
166 | 3628 | } | |
167 | 11527 | } | |
168 | |||
169 | } // namespace spice::compiler | ||
170 |