GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 92.5% 458 / 10 / 505
Functions: 100.0% 19 / 0 / 19
Branches: 52.4% 801 / 32 / 1560

src/typechecker/TypeCheckerExpressions.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "TypeChecker.h"
4
5 #include <SourceFile.h>
6 #include <driver/Driver.h>
7 #include <global/GlobalResourceManager.h>
8 #include <symboltablebuilder/Scope.h>
9 #include <symboltablebuilder/ScopeHandle.h>
10 #include <symboltablebuilder/SymbolTableBuilder.h>
11 #include <typechecker/FunctionManager.h>
12 #include <typechecker/MacroDefs.h>
13
14 namespace spice::compiler {
15
16 76919 std::any TypeChecker::visitAssignExpr(AssignExprNode *node) {
17 // Check if ternary
18
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 10 taken 76919 times.
76919 if (node->ternaryExpr) {
19 ✗ auto result = std::any_cast<ExprResult>(visit(node->ternaryExpr));
20 ✗ node->setEvaluatedSymbolType(result.type, manIdx);
21 ✗ return result;
22 }
23
24 // Check if assignment
25
1/2
✓ Branch 10 → 11 taken 76919 times.
✗ Branch 10 → 142 not taken.
76919 if (node->op != AssignExprNode::AssignOp::OP_NONE) {
26 // Visit the right side first
27
2/4
✓ Branch 11 → 12 taken 76919 times.
✗ Branch 11 → 157 not taken.
✓ Branch 12 → 13 taken 76919 times.
✗ Branch 12 → 155 not taken.
76919 auto rhs = std::any_cast<ExprResult>(visit(node->rhs));
28 76919 auto [rhsType, rhsEntry] = rhs;
29
5/8
✓ Branch 14 → 15 taken 76919 times.
✗ Branch 14 → 201 not taken.
✓ Branch 15 → 16 taken 2 times.
✓ Branch 15 → 20 taken 76917 times.
✓ Branch 16 → 17 taken 2 times.
✗ Branch 16 → 158 not taken.
✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 158 not taken.
76921 HANDLE_UNRESOLVED_TYPE_ER(rhsType)
30 // Then visit the left side
31
2/4
✓ Branch 20 → 21 taken 76917 times.
✗ Branch 20 → 161 not taken.
✓ Branch 21 → 22 taken 76917 times.
✗ Branch 21 → 159 not taken.
76917 auto lhs = std::any_cast<ExprResult>(visit(node->lhs));
32 76917 auto [lhsType, lhsVar] = lhs;
33
5/8
✓ Branch 23 → 24 taken 76917 times.
✗ Branch 23 → 201 not taken.
✓ Branch 24 → 25 taken 4 times.
✓ Branch 24 → 29 taken 76913 times.
✓ Branch 25 → 26 taken 4 times.
✗ Branch 25 → 162 not taken.
✓ Branch 26 → 27 taken 4 times.
✗ Branch 26 → 162 not taken.
76921 HANDLE_UNRESOLVED_TYPE_ER(lhsType)
34
35 // A native lambda stores its captures in the stack frame that created it. Storing a capturing lambda in a
36 // struct field lets it outlive those captures, so the captures would dangle. Reject this and direct the user
37 // to the owning std Lambda wrapper. Non-capturing lambdas (plain function pointers) are safe to store and are
38 // not affected. The std Lambda wrapper itself stores the captures-flagged lambda in its own field, so std
39 // files are exempt.
40
5/6
✓ Branch 30 → 31 taken 69344 times.
✓ Branch 30 → 40 taken 2 times.
✓ Branch 31 → 32 taken 69344 times.
✗ Branch 31 → 163 not taken.
✓ Branch 32 → 33 taken 56348 times.
✓ Branch 32 → 40 taken 12996 times.
69346 if (node->op == AssignExprNode::AssignOp::OP_ASSIGN && lhs.entry != nullptr && lhs.entry->isField() &&
41
13/16
✓ Branch 29 → 30 taken 69346 times.
✓ Branch 29 → 40 taken 7567 times.
✓ Branch 33 → 34 taken 56348 times.
✗ Branch 33 → 163 not taken.
✓ Branch 34 → 35 taken 56348 times.
✗ Branch 34 → 163 not taken.
✓ Branch 35 → 36 taken 568 times.
✓ Branch 35 → 40 taken 55780 times.
✓ Branch 36 → 37 taken 568 times.
✗ Branch 36 → 163 not taken.
✓ Branch 37 → 38 taken 40 times.
✓ Branch 37 → 40 taken 528 times.
✓ Branch 38 → 39 taken 2 times.
✓ Branch 38 → 40 taken 38 times.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 41 → 55 taken 76911 times.
146259 rhsType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && rhsType.hasLambdaCaptures() && !sourceFile->isStdFile)
42
8/16
✓ Branch 42 → 43 taken 2 times.
✗ Branch 42 → 171 not taken.
✓ Branch 43 → 44 taken 2 times.
✗ Branch 43 → 171 not taken.
✓ Branch 44 → 45 taken 2 times.
✗ Branch 44 → 171 not taken.
✓ Branch 45 → 46 taken 2 times.
✗ Branch 45 → 169 not taken.
✓ Branch 46 → 47 taken 2 times.
✗ Branch 46 → 167 not taken.
✓ Branch 47 → 48 taken 2 times.
✗ Branch 47 → 165 not taken.
✓ Branch 51 → 52 taken 2 times.
✗ Branch 51 → 176 not taken.
✓ Branch 52 → 53 taken 2 times.
✗ Branch 52 → 176 not taken.
4 SOFT_ERROR_ER(node->rhs, LAMBDA_CAPTURE_ESCAPE,
43 "A capturing lambda cannot be stored in a field, because its captures live in the frame that "
44 "created it and would dangle. Wrap it in 'Lambda<" +
45 rhsType.getBase().getWithLambdaCaptures(false).getName(false) + ">' from std/type/lambda instead.")
46
47 // Take a look at the operator
48
2/2
✓ Branch 55 → 56 taken 69344 times.
✓ Branch 55 → 98 taken 7567 times.
76911 if (node->op == AssignExprNode::AssignOp::OP_ASSIGN) {
49
8/10
✓ Branch 56 → 57 taken 69342 times.
✓ Branch 56 → 63 taken 2 times.
✓ Branch 57 → 58 taken 69342 times.
✗ Branch 57 → 188 not taken.
✓ Branch 58 → 59 taken 56346 times.
✓ Branch 58 → 63 taken 12996 times.
✓ Branch 60 → 61 taken 56346 times.
✗ Branch 60 → 188 not taken.
✓ Branch 61 → 62 taken 13402 times.
✓ Branch 61 → 63 taken 42944 times.
69344 const bool isDecl = lhs.entry != nullptr && lhs.entry->isField() && !lhs.entry->getLifecycle().isInitialized();
50
2/2
✓ Branch 64 → 65 taken 69342 times.
✓ Branch 64 → 188 taken 2 times.
69344 const auto [assignType, copyCtor] = opRuleManager.getAssignResultType(node, lhs, rhs, isDecl);
51 69342 rhsType = assignType;
52 // If the assignment overwrites an already initialized struct by copying a new value into it, the old value
53 // of the lhs must be destructed first. Otherwise its owning members (heap pointers, strings, ...) would leak.
54 // A non-null copy ctor signals that a real copy (not a move/temporary steal or ref assignment) takes place.
55 // 'isInitialized()' is false for declarations, uninitialized fields and moved-from values, so those are skipped.
56 // Unsafe blocks are excluded on purpose: code that manually manages object lifetimes there (e.g. the raw
57 // element shifts in container implementations) relies on assignments not implicitly destructing the lhs.
58
10/12
✓ Branch 67 → 68 taken 479 times.
✓ Branch 67 → 75 taken 68863 times.
✓ Branch 68 → 69 taken 152 times.
✓ Branch 68 → 75 taken 327 times.
✓ Branch 69 → 70 taken 152 times.
✗ Branch 69 → 75 not taken.
✓ Branch 70 → 71 taken 152 times.
✗ Branch 70 → 188 not taken.
✓ Branch 71 → 72 taken 104 times.
✓ Branch 71 → 75 taken 48 times.
✓ Branch 76 → 77 taken 82 times.
✓ Branch 76 → 97 taken 69260 times.
69446 if (copyCtor != nullptr && !isDecl && lhs.entry != nullptr && lhs.entry->isInitialized() &&
59
3/4
✓ Branch 72 → 73 taken 104 times.
✗ Branch 72 → 188 not taken.
✓ Branch 73 → 74 taken 82 times.
✓ Branch 73 → 75 taken 22 times.
104 !currentScope->doesAllowUnsafeOperations()) {
60
2/4
✓ Branch 77 → 78 taken 82 times.
✗ Branch 77 → 177 not taken.
✓ Branch 78 → 79 taken 82 times.
✗ Branch 78 → 177 not taken.
82 const QualType lhsSType = lhs.type.removeReferenceWrapper().toNonConst();
61
5/10
✓ Branch 79 → 80 taken 82 times.
✗ Branch 79 → 187 not taken.
✓ Branch 80 → 81 taken 82 times.
✗ Branch 80 → 84 not taken.
✓ Branch 81 → 82 taken 82 times.
✗ Branch 81 → 187 not taken.
✓ Branch 82 → 83 taken 82 times.
✗ Branch 82 → 84 not taken.
✓ Branch 85 → 86 taken 82 times.
✗ Branch 85 → 96 not taken.
82 if (lhsSType.is(TY_STRUCT) && !lhsSType.isTriviallyDestructible(node))
62
2/4
✓ Branch 89 → 90 taken 82 times.
✗ Branch 89 → 180 not taken.
✓ Branch 90 → 91 taken 82 times.
✗ Branch 90 → 178 not taken.
246 node->lhsDtorFct[manIdx] = implicitlyCallStructMethod(lhsSType, DTOR_FUNCTION_NAME, {}, node);
63 }
64
2/2
✓ Branch 98 → 99 taken 5025 times.
✓ Branch 98 → 101 taken 2542 times.
7567 } else if (node->op == AssignExprNode::AssignOp::OP_PLUS_EQUAL) {
65
1/2
✓ Branch 99 → 100 taken 5025 times.
✗ Branch 99 → 189 not taken.
5025 rhsType = opRuleManager.getPlusEqualResultType(node, lhs, rhs).type;
66
2/2
✓ Branch 101 → 102 taken 211 times.
✓ Branch 101 → 104 taken 2331 times.
2542 } else if (node->op == AssignExprNode::AssignOp::OP_MINUS_EQUAL) {
67
1/2
✓ Branch 102 → 103 taken 211 times.
✗ Branch 102 → 190 not taken.
211 rhsType = opRuleManager.getMinusEqualResultType(node, lhs, rhs).type;
68
2/2
✓ Branch 104 → 105 taken 668 times.
✓ Branch 104 → 107 taken 1663 times.
2331 } else if (node->op == AssignExprNode::AssignOp::OP_MUL_EQUAL) {
69
1/2
✓ Branch 105 → 106 taken 668 times.
✗ Branch 105 → 191 not taken.
668 rhsType = opRuleManager.getMulEqualResultType(node, lhs, rhs).type;
70
2/2
✓ Branch 107 → 108 taken 404 times.
✓ Branch 107 → 110 taken 1259 times.
1663 } else if (node->op == AssignExprNode::AssignOp::OP_DIV_EQUAL) {
71
1/2
✓ Branch 108 → 109 taken 404 times.
✗ Branch 108 → 192 not taken.
404 rhsType = opRuleManager.getDivEqualResultType(node, lhs, rhs).type;
72
2/2
✓ Branch 110 → 111 taken 98 times.
✓ Branch 110 → 113 taken 1161 times.
1259 } else if (node->op == AssignExprNode::AssignOp::OP_REM_EQUAL) {
73
1/2
✓ Branch 111 → 112 taken 98 times.
✗ Branch 111 → 193 not taken.
98 rhsType = opRuleManager.getRemEqualResultType(node, lhs, rhs);
74
2/2
✓ Branch 113 → 114 taken 24 times.
✓ Branch 113 → 116 taken 1137 times.
1161 } else if (node->op == AssignExprNode::AssignOp::OP_SHL_EQUAL) {
75
1/2
✓ Branch 114 → 115 taken 24 times.
✗ Branch 114 → 194 not taken.
24 rhsType = opRuleManager.getSHLEqualResultType(node, lhs, rhs);
76
2/2
✓ Branch 116 → 117 taken 34 times.
✓ Branch 116 → 119 taken 1103 times.
1137 } else if (node->op == AssignExprNode::AssignOp::OP_SHR_EQUAL) {
77
1/2
✓ Branch 117 → 118 taken 34 times.
✗ Branch 117 → 195 not taken.
34 rhsType = opRuleManager.getSHREqualResultType(node, lhs, rhs);
78
2/2
✓ Branch 119 → 120 taken 56 times.
✓ Branch 119 → 122 taken 1047 times.
1103 } else if (node->op == AssignExprNode::AssignOp::OP_AND_EQUAL) {
79
1/2
✓ Branch 120 → 121 taken 56 times.
✗ Branch 120 → 196 not taken.
56 rhsType = opRuleManager.getAndEqualResultType(node, lhs, rhs);
80
2/2
✓ Branch 122 → 123 taken 56 times.
✓ Branch 122 → 125 taken 991 times.
1047 } else if (node->op == AssignExprNode::AssignOp::OP_OR_EQUAL) {
81
1/2
✓ Branch 123 → 124 taken 56 times.
✗ Branch 123 → 197 not taken.
56 rhsType = opRuleManager.getOrEqualResultType(node, lhs, rhs);
82
1/2
✓ Branch 125 → 126 taken 991 times.
✗ Branch 125 → 128 not taken.
991 } else if (node->op == AssignExprNode::AssignOp::OP_XOR_EQUAL) {
83
1/2
✓ Branch 126 → 127 taken 991 times.
✗ Branch 126 → 198 not taken.
991 rhsType = opRuleManager.getXorEqualResultType(node, lhs, rhs);
84 }
85
86
1/2
✓ Branch 128 → 129 taken 76909 times.
✗ Branch 128 → 137 not taken.
76909 if (lhsVar) { // Variable is involved on the left side
87 // Perform type inference
88
3/4
✓ Branch 129 → 130 taken 76909 times.
✗ Branch 129 → 201 not taken.
✓ Branch 130 → 131 taken 2 times.
✓ Branch 130 → 132 taken 76907 times.
76909 if (lhsType.is(TY_DYN))
89
1/2
✓ Branch 131 → 132 taken 2 times.
✗ Branch 131 → 201 not taken.
2 lhsVar->updateType(rhsType, false);
90
91 // In case the lhs variable is captured, notify the capture about the write access
92
3/4
✓ Branch 132 → 133 taken 76909 times.
✗ Branch 132 → 201 not taken.
✓ Branch 133 → 134 taken 18 times.
✓ Branch 133 → 135 taken 76891 times.
76909 if (Capture *lhsCapture = currentScope->symbolTable.lookupCapture(lhsVar->name); lhsCapture)
93
1/2
✓ Branch 134 → 135 taken 18 times.
✗ Branch 134 → 201 not taken.
18 lhsCapture->setAccessType(READ_WRITE);
94
95 // Update the state of the variable
96
1/2
✓ Branch 135 → 136 taken 76909 times.
✗ Branch 135 → 199 not taken.
76909 lhsVar->updateState(INITIALIZED, node);
97 }
98
99
2/4
✓ Branch 137 → 138 taken 76909 times.
✗ Branch 137 → 200 not taken.
✓ Branch 138 → 139 taken 76909 times.
✗ Branch 138 → 200 not taken.
153818 return ExprResult{node->setEvaluatedSymbolType(rhsType, manIdx)};
100 }
101
102 − throw CompilerError(UNHANDLED_BRANCH, "AssignExpr fall-through"); // GCOV_EXCL_LINE
103 }
104
105 4877 std::any TypeChecker::visitTernaryExpr(TernaryExprNode *node) {
106 // Check if there is a ternary operator applied
107
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 4877 times.
4877 if (!node->falseExpr)
108 ✗ return visit(node->condition);
109
110 // Visit condition
111
2/4
✓ Branch 5 → 6 taken 4877 times.
✗ Branch 5 → 156 not taken.
✓ Branch 6 → 7 taken 4877 times.
✗ Branch 6 → 154 not taken.
4877 const auto condition = std::any_cast<ExprResult>(visit(node->condition));
112
2/8
✓ Branch 8 → 9 taken 4877 times.
✗ Branch 8 → 186 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 14 taken 4877 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 157 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 157 not taken.
4877 HANDLE_UNRESOLVED_TYPE_ER(condition.type)
113 // Visit the branches. Only one of them is evaluated, so each one has a scope of its own for its temporaries. The dtor calls for
114 // those are considered further down, once it is clear which temporaries the result takes over.
115 14629 const auto visitBranch = [&](ExprNode *branch) {
116
1/2
✓ Branch 2 → 3 taken 9752 times.
✗ Branch 2 → 17 not taken.
9752 const ExprScopeHandle exprScopeHandle(this, branch);
117
2/4
✓ Branch 3 → 4 taken 9752 times.
✗ Branch 3 → 13 not taken.
✓ Branch 4 → 5 taken 9752 times.
✗ Branch 4 → 11 not taken.
19504 return std::pair{std::any_cast<ExprResult>(visit(branch)), exprScopeHandle.getExprScope()};
118 9752 };
119 4877 const auto [trueExpr, trueScope] =
120
3/4
✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 16 taken 4875 times.
✓ Branch 16 → 17 taken 4875 times.
✗ Branch 16 → 158 not taken.
4877 node->isShortened ? std::pair<ExprResult, Scope *>{condition, nullptr} : visitBranch(node->trueExpr);
121 4877 const auto [trueType, trueEntry] = trueExpr;
122
2/8
✓ Branch 19 → 20 taken 4877 times.
✗ Branch 19 → 186 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 25 taken 4877 times.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 159 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 159 not taken.
4877 HANDLE_UNRESOLVED_TYPE_ER(trueType)
123
1/2
✓ Branch 25 → 26 taken 4877 times.
✗ Branch 25 → 186 not taken.
4877 const auto [falseExpr, falseScope] = visitBranch(node->falseExpr);
124 4877 const auto [falseType, falseEntry] = falseExpr;
125
2/8
✓ Branch 28 → 29 taken 4877 times.
✗ Branch 28 → 186 not taken.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 34 taken 4877 times.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 160 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 160 not taken.
4877 HANDLE_UNRESOLVED_TYPE_ER(falseType)
126
127 // Check if the condition evaluates to bool
128
3/4
✓ Branch 34 → 35 taken 4877 times.
✗ Branch 34 → 186 not taken.
✓ Branch 35 → 36 taken 2 times.
✓ Branch 35 → 46 taken 4875 times.
4877 if (!condition.type.is(TY_BOOL))
129
4/8
✓ Branch 38 → 39 taken 2 times.
✗ Branch 38 → 163 not taken.
✓ Branch 39 → 40 taken 2 times.
✗ Branch 39 → 161 not taken.
✓ Branch 42 → 43 taken 2 times.
✗ Branch 42 → 167 not taken.
✓ Branch 43 → 44 taken 2 times.
✗ Branch 43 → 167 not taken.
8 SOFT_ERROR_ER(node->condition, OPERATOR_WRONG_DATA_TYPE, "Condition operand in ternary must be a bool")
130
131 // Check if trueType and falseType are matching
132
1/2
✓ Branch 46 → 47 taken 4875 times.
✗ Branch 46 → 186 not taken.
4875 const QualType trueTypeModified = trueType.removeReferenceWrapper();
133
1/2
✓ Branch 47 → 48 taken 4875 times.
✗ Branch 47 → 186 not taken.
4875 const QualType falseTypeModified = falseType.removeReferenceWrapper();
134
3/4
✓ Branch 48 → 49 taken 4875 times.
✗ Branch 48 → 186 not taken.
✓ Branch 49 → 50 taken 2 times.
✓ Branch 49 → 65 taken 4873 times.
4875 if (!trueTypeModified.matches(falseTypeModified, false, true, false))
135
8/16
✓ Branch 50 → 51 taken 2 times.
✗ Branch 50 → 181 not taken.
✓ Branch 51 → 52 taken 2 times.
✗ Branch 51 → 176 not taken.
✓ Branch 52 → 53 taken 2 times.
✗ Branch 52 → 174 not taken.
✓ Branch 53 → 54 taken 2 times.
✗ Branch 53 → 172 not taken.
✓ Branch 54 → 55 taken 2 times.
✗ Branch 54 → 170 not taken.
✓ Branch 55 → 56 taken 2 times.
✗ Branch 55 → 168 not taken.
✓ Branch 61 → 62 taken 2 times.
✗ Branch 61 → 183 not taken.
✓ Branch 62 → 63 taken 2 times.
✗ Branch 62 → 183 not taken.
4 SOFT_ERROR_ER(node, OPERATOR_WRONG_DATA_TYPE,
136 "True and false operands in ternary must be of same data type. Got " + trueType.getName(true) + " and " +
137 falseType.getName(true))
138
139 // An anonymous entry marks a temporary (e.g. a fresh struct instantiation or a non-trivial function-call
140 // result), whose storage does not outlive this expression. The only exception is an anonymous entry of reference type. It
141 // belongs to a nested ternary with a reference result, which just refers to the storage of one of its operands.
142 9746 const auto isTemporary = [](const SymbolTableEntry *entry) {
143
6/6
✓ Branch 2 → 3 taken 4680 times.
✓ Branch 2 → 8 taken 5066 times.
✓ Branch 3 → 4 taken 1461 times.
✓ Branch 3 → 8 taken 3219 times.
✓ Branch 6 → 7 taken 1456 times.
✓ Branch 6 → 8 taken 5 times.
9746 return entry != nullptr && entry->anonymous && !entry->getQualType().isRef();
144 };
145
1/2
✓ Branch 65 → 66 taken 4873 times.
✗ Branch 65 → 186 not taken.
4873 const bool trueIsTemporary = isTemporary(trueEntry);
146
1/2
✓ Branch 66 → 67 taken 4873 times.
✗ Branch 66 → 186 not taken.
4873 const bool falseIsTemporary = isTemporary(falseEntry);
147
148 // The result type must be a reference if one of true/false branch is of reference type. Otherwise,
149 // the copy ctor is not called correctly. This can only be done if neither branch is a temporary though:
150 // a temporary's storage ends with this expression, so the result must take a copy of it instead of
151 // referencing it, or the copy ctor call for the temporary would end up being skipped further down,
152 // leaking it.
153 4873 QualType resultType;
154 // If both branches are lvalues, the result is one as well, so it can be bound to a non-const reference.
155
8/8
✓ Branch 67 → 68 taken 2405 times.
✓ Branch 67 → 72 taken 2468 times.
✓ Branch 68 → 69 taken 1107 times.
✓ Branch 68 → 72 taken 1298 times.
✓ Branch 69 → 70 taken 359 times.
✓ Branch 69 → 72 taken 748 times.
✓ Branch 70 → 71 taken 351 times.
✓ Branch 70 → 72 taken 8 times.
4873 const bool bothLvalues = trueEntry != nullptr && falseEntry != nullptr && !trueIsTemporary && !falseIsTemporary;
156
12/14
✓ Branch 73 → 74 taken 4125 times.
✓ Branch 73 → 80 taken 748 times.
✓ Branch 74 → 75 taken 4117 times.
✓ Branch 74 → 80 taken 8 times.
✓ Branch 75 → 76 taken 4117 times.
✗ Branch 75 → 186 not taken.
✓ Branch 76 → 77 taken 4092 times.
✓ Branch 76 → 79 taken 25 times.
✓ Branch 77 → 78 taken 4092 times.
✗ Branch 77 → 186 not taken.
✓ Branch 78 → 79 taken 12 times.
✓ Branch 78 → 80 taken 4080 times.
✓ Branch 81 → 82 taken 37 times.
✓ Branch 81 → 87 taken 4836 times.
4873 if (!trueIsTemporary && !falseIsTemporary && (trueType.isRef() || falseType.isRef()))
157
3/4
✓ Branch 82 → 83 taken 37 times.
✗ Branch 82 → 186 not taken.
✓ Branch 83 → 84 taken 25 times.
✓ Branch 83 → 85 taken 12 times.
37 resultType = trueType.isRef() ? trueType : falseType;
158
2/2
✓ Branch 87 → 88 taken 322 times.
✓ Branch 87 → 90 taken 4514 times.
4836 else if (bothLvalues)
159
1/2
✓ Branch 88 → 89 taken 322 times.
✗ Branch 88 → 184 not taken.
322 resultType = trueTypeModified.toRef(node);
160 else
161 4514 resultType = trueTypeModified;
162 // Infer the const-ness from the more restrictive operand
163
7/10
✓ Branch 91 → 92 taken 4873 times.
✗ Branch 91 → 186 not taken.
✓ Branch 92 → 93 taken 3591 times.
✓ Branch 92 → 95 taken 1282 times.
✓ Branch 93 → 94 taken 3591 times.
✗ Branch 93 → 186 not taken.
✓ Branch 94 → 95 taken 888 times.
✓ Branch 94 → 96 taken 2703 times.
✓ Branch 97 → 98 taken 4873 times.
✗ Branch 97 → 186 not taken.
4873 resultType.makeConst(trueType.isConst() || falseType.isConst());
164
165 // If there is an anonymous symbol attached to left or right, remove it,
166 // since the result takes over the ownership of any destructible object.
167 // The symbol lives in the scope of its branch, or in the current scope if the branch has no scope of its own.
168 4873 bool removedAnonymousSymbols = false;
169
2/2
✓ Branch 98 → 99 taken 2405 times.
✓ Branch 98 → 113 taken 2468 times.
4873 if (trueEntry) {
170
2/2
✓ Branch 99 → 100 taken 748 times.
✓ Branch 99 → 105 taken 1657 times.
2405 if (trueIsTemporary) {
171
1/2
✓ Branch 100 → 101 taken 748 times.
✗ Branch 100 → 102 not taken.
748 Scope *anonymousSymbolScope = trueScope != nullptr ? trueScope : currentScope;
172
1/2
✓ Branch 103 → 104 taken 748 times.
✗ Branch 103 → 186 not taken.
748 anonymousSymbolScope->symbolTable.deleteAnonymous(trueEntry->name);
173 748 removedAnonymousSymbols = true;
174
8/10
✓ Branch 105 → 106 taken 1657 times.
✗ Branch 105 → 186 not taken.
✓ Branch 106 → 107 taken 1304 times.
✓ Branch 106 → 110 taken 353 times.
✓ Branch 107 → 108 taken 1304 times.
✗ Branch 107 → 186 not taken.
✓ Branch 108 → 109 taken 8 times.
✓ Branch 108 → 110 taken 1296 times.
✓ Branch 111 → 112 taken 8 times.
✓ Branch 111 → 113 taken 1649 times.
1657 } else if (!resultType.isRef() && !trueTypeModified.isTriviallyCopyable(node)) {
175 8 node->trueSideCallsCopyCtor = true;
176 }
177 }
178
2/2
✓ Branch 113 → 114 taken 2275 times.
✓ Branch 113 → 128 taken 2598 times.
4873 if (falseEntry) {
179
2/2
✓ Branch 114 → 115 taken 708 times.
✓ Branch 114 → 120 taken 1567 times.
2275 if (falseIsTemporary) {
180
1/2
✓ Branch 115 → 116 taken 708 times.
✗ Branch 115 → 117 not taken.
708 Scope *anonymousSymbolScope = falseScope != nullptr ? falseScope : currentScope;
181
1/2
✓ Branch 118 → 119 taken 708 times.
✗ Branch 118 → 186 not taken.
708 anonymousSymbolScope->symbolTable.deleteAnonymous(falseEntry->name);
182 708 removedAnonymousSymbols = true;
183
8/10
✓ Branch 120 → 121 taken 1567 times.
✗ Branch 120 → 186 not taken.
✓ Branch 121 → 122 taken 1216 times.
✓ Branch 121 → 125 taken 351 times.
✓ Branch 122 → 123 taken 1216 times.
✗ Branch 122 → 186 not taken.
✓ Branch 123 → 124 taken 88 times.
✓ Branch 123 → 125 taken 1128 times.
✓ Branch 126 → 127 taken 88 times.
✓ Branch 126 → 128 taken 1479 times.
1567 } else if (!resultType.isRef() && !falseTypeModified.isTriviallyCopyable(node)) {
184 88 node->falseSideCallsCopyCtor = true;
185 }
186 }
187
188 // Create a new anonymous symbol for the result if required
189 4873 SymbolTableEntry *anonymousSymbol = nullptr;
190
4/4
✓ Branch 128 → 129 taken 4865 times.
✓ Branch 128 → 130 taken 8 times.
✓ Branch 129 → 130 taken 88 times.
✓ Branch 129 → 131 taken 4777 times.
4873 const bool calledCopyCtor = node->trueSideCallsCopyCtor || node->falseSideCallsCopyCtor;
191
9/10
✓ Branch 132 → 133 taken 4117 times.
✓ Branch 132 → 136 taken 756 times.
✓ Branch 133 → 134 taken 4077 times.
✓ Branch 133 → 136 taken 40 times.
✓ Branch 134 → 135 taken 4077 times.
✗ Branch 134 → 186 not taken.
✓ Branch 135 → 136 taken 359 times.
✓ Branch 135 → 137 taken 3718 times.
✓ Branch 138 → 139 taken 1155 times.
✓ Branch 138 → 141 taken 3718 times.
4873 if (removedAnonymousSymbols || calledCopyCtor || resultType.isRef())
192
1/2
✓ Branch 139 → 140 taken 1155 times.
✗ Branch 139 → 186 not taken.
1155 anonymousSymbol = currentScope->symbolTable.insertAnonymous(resultType, node);
193
194 // Look up the copy ctor if at least one side needs it
195
4/4
✓ Branch 141 → 142 taken 4865 times.
✓ Branch 141 → 143 taken 8 times.
✓ Branch 142 → 143 taken 88 times.
✓ Branch 142 → 145 taken 4777 times.
4873 if (node->trueSideCallsCopyCtor || node->falseSideCallsCopyCtor)
196
1/2
✓ Branch 143 → 144 taken 96 times.
✗ Branch 143 → 186 not taken.
96 node->calledCopyCtor = matchCopyCtor(trueTypeModified, node);
197
198 // Consider the dtor calls for the temporaries of the branches. A result of reference type may refer to a temporary of its
199 // branch, so those must not be destructed. Not destructing them is the safe choice here, as it only leaks the temporary.
200 14617 const auto doBranchCleanup = [&](const ExprNode *branch, const Scope *branchScope) {
201
2/2
✓ Branch 2 → 3 taken 12 times.
✓ Branch 2 → 4 taken 9732 times.
9744 if (branchScope == nullptr)
202 12 return;
203
1/2
✓ Branch 4 → 5 taken 9732 times.
✗ Branch 4 → 15 not taken.
9732 const ExprScopeHandle exprScopeHandle(this, branch);
204
3/4
✓ Branch 5 → 6 taken 9732 times.
✗ Branch 5 → 13 not taken.
✓ Branch 6 → 7 taken 718 times.
✓ Branch 6 → 9 taken 9014 times.
9732 if (resultType.isRef())
205 718 exprScopeHandle.getExprScope()->temporaryDtorsToCall.clear();
206 else
207
1/2
✓ Branch 9 → 10 taken 9014 times.
✗ Branch 9 → 13 not taken.
9014 doExprScopeCleanup(branch);
208 9732 };
209
2/2
✓ Branch 145 → 146 taken 4871 times.
✓ Branch 145 → 147 taken 2 times.
4873 if (!node->isShortened)
210
1/2
✓ Branch 146 → 147 taken 4871 times.
✗ Branch 146 → 186 not taken.
4871 doBranchCleanup(node->trueExpr, trueScope);
211
1/2
✓ Branch 147 → 148 taken 4873 times.
✗ Branch 147 → 186 not taken.
4873 doBranchCleanup(node->falseExpr, falseScope);
212
213
2/4
✓ Branch 148 → 149 taken 4873 times.
✗ Branch 148 → 185 not taken.
✓ Branch 149 → 150 taken 4873 times.
✗ Branch 149 → 185 not taken.
9746 return ExprResult{node->setEvaluatedSymbolType(resultType, manIdx), anonymousSymbol};
214 }
215
216 6102 std::any TypeChecker::visitLogicalOrExpr(LogicalOrExprNode *node) {
217 // Check if a logical or operator is applied
218
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 6102 times.
6102 if (node->operands.size() == 1)
219 ✗ return visit(node->operands.front());
220
221 // Visit leftmost operand
222
2/4
✓ Branch 8 → 9 taken 6102 times.
✗ Branch 8 → 37 not taken.
✓ Branch 9 → 10 taken 6102 times.
✗ Branch 9 → 35 not taken.
6102 auto currentOperand = std::any_cast<ExprResult>(visit(node->operands[0]));
223
2/8
✓ Branch 11 → 12 taken 6102 times.
✗ Branch 11 → 41 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 6102 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 38 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 38 not taken.
6102 HANDLE_UNRESOLVED_TYPE_ER(currentOperand.type)
224
225 // Loop through all remaining operands
226
2/2
✓ Branch 29 → 18 taken 8204 times.
✓ Branch 29 → 30 taken 6100 times.
14304 for (size_t i = 1; i < node->operands.size(); i++) {
227 // The operand is only evaluated if the ones before did not short-circuit, so it gets its temporaries destructed on its own
228
1/2
✓ Branch 19 → 20 taken 8204 times.
✗ Branch 19 → 40 not taken.
8204 auto rhsOperand = visitInExprScope(node->operands[i]);
229
2/8
✓ Branch 20 → 21 taken 8204 times.
✗ Branch 20 → 40 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 26 taken 8204 times.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 39 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 39 not taken.
8204 HANDLE_UNRESOLVED_TYPE_ER(rhsOperand.type)
230
2/2
✓ Branch 26 → 27 taken 8202 times.
✓ Branch 26 → 40 taken 2 times.
8204 currentOperand = {OpRuleManager::getLogicalOrResultType(node, currentOperand, rhsOperand)};
231 }
232
233
1/2
✓ Branch 30 → 31 taken 6100 times.
✗ Branch 30 → 41 not taken.
6100 node->setEvaluatedSymbolType(currentOperand.type, manIdx);
234
1/2
✓ Branch 31 → 32 taken 6100 times.
✗ Branch 31 → 41 not taken.
6100 return currentOperand;
235 }
236
237 5352 std::any TypeChecker::visitLogicalAndExpr(LogicalAndExprNode *node) {
238 // Check if a logical and operator is applied
239
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 5352 times.
5352 if (node->operands.size() == 1)
240 ✗ return visit(node->operands.front());
241
242 // Visit leftmost operand
243
2/4
✓ Branch 8 → 9 taken 5352 times.
✗ Branch 8 → 37 not taken.
✓ Branch 9 → 10 taken 5352 times.
✗ Branch 9 → 35 not taken.
5352 auto currentOperand = std::any_cast<ExprResult>(visit(node->operands[0]));
244
2/8
✓ Branch 11 → 12 taken 5352 times.
✗ Branch 11 → 41 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 5352 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 38 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 38 not taken.
5352 HANDLE_UNRESOLVED_TYPE_ER(currentOperand.type)
245
246 // Loop through all remaining operands
247
2/2
✓ Branch 29 → 18 taken 7458 times.
✓ Branch 29 → 30 taken 5352 times.
12810 for (size_t i = 1; i < node->operands.size(); i++) {
248 // The operand is only evaluated if the ones before did not short-circuit, so it gets its temporaries destructed on its own
249
1/2
✓ Branch 19 → 20 taken 7458 times.
✗ Branch 19 → 40 not taken.
7458 auto rhsOperand = visitInExprScope(node->operands[i]);
250
2/8
✓ Branch 20 → 21 taken 7458 times.
✗ Branch 20 → 40 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 26 taken 7458 times.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 39 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 39 not taken.
7458 HANDLE_UNRESOLVED_TYPE_ER(rhsOperand.type)
251
1/2
✓ Branch 26 → 27 taken 7458 times.
✗ Branch 26 → 40 not taken.
7458 currentOperand = {OpRuleManager::getLogicalAndResultType(node, currentOperand, rhsOperand)};
252 }
253
254
1/2
✓ Branch 30 → 31 taken 5352 times.
✗ Branch 30 → 41 not taken.
5352 node->setEvaluatedSymbolType(currentOperand.type, manIdx);
255
1/2
✓ Branch 31 → 32 taken 5352 times.
✗ Branch 31 → 41 not taken.
5352 return currentOperand;
256 }
257
258 1100 std::any TypeChecker::visitBitwiseOrExpr(BitwiseOrExprNode *node) {
259 // Check if a bitwise or operator is applied
260
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 1100 times.
1100 if (node->operands.size() == 1)
261 ✗ return visit(node->operands.front());
262
263 // Visit leftmost operand
264
2/4
✓ Branch 8 → 9 taken 1100 times.
✗ Branch 8 → 39 not taken.
✓ Branch 9 → 10 taken 1100 times.
✗ Branch 9 → 37 not taken.
1100 auto currentOperand = std::any_cast<ExprResult>(visit(node->operands[0]));
265
2/8
✓ Branch 11 → 12 taken 1100 times.
✗ Branch 11 → 46 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 1100 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 40 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 40 not taken.
1100 HANDLE_UNRESOLVED_TYPE_ER(currentOperand.type)
266
267 // Loop through all remaining operands
268
2/2
✓ Branch 31 → 18 taken 1186 times.
✓ Branch 31 → 32 taken 1098 times.
2284 for (size_t i = 1; i < node->operands.size(); i++) {
269
2/4
✓ Branch 19 → 20 taken 1186 times.
✗ Branch 19 → 43 not taken.
✓ Branch 20 → 21 taken 1186 times.
✗ Branch 20 → 41 not taken.
1186 auto rhsOperand = std::any_cast<ExprResult>(visit(node->operands[i]));
270
2/8
✓ Branch 22 → 23 taken 1186 times.
✗ Branch 22 → 45 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 28 taken 1186 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 44 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 44 not taken.
1186 HANDLE_UNRESOLVED_TYPE_ER(rhsOperand.type)
271
2/2
✓ Branch 28 → 29 taken 1184 times.
✓ Branch 28 → 45 taken 2 times.
1186 currentOperand = opRuleManager.getBitwiseOrResultType(node, currentOperand, rhsOperand, i - 1);
272 }
273
274
1/2
✓ Branch 32 → 33 taken 1098 times.
✗ Branch 32 → 46 not taken.
1098 node->setEvaluatedSymbolType(currentOperand.type, manIdx);
275
1/2
✓ Branch 33 → 34 taken 1098 times.
✗ Branch 33 → 46 not taken.
1098 return currentOperand;
276 }
277
278 171 std::any TypeChecker::visitBitwiseXorExpr(BitwiseXorExprNode *node) {
279 // Check if a bitwise xor operator is applied
280
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 171 times.
171 if (node->operands.size() == 1)
281 ✗ return visit(node->operands.front());
282
283 // Visit leftmost operand
284
2/4
✓ Branch 8 → 9 taken 171 times.
✗ Branch 8 → 39 not taken.
✓ Branch 9 → 10 taken 171 times.
✗ Branch 9 → 37 not taken.
171 auto currentOperand = std::any_cast<ExprResult>(visit(node->operands[0]));
285
2/8
✓ Branch 11 → 12 taken 171 times.
✗ Branch 11 → 46 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 171 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 40 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 40 not taken.
171 HANDLE_UNRESOLVED_TYPE_ER(currentOperand.type)
286
287 // Loop through all remaining operands
288
2/2
✓ Branch 31 → 18 taken 177 times.
✓ Branch 31 → 32 taken 171 times.
348 for (size_t i = 1; i < node->operands.size(); i++) {
289
2/4
✓ Branch 19 → 20 taken 177 times.
✗ Branch 19 → 43 not taken.
✓ Branch 20 → 21 taken 177 times.
✗ Branch 20 → 41 not taken.
177 auto rhsOperand = std::any_cast<ExprResult>(visit(node->operands[i]));
290
2/8
✓ Branch 22 → 23 taken 177 times.
✗ Branch 22 → 45 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 28 taken 177 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 44 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 44 not taken.
177 HANDLE_UNRESOLVED_TYPE_ER(rhsOperand.type)
291
1/2
✓ Branch 28 → 29 taken 177 times.
✗ Branch 28 → 45 not taken.
177 currentOperand = opRuleManager.getBitwiseXorResultType(node, currentOperand, rhsOperand, i - 1);
292 }
293
294
1/2
✓ Branch 32 → 33 taken 171 times.
✗ Branch 32 → 46 not taken.
171 node->setEvaluatedSymbolType(currentOperand.type, manIdx);
295
1/2
✓ Branch 33 → 34 taken 171 times.
✗ Branch 33 → 46 not taken.
171 return currentOperand;
296 }
297
298 922 std::any TypeChecker::visitBitwiseAndExpr(BitwiseAndExprNode *node) {
299 // Check if a bitwise and operator is applied
300
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 922 times.
922 if (node->operands.size() == 1)
301 ✗ return visit(node->operands.front());
302
303 // Visit leftmost operand
304
2/4
✓ Branch 8 → 9 taken 922 times.
✗ Branch 8 → 39 not taken.
✓ Branch 9 → 10 taken 922 times.
✗ Branch 9 → 37 not taken.
922 auto currentOperand = std::any_cast<ExprResult>(visit(node->operands[0]));
305
2/8
✓ Branch 11 → 12 taken 922 times.
✗ Branch 11 → 46 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 922 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 40 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 40 not taken.
922 HANDLE_UNRESOLVED_TYPE_ER(currentOperand.type)
306
307 // Loop through all remaining operands
308
2/2
✓ Branch 31 → 18 taken 928 times.
✓ Branch 31 → 32 taken 922 times.
1850 for (size_t i = 1; i < node->operands.size(); i++) {
309
2/4
✓ Branch 19 → 20 taken 928 times.
✗ Branch 19 → 43 not taken.
✓ Branch 20 → 21 taken 928 times.
✗ Branch 20 → 41 not taken.
928 auto rhsOperand = std::any_cast<ExprResult>(visit(node->operands[i]));
310
2/8
✓ Branch 22 → 23 taken 928 times.
✗ Branch 22 → 45 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 28 taken 928 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 44 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 44 not taken.
928 HANDLE_UNRESOLVED_TYPE_ER(rhsOperand.type)
311
1/2
✓ Branch 28 → 29 taken 928 times.
✗ Branch 28 → 45 not taken.
928 currentOperand = opRuleManager.getBitwiseAndResultType(node, currentOperand, rhsOperand, i - 1);
312 }
313
314
1/2
✓ Branch 32 → 33 taken 922 times.
✗ Branch 32 → 46 not taken.
922 node->setEvaluatedSymbolType(currentOperand.type, manIdx);
315
1/2
✓ Branch 33 → 34 taken 922 times.
✗ Branch 33 → 46 not taken.
922 return currentOperand;
316 }
317
318 60011 std::any TypeChecker::visitEqualityExpr(EqualityExprNode *node) {
319 // Check if at least one equality operator is applied
320
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 60011 times.
60011 if (node->operands.size() == 1)
321 ✗ return visit(node->operands.front());
322
323 // Visit right side first, then left side
324
2/4
✓ Branch 8 → 9 taken 60011 times.
✗ Branch 8 → 58 not taken.
✓ Branch 9 → 10 taken 60011 times.
✗ Branch 9 → 56 not taken.
60011 const auto rhs = std::any_cast<ExprResult>(visit(node->operands[1]));
325
2/8
✓ Branch 11 → 12 taken 60011 times.
✗ Branch 11 → 73 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 60011 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 59 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 59 not taken.
60011 HANDLE_UNRESOLVED_TYPE_ER(rhs.type)
326
2/4
✓ Branch 18 → 19 taken 60011 times.
✗ Branch 18 → 62 not taken.
✓ Branch 19 → 20 taken 60011 times.
✗ Branch 19 → 60 not taken.
60011 const auto lhs = std::any_cast<ExprResult>(visit(node->operands[0]));
327
2/8
✓ Branch 21 → 22 taken 60011 times.
✗ Branch 21 → 73 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 27 taken 60011 times.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 63 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 63 not taken.
60011 HANDLE_UNRESOLVED_TYPE_ER(lhs.type)
328
329 // Check if we need the string runtime to perform a string comparison
330
10/14
✓ Branch 27 → 28 taken 60011 times.
✗ Branch 27 → 73 not taken.
✓ Branch 28 → 29 taken 1337 times.
✓ Branch 28 → 36 taken 58674 times.
✓ Branch 29 → 30 taken 1337 times.
✗ Branch 29 → 73 not taken.
✓ Branch 30 → 31 taken 1335 times.
✓ Branch 30 → 36 taken 2 times.
✓ Branch 31 → 32 taken 1335 times.
✗ Branch 31 → 73 not taken.
✓ Branch 34 → 35 taken 1335 times.
✗ Branch 34 → 36 not taken.
✓ Branch 37 → 38 taken 1335 times.
✓ Branch 37 → 39 taken 58676 times.
61346 if (lhs.type.is(TY_STRING) && rhs.type.is(TY_STRING) && !sourceFile->isStringRT())
331
1/2
✓ Branch 38 → 39 taken 1335 times.
✗ Branch 38 → 73 not taken.
1335 sourceFile->requestRuntimeModule(STRING_RT);
332
333 // Check operator
334 60011 ExprResult result;
335
2/2
✓ Branch 39 → 40 taken 49530 times.
✓ Branch 39 → 41 taken 10481 times.
60011 if (node->op == EqualityExprNode::EqualityOp::OP_EQUAL) // Operator was equal
336
2/2
✓ Branch 40 → 51 taken 49528 times.
✓ Branch 40 → 73 taken 2 times.
49530 result = opRuleManager.getEqualResultType(node, lhs, rhs);
337
1/2
✓ Branch 41 → 42 taken 10481 times.
✗ Branch 41 → 43 not taken.
10481 else if (node->op == EqualityExprNode::EqualityOp::OP_NOT_EQUAL) // Operator was not equal
338
1/2
✓ Branch 42 → 51 taken 10481 times.
✗ Branch 42 → 73 not taken.
10481 result = opRuleManager.getNotEqualResultType(node, lhs, rhs);
339 else
340 − throw CompilerError(UNHANDLED_BRANCH, "EqualityExpr fall-through"); // GCOV_EXCL_LINE
341
342
1/2
✓ Branch 51 → 52 taken 60009 times.
✗ Branch 51 → 73 not taken.
60009 node->setEvaluatedSymbolType(result.type, manIdx);
343
1/2
✓ Branch 52 → 53 taken 60009 times.
✗ Branch 52 → 73 not taken.
60009 return result;
344 }
345
346 35332 std::any TypeChecker::visitRelationalExpr(RelationalExprNode *node) {
347 // Check if a relational operator is applied
348
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 35332 times.
35332 if (node->operands.size() == 1)
349 ✗ return visit(node->operands.front());
350
351 // Visit right side first, then left side
352
2/4
✓ Branch 8 → 9 taken 35332 times.
✗ Branch 8 → 55 not taken.
✓ Branch 9 → 10 taken 35332 times.
✗ Branch 9 → 53 not taken.
35332 const auto rhs = std::any_cast<ExprResult>(visit(node->operands[1]));
353
5/8
✓ Branch 11 → 12 taken 35332 times.
✗ Branch 11 → 75 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 17 taken 35330 times.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 56 not taken.
✓ Branch 14 → 15 taken 2 times.
✗ Branch 14 → 56 not taken.
35334 HANDLE_UNRESOLVED_TYPE_ER(rhs.type)
354
2/4
✓ Branch 18 → 19 taken 35330 times.
✗ Branch 18 → 59 not taken.
✓ Branch 19 → 20 taken 35330 times.
✗ Branch 19 → 57 not taken.
35330 const auto lhs = std::any_cast<ExprResult>(visit(node->operands[0]));
355
2/8
✓ Branch 21 → 22 taken 35330 times.
✗ Branch 21 → 75 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 27 taken 35330 times.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 60 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 60 not taken.
35330 HANDLE_UNRESOLVED_TYPE_ER(lhs.type)
356
357 // Check operator
358 35330 QualType resultType;
359
2/2
✓ Branch 27 → 28 taken 14747 times.
✓ Branch 27 → 30 taken 20583 times.
35330 if (node->op == RelationalExprNode::RelationalOp::OP_LESS) // Operator was less
360
1/2
✓ Branch 28 → 29 taken 14747 times.
✗ Branch 28 → 61 not taken.
14747 resultType = OpRuleManager::getLessResultType(node, lhs, rhs);
361
2/2
✓ Branch 30 → 31 taken 8540 times.
✓ Branch 30 → 33 taken 12043 times.
20583 else if (node->op == RelationalExprNode::RelationalOp::OP_GREATER) // Operator was greater
362
2/2
✓ Branch 31 → 32 taken 8538 times.
✓ Branch 31 → 62 taken 2 times.
8540 resultType = OpRuleManager::getGreaterResultType(node, lhs, rhs);
363
2/2
✓ Branch 33 → 34 taken 4094 times.
✓ Branch 33 → 36 taken 7949 times.
12043 else if (node->op == RelationalExprNode::RelationalOp::OP_LESS_EQUAL) // Operator was less equal
364
1/2
✓ Branch 34 → 35 taken 4094 times.
✗ Branch 34 → 63 not taken.
4094 resultType = OpRuleManager::getLessEqualResultType(node, lhs, rhs);
365
1/2
✓ Branch 36 → 37 taken 7949 times.
✗ Branch 36 → 39 not taken.
7949 else if (node->op == RelationalExprNode::RelationalOp::OP_GREATER_EQUAL) // Operator was greater equal
366
1/2
✓ Branch 37 → 38 taken 7949 times.
✗ Branch 37 → 64 not taken.
7949 resultType = OpRuleManager::getGreaterEqualResultType(node, lhs, rhs);
367 else
368 − throw CompilerError(UNHANDLED_BRANCH, "RelationalExpr fall-through"); // GCOV_EXCL_LINE
369
370
2/4
✓ Branch 47 → 48 taken 35328 times.
✗ Branch 47 → 74 not taken.
✓ Branch 48 → 49 taken 35328 times.
✗ Branch 48 → 74 not taken.
70656 return ExprResult{node->setEvaluatedSymbolType(resultType, manIdx)};
371 }
372
373 5666 std::any TypeChecker::visitShiftExpr(ShiftExprNode *node) {
374 // Check if at least one shift operator is applied
375
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 5666 times.
5666 if (node->operands.size() == 1)
376 ✗ return visit(node->operands.front());
377
378 // Visit leftmost operand
379
2/4
✓ Branch 8 → 9 taken 5666 times.
✗ Branch 8 → 53 not taken.
✓ Branch 9 → 10 taken 5666 times.
✗ Branch 9 → 51 not taken.
5666 auto currentResult = std::any_cast<ExprResult>(visit(node->operands[0]));
380
2/8
✓ Branch 11 → 12 taken 5666 times.
✗ Branch 11 → 69 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 5666 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 54 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 54 not taken.
5666 HANDLE_UNRESOLVED_TYPE_ER(currentResult.type)
381
382 // Loop through remaining operands
383
2/2
✓ Branch 45 → 18 taken 7772 times.
✓ Branch 45 → 46 taken 5666 times.
13438 for (size_t i = 0; i < node->opQueue.size(); i++) {
384
2/4
✓ Branch 19 → 20 taken 7772 times.
✗ Branch 19 → 57 not taken.
✓ Branch 20 → 21 taken 7772 times.
✗ Branch 20 → 55 not taken.
7772 auto operandResult = std::any_cast<ExprResult>(visit(node->operands[i + 1]));
385
2/8
✓ Branch 22 → 23 taken 7772 times.
✗ Branch 22 → 68 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 28 taken 7772 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 58 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 58 not taken.
7772 HANDLE_UNRESOLVED_TYPE_ER(operandResult.type)
386
387 // Check operator
388
1/2
✓ Branch 28 → 29 taken 7772 times.
✗ Branch 28 → 68 not taken.
7772 const ShiftExprNode::ShiftOp &op = node->opQueue.front().first;
389
2/2
✓ Branch 29 → 30 taken 6755 times.
✓ Branch 29 → 31 taken 1017 times.
7772 if (op == ShiftExprNode::ShiftOp::OP_SHIFT_LEFT)
390
1/2
✓ Branch 30 → 41 taken 6755 times.
✗ Branch 30 → 68 not taken.
6755 currentResult = opRuleManager.getShiftLeftResultType(node, currentResult, operandResult, i);
391
1/2
✓ Branch 31 → 32 taken 1017 times.
✗ Branch 31 → 33 not taken.
1017 else if (op == ShiftExprNode::ShiftOp::OP_SHIFT_RIGHT)
392
1/2
✓ Branch 32 → 41 taken 1017 times.
✗ Branch 32 → 68 not taken.
1017 currentResult = opRuleManager.getShiftRightResultType(node, currentResult, operandResult, i);
393 else
394 − throw CompilerError(UNHANDLED_BRANCH, "ShiftExpr fall-through"); // GCOV_EXCL_LINE
395
396 // Push the new item and pop the old one on the other side of the queue
397
1/2
✓ Branch 41 → 42 taken 7772 times.
✗ Branch 41 → 68 not taken.
7772 node->opQueue.emplace(op, currentResult.type);
398
1/2
✓ Branch 42 → 43 taken 7772 times.
✗ Branch 42 → 68 not taken.
7772 node->opQueue.pop();
399 }
400
401
1/2
✓ Branch 46 → 47 taken 5666 times.
✗ Branch 46 → 69 not taken.
5666 node->setEvaluatedSymbolType(currentResult.type, manIdx);
402
1/2
✓ Branch 47 → 48 taken 5666 times.
✗ Branch 47 → 69 not taken.
5666 return currentResult;
403 }
404
405 28314 std::any TypeChecker::visitAdditiveExpr(AdditiveExprNode *node) {
406 // Check if at least one additive operator is applied
407
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 28314 times.
28314 if (node->operands.size() == 1)
408 ✗ return visit(node->operands.front());
409
410 // Visit leftmost operand
411
2/4
✓ Branch 8 → 9 taken 28314 times.
✗ Branch 8 → 53 not taken.
✓ Branch 9 → 10 taken 28314 times.
✗ Branch 9 → 51 not taken.
28314 auto currentResult = std::any_cast<ExprResult>(visit(node->operands[0]));
412
2/8
✓ Branch 11 → 12 taken 28314 times.
✗ Branch 11 → 69 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 28314 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 54 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 54 not taken.
28314 HANDLE_UNRESOLVED_TYPE_ER(currentResult.type)
413
414 // Loop through remaining operands
415
2/2
✓ Branch 45 → 18 taken 32087 times.
✓ Branch 45 → 46 taken 28312 times.
60399 for (size_t i = 0; i < node->opQueue.size(); i++) {
416
2/4
✓ Branch 19 → 20 taken 32087 times.
✗ Branch 19 → 57 not taken.
✓ Branch 20 → 21 taken 32087 times.
✗ Branch 20 → 55 not taken.
32087 auto operandResult = std::any_cast<ExprResult>(visit(node->operands[i + 1]));
417
2/8
✓ Branch 22 → 23 taken 32087 times.
✗ Branch 22 → 68 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 28 taken 32087 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 58 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 58 not taken.
32087 HANDLE_UNRESOLVED_TYPE_ER(operandResult.type)
418
419 // Check operator
420
1/2
✓ Branch 28 → 29 taken 32087 times.
✗ Branch 28 → 68 not taken.
32087 const AdditiveExprNode::AdditiveOp &op = node->opQueue.front().first;
421
2/2
✓ Branch 29 → 30 taken 20787 times.
✓ Branch 29 → 31 taken 11300 times.
32087 if (op == AdditiveExprNode::AdditiveOp::OP_PLUS)
422
2/2
✓ Branch 30 → 41 taken 20785 times.
✓ Branch 30 → 68 taken 2 times.
20787 currentResult = opRuleManager.getPlusResultType(node, currentResult, operandResult, i);
423
1/2
✓ Branch 31 → 32 taken 11300 times.
✗ Branch 31 → 33 not taken.
11300 else if (op == AdditiveExprNode::AdditiveOp::OP_MINUS)
424
1/2
✓ Branch 32 → 41 taken 11300 times.
✗ Branch 32 → 68 not taken.
11300 currentResult = opRuleManager.getMinusResultType(node, currentResult, operandResult, i);
425 else
426 − throw CompilerError(UNHANDLED_BRANCH, "AdditiveExpr fall-through"); // GCOV_EXCL_LINE
427
428 // Push the new item and pop the old one on the other side of the queue
429
1/2
✓ Branch 41 → 42 taken 32085 times.
✗ Branch 41 → 68 not taken.
32085 node->opQueue.emplace(op, currentResult.type);
430
1/2
✓ Branch 42 → 43 taken 32085 times.
✗ Branch 42 → 68 not taken.
32085 node->opQueue.pop();
431 }
432
433
1/2
✓ Branch 46 → 47 taken 28312 times.
✗ Branch 46 → 69 not taken.
28312 node->setEvaluatedSymbolType(currentResult.type, manIdx);
434
1/2
✓ Branch 47 → 48 taken 28312 times.
✗ Branch 47 → 69 not taken.
28312 return currentResult;
435 }
436
437 7949 std::any TypeChecker::visitMultiplicativeExpr(MultiplicativeExprNode *node) {
438 // Check if at least one multiplicative operator is applied
439
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 7949 times.
7949 if (node->operands.size() == 1)
440 ✗ return visit(node->operands.front());
441
442 // Visit leftmost operand
443
2/4
✓ Branch 8 → 9 taken 7949 times.
✗ Branch 8 → 55 not taken.
✓ Branch 9 → 10 taken 7949 times.
✗ Branch 9 → 53 not taken.
7949 auto currentResult = std::any_cast<ExprResult>(visit(node->operands[0]));
444
2/8
✓ Branch 11 → 12 taken 7949 times.
✗ Branch 11 → 71 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 7949 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 56 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 56 not taken.
7949 HANDLE_UNRESOLVED_TYPE_ER(currentResult.type)
445 // Loop through remaining operands
446
2/2
✓ Branch 47 → 18 taken 8341 times.
✓ Branch 47 → 48 taken 7947 times.
16288 for (size_t i = 0; i < node->opQueue.size(); i++) {
447
2/4
✓ Branch 19 → 20 taken 8341 times.
✗ Branch 19 → 59 not taken.
✓ Branch 20 → 21 taken 8341 times.
✗ Branch 20 → 57 not taken.
8341 auto operandResult = std::any_cast<ExprResult>(visit(node->operands[i + 1]));
448
2/8
✓ Branch 22 → 23 taken 8341 times.
✗ Branch 22 → 70 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 28 taken 8341 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 60 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 60 not taken.
8341 HANDLE_UNRESOLVED_TYPE_ER(operandResult.type)
449
450 // Check operator
451
1/2
✓ Branch 28 → 29 taken 8341 times.
✗ Branch 28 → 70 not taken.
8341 const MultiplicativeExprNode::MultiplicativeOp &op = node->opQueue.front().first;
452
2/2
✓ Branch 29 → 30 taken 6891 times.
✓ Branch 29 → 31 taken 1450 times.
8341 if (op == MultiplicativeExprNode::MultiplicativeOp::OP_MUL)
453
2/2
✓ Branch 30 → 43 taken 6889 times.
✓ Branch 30 → 70 taken 2 times.
6891 currentResult = opRuleManager.getMulResultType(node, currentResult, operandResult, i);
454
2/2
✓ Branch 31 → 32 taken 1184 times.
✓ Branch 31 → 33 taken 266 times.
1450 else if (op == MultiplicativeExprNode::MultiplicativeOp::OP_DIV)
455
1/2
✓ Branch 32 → 43 taken 1184 times.
✗ Branch 32 → 70 not taken.
1184 currentResult = opRuleManager.getDivResultType(node, currentResult, operandResult, i);
456
1/2
✓ Branch 33 → 34 taken 266 times.
✗ Branch 33 → 35 not taken.
266 else if (op == MultiplicativeExprNode::MultiplicativeOp::OP_REM)
457
1/2
✓ Branch 34 → 43 taken 266 times.
✗ Branch 34 → 70 not taken.
266 currentResult = OpRuleManager::getRemResultType(node, currentResult, operandResult);
458 else
459 − throw CompilerError(UNHANDLED_BRANCH, "Multiplicative fall-through"); // GCOV_EXCL_LINE
460
461 // Push the new item and pop the old one on the other side of the queue
462
1/2
✓ Branch 43 → 44 taken 8339 times.
✗ Branch 43 → 70 not taken.
8339 node->opQueue.emplace(op, currentResult.type);
463
1/2
✓ Branch 44 → 45 taken 8339 times.
✗ Branch 44 → 70 not taken.
8339 node->opQueue.pop();
464 }
465
466
1/2
✓ Branch 48 → 49 taken 7947 times.
✗ Branch 48 → 71 not taken.
7947 node->setEvaluatedSymbolType(currentResult.type, manIdx);
467
1/2
✓ Branch 49 → 50 taken 7947 times.
✗ Branch 49 → 71 not taken.
7947 return currentResult;
468 }
469
470 30143 std::any TypeChecker::visitCastExpr(CastExprNode *node) {
471 // Check if cast is applied
472
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 30143 times.
30143 if (!node->isCast)
473 ✗ return visit(node->prefixUnaryExpr);
474
475 // Visit destination type
476
2/4
✓ Branch 5 → 6 taken 30143 times.
✗ Branch 5 → 49 not taken.
✓ Branch 6 → 7 taken 30143 times.
✗ Branch 6 → 47 not taken.
30143 const auto dstType = std::any_cast<QualType>(visit(node->dataType));
477
2/8
✓ Branch 8 → 9 taken 30143 times.
✗ Branch 8 → 65 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 14 taken 30143 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 50 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 50 not taken.
30143 HANDLE_UNRESOLVED_TYPE_ER(dstType)
478 // Visit source type
479
2/4
✓ Branch 14 → 15 taken 30143 times.
✗ Branch 14 → 53 not taken.
✓ Branch 15 → 16 taken 30143 times.
✗ Branch 15 → 51 not taken.
30143 const auto src = std::any_cast<ExprResult>(visit(node->assignExpr));
480
2/8
✓ Branch 17 → 18 taken 30143 times.
✗ Branch 17 → 65 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 23 taken 30143 times.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 54 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 54 not taken.
30143 HANDLE_UNRESOLVED_TYPE_ER(src.type)
481
482 // Check for identity cast
483
3/4
✓ Branch 23 → 24 taken 30143 times.
✗ Branch 23 → 65 not taken.
✓ Branch 24 → 25 taken 3390 times.
✓ Branch 24 → 34 taken 26753 times.
30143 if (src.type == dstType) {
484
2/4
✓ Branch 27 → 28 taken 3390 times.
✗ Branch 27 → 57 not taken.
✓ Branch 28 → 29 taken 3390 times.
✗ Branch 28 → 55 not taken.
6780 const CompilerWarning warning(node->codeLoc, IDENTITY_CAST, "You cast from a type to itself. Thus, this can be simplified.");
485
1/2
✓ Branch 31 → 32 taken 3390 times.
✗ Branch 31 → 61 not taken.
3390 sourceFile->compilerOutput.warnings.push_back(warning);
486 3390 }
487
488 // Get result type
489
2/2
✓ Branch 34 → 35 taken 30141 times.
✓ Branch 34 → 65 taken 2 times.
30143 const QualType resultType = opRuleManager.getCastResultType(node, dstType, src);
490
491
1/2
✓ Branch 35 → 36 taken 30141 times.
✗ Branch 35 → 65 not taken.
30141 const bool typesMatch = dstType.matches(src.type, false, true, true);
492
1/2
✓ Branch 36 → 37 taken 30141 times.
✗ Branch 36 → 65 not taken.
30141 const bool sameContainerType = src.type.isSameContainerTypeAs(dstType);
493
4/4
✓ Branch 37 → 38 taken 26751 times.
✓ Branch 37 → 39 taken 3390 times.
✓ Branch 38 → 39 taken 10278 times.
✓ Branch 38 → 40 taken 16473 times.
30141 SymbolTableEntry *entry = typesMatch || sameContainerType ? src.entry : nullptr;
494
2/4
✓ Branch 41 → 42 taken 30141 times.
✗ Branch 41 → 64 not taken.
✓ Branch 42 → 43 taken 30141 times.
✗ Branch 42 → 64 not taken.
60282 return ExprResult{node->setEvaluatedSymbolType(resultType, manIdx), entry};
495 }
496
497 25225 std::any TypeChecker::visitPrefixUnaryExpr(PrefixUnaryExprNode *node) {
498 // If no operator is applied, simply visit the postfix unary expression
499
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 25225 times.
25225 if (node->op == PrefixUnaryExprNode::PrefixUnaryOp::OP_NONE)
500 ✗ return visit(node->postfixUnaryExpr);
501
502 // Visit the right side
503 25225 ExprNode *rhsNode = node->prefixUnaryExpr;
504
2/4
✓ Branch 5 → 6 taken 25225 times.
✗ Branch 5 → 58 not taken.
✓ Branch 6 → 7 taken 25225 times.
✗ Branch 6 → 56 not taken.
25225 auto operand = std::any_cast<ExprResult>(visit(rhsNode));
505 25225 auto [operandType, operandEntry] = operand;
506
5/8
✓ Branch 8 → 9 taken 25225 times.
✗ Branch 8 → 79 not taken.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 14 taken 25223 times.
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 59 not taken.
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 59 not taken.
25227 HANDLE_UNRESOLVED_TYPE_ER(operandType)
507 // Determine action, based on the given operator
508
7/8
✓ Branch 14 → 15 taken 2071 times.
✓ Branch 14 → 17 taken 36 times.
✓ Branch 14 → 25 taken 44 times.
✓ Branch 14 → 33 taken 15209 times.
✓ Branch 14 → 35 taken 60 times.
✓ Branch 14 → 38 taken 3318 times.
✓ Branch 14 → 40 taken 4485 times.
✗ Branch 14 → 42 not taken.
25223 switch (node->op) {
509 2071 case PrefixUnaryExprNode::PrefixUnaryOp::OP_MINUS:
510
1/2
✓ Branch 15 → 16 taken 2071 times.
✗ Branch 15 → 60 not taken.
2071 operandType = OpRuleManager::getPrefixMinusResultType(node, operand);
511 2071 break;
512 36 case PrefixUnaryExprNode::PrefixUnaryOp::OP_PLUS_PLUS:
513
1/2
✓ Branch 17 → 18 taken 36 times.
✗ Branch 17 → 61 not taken.
36 operandType = opRuleManager.getPrefixPlusPlusResultType(node, operand);
514
515
2/2
✓ Branch 18 → 19 taken 30 times.
✓ Branch 18 → 24 taken 6 times.
36 if (operandEntry) {
516 // In case the lhs is captured, notify the capture about the write access
517
2/4
✓ Branch 19 → 20 taken 30 times.
✗ Branch 19 → 79 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 30 times.
30 if (Capture *lhsCapture = currentScope->symbolTable.lookupCapture(operandEntry->name); lhsCapture)
518 ✗ lhsCapture->setAccessType(READ_WRITE);
519
520 // Update the state of the variable
521
1/2
✓ Branch 22 → 23 taken 30 times.
✗ Branch 22 → 62 not taken.
30 operandEntry->updateState(INITIALIZED, node);
522 }
523
524 36 break;
525 44 case PrefixUnaryExprNode::PrefixUnaryOp::OP_MINUS_MINUS:
526
2/2
✓ Branch 25 → 26 taken 42 times.
✓ Branch 25 → 63 taken 2 times.
44 operandType = opRuleManager.getPrefixMinusMinusResultType(node, operand);
527
528
2/2
✓ Branch 26 → 27 taken 36 times.
✓ Branch 26 → 32 taken 6 times.
42 if (operandEntry) {
529 // In case the lhs is captured, notify the capture about the write access
530
2/4
✓ Branch 27 → 28 taken 36 times.
✗ Branch 27 → 79 not taken.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 36 times.
36 if (Capture *lhsCapture = currentScope->symbolTable.lookupCapture(operandEntry->name); lhsCapture)
531 ✗ lhsCapture->setAccessType(READ_WRITE);
532
533 // Update the state of the variable
534
1/2
✓ Branch 30 → 31 taken 36 times.
✗ Branch 30 → 64 not taken.
36 operandEntry->updateState(INITIALIZED, node);
535 }
536
537 42 break;
538 15209 case PrefixUnaryExprNode::PrefixUnaryOp::OP_NOT:
539
1/2
✓ Branch 33 → 34 taken 15209 times.
✗ Branch 33 → 65 not taken.
15209 operandType = OpRuleManager::getPrefixNotResultType(node, operand);
540 15209 break;
541 60 case PrefixUnaryExprNode::PrefixUnaryOp::OP_BITWISE_NOT: {
542
1/2
✓ Branch 35 → 36 taken 60 times.
✗ Branch 35 → 66 not taken.
60 const ExprResult result = opRuleManager.getPrefixBitwiseNotResultType(node, operand);
543 60 operandType = result.type;
544 60 operandEntry = result.entry;
545 60 break;
546 }
547 3318 case PrefixUnaryExprNode::PrefixUnaryOp::OP_DEREFERENCE:
548
2/2
✓ Branch 38 → 39 taken 3316 times.
✓ Branch 38 → 67 taken 2 times.
3318 operandType = OpRuleManager::getPrefixMulResultType(node, operand);
549 3316 break;
550 4485 case PrefixUnaryExprNode::PrefixUnaryOp::OP_ADDRESS_OF:
551
1/2
✓ Branch 40 → 41 taken 4485 times.
✗ Branch 40 → 68 not taken.
4485 operandType = OpRuleManager::getPrefixBitwiseAndResultType(node, operand);
552 4485 break;
553 − default: // GCOV_EXCL_LINE
554 − throw CompilerError(UNHANDLED_BRANCH, "PrefixUnaryExpr fall-through"); // GCOV_EXCL_LINE
555 }
556
557
2/4
✓ Branch 50 → 51 taken 25219 times.
✗ Branch 50 → 78 not taken.
✓ Branch 51 → 52 taken 25219 times.
✗ Branch 51 → 78 not taken.
50438 return ExprResult{node->setEvaluatedSymbolType(operandType, manIdx), operandEntry};
558 }
559
560 253745 std::any TypeChecker::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) {
561 // If no operator is applied, simply visit the atomic expression
562
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 253745 times.
253745 if (node->op == PostfixUnaryExprNode::PostfixUnaryOp::OP_NONE)
563 ✗ return visit(node->atomicExpr);
564
565 // Visit left side
566 253745 ExprNode *lhsNode = node->postfixUnaryExpr;
567
2/4
✓ Branch 5 → 6 taken 253745 times.
✗ Branch 5 → 393 not taken.
✓ Branch 6 → 7 taken 253745 times.
✗ Branch 6 → 391 not taken.
253745 auto operand = std::any_cast<ExprResult>(visit(lhsNode));
568 253745 auto [operandType, operandEntry] = operand;
569
5/8
✓ Branch 8 → 9 taken 253745 times.
✗ Branch 8 → 608 not taken.
✓ Branch 9 → 10 taken 12 times.
✓ Branch 9 → 14 taken 253733 times.
✓ Branch 10 → 11 taken 12 times.
✗ Branch 10 → 394 not taken.
✓ Branch 11 → 12 taken 12 times.
✗ Branch 11 → 394 not taken.
253757 HANDLE_UNRESOLVED_TYPE_ER(operandType)
570
571
5/6
✓ Branch 14 → 15 taken 23716 times.
✓ Branch 14 → 101 taken 211230 times.
✓ Branch 14 → 213 taken 17204 times.
✓ Branch 14 → 221 taken 1327 times.
✓ Branch 14 → 229 taken 256 times.
✗ Branch 14 → 357 not taken.
253733 switch (node->op) {
572 23716 case PostfixUnaryExprNode::PostfixUnaryOp::OP_SUBSCRIPT: {
573 // Visit index assignment
574 23716 ExprNode *indexAssignExpr = node->subscriptIndexExpr;
575
2/4
✓ Branch 15 → 16 taken 23716 times.
✗ Branch 15 → 397 not taken.
✓ Branch 16 → 17 taken 23716 times.
✗ Branch 16 → 395 not taken.
23716 const auto index = std::any_cast<ExprResult>(visit(indexAssignExpr));
576
2/8
✓ Branch 18 → 19 taken 23716 times.
✗ Branch 18 → 440 not taken.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 24 taken 23716 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 398 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 398 not taken.
23716 HANDLE_UNRESOLVED_TYPE_ER(index.type)
577
578 // Check is there is an overloaded operator function available, if yes accept it
579
1/2
✓ Branch 24 → 25 taken 23716 times.
✗ Branch 24 → 399 not taken.
23716 const auto [type, _] = opRuleManager.isOperatorOverloadingFctAvailable<2>(node, OP_FCT_SUBSCRIPT, {operand, index}, 0);
580
3/4
✓ Branch 25 → 26 taken 23716 times.
✗ Branch 25 → 440 not taken.
✓ Branch 26 → 27 taken 1176 times.
✓ Branch 26 → 28 taken 22540 times.
23716 if (!type.is(TY_INVALID)) {
581 1176 operandType = type;
582 23712 break;
583 }
584
585
1/2
✓ Branch 28 → 29 taken 22540 times.
✗ Branch 28 → 400 not taken.
22540 operandType = operandType.removeReferenceWrapper();
586
587 // Check if the index is of the right type
588
3/4
✓ Branch 29 → 30 taken 22540 times.
✗ Branch 29 → 401 not taken.
✓ Branch 30 → 31 taken 2 times.
✓ Branch 30 → 41 taken 22538 times.
22540 if (!index.type.isOneOf({TY_INT, TY_LONG}))
589
4/8
✓ Branch 33 → 34 taken 2 times.
✗ Branch 33 → 404 not taken.
✓ Branch 34 → 35 taken 2 times.
✗ Branch 34 → 402 not taken.
✓ Branch 37 → 38 taken 2 times.
✗ Branch 37 → 408 not taken.
✓ Branch 38 → 39 taken 2 times.
✗ Branch 38 → 408 not taken.
8 SOFT_ERROR_ER(node, ARRAY_INDEX_NOT_INT_OR_LONG, "Array index must be of type int or long")
590
591 // Check if we can apply the subscript operator on the lhs type
592
2/4
✓ Branch 41 → 42 taken 22538 times.
✗ Branch 41 → 409 not taken.
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 52 taken 22538 times.
22538 if (!operandType.isOneOf({TY_ARRAY, TY_PTR, TY_STRING}))
593 ✗ SOFT_ERROR_ER(node, OPERATOR_WRONG_DATA_TYPE,
594 "Can only apply subscript operator on array type, got " + operandType.getName(true))
595
596 // Check if we have an unsafe operation
597
6/10
✓ Branch 52 → 53 taken 22538 times.
✗ Branch 52 → 440 not taken.
✓ Branch 53 → 54 taken 17835 times.
✓ Branch 53 → 57 taken 4703 times.
✓ Branch 54 → 55 taken 17835 times.
✗ Branch 54 → 440 not taken.
✗ Branch 55 → 56 not taken.
✓ Branch 55 → 57 taken 17835 times.
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 69 taken 22538 times.
22538 if (operandType.isPtr() && !currentScope->doesAllowUnsafeOperations())
598 ✗ SOFT_ERROR_ER(
599 node, UNSAFE_OPERATION_IN_SAFE_CONTEXT,
600 "The subscript operator on pointers is an unsafe operation. Use unsafe blocks if you know what you are doing.")
601
602 // In case of compile time index value and known array size, perform a compile time out-of-bounds check
603
8/10
✓ Branch 69 → 70 taken 22538 times.
✗ Branch 69 → 440 not taken.
✓ Branch 70 → 71 taken 2785 times.
✓ Branch 70 → 76 taken 19753 times.
✓ Branch 71 → 72 taken 2785 times.
✗ Branch 71 → 440 not taken.
✓ Branch 72 → 73 taken 2489 times.
✓ Branch 72 → 76 taken 296 times.
✓ Branch 77 → 78 taken 895 times.
✓ Branch 77 → 96 taken 21643 times.
25027 if (operandType.isArray() && operandType.getArraySize() != ARRAY_SIZE_UNKNOWN &&
604
3/4
✓ Branch 73 → 74 taken 2489 times.
✗ Branch 73 → 440 not taken.
✓ Branch 74 → 75 taken 895 times.
✓ Branch 74 → 76 taken 1594 times.
2489 indexAssignExpr->hasCompileTimeValue(manIdx)) {
605
1/2
✓ Branch 78 → 79 taken 895 times.
✗ Branch 78 → 440 not taken.
895 const int32_t constIndex = indexAssignExpr->getCompileTimeValue(manIdx).intValue;
606
1/2
✓ Branch 79 → 80 taken 895 times.
✗ Branch 79 → 440 not taken.
895 const unsigned int constSize = operandType.getArraySize();
607 // Check if we are accessing out-of-bounds memory
608
2/2
✓ Branch 80 → 81 taken 2 times.
✓ Branch 80 → 96 taken 893 times.
895 if (constIndex >= static_cast<int32_t>(constSize)) {
609 2 const std::string idxStr = std::to_string(constIndex);
610 2 const std::string sizeStr = std::to_string(constSize);
611
6/12
✓ Branch 83 → 84 taken 2 times.
✗ Branch 83 → 430 not taken.
✓ Branch 84 → 85 taken 2 times.
✗ Branch 84 → 428 not taken.
✓ Branch 85 → 86 taken 2 times.
✗ Branch 85 → 426 not taken.
✓ Branch 86 → 87 taken 2 times.
✗ Branch 86 → 424 not taken.
✓ Branch 90 → 91 taken 2 times.
✗ Branch 90 → 433 not taken.
✓ Branch 91 → 92 taken 2 times.
✗ Branch 91 → 433 not taken.
2 SOFT_ERROR_ER(node, ARRAY_INDEX_OUT_OF_BOUNDS,
612 "You are trying to access element with index " + idxStr + " of an array with size " + sizeStr)
613 2 }
614 }
615
616 // Get item type
617
1/2
✓ Branch 96 → 97 taken 22536 times.
✗ Branch 96 → 439 not taken.
22536 operandType = operandType.getContained();
618
619 // Remove heap qualifier
620 22536 operandType.getQualifiers().isHeap = false;
621
622 22536 break;
623 }
624 211230 case PostfixUnaryExprNode::PostfixUnaryOp::OP_MEMBER_ACCESS: {
625 211230 const std::string &fieldName = node->identifier;
626
627 // Check if lhs is enum or strobj
628
1/2
✓ Branch 101 → 102 taken 211230 times.
✗ Branch 101 → 487 not taken.
211230 const QualType lhsBaseTy = operandType.autoDeReference();
629
630 // Union field access is handled separately: a union field is always a direct, non-composed lookup by name
631 // (unlike struct fields, union fields never nest/compose), and reading/writing it requires a runtime tag
632 // check/update in the IR generator, which relies on the field entry resolved here.
633
3/4
✓ Branch 102 → 103 taken 211230 times.
✗ Branch 102 → 487 not taken.
✓ Branch 103 → 104 taken 146 times.
✓ Branch 103 → 156 taken 211084 times.
211230 if (lhsBaseTy.is(TY_UNION)) {
634
1/2
✓ Branch 104 → 105 taken 146 times.
✗ Branch 104 → 459 not taken.
146 const std::string &unionName = lhsBaseTy.getSubType();
635
1/2
✓ Branch 105 → 106 taken 146 times.
✗ Branch 105 → 459 not taken.
146 Scope *unionScope = lhsBaseTy.getBodyScope();
636
637 // If we only have the generic union scope, lookup the concrete manifestation scope
638
1/2
✗ Branch 106 → 107 not taken.
✓ Branch 106 → 111 taken 146 times.
146 if (unionScope->isGenericScope) {
639 ✗ const Union *spiceUnion = lhsBaseTy.getUnion(node);
640 ✗ assert(spiceUnion != nullptr);
641 ✗ unionScope = spiceUnion->scope;
642 }
643
1/2
✗ Branch 111 → 112 not taken.
✓ Branch 111 → 113 taken 146 times.
146 assert(!unionScope->isGenericScope); // At this point we always expect a substantiation scope
644
645 // Get accessed field
646
1/2
✓ Branch 113 → 114 taken 146 times.
✗ Branch 113 → 459 not taken.
146 SymbolTableEntry *memberEntry = unionScope->symbolTable.lookupStrict(fieldName);
647
2/2
✓ Branch 114 → 115 taken 2 times.
✓ Branch 114 → 126 taken 144 times.
146 if (!memberEntry)
648
6/12
✓ Branch 115 → 116 taken 2 times.
✗ Branch 115 → 447 not taken.
✓ Branch 116 → 117 taken 2 times.
✗ Branch 116 → 445 not taken.
✓ Branch 117 → 118 taken 2 times.
✗ Branch 117 → 443 not taken.
✓ Branch 118 → 119 taken 2 times.
✗ Branch 118 → 441 not taken.
✓ Branch 122 → 123 taken 2 times.
✗ Branch 122 → 450 not taken.
✓ Branch 123 → 124 taken 2 times.
✗ Branch 123 → 450 not taken.
4 SOFT_ERROR_ER(node, REFERENCED_UNDEFINED_VARIABLE, "Field '" + node->identifier + "' not found in union " + unionName)
649
1/2
✓ Branch 126 → 127 taken 144 times.
✗ Branch 126 → 459 not taken.
144 const QualType memberType = memberEntry->getQualType();
650
651 // Check for insufficient visibility
652
8/14
✓ Branch 127 → 128 taken 144 times.
✗ Branch 127 → 451 not taken.
✓ Branch 128 → 129 taken 54 times.
✓ Branch 128 → 134 taken 90 times.
✓ Branch 129 → 130 taken 54 times.
✗ Branch 129 → 451 not taken.
✓ Branch 130 → 131 taken 54 times.
✗ Branch 130 → 451 not taken.
✓ Branch 131 → 132 taken 54 times.
✗ Branch 131 → 451 not taken.
✗ Branch 132 → 133 not taken.
✓ Branch 132 → 134 taken 54 times.
✗ Branch 135 → 136 not taken.
✓ Branch 135 → 145 taken 144 times.
144 if (unionScope->isImportedBy(rootScope) && !memberEntry->getQualType().getBase().isPublic())
653 ✗ SOFT_ERROR_ER(node, INSUFFICIENT_VISIBILITY, "Cannot access field '" + fieldName + "' due to its private visibility")
654
655 // Set field to used
656 144 memberEntry->used = true;
657
658 // A read of an inactive field panics, which prints the stack trace, so the runtime that does that has to be loaded
659
2/8
✗ Branch 146 → 147 not taken.
✓ Branch 146 → 150 taken 144 times.
✗ Branch 147 → 148 not taken.
✗ Branch 147 → 459 not taken.
✗ Branch 148 → 149 not taken.
✗ Branch 148 → 150 not taken.
✗ Branch 151 → 152 not taken.
✓ Branch 151 → 153 taken 144 times.
144 if (cliOptions.printsStackTraceOnAbort() && !sourceFile->isRT(STACK_TRACE_RT))
660 ✗ sourceFile->requestRuntimeModule(STACK_TRACE_RT);
661
662 // Overwrite type and entry of left side with member type and entry
663 144 operandType = memberType;
664 144 operandEntry = memberEntry;
665 144 break;
666 }
667
668
3/4
✓ Branch 156 → 157 taken 211084 times.
✗ Branch 156 → 487 not taken.
✓ Branch 157 → 158 taken 2 times.
✓ Branch 157 → 167 taken 211082 times.
211084 if (!lhsBaseTy.is(TY_STRUCT))
669
5/10
✓ Branch 158 → 159 taken 2 times.
✗ Branch 158 → 464 not taken.
✓ Branch 159 → 160 taken 2 times.
✗ Branch 159 → 462 not taken.
✓ Branch 160 → 161 taken 2 times.
✗ Branch 160 → 460 not taken.
✓ Branch 163 → 164 taken 2 times.
✗ Branch 163 → 466 not taken.
✓ Branch 164 → 165 taken 2 times.
✗ Branch 164 → 466 not taken.
4 SOFT_ERROR_ER(node, INVALID_MEMBER_ACCESS, "Cannot apply member access operator on " + operandType.getName(false))
670
671 // Retrieve registry entry
672
1/2
✓ Branch 167 → 168 taken 211082 times.
✗ Branch 167 → 487 not taken.
211082 const std::string &structName = lhsBaseTy.getSubType();
673
1/2
✓ Branch 168 → 169 taken 211082 times.
✗ Branch 168 → 487 not taken.
211082 Scope *structScope = lhsBaseTy.getBodyScope();
674
675 // If we only have the generic struct scope, lookup the concrete manifestation scope
676
2/2
✓ Branch 169 → 170 taken 783 times.
✓ Branch 169 → 174 taken 210299 times.
211082 if (structScope->isGenericScope) {
677
1/2
✓ Branch 170 → 171 taken 783 times.
✗ Branch 170 → 487 not taken.
783 const Struct *spiceStruct = lhsBaseTy.getStruct(node);
678
1/2
✗ Branch 171 → 172 not taken.
✓ Branch 171 → 173 taken 783 times.
783 assert(spiceStruct != nullptr);
679 783 structScope = spiceStruct->scope;
680 }
681
1/2
✗ Branch 174 → 175 not taken.
✓ Branch 174 → 176 taken 211082 times.
211082 assert(!structScope->isGenericScope); // At this point we always expect a substantiation scope
682
683 // Get accessed field
684 211082 std::vector<size_t> indexPath;
685
1/2
✓ Branch 176 → 177 taken 211082 times.
✗ Branch 176 → 485 not taken.
211082 SymbolTableEntry *memberEntry = structScope->symbolTable.lookupInComposedFields(fieldName, indexPath);
686
2/2
✓ Branch 177 → 178 taken 4 times.
✓ Branch 177 → 189 taken 211078 times.
211082 if (!memberEntry)
687
6/12
✓ Branch 178 → 179 taken 4 times.
✗ Branch 178 → 473 not taken.
✓ Branch 179 → 180 taken 4 times.
✗ Branch 179 → 471 not taken.
✓ Branch 180 → 181 taken 4 times.
✗ Branch 180 → 469 not taken.
✓ Branch 181 → 182 taken 4 times.
✗ Branch 181 → 467 not taken.
✓ Branch 185 → 186 taken 4 times.
✗ Branch 185 → 476 not taken.
✓ Branch 186 → 187 taken 4 times.
✗ Branch 186 → 476 not taken.
8 SOFT_ERROR_ER(node, REFERENCED_UNDEFINED_VARIABLE, "Field '" + node->identifier + "' not found in struct " + structName)
688
1/2
✓ Branch 189 → 190 taken 211078 times.
✗ Branch 189 → 485 not taken.
211078 const QualType memberType = memberEntry->getQualType();
689
690 // Check for insufficient visibility
691
8/14
✓ Branch 190 → 191 taken 211078 times.
✗ Branch 190 → 477 not taken.
✓ Branch 191 → 192 taken 9090 times.
✓ Branch 191 → 197 taken 201988 times.
✓ Branch 192 → 193 taken 9090 times.
✗ Branch 192 → 477 not taken.
✓ Branch 193 → 194 taken 9090 times.
✗ Branch 193 → 477 not taken.
✓ Branch 194 → 195 taken 9090 times.
✗ Branch 194 → 477 not taken.
✗ Branch 195 → 196 not taken.
✓ Branch 195 → 197 taken 9090 times.
✗ Branch 198 → 199 not taken.
✓ Branch 198 → 208 taken 211078 times.
211078 if (structScope->isImportedBy(rootScope) && !memberEntry->getQualType().getBase().isPublic())
692 ✗ SOFT_ERROR_ER(node, INSUFFICIENT_VISIBILITY, "Cannot access field '" + fieldName + "' due to its private visibility")
693
694 // Set field to used
695 211078 memberEntry->used = true;
696
697 // Overwrite type and entry of left side with member type and entry
698 211078 operandType = memberType;
699 211078 operandEntry = memberEntry;
700 211078 break;
701
2/2
✓ Branch 210 → 211 taken 4 times.
✓ Branch 210 → 212 taken 211078 times.
211082 }
702 17204 case PostfixUnaryExprNode::PostfixUnaryOp::OP_PLUS_PLUS: {
703
2/2
✓ Branch 213 → 214 taken 17200 times.
✓ Branch 213 → 488 taken 4 times.
17204 operandType = opRuleManager.getPostfixPlusPlusResultType(node, operand).type;
704
705
2/2
✓ Branch 214 → 215 taken 17192 times.
✓ Branch 214 → 220 taken 8 times.
17200 if (operandEntry) {
706 // In case the lhs is captured, notify the capture about the write access
707
3/4
✓ Branch 215 → 216 taken 17192 times.
✗ Branch 215 → 608 not taken.
✓ Branch 216 → 217 taken 8 times.
✓ Branch 216 → 218 taken 17184 times.
17192 if (Capture *lhsCapture = currentScope->symbolTable.lookupCapture(operandEntry->name); lhsCapture)
708
1/2
✓ Branch 217 → 218 taken 8 times.
✗ Branch 217 → 608 not taken.
8 lhsCapture->setAccessType(READ_WRITE);
709
710 // Update the state of the variable
711
1/2
✓ Branch 218 → 219 taken 17192 times.
✗ Branch 218 → 489 not taken.
17192 operandEntry->updateState(INITIALIZED, node);
712 }
713
714 17200 break;
715 }
716 1327 case PostfixUnaryExprNode::PostfixUnaryOp::OP_MINUS_MINUS: {
717
1/2
✓ Branch 221 → 222 taken 1327 times.
✗ Branch 221 → 490 not taken.
1327 operandType = opRuleManager.getPostfixMinusMinusResultType(node, operand).type;
718
719
2/2
✓ Branch 222 → 223 taken 1319 times.
✓ Branch 222 → 228 taken 8 times.
1327 if (operandEntry) {
720 // In case the lhs is captured, notify the capture about the write access
721
2/4
✓ Branch 223 → 224 taken 1319 times.
✗ Branch 223 → 608 not taken.
✗ Branch 224 → 225 not taken.
✓ Branch 224 → 226 taken 1319 times.
1319 if (Capture *lhsCapture = currentScope->symbolTable.lookupCapture(operandEntry->name); lhsCapture)
722 ✗ lhsCapture->setAccessType(READ_WRITE);
723
724 // Update the state of the variable
725
1/2
✓ Branch 226 → 227 taken 1319 times.
✗ Branch 226 → 491 not taken.
1319 operandEntry->updateState(INITIALIZED, node);
726 }
727
728 1327 break;
729 }
730 256 case PostfixUnaryExprNode::PostfixUnaryOp::OP_ERR_PROPAGATION: {
731 // Check if the operand is of type Result<T>
732
1/2
✓ Branch 229 → 230 taken 256 times.
✗ Branch 229 → 582 not taken.
256 const QualType resultType = operandType.removeReferenceWrapper();
733
3/4
✓ Branch 230 → 231 taken 256 times.
✗ Branch 230 → 582 not taken.
✓ Branch 231 → 232 taken 2 times.
✓ Branch 231 → 241 taken 254 times.
256 if (!resultType.isResultObj())
734
5/10
✓ Branch 232 → 233 taken 2 times.
✗ Branch 232 → 496 not taken.
✓ Branch 233 → 234 taken 2 times.
✗ Branch 233 → 494 not taken.
✓ Branch 234 → 235 taken 2 times.
✗ Branch 234 → 492 not taken.
✓ Branch 237 → 238 taken 2 times.
✗ Branch 237 → 498 not taken.
✓ Branch 238 → 239 taken 2 times.
✗ Branch 238 → 498 not taken.
4 SOFT_ERROR_ER(node, ERR_PROPAGATION_INVALID_OPERAND,
735 "The error propagation operator '!' can only be applied to a value of type Result<T>, got " +
736 operandType.getName(false))
737
738 // Check if the enclosing function/procedure/lambda itself returns Result<U>
739
1/2
✓ Branch 243 → 244 taken 254 times.
✗ Branch 243 → 501 not taken.
762 const SymbolTableEntry *enclosingReturnVar = currentScope->lookup(RETURN_VARIABLE_NAME);
740
2/6
✓ Branch 249 → 250 taken 254 times.
✗ Branch 249 → 252 not taken.
✓ Branch 250 → 251 taken 254 times.
✗ Branch 250 → 582 not taken.
✗ Branch 252 → 253 not taken.
✗ Branch 252 → 582 not taken.
254 const QualType enclosingReturnType = enclosingReturnVar ? enclosingReturnVar->getQualType() : QualType(TY_DYN);
741
6/8
✓ Branch 253 → 254 taken 254 times.
✗ Branch 253 → 256 not taken.
✓ Branch 254 → 255 taken 254 times.
✗ Branch 254 → 582 not taken.
✓ Branch 255 → 256 taken 2 times.
✓ Branch 255 → 257 taken 252 times.
✓ Branch 258 → 259 taken 2 times.
✓ Branch 258 → 269 taken 252 times.
254 if (!enclosingReturnVar || !enclosingReturnType.isResultObj())
742
4/8
✓ Branch 261 → 262 taken 2 times.
✗ Branch 261 → 507 not taken.
✓ Branch 262 → 263 taken 2 times.
✗ Branch 262 → 505 not taken.
✓ Branch 265 → 266 taken 2 times.
✗ Branch 265 → 511 not taken.
✓ Branch 266 → 267 taken 2 times.
✗ Branch 266 → 511 not taken.
8 SOFT_ERROR_ER(node, ERR_PROPAGATION_INVALID_CONTEXT,
743 "The error propagation operator '!' can only be used inside a function that itself returns Result<U>")
744
745 // Retrieve the substantiation scope of the operand's Result<T>
746
1/2
✓ Branch 269 → 270 taken 252 times.
✗ Branch 269 → 582 not taken.
252 Scope *resultScope = resultType.getBodyScope();
747
1/2
✗ Branch 270 → 271 not taken.
✓ Branch 270 → 275 taken 252 times.
252 if (resultScope->isGenericScope) {
748 ✗ const Struct *resultStruct = resultType.getStruct(node);
749 ✗ assert(resultStruct != nullptr);
750 ✗ resultScope = resultStruct->scope;
751 }
752
1/2
✗ Branch 275 → 276 not taken.
✓ Branch 275 → 277 taken 252 times.
252 assert(!resultScope->isGenericScope);
753
754 // Resolve Result<T>.isErr(), Result<T>.unwrap() and Result<T>.getErr()
755
2/4
✓ Branch 281 → 282 taken 252 times.
✗ Branch 281 → 514 not taken.
✓ Branch 282 → 283 taken 252 times.
✗ Branch 282 → 512 not taken.
756 node->errPropIsErrFct = FunctionManager::match(resultScope, "isErr", resultType, {}, {}, false, node);
756
2/4
✓ Branch 291 → 292 taken 252 times.
✗ Branch 291 → 526 not taken.
✓ Branch 292 → 293 taken 252 times.
✗ Branch 292 → 524 not taken.
756 node->errPropUnwrapFct = FunctionManager::match(resultScope, "unwrap", resultType, {}, {}, false, node);
757
2/4
✓ Branch 301 → 302 taken 252 times.
✗ Branch 301 → 538 not taken.
✓ Branch 302 → 303 taken 252 times.
✗ Branch 302 → 536 not taken.
756 node->errPropGetErrFct = FunctionManager::match(resultScope, "getErr", resultType, {}, {}, false, node);
758
3/6
✓ Branch 307 → 308 taken 252 times.
✗ Branch 307 → 310 not taken.
✓ Branch 308 → 309 taken 252 times.
✗ Branch 308 → 310 not taken.
✗ Branch 309 → 310 not taken.
✓ Branch 309 → 320 taken 252 times.
252 if (!node->errPropIsErrFct || !node->errPropUnwrapFct || !node->errPropGetErrFct)
759 ✗ SOFT_ERROR_ER(node, ERR_PROPAGATION_INVALID_CONTEXT,
760 "Failed to resolve the Result<T> helper methods required for the error propagation operator")
761
762 // Resolve err<U>(const Error&), located next to Result in the same module
763
2/4
✓ Branch 322 → 323 taken 252 times.
✗ Branch 322 → 557 not taken.
✓ Branch 323 → 324 taken 252 times.
✗ Branch 323 → 555 not taken.
504 const NameRegistryEntry *resultRegistryEntry = sourceFile->getNameRegistryEntry(RESULTOBJ_NAME);
764
1/2
✗ Branch 326 → 327 not taken.
✓ Branch 326 → 328 taken 252 times.
252 assert(resultRegistryEntry != nullptr);
765 252 Scope *resultModuleScope = resultRegistryEntry->targetScope->parent;
766
1/2
✓ Branch 328 → 329 taken 252 times.
✗ Branch 328 → 582 not taken.
252 const QualTypeList &enclosingTemplateTypes = enclosingReturnType.getTemplateTypes();
767
1/2
✓ Branch 332 → 333 taken 252 times.
✗ Branch 332 → 561 not taken.
504 const ArgList errArgs = {{node->errPropGetErrFct->returnType, false}};
768 252 node->errPropCtorFct =
769
3/6
✓ Branch 334 → 335 taken 252 times.
✗ Branch 334 → 572 not taken.
✓ Branch 337 → 338 taken 252 times.
✗ Branch 337 → 568 not taken.
✓ Branch 338 → 339 taken 252 times.
✗ Branch 338 → 566 not taken.
504 FunctionManager::match(resultModuleScope, "err", QualType(TY_DYN), errArgs, enclosingTemplateTypes, false, node);
770
1/2
✗ Branch 341 → 342 not taken.
✓ Branch 341 → 352 taken 252 times.
252 if (!node->errPropCtorFct)
771 ✗ SOFT_ERROR_ER(node, ERR_PROPAGATION_INVALID_CONTEXT,
772 "Failed to resolve 'err<U>(const Error&)' required for the error propagation operator")
773
774 // The whole expression evaluates to the unwrapped payload
775 252 operandType = node->errPropUnwrapFct->returnType;
776 252 operandEntry = nullptr;
777
778 252 break;
779
1/2
✗ Branch 354 → 355 not taken.
✓ Branch 354 → 356 taken 252 times.
252 }
780 − default: // GCOV_EXCL_LINE
781 − throw CompilerError(UNHANDLED_BRANCH, "PostfixUnaryExpr fall-through"); // GCOV_EXCL_LINE
782 }
783
784
2/4
✓ Branch 365 → 366 taken 253713 times.
✗ Branch 365 → 608 not taken.
✗ Branch 366 → 367 not taken.
✓ Branch 366 → 385 taken 253713 times.
253713 if (operandType.is(TY_INVALID)) {
785 ✗ const std::string &varName = operandEntry ? operandEntry->name : "";
786 ✗ SOFT_ERROR_ER(node, REFERENCED_UNDEFINED_VARIABLE, "Variable '" + varName + "' was referenced before declared")
787 ✗ }
788
789
2/4
✓ Branch 385 → 386 taken 253713 times.
✗ Branch 385 → 607 not taken.
✓ Branch 386 → 387 taken 253713 times.
✗ Branch 386 → 607 not taken.
507426 return ExprResult{node->setEvaluatedSymbolType(operandType, manIdx), operandEntry};
790 }
791
792 1029434 std::any TypeChecker::visitAtomicExpr(AtomicExprNode *node) {
793 // Check if constant
794
2/2
✓ Branch 2 → 3 taken 195476 times.
✓ Branch 2 → 5 taken 833958 times.
1029434 if (node->constant)
795
1/2
✓ Branch 3 → 4 taken 195476 times.
✗ Branch 3 → 218 not taken.
195476 return visit(node->constant);
796
797 // Check if value
798
2/2
✓ Branch 5 → 6 taken 259233 times.
✓ Branch 5 → 8 taken 574725 times.
833958 if (node->value)
799
2/2
✓ Branch 6 → 7 taken 259225 times.
✓ Branch 6 → 218 taken 8 times.
259233 return visit(node->value);
800
801 // Check for assign expression within parentheses
802
2/2
✓ Branch 8 → 9 taken 4118 times.
✓ Branch 8 → 11 taken 570607 times.
574725 if (node->assignExpr)
803
2/2
✓ Branch 9 → 10 taken 4112 times.
✓ Branch 9 → 218 taken 6 times.
4118 return visit(node->assignExpr);
804
805 // Identifier (local or global variable access)
806
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 570607 times.
570607 assert(!node->fqIdentifier.empty());
807
808
1/2
✓ Branch 14 → 15 taken 570607 times.
✗ Branch 14 → 218 not taken.
570607 auto &[entry, accessScope, capture] = node->data.at(manIdx);
809 570607 accessScope = currentScope;
810
811 // Check if a local or global variable can be found by searching for the name
812
2/2
✓ Branch 16 → 17 taken 552856 times.
✓ Branch 16 → 22 taken 17751 times.
570607 if (node->identifierFragments.size() == 1)
813 1105712 entry = accessScope->lookup(node->identifierFragments.back());
814
815 // If no local or global was found, search in the name registry
816
2/2
✓ Branch 22 → 23 taken 18585 times.
✓ Branch 22 → 35 taken 552022 times.
570607 if (!entry) {
817
1/2
✓ Branch 23 → 24 taken 18585 times.
✗ Branch 23 → 218 not taken.
18585 const NameRegistryEntry *registryEntry = sourceFile->getNameRegistryEntry(node->fqIdentifier);
818
2/2
✓ Branch 24 → 25 taken 2 times.
✓ Branch 24 → 34 taken 18583 times.
18585 if (!registryEntry)
819
5/10
✓ Branch 25 → 26 taken 2 times.
✗ Branch 25 → 166 not taken.
✓ Branch 26 → 27 taken 2 times.
✗ Branch 26 → 164 not taken.
✓ Branch 27 → 28 taken 2 times.
✗ Branch 27 → 162 not taken.
✓ Branch 30 → 31 taken 2 times.
✗ Branch 30 → 168 not taken.
✓ Branch 31 → 32 taken 2 times.
✗ Branch 31 → 168 not taken.
4 SOFT_ERROR_ER(node, REFERENCED_UNDEFINED_VARIABLE, "The variable '" + node->fqIdentifier + "' could not be found")
820 18583 entry = registryEntry->targetEntry;
821 18583 accessScope = registryEntry->targetScope;
822 }
823
1/2
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 570605 times.
570605 assert(entry != nullptr);
824 570605 entry->used = true;
825
1/2
✓ Branch 37 → 38 taken 570605 times.
✗ Branch 37 → 218 not taken.
570605 capture = accessScope->symbolTable.lookupCapture(entry->name);
826
827
1/2
✓ Branch 38 → 39 taken 570605 times.
✗ Branch 38 → 218 not taken.
570605 const QualType varType = entry->getQualType();
828
5/8
✓ Branch 39 → 40 taken 570605 times.
✗ Branch 39 → 218 not taken.
✓ Branch 40 → 41 taken 22 times.
✓ Branch 40 → 45 taken 570583 times.
✓ Branch 41 → 42 taken 22 times.
✗ Branch 41 → 169 not taken.
✓ Branch 42 → 43 taken 22 times.
✗ Branch 42 → 169 not taken.
570627 HANDLE_UNRESOLVED_TYPE_ER(varType)
829
3/4
✓ Branch 45 → 46 taken 570583 times.
✗ Branch 45 → 218 not taken.
✓ Branch 46 → 47 taken 4 times.
✓ Branch 46 → 56 taken 570579 times.
570583 if (varType.is(TY_INVALID))
830
5/10
✓ Branch 47 → 48 taken 4 times.
✗ Branch 47 → 174 not taken.
✓ Branch 48 → 49 taken 4 times.
✗ Branch 48 → 172 not taken.
✓ Branch 49 → 50 taken 4 times.
✗ Branch 49 → 170 not taken.
✓ Branch 52 → 53 taken 4 times.
✗ Branch 52 → 176 not taken.
✓ Branch 53 → 54 taken 4 times.
✗ Branch 53 → 176 not taken.
8 SOFT_ERROR_ER(node, USED_BEFORE_DECLARED, "Symbol '" + entry->name + "' was used before declared.")
831
832
7/8
✓ Branch 56 → 57 taken 570579 times.
✗ Branch 56 → 177 not taken.
✓ Branch 57 → 58 taken 520 times.
✓ Branch 57 → 60 taken 570059 times.
✓ Branch 58 → 59 taken 102 times.
✓ Branch 58 → 60 taken 418 times.
✓ Branch 61 → 62 taken 102 times.
✓ Branch 61 → 90 taken 570477 times.
570579 if (varType.isOneOf({TY_FUNCTION, TY_PROCEDURE}) && entry->global) {
833 // Check if overloaded function was referenced
834
1/2
✓ Branch 62 → 63 taken 102 times.
✗ Branch 62 → 218 not taken.
102 const std::vector<Function *> *manifestations = entry->declNode->getFctManifestations(entry->name);
835
2/2
✓ Branch 64 → 65 taken 2 times.
✓ Branch 64 → 75 taken 100 times.
102 if (manifestations->size() > 1)
836
4/8
✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 180 not taken.
✓ Branch 68 → 69 taken 2 times.
✗ Branch 68 → 178 not taken.
✓ Branch 71 → 72 taken 2 times.
✗ Branch 71 → 184 not taken.
✓ Branch 72 → 73 taken 2 times.
✗ Branch 72 → 184 not taken.
8 SOFT_ERROR_ER(node, REFERENCED_OVERLOADED_FCT, "Overloaded functions / functions with optional params cannot be referenced")
837
2/2
✓ Branch 77 → 78 taken 2 times.
✓ Branch 77 → 88 taken 98 times.
100 if (!manifestations->front()->templateTypes.empty())
838
4/8
✓ Branch 80 → 81 taken 2 times.
✗ Branch 80 → 187 not taken.
✓ Branch 81 → 82 taken 2 times.
✗ Branch 81 → 185 not taken.
✓ Branch 84 → 85 taken 2 times.
✗ Branch 84 → 191 not taken.
✓ Branch 85 → 86 taken 2 times.
✗ Branch 85 → 191 not taken.
8 SOFT_ERROR_ER(node, REFERENCED_OVERLOADED_FCT, "Generic functions cannot be referenced")
839 // Set referenced function to used
840 98 Function *referencedFunction = manifestations->front();
841 98 referencedFunction->used = true;
842 98 referencedFunction->entry->used = true;
843 }
844
845 // The base type should be an extended primitive
846
1/2
✓ Branch 90 → 91 taken 570575 times.
✗ Branch 90 → 218 not taken.
570575 const QualType baseType = varType.getBase();
847
6/10
✓ Branch 91 → 92 taken 570575 times.
✗ Branch 91 → 218 not taken.
✓ Branch 92 → 93 taken 8 times.
✓ Branch 92 → 96 taken 570567 times.
✓ Branch 93 → 94 taken 8 times.
✗ Branch 93 → 218 not taken.
✗ Branch 94 → 95 not taken.
✓ Branch 94 → 96 taken 8 times.
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 109 taken 570575 times.
570575 if (!baseType.isExtendedPrimitive() && !baseType.is(TY_DYN))
848 ✗ SOFT_ERROR_ER(node, INVALID_SYMBOL_ACCESS, "A symbol of type " + varType.getName(false) + " cannot be accessed here")
849
850 // Check if we have seen a 'this.' prefix, because the generator needs that
851
6/8
✓ Branch 109 → 110 taken 2 times.
✓ Branch 109 → 114 taken 570573 times.
✓ Branch 111 → 112 taken 2 times.
✗ Branch 111 → 218 not taken.
✓ Branch 112 → 113 taken 2 times.
✗ Branch 112 → 114 not taken.
✓ Branch 115 → 116 taken 2 times.
✓ Branch 115 → 125 taken 570573 times.
570575 if (entry->scope->type == ScopeType::STRUCT && node->identifierFragments.front() != THIS_VARIABLE_NAME)
852
5/10
✓ Branch 116 → 117 taken 2 times.
✗ Branch 116 → 206 not taken.
✓ Branch 117 → 118 taken 2 times.
✗ Branch 117 → 204 not taken.
✓ Branch 118 → 119 taken 2 times.
✗ Branch 118 → 202 not taken.
✓ Branch 121 → 122 taken 2 times.
✗ Branch 121 → 208 not taken.
✓ Branch 122 → 123 taken 2 times.
✗ Branch 122 → 208 not taken.
4 SOFT_ERROR_ER(node, REFERENCED_UNDEFINED_VARIABLE,
853 "The symbol '" + node->fqIdentifier + "' could not be found. Missing 'this.' prefix?")
854
855 // Ensure that the entry is public, if the symbol is imported.
856 // An exception are enum items. There it is sufficient, that the enum itself is public.
857
7/8
✓ Branch 125 → 126 taken 570573 times.
✗ Branch 125 → 218 not taken.
✓ Branch 126 → 127 taken 12434 times.
✓ Branch 126 → 129 taken 558139 times.
✓ Branch 127 → 128 taken 1232 times.
✓ Branch 127 → 129 taken 11202 times.
✓ Branch 130 → 131 taken 1232 times.
✓ Branch 130 → 144 taken 569341 times.
570573 if (accessScope->isImportedBy(rootScope) && accessScope->type != ScopeType::ENUM)
858
5/8
✓ Branch 131 → 132 taken 1232 times.
✗ Branch 131 → 209 not taken.
✓ Branch 132 → 133 taken 1232 times.
✗ Branch 132 → 209 not taken.
✓ Branch 133 → 134 taken 1232 times.
✗ Branch 133 → 209 not taken.
✓ Branch 134 → 135 taken 2 times.
✓ Branch 134 → 144 taken 1230 times.
1232 if (!entry->getQualType().getBase().isPublic())
859
5/10
✓ Branch 135 → 136 taken 2 times.
✗ Branch 135 → 214 not taken.
✓ Branch 136 → 137 taken 2 times.
✗ Branch 136 → 212 not taken.
✓ Branch 137 → 138 taken 2 times.
✗ Branch 137 → 210 not taken.
✓ Branch 140 → 141 taken 2 times.
✗ Branch 140 → 216 not taken.
✓ Branch 141 → 142 taken 2 times.
✗ Branch 141 → 216 not taken.
4 SOFT_ERROR_ER(node, INSUFFICIENT_VISIBILITY, "Cannot access '" + entry->name + "' due to its private visibility")
860
861 // For enum item access, use access scope of the enum
862
2/2
✓ Branch 144 → 145 taken 17737 times.
✓ Branch 144 → 146 taken 552834 times.
570571 if (entry->scope->type == ScopeType::ENUM)
863 17737 accessScope = entry->scope;
864
865 // For struct access, use access scope of the struct
866
3/4
✓ Branch 146 → 147 taken 570571 times.
✗ Branch 146 → 218 not taken.
✓ Branch 147 → 148 taken 264037 times.
✓ Branch 147 → 156 taken 306534 times.
570571 if (baseType.is(TY_STRUCT)) {
867
1/2
✓ Branch 148 → 149 taken 264037 times.
✗ Branch 148 → 218 not taken.
264037 const std::string &structName = baseType.getSubType();
868
1/2
✓ Branch 149 → 150 taken 264037 times.
✗ Branch 149 → 218 not taken.
264037 const NameRegistryEntry *nameRegistryEntry = sourceFile->getNameRegistryEntry(structName);
869 // The struct may only be reachable transitively (i.e. it is not present in this file's name registry under
870 // its unqualified name). In that case, fall back to the struct's body scope, which is exactly what the
871 // registry entry's targetScope would point to (see SymbolTableBuilder struct registration). This keeps
872 // member access working across deep transitive imports instead of crashing.
873
3/4
✓ Branch 150 → 151 taken 263811 times.
✓ Branch 150 → 152 taken 226 times.
✓ Branch 152 → 153 taken 226 times.
✗ Branch 152 → 218 not taken.
264037 accessScope = nameRegistryEntry != nullptr ? nameRegistryEntry->targetScope : baseType.getBodyScope();
874
1/2
✗ Branch 154 → 155 not taken.
✓ Branch 154 → 156 taken 264037 times.
264037 assert(accessScope != nullptr);
875 }
876
877
2/4
✓ Branch 156 → 157 taken 570571 times.
✗ Branch 156 → 217 not taken.
✓ Branch 157 → 158 taken 570571 times.
✗ Branch 157 → 217 not taken.
1141142 return ExprResult{node->setEvaluatedSymbolType(varType, manIdx), entry};
878 }
879
880 } // namespace spice::compiler
881