GCC Code Coverage Report


Directory: ../
File: src/typechecker/TypeMatcher.cpp
Date: 2025-02-05 01:09:36
Exec Total Coverage
Lines: 84 95 88.4%
Functions: 4 4 100.0%
Branches: 109 194 56.2%

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