GCC Code Coverage Report


Directory: ../
File: src/typechecker/TypeMatcher.cpp
Date: 2024-12-24 01:17:15
Exec Total Coverage
Lines: 84 95 88.4%
Functions: 4 4 100.0%
Branches: 107 190 56.3%

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 &paramOrReturnType : 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