GCC Code Coverage Report


Directory: ../
File: src/typechecker/TypeMatcher.cpp
Date: 2025-03-05 01:50:32
Exec Total Coverage
Lines: 84 95 88.4%
Functions: 4 4 100.0%
Branches: 106 188 56.4%

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