GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
68 / 68
100.0%
Branches:
10 / 10, 44 excl
100.0%
Functions:
18 / 18
100.0%

typechecker/post-type-checking-verifier.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/type/any";
5
6 // Own imports
7 import "@/compiler-pass";
8 import "@/ast/abstract-ast-visitor-intf";
9 import "@/ast/ast-nodes";
10 import "@/global/global-resource-manager";
11 import "@/model/function";
12 import "@/source-file";
13 import "@/symboltablebuilder/scope";
14 import "@/symboltablebuilder/symbol-table-entry";
15 import "@/typechecker/builtin-functions";
16
17 /**
18 * Verifies that the TypeChecker has fully annotated the AST before codegen begins.
19 *
20 * Runs only in debug builds (guarded by the spice.is_debug build var in source-file.spice). Asserts
21 * that every field the IRGenerator reads — entry pointers, per-manifestation vectors,
22 * FctCallData::callee — is non-nil and correctly sized. A failure here means a TC
23 * regression left a gap in the semantic model, which would otherwise surface as a
24 * cryptic nil-dereference inside the IRGenerator.
25 */
26 public type PostTypeCheckingVerifier struct : IAbstractAstVisitor {
27 compose public CompilerPass compilerPass
28 }
29
30 /**
31 * Construct a verifier for the given source file
32 *
33 * @param resourceManager Global resource manager
34 * @param sourceFile Source file, whose AST is verified
35 */
36 3696 public p PostTypeCheckingVerifier.ctor(GlobalResourceManager& resourceManager, SourceFile* sourceFile) {
37 3696 this.compilerPass = CompilerPass(resourceManager, sourceFile);
38 3696 }
39
40 /**
41 * Verify that the type checker fully annotated the given AST. Fails an assertion otherwise.
42 *
43 * @param ast Root node of the AST to verify
44 */
45 3696 public p PostTypeCheckingVerifier.verify(ASTEntryNode* ast) {
46 3696 this.visit(ast);
47 3696 }
48
49 // ======================================================== Verification ========================================================
50
51 28513 public f<Any> PostTypeCheckingVerifier.visitFctDef(ASTFctDefNode* node) {
52 28513 assert node.node.entry != nil<SymbolTableEntry*>;
53 28513 assert !node.node.manifestations.isEmpty();
54 28513 return this.visitChildren(cast<ASTNode*>(node));
55 }
56
57 17644 public f<Any> PostTypeCheckingVerifier.visitProcDef(ASTProcDefNode* node) {
58 17644 assert node.node.entry != nil<SymbolTableEntry*>;
59 17644 assert !node.node.manifestations.isEmpty();
60 17644 return this.visitChildren(cast<ASTNode*>(node));
61 }
62
63 3136 public f<Any> PostTypeCheckingVerifier.visitStructDef(ASTStructDefNode* node) {
64 3136 assert node.entry != nil<SymbolTableEntry*>;
65 3136 return this.visitChildren(cast<ASTNode*>(node));
66 }
67
68 297 public f<Any> PostTypeCheckingVerifier.visitInterfaceDef(ASTInterfaceDefNode* node) {
69 297 assert node.entry != nil<SymbolTableEntry*>;
70 297 return this.visitChildren(cast<ASTNode*>(node));
71 }
72
73 17 public f<Any> PostTypeCheckingVerifier.visitUnionDef(ASTUnionDefNode* node) {
74 17 assert node.entry != nil<SymbolTableEntry*>;
75 17 return this.visitChildren(cast<ASTNode*>(node));
76 }
77
78 443 public f<Any> PostTypeCheckingVerifier.visitEnumDef(ASTEnumDefNode* node) {
79 443 assert node.entry != nil<SymbolTableEntry*>;
80 443 return this.visitChildren(cast<ASTNode*>(node));
81 }
82
83 153 public f<Any> PostTypeCheckingVerifier.visitAliasDef(ASTAliasDefNode* node) {
84 153 assert node.entry != nil<SymbolTableEntry*>;
85 153 return this.visitChildren(cast<ASTNode*>(node));
86 }
87
88 4259 public f<Any> PostTypeCheckingVerifier.visitGlobalVarDef(ASTGlobalVarDefNode* node) {
89 4259 assert node.entry != nil<SymbolTableEntry*>;
90 4259 return this.visitChildren(cast<ASTNode*>(node));
91 }
92
93 699 public f<Any> PostTypeCheckingVerifier.visitSignature(ASTSignatureNode* node) {
94 699 assert node.entry != nil<SymbolTableEntry*>;
95 699 return this.visitChildren(cast<ASTNode*>(node));
96 }
97
98 83700 public f<Any> PostTypeCheckingVerifier.visitDeclStmt(ASTDeclStmtNode* node) {
99 // entries is resized to manifestationCount; unsubstantiated generic slots remain nil.
100 // We can only assert the vector was populated at all.
101
2/2
✓ Branch 0 taken 431 times.
✓ Branch 1 taken 83269 times.
83700 if !node.isForEachItem {
102 83269 assert !node.entries.isEmpty();
103 83269 }
104 83700 return this.visitChildren(cast<ASTNode*>(node));
105 }
106
107 398591 public f<Any> PostTypeCheckingVerifier.visitAtomicExpr(ASTAtomicExprNode* node) {
108 // data is only populated for identifier accesses (not for constants / nested exprs).
109 // Within identifier accesses, only substantiated manifestation slots have accessScope set;
110 // unsubstantiated generic slots are left at the default-initialized nil.
111
2/2
✓ Branch 0 taken 168633 times.
✓ Branch 1 taken 229958 times.
398591 if !node.fqIdentifier.isEmpty() {
112 229958 assert !node.data.isEmpty();
113
2/2
✓ Branch 0 taken 229958 times.
✓ Branch 1 taken 326898 times.
556856 foreach const VarAccessData& d : node.data {
114 326898 assert d.accessScope == nil<Scope*> || d.entry != nil<SymbolTableEntry*>;
115 326898 }
116 229958 }
117 398591 return this.visitChildren(cast<ASTNode*>(node));
118 }
119
120 82507 public f<Any> PostTypeCheckingVerifier.visitFctCall(ASTFctCallNode* node) {
121 // Builtin calls (printf, len, panic, etc.) return early in the TypeChecker before
122 // populating callee/calleeParentScope — skip them, matching the IRGenerator's behavior.
123
2/2
✓ Branch 0 taken 10596 times.
✓ Branch 1 taken 71911 times.
82507 if isBuiltinFunction(node.fqFunctionName) {
124 10596 return this.visitChildren(cast<ASTNode*>(node));
125 }
126
127 71911 assert !node.data.isEmpty();
128 // calleeParentScope is set for every visited non-fct-ptr non-builtin slot; use it as the
129 // "was this slot actually resolved?" indicator. Unsubstantiated/uncompiled slots have it nil.
130
2/2
✓ Branch 0 taken 71911 times.
✓ Branch 1 taken 94062 times.
165973 foreach const FctCallData& d : node.data {
131 94062 assert d.calleeParentScope == nil<Scope*> || d.callee != nil<const Function*>;
132 94062 }
133 71911 return this.visitChildren(cast<ASTNode*>(node));
134 }
135
136 1462 public f<Any> PostTypeCheckingVerifier.visitStructInstantiation(ASTStructInstantiationNode* node) {
137 // instantiatedStructs is resized to manifestationCount; unsubstantiated generic slots remain nil.
138 // We can only safely assert the vector was populated — element-level nil checks would false-positive
139 // on unsubstantiated generic manifestation slots.
140 1462 assert !node.instantiatedStructs.isEmpty();
141 1462 return this.visitChildren(cast<ASTNode*>(node));
142 }
143
144 61 public f<Any> PostTypeCheckingVerifier.visitLambdaFunc(ASTLambdaFuncNode* node) {
145 61 assert !node.node.manifestations.isEmpty();
146 61 return this.visitChildren(cast<ASTNode*>(node));
147 }
148
149 71 public f<Any> PostTypeCheckingVerifier.visitLambdaProc(ASTLambdaProcNode* node) {
150 71 assert !node.node.manifestations.isEmpty();
151 71 return this.visitChildren(cast<ASTNode*>(node));
152 }
153
154 4 public f<Any> PostTypeCheckingVerifier.visitLambdaExpr(ASTLambdaExprNode* node) {
155 4 assert !node.node.manifestations.isEmpty();
156 4 return this.visitChildren(cast<ASTNode*>(node));
157 }
158