Skip to main content

rucc_sema/
check.rs

1//! The pass: what walks the untyped tree and builds the typed one.
2//!
3//! Design: `spec/07-types-and-semantics.md`.
4//!
5//! The shape is the parser's, because the job is the same shape: a context of the things that do
6//! not change, a walk that holds the things that do, and one structure handed back at the end.
7//! What is different is that this walk has two trees, one it reads and one it writes, and the
8//! reason a node is copied across rather than annotated in place is that the two are not the
9//! same tree. A `p->x` is one node in the source and two here, an `int` meeting a `long` is
10//! three, and an array used as a pointer is a node that the source does not contain at all.
11//!
12//! # What is here so far
13//!
14//! Expressions, which is where the constraints of 6.5 live. The ones that name a type are in
15//! `check/expr/typeop.rs` and the rest are in `check/expr.rs`, which is a split by what the two
16//! do rather than by size: an operator that names a type asks the type builder a question first
17//! and most of them answer with a constant. The two that build an object rather than producing a
18//! value, which are the compound literal and GNU's cast to a union type, are in `check/init.rs`
19//! with the rest of initialization.
20//!
21//! Declarations, in `check/decl.rs`, which is what decides the linkage, the storage duration and
22//! the definition state of each name and what reconciles the declarations that share one. The
23//! type a declaration declares comes from `check/ty.rs`, through [`Checker::declared_type`] and
24//! [`Checker::type_name`], which fold a declarator onto the type a specifier list named.
25//!
26//! Statements, in `check/stmt.rs`, which is the one walk here that carries state: what encloses
27//! a statement is what decides whether it is allowed. The function definition is there too, since
28//! a body is the only thing a statement list is ever part of, and so is [`Checker::check_unit`],
29//! which walks a whole translation unit.
30//!
31//! Initialization, in `check/init.rs`, which turns the tree an initializer was parsed into
32//! into a flat list of what goes where. It is its own module because it is its own algorithm:
33//! a cursor over the object being initialized rather than a walk over the source, which is what
34//! makes brace elision, designation and a string literal filling an array all the same thing
35//! seen from different places. The compound literal is there too, since an unnamed object with
36//! an initializer is what it is.
37//!
38//! Folding is reachable from here through [`Checker::eval_constant`] and
39//! [`Checker::eval_integer`], and the checking asks for it in seven places: a narrowing
40//! conversion that changes the value, an `alignas`, a `static_assert`, the initializer of a
41//! `constexpr` object, a case label, the index of a designation, and each element of an
42//! initializer for an object that exists before the program runs.
43//!
44//! # Poisoning
45//!
46//! The rule is the parser's, in `spec/06-lexer-and-parser.md` section 6.8, and it is the same
47//! rule for the same reason. An expression that has been diagnosed becomes
48//! [`ExprKind::Error`](crate::ExprKind::Error), and an operator whose operand is poisoned is
49//! poisoned in turn without a word said about it. That is what keeps one undeclared name from
50//! producing an error for every operator it appears under, and it is why nothing below asks
51//! whether an error has already been reported: it asks whether the node in its hand is one.
52
53use rucc_ast::Ast;
54use rucc_base::{Interner, Symbol};
55use rucc_diag::{DEFAULT_ERROR_LIMIT, Diagnostic, Errors, Span};
56use rucc_session::Std;
57use rucc_target::TargetInfo;
58use rucc_types::{ArrayLen, IntKind, TypeId, TypeKind, Types, int_width};
59
60use crate::convert::Conv;
61use crate::decl::{Decl, DeclId, DeclKind, DeclList, Definition, Linkage, StorageDuration};
62use crate::eval::{Eval, NotConstant};
63use crate::expr::{Category, Expr, ExprId, ExprKind};
64use crate::scope::Scopes;
65use crate::tast::{Const, Tast};
66
67mod attr;
68mod builtin;
69mod decl;
70mod expr;
71mod init;
72mod stmt;
73mod ty;
74
75/// What the checking needs and does not change.
76#[derive(Debug, Clone, Copy)]
77pub struct Context<'a> {
78    /// The spellings, for the diagnostics that name an identifier.
79    pub names: &'a Interner,
80    /// What the target's types are, which every layout and every promotion is decided by.
81    pub target: &'a TargetInfo,
82    /// The dialect.
83    pub std: Std,
84    /// Whether the GNU extensions are on.
85    pub gnu: bool,
86    /// Whether `-pedantic` was given.
87    pub pedantic: bool,
88    /// How many errors to report before stopping, with zero meaning no limit.
89    pub error_limit: usize,
90}
91
92impl<'a> Context<'a> {
93    /// A context with the defaults, for a caller that has an interner and a target to hand.
94    #[must_use]
95    pub fn new(names: &'a Interner, target: &'a TargetInfo, std: Std) -> Context<'a> {
96        Context { names, target, std, gnu: true, pedantic: false, error_limit: DEFAULT_ERROR_LIMIT }
97    }
98}
99
100/// What one run of the checking produced.
101#[derive(Debug)]
102pub struct Checked {
103    /// The typed tree, which holds poisoned nodes where the source did not check.
104    pub tast: Tast,
105    /// The types, which the tree points into and which outlive it.
106    pub types: Types,
107    /// What went wrong, in the order it was found.
108    pub diagnostics: Vec<Diagnostic>,
109}
110
111impl Checked {
112    /// Whether anything was reported at an error severity.
113    #[must_use]
114    pub fn failed(&self) -> bool {
115        self.diagnostics.iter().any(|d| d.severity.is_fatal())
116    }
117}
118
119/// The checking pass.
120#[derive(Debug)]
121pub struct Checker<'a> {
122    pub(crate) ast: &'a Ast,
123    pub(crate) tast: Tast,
124    pub(crate) types: Types,
125    pub(crate) scopes: Scopes,
126    pub(crate) errors: Errors,
127    pub(crate) cx: Context<'a>,
128    /// What the type builder has already worked out, which is in `check/ty.rs` with the code
129    /// that fills it in.
130    pub(crate) built: ty::Built,
131    /// The function body being checked, absent everywhere else. What is in it is in
132    /// `check/stmt.rs`, which is the only code that reads it.
133    pub(in crate::check) body: Option<stmt::Body>,
134    /// The declarations whose initializers are being checked and whose types or values are not
135    /// known until that finishes, which is what C23 calls underspecified. A name is in scope
136    /// inside its own initializer, so this is what tells a reference to one from a use of the
137    /// object it will become. Nested, because a statement expression may declare another.
138    pub(in crate::check) underspecified: Vec<DeclId>,
139}
140
141impl<'a> Checker<'a> {
142    /// A checker over one untyped tree.
143    #[must_use]
144    pub fn new(ast: &'a Ast, cx: Context<'a>) -> Checker<'a> {
145        Checker {
146            ast,
147            tast: Tast::new(),
148            types: Types::new(),
149            scopes: Scopes::new(),
150            errors: Errors::new(cx.error_limit),
151            cx,
152            built: ty::Built::default(),
153            body: None,
154            underspecified: Vec::new(),
155        }
156    }
157
158    /// Checks a whole translation unit, which is what a compilation does.
159    ///
160    /// The declarations are checked in the order they were written, since that is the order the
161    /// scopes are built in and the order the diagnostics belong in.
162    pub fn check_unit(&mut self) {
163        // Copied out because it is a shared reference with the checker's own lifetime, so holding
164        // it does not borrow the checker that each declaration is checked through.
165        let ast = self.ast;
166        for &decl in ast.top_level() {
167            self.check_decl(decl);
168        }
169    }
170
171    /// Checks one expression and gives back the node it became.
172    ///
173    /// Always gives back a node. An expression that does not check is poisoned rather than
174    /// absent, so that the operators around it are still checked and the diagnostics they would
175    /// produce are still held back.
176    pub fn check_expr(&mut self, id: rucc_ast::ExprId) -> ExprId {
177        self.expr(id)
178    }
179
180    /// Folds a checked expression, reporting whatever the folding itself found wrong.
181    ///
182    /// # Errors
183    ///
184    /// [`NotConstant`] when the expression is not one. It is handed back rather than reported
185    /// because the message names the context: `case label does not reduce to an integer
186    /// constant` and `enumerator value for 'x' is not an integer constant` are two sentences
187    /// about the same failure, and only the caller knows which one to write.
188    pub fn eval_constant(&mut self, expr: ExprId) -> Result<Const, NotConstant> {
189        let mut eval = self.eval();
190        let value = eval.constant(expr);
191        self.absorb(eval.finish());
192        value
193    }
194
195    /// The same, for a context that needs an integer constant expression.
196    ///
197    /// # Errors
198    ///
199    /// [`NotConstant`] when the expression is not one, or is a constant of some other type.
200    pub fn eval_integer(&mut self, expr: ExprId) -> Result<i128, NotConstant> {
201        let mut eval = self.eval();
202        let value = eval.integer(expr);
203        self.absorb(eval.finish());
204        value
205    }
206
207    /// The tree, the types and the diagnostics.
208    #[must_use]
209    pub fn finish(self) -> Checked {
210        Checked { tast: self.tast, types: self.types, diagnostics: self.errors.finish() }
211    }
212
213    /// Declares an object in the current scope without a declaration to read it from.
214    ///
215    /// [`Checker::check_decl`] is what a translation unit goes through. This is for the caller
216    /// that wants to check one expression against names it has decided on itself, which is what
217    /// [`Checker::check_expr`] is for and what the tests here are built on.
218    pub fn declare_object(&mut self, name: Symbol, ty: TypeId, span: Span) -> DeclId {
219        let kind = if rucc_types::is_function(&self.types, ty) {
220            DeclKind::Function
221        } else {
222            DeclKind::Object
223        };
224        let decl = self.tast.decl(
225            Decl {
226                name: Some(name),
227                ty,
228                kind,
229                linkage: Linkage::None,
230                duration: StorageDuration::Automatic,
231                state: Definition::Defined,
232                alignment: None,
233                constant: false,
234                init: None,
235                params: DeclList::EMPTY,
236                body: None,
237            },
238            span,
239        );
240        self.scopes.declare(name, crate::scope::Binding::Decl(decl));
241        decl
242    }
243
244    /// The conversions, over this tree and these types.
245    pub(crate) fn conv(&mut self) -> Conv<'_> {
246        // The target is copied out first because it is a shared reference living as long as the
247        // context, so taking it does not borrow the checker the two mutable ones are taken from.
248        let target = self.cx.target;
249        Conv { tast: &mut self.tast, types: &mut self.types, target }
250    }
251
252    /// The constant folding, over this tree and these types.
253    pub(crate) fn eval(&self) -> Eval<'_> {
254        Eval::new(&self.tast, &self.types, self.cx.target, self.cx.names)
255    }
256
257    /// Reports a diagnostic.
258    pub(crate) fn report(&mut self, diagnostic: Diagnostic) {
259        self.errors.push(diagnostic);
260    }
261
262    /// Reports everything the folding found, which it collects rather than pushing itself
263    /// because it holds the tree while it runs and the error list is beside the tree.
264    pub(crate) fn absorb(&mut self, diagnostics: Vec<Diagnostic>) {
265        for diagnostic in diagnostics {
266            self.errors.push(diagnostic);
267        }
268    }
269
270    /// Whether a checked expression is one that was already the subject of a diagnostic.
271    pub(crate) fn is_poisoned(&self, id: ExprId) -> bool {
272        matches!(self.tast[id].kind, ExprKind::Error)
273    }
274
275    /// A poisoned expression, for the operand that did not check.
276    ///
277    /// Its type is `int` because every node has a type and there is no type meaning "no idea".
278    /// Nothing reads it, since every operator that meets a poisoned operand poisons itself
279    /// before it looks at what type the operand had.
280    pub(crate) fn poison(&mut self, span: Span) -> ExprId {
281        let int = self.types.int(IntKind::Int);
282        self.tast.expr(Expr::new(ExprKind::Error, int, Category::Rvalue), span)
283    }
284
285    /// How a type is written, for a diagnostic that names one.
286    pub(crate) fn spell(&self, ty: TypeId) -> String {
287        rucc_types::spell(&self.types, self.cx.names, ty)
288    }
289
290    /// What a name is spelled, for a diagnostic that quotes one.
291    pub(crate) fn text(&self, name: Symbol) -> &str {
292        self.cx.names.resolve(name)
293    }
294
295    /// `int`, which is the type of every comparison and of `!`.
296    pub(crate) fn int(&self) -> TypeId {
297        self.types.int(IntKind::Int)
298    }
299
300    /// The type `sizeof` and `alignof` answer in, and the one an offset is measured in.
301    ///
302    /// Derived the same way [`Checker::ptrdiff`] is and for the same reason, since `size_t` is
303    /// the unsigned type as wide as a pointer on every target this compiles for and asking the
304    /// widths keeps the two from disagreeing about which one that is.
305    pub(crate) fn size_type(&self) -> TypeId {
306        let width = self.cx.target.pointer_width;
307        for kind in [IntKind::UInt, IntKind::ULong, IntKind::ULongLong] {
308            if int_width(kind, self.cx.target) >= width {
309                return self.types.int(kind);
310            }
311        }
312        self.types.int(IntKind::ULongLong)
313    }
314
315    /// Whether a type's size is worked out where it is reached rather than here.
316    ///
317    /// True for an array whose length is an expression, however deep it is: `int a[n][3]` is one
318    /// and so is `int a[3][n]`. Shared between the operator that measures a type and the
319    /// declaration that has to decide whether the object can live anywhere but the stack.
320    pub(crate) fn is_variable_length(&self, ty: TypeId) -> bool {
321        match self.types.kind(self.types.canonical(ty)) {
322            TypeKind::Array { elem, len } => {
323                matches!(len, ArrayLen::Variable(_)) || self.is_variable_length(elem)
324            }
325            _ => false,
326        }
327    }
328
329    /// Whether a type is variably modified, which is a variable length array or anything built
330    /// out of one.
331    ///
332    /// `int a[n]` is one and so is `int (*p)[n]`, which is where this differs from
333    /// [`Checker::is_variable_length`]: the pointer has the size every pointer has, and the
334    /// thing it points at has a size the program worked out where the declaration was. That is
335    /// why C says a jump may not enter the scope of either of them.
336    pub(crate) fn is_variably_modified(&self, ty: TypeId) -> bool {
337        match self.types.kind(self.types.canonical(ty)) {
338            TypeKind::Array { elem, len } => {
339                matches!(len, ArrayLen::Variable(_)) || self.is_variably_modified(elem)
340            }
341            TypeKind::Pointer(pointee) => self.is_variably_modified(pointee),
342            _ => false,
343        }
344    }
345
346    /// The type of the difference between two pointers.
347    ///
348    /// Derived rather than stored, because `ptrdiff_t` is whatever signed type is as wide as a
349    /// pointer and that is `long` on every LP64 target and `long long` on Windows, which is the
350    /// same fact `long_width` already records. Asking the widths keeps the two from disagreeing.
351    pub(crate) fn ptrdiff(&self) -> TypeId {
352        let width = self.cx.target.pointer_width;
353        for kind in [IntKind::Int, IntKind::Long, IntKind::LongLong] {
354            if int_width(kind, self.cx.target) >= width {
355                return self.types.int(kind);
356            }
357        }
358        self.types.int(IntKind::LongLong)
359    }
360}