1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
/*
* Declarations for semantic checking functions.
*
* Copyright © 2025 Samuel Lidén Borell <samuel@kodafritt.se>
*
* SPDX-License-Identifier: EUPL-1.2+ OR LGPL-2.1-or-later
*/
#ifndef SLUL_SEMCHK_H
#define SLUL_SEMCHK_H
#include "compiler.h"
/**
* Binds types of the subexpressions in an expression, and also performs
* type checks. If the typeref parameter is NULL, then the expr can have
* any result type (but it must have known and non-ambiguous type), and
* if not NULL, then it must be assignment-compatible to the given
* typref.
*/
void typecheck_expr(const struct TypeRef *typeref, struct Expr *expr);
/**
* Determines the type of the expression.
*
* The return value must be free'd by the caller, with typeref_free().
*/
struct TypeRef *determine_expr_typeref(struct Expr *expr);
/**
* Type checks a function call expression. Called by typecheck_expr().
*
* The typescope parameter is used to look up type-scoped identifiers,
* i.e. constructors in this case.
*/
struct TypeRef funccall_check(struct Expr *e, struct Type *typescope);
enum TypeCompatMode {
TC_ASSIGN, /**< Assignment a=b */
TC_COMPARE /**< Comparison */
};
/**
* Checks whether two types are compatible.
*/
void check_type_compat(
const struct TypeRef *tr_a,
const struct TypeRef *tr_b,
enum TypeCompatMode mode);
/** Returns true if a range can only contain one specific value,
i.e. that min==max. */
bool is_const(const struct Range *range);
/** Returns true if an expression is constant */
bool is_expr_const(const struct Expr *expr);
/** Determines the range of an arithmetic operation
given the ranges of the inputs */
struct Range arithmetic_op_range(
enum ExprKind operation,
const struct Range *a,
const struct Range *b);
/** The kind of scope that is being exited */
enum ScopeLeaveKind {
/** Leaving a control statement where exactly one block is executed:
- `if`/`elif` with `else`,
- `switch` with `default` */
SLK_NORMAL,
/** Leaving a control statement that might not be executed
- `if` without `else`
- `switch` without default and where not all values are covered. */
SLK_NON_EXHAUSTIVE,
/** Leaving a loop, that might be executed 0, 1 or multiple times. */
SLK_LOOP
};
/** Semantic checking at the start of a function */
void varstate_function_start(void);
/** Semantic checking when a variable scope is entered */
void varstate_enter_scope(void);
/** Semantic checking when a variable scope is exited */
void varstate_leave_scope(enum ScopeLeaveKind);
/** Semantic checking at `else`/`case` */
void varstate_else(void);
/** Semantic checking at `return` */
void varstate_return(void);
/** Semantic checking at `break` */
void varstate_break(void);
/** Semantic checking at `continue` */
void varstate_continue(void);
/** Semantic checking when a variable is declared */
void varstate_mark_declared(struct Var *var);
/** Semantic checking when a variable is assigned */
void varstate_mark_assigned(struct Var *var);
/** Checks that the given variable is assigned at this point
in semantic checking. */
void varstate_require_assigned(const struct Var *var);
/** Checks that the given variable may be assigned at this point
in semantic checking. */
void varstate_require_modifiable(struct Var *var);
/** Reports a warning if the current statement is unreachable. */
void varstate_warn_if_unreachable(void);
#endif
|