• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rieske / trans / 28379534445

29 Jun 2026 02:29PM UTC coverage: 90.44% (-0.2%) from 90.635%
28379534445

Pull #41

github

rieske
Add file-scope global variables

Introduce a file-scope symbol table with isGlobal ValueEntries and
optional folded constant initializers. Constant expressions are
evaluated in semantics for static init; codegen skips TU-level assigns
and emits globals in .data.

MemoryOperand unifies stack and RIP-relative global addressing in the
instruction set and StackMachine. Globals are always memory-backed
(bindResult stores to [rel name]). The driver maps semantic globals to
a small GlobalVariable DTO for preamble emission and StackMachine
registration.

Functional coverage includes init expressions, shadowing, multi-function
visibility, and compound assignment through globals.
Pull Request #41: Add file-scope global variables

300 of 358 new or added lines in 19 files covered. (83.8%)

8 existing lines in 2 files now uncovered.

5052 of 5586 relevant lines covered (90.44%)

242657.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.54
/src/semantic_analyzer/SemanticAnalysisVisitor.cpp
1
#include "SemanticAnalysisVisitor.h"
2

3
#include <algorithm>
4
#include <stdexcept>
5

6
#include "translation_unit/Context.h"
7
#include "types/Type.h"
8
#include "util/Logger.h"
9
#include "util/LogManager.h"
10

11
namespace semantic_analyzer {
12

13
static const translation_unit::Context EXTERNAL_CONTEXT {"external", 0};
14

15
static Logger& err = LogManager::getErrorLogger();
16

17
SemanticAnalysisVisitor::SemanticAnalysisVisitor() {
360✔
18
    type::Type functionType = type::function(type::signedInteger());
360✔
19
    symbolTable.insertFunction("printf", functionType.getFunction(), EXTERNAL_CONTEXT);
1,080✔
20
    symbolTable.insertFunction("scanf", functionType.getFunction(), EXTERNAL_CONTEXT);
1,080✔
21
}
360✔
22

23
SemanticAnalysisVisitor::~SemanticAnalysisVisitor() {
360✔
24
}
360✔
25

26
void SemanticAnalysisVisitor::printSymbolTable() const {
348✔
27
    symbolTable.printTable();
348✔
28
}
348✔
29

30
void SemanticAnalysisVisitor::visit(ast::DeclarationSpecifiers& declarationSpecifiers) {
1,074✔
31
    // FIXME: this would look so much better
32
    /*for (std::string error : declarationSpecifiers.getSemanticErrors()) {
33
     semanticError(error, globalContext);
34
     }*/
35
    if (declarationSpecifiers.getStorageSpecifiers().size() > 1) {
1,074✔
36
        semanticError("multiple storage classes in declaration specifiers",
×
37
                declarationSpecifiers.getStorageSpecifiers().at(1).getContext());
×
38
    }
39
}
1,074✔
40

41
void SemanticAnalysisVisitor::visit(ast::Declaration& declaration) {
462✔
42
    declaration.visitChildren(*this);
462✔
43

44
    auto baseType = declaration.getDeclarationSpecifiers().getTypeSpecifiers().at(0).getType();
462✔
45
    for (const auto& declarator : declaration.getDeclarators()) {
1,046✔
46
        auto type = declarator->getFundamentalType(baseType);
584✔
47
        if (type.isVoid()) {
584✔
48
            semanticError("variable `" + declarator->getName() + "` declared void", declarator->getContext());
2✔
49
        } else if (symbolTable.isAtFileScope() && symbolTable.hasFunction(declarator->getName())) {
582✔
NEW
50
            semanticError("symbol `" + declarator->getName() + "` declaration conflicts with function of the same name",
×
NEW
51
                    declarator->getContext());
×
52
        } else if (symbolTable.insertSymbol(declarator->getName(), type, declarator->getContext())) {
582✔
53
            declarator->setHolder(symbolTable.lookup(declarator->getName()));
580✔
54
            // TODO: type check initializers
55
            if (declarator->hasInitializer() && symbolTable.isAtFileScope()) {
580✔
56
                long initValue = 0;
28✔
57
                if (declarator->getInitializer()->evaluateConstant(initValue)) {
28✔
58
                    symbolTable.setGlobalInitializer(declarator->getName(), initValue);
28✔
59
                } else {
NEW
60
                    semanticError("global initializer is not a constant expression", declarator->getContext());
×
61
                }
62
            }
63
        } else {
64
            semanticError(
2✔
65
                    "symbol `" + declarator->getName() +
4✔
66
                            "` declaration conflicts with previous declaration on " +
4✔
67
                            to_string(symbolTable.lookup(declarator->getName()).getContext()),
4✔
68
                    declarator->getContext());
4✔
69
        }
70
    }
584✔
71
}
462✔
72

73
void SemanticAnalysisVisitor::visit(ast::Declarator& declarator) {
1,196✔
74
    declarator.visitChildren(*this);
1,196✔
75
}
1,196✔
76

77
void SemanticAnalysisVisitor::visit(ast::InitializedDeclarator& declarator) {
584✔
78
    declarator.visitChildren(*this);
584✔
79
}
584✔
80

81
void SemanticAnalysisVisitor::visit(ast::ArrayAccess& arrayAccess) {
×
82
    arrayAccess.visitLeftOperand(*this);
×
83
    arrayAccess.visitRightOperand(*this);
×
84

85
    auto type = arrayAccess.leftOperandType();
×
86
    if (type.isPointer()) {
×
87
        arrayAccess.setLvalue(symbolTable.createTemporarySymbol(type.dereference()));
×
88
        arrayAccess.setResultSymbol(symbolTable.createTemporarySymbol(type.dereference()));
×
89
    } else {
90
        semanticError("invalid type for operator[]\n", arrayAccess.getContext());
×
91
    }
92
}
×
93

94
void SemanticAnalysisVisitor::visit(ast::FunctionCall& functionCall) {
876✔
95
    functionCall.visitOperand(*this);
876✔
96
    functionCall.visitArguments(*this);
876✔
97

98
    auto functionSymbol = symbolTable.findFunction(functionCall.operandSymbol()->getName());
876✔
99

100
    functionCall.setSymbol(functionSymbol);
876✔
101

102
    auto& arguments = functionCall.getArgumentList();
876✔
103
    if (arguments.size() == functionSymbol.argumentCount()) {
876✔
104
        auto declaredArguments = functionSymbol.arguments();
102✔
105
        for (std::size_t i { 0 }; i < arguments.size(); ++i) {
288✔
106
            const auto& declaredArgument = declaredArguments.at(i);
186✔
107
            const auto& actualArgument = arguments.at(i)->getResultSymbol();
186✔
108
            typeCheck(actualArgument->getType(), declaredArgument, functionCall.getContext());
186✔
109
        }
110

111
        auto returnType = functionSymbol.returnType();
102✔
112
        if (!returnType.isVoid()) {
102✔
113
            functionCall.setResultSymbol(symbolTable.createTemporarySymbol(returnType));
102✔
114
        }
115
    } else if (functionSymbol.getContext() == EXTERNAL_CONTEXT) {
876✔
116
    // FIXME: using EXTERNAL_CONTEXT as a workaround for printf/scanf external functions until varargs are properly implemented
117
        auto returnType = functionSymbol.returnType();
774✔
118
        if (!returnType.isVoid()) {
774✔
119
            functionCall.setResultSymbol(symbolTable.createTemporarySymbol(returnType));
774✔
120
        }
121
    } else {
774✔
122
        semanticError("no match for function " + functionSymbol.getType().to_string(), functionCall.getContext());
×
123
    }
124
}
876✔
125

126
void SemanticAnalysisVisitor::visit(ast::IdentifierExpression& identifier) {
2,560✔
127
    if (symbolTable.hasSymbol(identifier.getIdentifier())) {
2,560✔
128
        identifier.setResultSymbol(symbolTable.lookup(identifier.getIdentifier()));
2,558✔
129
    } else {
130
        semanticError("symbol `" + identifier.getIdentifier() + "` is not defined", identifier.getContext());
2✔
131
    }
132
}
2,560✔
133

134
void SemanticAnalysisVisitor::visit(ast::ConstantExpression& constant) {
1,190✔
135
    constant.setResultSymbol(symbolTable.createTemporarySymbol(constant.getType()));
1,190✔
136
}
1,190✔
137

138
void SemanticAnalysisVisitor::visit(ast::StringLiteralExpression& stringLiteral) {
774✔
139
    std::string constantSymbol = symbolTable.newConstant(stringLiteral.getValue());
774✔
140
    stringLiteral.setConstantSymbol(constantSymbol);
774✔
141
    stringLiteral.setResultSymbol(symbolTable.createTemporarySymbol(stringLiteral.getType()));
774✔
142
}
774✔
143

144
void SemanticAnalysisVisitor::visit(ast::PostfixExpression& expression) {
24✔
145
    expression.visitOperand(*this);
24✔
146

147
    expression.setType(expression.operandType());
24✔
148
    auto operandSymbol = *expression.operandSymbol();
24✔
149
    expression.setResultSymbol(operandSymbol);
24✔
150

151
    auto preOperationSymbolName = operandSymbol.getName() + "_pre";
24✔
152
    symbolTable.insertSymbol(preOperationSymbolName, operandSymbol.getType(), operandSymbol.getContext());
24✔
153
    expression.setPreOperationSymbol(symbolTable.lookup(preOperationSymbolName));
24✔
154

155
    if (!expression.isLval()) {
24✔
156
        semanticError("lvalue required as increment operand", expression.getContext());
×
157
    }
158
}
24✔
159

160
void SemanticAnalysisVisitor::visit(ast::PrefixExpression& expression) {
28✔
161
    expression.visitOperand(*this);
28✔
162

163
    expression.setType(expression.operandType());
28✔
164
    expression.setResultSymbol(*expression.operandSymbol());
28✔
165

166
    if (!expression.isLval()) {
28✔
167
        semanticError("lvalue required as increment operand", expression.getContext());
×
168
    }
169
}
28✔
170

171
void SemanticAnalysisVisitor::visit(ast::UnaryExpression& expression) {
518✔
172
    expression.visitOperand(*this);
518✔
173

174
    switch (expression.getOperator()->getLexeme().front()) {
518✔
175
    case '&':
318✔
176
        expression.setResultSymbol(symbolTable.createTemporarySymbol(type::pointer(expression.operandType())));
318✔
177
        break;
318✔
178
    case '*':
78✔
179
        if (expression.operandType().isPointer()) {
78✔
180
            expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.operandType().dereference()));
78✔
181
            expression.setLvalueSymbol(symbolTable.createTemporarySymbol(expression.operandType()));
78✔
182
        } else {
183
            semanticError("invalid type argument of ‘unary *’ :" + expression.operandType().to_string(), expression.getContext());
×
184
        }
185
        break;
78✔
186
    case '+':
4✔
187
        expression.setResultSymbol(*expression.operandSymbol());
4✔
188
        break;
4✔
189
    case '-':
88✔
190
        expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.operandType()));
88✔
191
        break;
88✔
192
    case '!':
30✔
193
        expression.setResultSymbol(symbolTable.createTemporarySymbol(type::signedInteger()));
30✔
194
        expression.setTruthyLabel(symbolTable.newLabel());
30✔
195
        expression.setFalsyLabel(symbolTable.newLabel());
30✔
196
        break;
30✔
197
    default:
×
198
        throw std::runtime_error { "Unidentified increment operator: " + expression.getOperator()->getLexeme() };
×
199
    }
200
}
518✔
201

202
void SemanticAnalysisVisitor::visit(ast::TypeCast& expression) {
×
203
    expression.visitOperand(*this);
×
204

205
    expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.getType().getType()));
×
206
}
×
207

208
void SemanticAnalysisVisitor::visit(ast::ArithmeticExpression& expression) {
110✔
209
    expression.visitLeftOperand(*this);
110✔
210
    expression.visitRightOperand(*this);
110✔
211

212
    typeCheck(
110✔
213
            expression.leftOperandType(),
220✔
214
            expression.rightOperandType(),
220✔
215
            expression.getContext());
220✔
216
    // FIXME: type conversion
217
    expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.leftOperandType()));
110✔
218
}
110✔
219

220
void SemanticAnalysisVisitor::visit(ast::ShiftExpression& expression) {
20✔
221
    expression.visitLeftOperand(*this);
20✔
222
    expression.visitRightOperand(*this);
20✔
223

224
    if (expression.rightOperandType().isPrimitive() && !expression.rightOperandType().getPrimitive().isFloating()) {
20✔
225
        expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.leftOperandType()));
20✔
226
    } else {
227
        semanticError("argument of type int required for shift expression", expression.getContext());
×
228
    }
229
}
20✔
230

231
void SemanticAnalysisVisitor::visit(ast::ComparisonExpression& expression) {
180✔
232
    expression.visitLeftOperand(*this);
180✔
233
    expression.visitRightOperand(*this);
180✔
234

235
    typeCheck(
180✔
236
            expression.leftOperandType(),
360✔
237
            expression.rightOperandType(),
360✔
238
            expression.getContext());
360✔
239

240
    expression.setResultSymbol(symbolTable.createTemporarySymbol(type::signedInteger()));
180✔
241
    expression.setTruthyLabel(symbolTable.newLabel());
180✔
242
    expression.setFalsyLabel(symbolTable.newLabel());
180✔
243
}
180✔
244

245
void SemanticAnalysisVisitor::visit(ast::BitwiseExpression& expression) {
16✔
246
    expression.visitLeftOperand(*this);
16✔
247
    expression.visitRightOperand(*this);
16✔
248
    expression.setType(expression.leftOperandType());
16✔
249

250
    typeCheck(
16✔
251
            expression.leftOperandType(),
32✔
252
            expression.rightOperandType(),
32✔
253
            expression.getContext());
32✔
254

255
    expression.setResultSymbol(
16✔
256
            symbolTable.createTemporarySymbol(expression.getType()));
32✔
257
}
16✔
258

259
void SemanticAnalysisVisitor::visit(ast::LogicalAndExpression& expression) {
12✔
260
    expression.visitLeftOperand(*this);
12✔
261
    expression.visitRightOperand(*this);
12✔
262

263
    typeCheck(
12✔
264
            expression.leftOperandType(),
24✔
265
            expression.rightOperandType(),
24✔
266
            expression.getContext());
24✔
267

268
    expression.setResultSymbol(symbolTable.createTemporarySymbol(type::signedInteger()));
12✔
269
    expression.setExitLabel(symbolTable.newLabel());
12✔
270
}
12✔
271

272
void SemanticAnalysisVisitor::visit(ast::LogicalOrExpression& expression) {
12✔
273
    expression.visitLeftOperand(*this);
12✔
274
    expression.visitRightOperand(*this);
12✔
275

276
    typeCheck(
12✔
277
            expression.leftOperandType(),
24✔
278
            expression.rightOperandType(),
24✔
279
            expression.getContext());
24✔
280

281
    expression.setResultSymbol(symbolTable.createTemporarySymbol(type::signedInteger()));
12✔
282
    expression.setExitLabel(symbolTable.newLabel());
12✔
283
}
12✔
284

285
void SemanticAnalysisVisitor::visit(ast::AssignmentExpression& expression) {
416✔
286
    expression.visitLeftOperand(*this);
416✔
287
    expression.visitRightOperand(*this);
416✔
288

289
    if (expression.isLval()) {
416✔
290
        typeCheck(
412✔
291
                expression.leftOperandType(),
824✔
292
                expression.rightOperandType(),
824✔
293
                expression.getContext());
824✔
294

295
        expression.setResultSymbol(*expression.leftOperandSymbol());
412✔
296
    } else {
297
        semanticError("lvalue required on the left side of assignment", expression.getContext());
12✔
298
    }
299
}
416✔
300

301
void SemanticAnalysisVisitor::visit(ast::ExpressionList& expression) {
2✔
302
    expression.visitLeftOperand(*this);
2✔
303
    expression.visitRightOperand(*this);
2✔
304
    // Comma operator: value and type of the right operand
305
    expression.setType(expression.rightOperandType());
2✔
306
    expression.setResultSymbol(*expression.rightOperandSymbol());
2✔
307
}
2✔
308

309
void SemanticAnalysisVisitor::visit(ast::Operator&) {
×
310
}
×
311

312
void SemanticAnalysisVisitor::visit(ast::JumpStatement& statement) {
×
313
    throw std::runtime_error { "not implemented" };
×
314
}
315

316
void SemanticAnalysisVisitor::visit(ast::ReturnStatement& statement) {
432✔
317
    statement.returnExpression->accept(*this);
432✔
318
}
432✔
319

320
void SemanticAnalysisVisitor::visit(ast::VoidReturnStatement& statement) {
16✔
321
}
16✔
322

323
void SemanticAnalysisVisitor::visit(ast::IfStatement& statement) {
20✔
324
    statement.testExpression->accept(*this);
20✔
325
    statement.body->accept(*this);
20✔
326

327
    statement.setFalsyLabel(symbolTable.newLabel());
20✔
328
}
20✔
329

330
void SemanticAnalysisVisitor::visit(ast::IfElseStatement& statement) {
16✔
331
    statement.testExpression->accept(*this);
16✔
332
    statement.truthyBody->accept(*this);
16✔
333
    statement.falsyBody->accept(*this);
16✔
334

335
    statement.setFalsyLabel(symbolTable.newLabel());
16✔
336
    statement.setExitLabel(symbolTable.newLabel());
16✔
337
}
16✔
338

339
void SemanticAnalysisVisitor::visit(ast::LoopStatement& loop) {
50✔
340
    loop.header->accept(*this);
50✔
341
    loop.body->accept(*this);
50✔
342
}
50✔
343

344
void SemanticAnalysisVisitor::visit(ast::ForLoopHeader& loopHeader) {
30✔
345
    if (loopHeader.initialization) {
30✔
346
        loopHeader.initialization->accept(*this);
20✔
347
    }
348
    if (loopHeader.clause) {
30✔
349
        loopHeader.clause->accept(*this);
22✔
350
    }
351
    if (loopHeader.increment) {
30✔
352
        loopHeader.increment->accept(*this);
22✔
353
    }
354

355
    loopHeader.setLoopEntry(symbolTable.newLabel());
30✔
356
    loopHeader.setLoopExit(symbolTable.newLabel());
30✔
357
}
30✔
358

359
void SemanticAnalysisVisitor::visit(ast::WhileLoopHeader& loopHeader) {
20✔
360
    loopHeader.clause->accept(*this);
20✔
361

362
    loopHeader.setLoopEntry(symbolTable.newLabel());
20✔
363
    loopHeader.setLoopExit(symbolTable.newLabel());
20✔
364
}
20✔
365

366
void SemanticAnalysisVisitor::visit(ast::Pointer&) {
58✔
367
}
58✔
368

369
void SemanticAnalysisVisitor::visit(ast::Identifier&) {
750✔
370
}
750✔
371

372
void SemanticAnalysisVisitor::visit(ast::ArrayDeclarator& declaration) {
×
373
    declaration.subscriptExpression->accept(*this);
×
374
    throw std::runtime_error { "not implemented" };
×
375
}
376

377
void SemanticAnalysisVisitor::visit(ast::FunctionDeclarator& declarator) {
446✔
378
    declarator.visitFormalArguments(*this);
446✔
379

380
    argumentNames.clear();
446✔
381
    std::vector<type::Type> arguments;
446✔
382
    for (auto& argumentDeclaration : declarator.getFormalArguments()) {
612✔
383
        arguments.push_back(argumentDeclaration.getType());
166✔
384
        argumentNames.push_back(argumentDeclaration.getName());
166✔
385
    }
386

387
    // FIXME: return type is not known at this point!
388
    type::Type functionType = type::function(type::signedInteger(), arguments);
446✔
389
    if (symbolTable.hasGlobalVariable(declarator.getName())) {
446✔
390
        semanticError("function `" + declarator.getName() + "` conflicts with global variable of the same name",
2✔
391
                declarator.getContext());
4✔
392
        return;
2✔
393
    }
394
    FunctionEntry functionEntry = symbolTable.insertFunction(
395
            declarator.getName(),
888✔
396
            functionType.getFunction(),
888✔
397
            declarator.getContext());
1,332✔
398

399
    if (functionEntry.getContext() != declarator.getContext()) {
444✔
400
        semanticError("function `" + declarator.getName() + "` definition conflicts with previous one on "
×
401
                + to_string(functionEntry.getContext()), declarator.getContext());
×
402
    }
403
}
448✔
404

405
void SemanticAnalysisVisitor::visit(ast::FormalArgument& argument) {
166✔
406
    argument.visitSpecifiers(*this);
166✔
407
    argument.visitDeclarator(*this);
166✔
408
    if (argument.getType().isVoid()) {
166✔
409
        semanticError("function argument ‘" + argument.getName() + "’ declared void", argument.getDeclarationContext());
×
410
    }
411
}
166✔
412

413
void SemanticAnalysisVisitor::visit(ast::FunctionDefinition& function) {
446✔
414
    function.visitReturnType(*this);
446✔
415
    function.visitDeclarator(*this);
446✔
416

417
    if (!symbolTable.hasFunction(function.getName())) {
446✔
418
        return;
2✔
419
    }
420
    function.setSymbol(symbolTable.findFunction(function.getName()));
444✔
421
    symbolTable.startFunction(function.getName(), argumentNames);
444✔
422
    // Parameters and outermost body declarations share one scope (C); do not enterBlockScope.
423
    function.visitBodyChildren(*this);
444✔
424
    function.setArguments(symbolTable.getCurrentScopeArguments());
444✔
425
    function.setLocalVariables(symbolTable.getCurrentScopeSymbols());
444✔
426
    symbolTable.endFunction();
444✔
427
}
428

429
void SemanticAnalysisVisitor::visit(ast::Block& block) {
112✔
430
    symbolTable.enterBlockScope();
112✔
431
    block.visitChildren(*this);
112✔
432
    symbolTable.exitBlockScope();
112✔
433
}
112✔
434

435
void SemanticAnalysisVisitor::typeCheck(const type::Type& typeFrom, const type::Type& typeTo,
928✔
436
        const translation_unit::Context& context)
437
{
438
    if (!typeTo.canAssignFrom(typeFrom)) {
928✔
439
        semanticError("type mismatch: can't convert " + typeFrom.to_string() + " to " + typeTo.to_string(), context);
×
440
    }
441
}
928✔
442

443
void SemanticAnalysisVisitor::semanticError(std::string message, const translation_unit::Context& context) {
12✔
444
    containsSemanticErrors = true;
12✔
445
    err << context << ": error: " << message << "\n";
12✔
446
}
12✔
447

448
bool SemanticAnalysisVisitor::successfulSemanticAnalysis() const {
360✔
449
    return !containsSemanticErrors;
360✔
450
}
451

452
std::map<std::string, std::string> SemanticAnalysisVisitor::getConstants() const {
348✔
453
    return symbolTable.getConstants();
348✔
454
}
455

456
std::vector<ValueEntry> SemanticAnalysisVisitor::getGlobalVariables() const {
348✔
457
    return symbolTable.getGlobalVariables();
348✔
458
}
459

460
} // namespace semantic_analyzer
461

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc