Skip to main content

rucc_sema/check/
stmt.rs

1//! Statements: what happens, in what order, and where control is allowed to go instead.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.14.
4//!
5//! An expression is checked against the types of its operands and nothing else, which is why the
6//! expression checking is a walk with no state in it. A statement is not. Whether `break` is
7//! allowed depends on what encloses it, what `return` may carry depends on the function it is in,
8//! and a `goto` may name a label that is fifty lines further down. So this walk carries a [`Body`]
9//! for as long as it is inside one, and everything a statement needs to know that is not in the
10//! statement itself is in there.
11//!
12//! # Labels are resolved over the whole function and not in order
13//!
14//! A label is one namespace, scoped to the function, and a `goto` is allowed to come first. So a
15//! label is created where its name is first met, whether that is the `goto` or the label itself,
16//! and the statement it names is filled in later. What is left over at the end of the function is
17//! the labels that were used and never defined, which is the one diagnostic here that cannot be
18//! written where it is found.
19//!
20//! GNU's `__label__` is the exception: it declares a label local to the block, which is what lets
21//! a macro that jumps to its own end be expanded twice in one function without the two colliding.
22//! Those are undone when the block ends, which is what the saved bindings in the body are for.
23//!
24//! # Why the case table is patched
25//!
26//! A `switch` holds its cases as a run, so that the walk to the IR builds a jump table from a
27//! table rather than by searching the body for labels. The run is not known until the body has
28//! been walked, and the `case` statements in the body are built while it is being walked, so each
29//! of them is written with a placeholder and given its real entry once the run exists. Collecting
30//! the whole run at the end is also what keeps a nested `switch` from interleaving its cases with
31//! the ones outside it, since each `switch` adds its cases in one go.
32//!
33//! # What is not here
34//!
35//! Reachability. `control reaches end of non-void function` and the unreachable code warnings are
36//! questions about a control flow graph, and the answer to them is in the IR rather than in the
37//! tree, so they wait for it. A label that is defined and never used is a warning gcc only gives
38//! under `-Wall`, and it waits for the flag rather than for anything here.
39
40use std::collections::{HashMap, HashSet};
41use std::mem;
42
43use rucc_ast::{self as ast, AsmQuals, ForInit, StorageClass};
44use rucc_base::Symbol;
45use rucc_diag::{Diagnostic, Span};
46use rucc_lex::{Encoding, Remarks, StringLiteral};
47use rucc_session::Std;
48use rucc_types::{IntegerInfo, Qualifiers, TypeId, is_integer, is_pointer, is_record, is_void};
49
50use crate::asm::{Asm, AsmOperand, AsmOperandList, LabelList};
51use crate::check::Checker;
52use crate::check::expr::Target;
53use crate::decl::{DeclId, DeclList};
54use crate::eval;
55use crate::expr::{Category, Expr, ExprId, ExprKind};
56use crate::stmt::{Case, Stmt, StmtId};
57use crate::tast::{Const, Label, LabelId, StrId};
58
59/// The spellings that stand for the name of the function they are written in. The first is the
60/// one C99 added and the other two are GNU's, which are the same thing in C and differ only in
61/// C++, where the pretty one spells out the signature.
62pub(in crate::check) const FUNCTION_NAMES: [&str; 3] =
63    ["__func__", "__FUNCTION__", "__PRETTY_FUNCTION__"];
64
65/// What the statements of one function body are checked against.
66#[derive(Debug)]
67pub(in crate::check) struct Body {
68    /// The return type, which every `return` in it answers to.
69    ret: TypeId,
70    /// Where the function was named, for the `declared here` note under a `return` that
71    /// disagrees with the return type.
72    at: Span,
73    /// Whether the parameter list ends in `...`, which is what says whether there is anything
74    /// for a `va_start` in here to start reading.
75    variadic: bool,
76    /// The last named parameter, which is what `va_start`'s second argument ought to name.
77    last_param: Option<DeclId>,
78    /// Every parameter, which is what tells an assignment to one of them from an assignment to
79    /// a local: gcc says `read-only parameter` for the first and `read-only variable` for the
80    /// second, and there is nothing on a declaration itself that says which it is.
81    params: DeclList,
82    /// The name the definition was written with, which is what `__func__` answers.
83    name: Option<Symbol>,
84    /// The string each of the three spellings was made into, so that every mention of one of them
85    /// in a function is one object rather than one per use. They are three objects and not one,
86    /// because gcc gives each spelling its own and a program is allowed to notice: comparing
87    /// `__func__` with `__FUNCTION__` there is false.
88    func_name: [Option<StrId>; FUNCTION_NAMES.len()],
89    /// The labels of the function, by the name they were written with.
90    labels: HashMap<Symbol, Labelled>,
91    /// What the enclosing blocks bound the names of their `__label__` declarations to, so that a
92    /// block-local label can be undone when the block ends.
93    shadowed: Vec<(Symbol, Option<Labelled>)>,
94    /// Where each enclosing block's run of those starts.
95    blocks: Vec<usize>,
96    /// The `switch` statements this one is inside, innermost last.
97    switches: Vec<Switch>,
98    /// How many loops it is inside, which is what `continue` asks and half of what `break` asks.
99    loops: usize,
100    /// The names this function has already been told about, so that a name nobody declared is
101    /// reported once rather than once per use. The message says `first use in this function`
102    /// and gcc means it: a typo in a loop body is one mistake however many times it is written.
103    undeclared: HashSet<Symbol>,
104}
105
106/// What a body is opened with, which is what the enclosing function says about itself.
107#[derive(Debug, Clone, Copy)]
108pub(in crate::check) struct Enclosing {
109    /// The return type, which every `return` answers to.
110    pub ret: TypeId,
111    /// Where the function was named.
112    pub at: Span,
113    /// Whether the parameter list ends in `...`.
114    pub variadic: bool,
115    /// The last named parameter, absent when there are none.
116    pub last_param: Option<DeclId>,
117    /// Every parameter of the definition, empty for a body that is not one.
118    pub params: DeclList,
119    /// The name the function was written with, absent for a body that is not a definition.
120    pub name: Option<Symbol>,
121}
122
123impl Enclosing {
124    /// A function returning `ret` and saying nothing else about itself, which is what a caller
125    /// that has a statement rather than a definition in its hand has.
126    pub(in crate::check) fn returning(ret: TypeId) -> Enclosing {
127        Enclosing {
128            ret,
129            at: Span::DUMMY,
130            variadic: false,
131            last_param: None,
132            params: DeclList::EMPTY,
133            name: None,
134        }
135    }
136}
137
138/// One label of a function.
139#[derive(Debug, Clone, Copy)]
140struct Labelled {
141    /// The label in the typed tree, made where the name was first met.
142    id: LabelId,
143    /// Whether the statement it names has been seen, and where the label was written.
144    defined: Option<Span>,
145    /// Where the name was first met, which is what an undefined label is reported at.
146    at: Span,
147}
148
149/// One `switch` being checked, and the case table it is collecting.
150#[derive(Debug)]
151struct Switch {
152    /// The promoted type of the controlling expression, which every case value is held in.
153    ty: TypeId,
154    /// The shape of the type before that promotion, which is the range a case value is warned
155    /// about for leaving. gcc measures against what was written rather than against what the
156    /// promotion widened it to, so `case 300` on a `char` is worth saying even though 300 is a
157    /// perfectly good `int`.
158    range: Option<IntegerInfo>,
159    /// The cases so far, in the order they were written.
160    cases: Vec<Case>,
161    /// Where each of them was written, for the note under a duplicate.
162    spans: Vec<Span>,
163    /// The statements those cases label, which are patched with their table entries once the
164    /// table exists. Each one says which entry is its own, so the order here does not matter.
165    labels: Vec<StmtId>,
166    /// The `default`, and where it was written, once one has been seen.
167    default: Option<(StmtId, Span)>,
168}
169
170impl Checker<'_> {
171    /// Checks one statement, as though it were the body of a function returning `ret`.
172    ///
173    /// The entry for a caller that has a statement rather than a translation unit, which is what
174    /// the tests here are built on. A body is opened around it and closed after, so that the
175    /// labels are resolved and reported the way they are in a real function.
176    pub fn check_stmt(&mut self, ret: TypeId, id: ast::StmtId) -> StmtId {
177        let previous = self.open_body(Enclosing::returning(ret));
178        let stmt = self.stmt(id);
179        self.close_body(previous);
180        stmt
181    }
182
183    /// Checks one statement and gives back the node it became.
184    pub(in crate::check) fn stmt(&mut self, id: ast::StmtId) -> StmtId {
185        let span = self.ast.stmt_span(id);
186        let node = match self.ast[id] {
187            ast::Stmt::Error => Stmt::Error,
188            ast::Stmt::Empty => Stmt::Empty,
189            ast::Stmt::Expr(value) => {
190                let value = self.expr(value);
191                Stmt::Expr(self.value(value))
192            }
193            ast::Stmt::Decl(decl) => Stmt::Decls(self.check_decl(decl)),
194            ast::Stmt::Compound(body) => Stmt::Block(self.block(body)),
195            ast::Stmt::If { cond, then, otherwise } => {
196                let cond = self.controlling(cond);
197                let then = self.stmt(then);
198                Stmt::If { cond, then, otherwise: otherwise.map(|id| self.stmt(id)) }
199            }
200            ast::Stmt::Switch { scrutinee, body } => self.switch(scrutinee, body),
201            ast::Stmt::While { cond, body } => {
202                let cond = self.controlling(cond);
203                Stmt::While { cond, body: self.loop_body(body) }
204            }
205            ast::Stmt::DoWhile { body, cond } => {
206                let body = self.loop_body(body);
207                Stmt::DoWhile { body, cond: self.controlling(cond) }
208            }
209            ast::Stmt::For { init, cond, step, body } => self.for_loop(init, cond, step, body),
210            ast::Stmt::Goto(name) => Stmt::Goto(self.label(name, span)),
211            ast::Stmt::GotoExpr(target) => self.computed_goto(target),
212            ast::Stmt::Continue => self.continue_stmt(span),
213            ast::Stmt::Break => self.break_stmt(span),
214            ast::Stmt::Return(value) => self.return_stmt(value, span),
215            ast::Stmt::Label { name, body, .. } => self.labelled(name, body, span),
216            ast::Stmt::Case { lo, hi, body } => self.case(lo, hi, body, span),
217            ast::Stmt::Default { body } => self.default(body, span),
218            ast::Stmt::LocalLabels(names) => {
219                self.local_labels(names, span);
220                Stmt::Empty
221            }
222            ast::Stmt::Asm(asm) => self.asm(asm, span),
223        };
224        let stmt = self.tast.stmt(node, span);
225        // The `switch` patches its cases once it has a table, and what it has to patch is the
226        // node that ended up in the body rather than the one the arm above built, so the case
227        // is registered here where that node exists.
228        if matches!(node, Stmt::Case { .. }) {
229            if let Some(switch) = self.switches() {
230                switch.labels.push(stmt);
231            }
232        }
233        stmt
234    }
235
236    /// `({ ... })`, GNU's statement expression, whose value is its last statement's.
237    ///
238    /// The type is the last statement's if that statement is an expression, and `void` otherwise,
239    /// which is gcc's rule and which makes `({ })` and `({ int x; })` both `void`. This works
240    /// because an expression statement holds the value of its expression rather than a conversion
241    /// of it to `void`: the statement is what discards the value, and here is where the value is
242    /// wanted instead.
243    pub(in crate::check) fn stmt_expr(&mut self, id: ast::StmtId, span: Span) -> ExprId {
244        let stmt = self.stmt(id);
245        let ty = match self.tast[stmt] {
246            Stmt::Block(body) => match self.tast[body].last() {
247                Some(&last) => match self.tast[last] {
248                    Stmt::Expr(value) => self.tast[value].ty,
249                    _ => self.types.void(),
250                },
251                None => self.types.void(),
252            },
253            _ => self.types.void(),
254        };
255        self.tast.expr(Expr::new(ExprKind::StmtExpr(stmt), ty, Category::Rvalue), span)
256    }
257
258    /// `&&name`, GNU's label address, whose type is `void *` and whose target is a label.
259    ///
260    /// Mentioning a label here is a use of it and not a definition, so a function that takes the
261    /// address of a label it never defines is reported the same way a `goto` to one is.
262    pub(in crate::check) fn label_addr(&mut self, name: Symbol, span: Span) -> ExprId {
263        let label = self.label(name, span);
264        let ty = self.types.pointer(self.types.void());
265        self.tast.expr(Expr::new(ExprKind::LabelAddr(label), ty, Category::Rvalue), span)
266    }
267
268    /// Opens a body, and gives back the one it displaced so that it can be put back.
269    ///
270    /// Displaced rather than asserted absent, because GNU's nested functions are a body inside a
271    /// body and each has its own labels, its own return type and its own loops.
272    pub(in crate::check) fn open_body(&mut self, func: Enclosing) -> Option<Body> {
273        let body = Body {
274            ret: func.ret,
275            at: func.at,
276            variadic: func.variadic,
277            last_param: func.last_param,
278            params: func.params,
279            name: func.name,
280            func_name: [None; FUNCTION_NAMES.len()],
281            labels: HashMap::new(),
282            shadowed: Vec::new(),
283            blocks: Vec::new(),
284            switches: Vec::new(),
285            loops: 0,
286            undeclared: HashSet::new(),
287        };
288        self.body.replace(body)
289    }
290
291    /// Whether the function being checked takes arguments past its named ones.
292    ///
293    /// False outside a function, where `va_start` is as wrong as it is in one with a fixed
294    /// parameter list and is reported in the same words.
295    pub(in crate::check) fn in_variadic_function(&self) -> bool {
296        self.body.as_ref().is_some_and(|body| body.variadic)
297    }
298
299    /// The last named parameter of the function being checked, which is what `va_start`'s
300    /// second argument ought to name.
301    pub(in crate::check) fn last_named_parameter(&self) -> Option<DeclId> {
302        self.body.as_ref().and_then(|body| body.last_param)
303    }
304
305    /// The string the `which`th spelling stands for in the function being checked, made on first
306    /// use.
307    ///
308    /// `None` outside a function, where the name is not declared at all. gcc gives it the empty
309    /// string there and warns, which is a warning nothing here can select yet, so a use outside
310    /// a function is left to the ordinary undeclared-name error.
311    pub(in crate::check) fn function_name_string(&mut self, which: usize) -> Option<StrId> {
312        let name = self.body.as_ref()?.name?;
313        if let Some(id) = self.body.as_ref().and_then(|body| body.func_name[which]) {
314            return Some(id);
315        }
316        let elements = self.text(name).chars().map(|c| c as u32).collect();
317        let literal =
318            StringLiteral { elements, encoding: Encoding::Plain, remarks: Remarks::default() };
319        let id = self.tast.add_string(literal);
320        if let Some(body) = &mut self.body {
321            body.func_name[which] = Some(id);
322        }
323        Some(id)
324    }
325
326    /// Whether a declaration is one of the parameters of the function being checked.
327    ///
328    /// False outside a function body, where every name in sight belongs to something else.
329    pub(in crate::check) fn is_parameter(&self, decl: DeclId) -> bool {
330        self.body.as_ref().is_some_and(|body| self.tast[body.params].contains(&decl))
331    }
332
333    /// Whether this is the first time the function being checked has used the undeclared name
334    /// `name`, and records it either way.
335    ///
336    /// Always true outside a function body, where there is nothing to remember it in and where
337    /// each declaration is its own context anyway.
338    pub(in crate::check) fn first_undeclared_use(&mut self, name: Symbol) -> bool {
339        match &mut self.body {
340            Some(body) => body.undeclared.insert(name),
341            None => true,
342        }
343    }
344
345    /// Closes a body, reporting the labels that were used and never defined.
346    pub(in crate::check) fn close_body(&mut self, previous: Option<Body>) {
347        let Some(body) = mem::replace(&mut self.body, previous) else {
348            return;
349        };
350        // Sorted, because a map has no order and a compiler whose diagnostics come out in a
351        // different order on two runs of the same input is one nobody can write a test against.
352        let mut undefined: Vec<Labelled> =
353            body.labels.into_values().filter(|label| label.defined.is_none()).collect();
354        undefined.sort_by_key(|label| label.at.lo);
355        for label in undefined {
356            self.undefined_label(label);
357        }
358    }
359
360    /// The body of a function definition, walked in the scope its parameters are already in.
361    ///
362    /// A function body is one scope with the parameters, which is why this exists rather than
363    /// the caller reaching [`Checker::stmt`]: that would open a second scope and make
364    /// `void f(int a) { int a; }` two declarations of `a` that never meet.
365    pub(in crate::check) fn body_block(&mut self, body: ast::StmtId) -> StmtId {
366        let span = self.ast.stmt_span(body);
367        let ast::Stmt::Compound(list) = self.ast[body] else {
368            return self.stmt(body);
369        };
370        let list = self.statements(list);
371        self.tast.stmt(Stmt::Block(list), span)
372    }
373
374    /// A block, which is a scope.
375    fn block(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
376        self.scopes.push();
377        let list = self.statements(body);
378        self.scopes.pop();
379        list
380    }
381
382    /// The statements of a block, with the block-local labels undone at the end of it.
383    fn statements(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
384        if let Some(state) = self.body.as_mut() {
385            let mark = state.shadowed.len();
386            state.blocks.push(mark);
387        }
388        let ids = self.ast[body].to_vec();
389        let mut stmts = Vec::with_capacity(ids.len());
390        for id in ids {
391            stmts.push(self.stmt(id));
392        }
393        self.end_block();
394        self.tast.add_stmt_refs(&stmts)
395    }
396
397    /// Undoes what `__label__` declared in the block that is ending.
398    fn end_block(&mut self) {
399        let Some(body) = self.body.as_mut() else {
400            return;
401        };
402        let Some(mark) = body.blocks.pop() else {
403            return;
404        };
405        let mut gone = Vec::new();
406        while body.shadowed.len() > mark {
407            let (name, previous) = body.shadowed.pop().expect("a saved binding");
408            let local = match previous {
409                Some(previous) => body.labels.insert(name, previous),
410                None => body.labels.remove(&name),
411            };
412            if let Some(local) = local {
413                if local.defined.is_none() {
414                    gone.push(local);
415                }
416            }
417        }
418        gone.sort_by_key(|label| label.at.lo);
419        for label in gone {
420            self.undefined_label(label);
421        }
422    }
423
424    /// The body of a loop, inside which `break` and `continue` both mean something.
425    fn loop_body(&mut self, body: ast::StmtId) -> StmtId {
426        if let Some(state) = self.body.as_mut() {
427            state.loops += 1;
428        }
429        let body = self.stmt(body);
430        if let Some(state) = self.body.as_mut() {
431            state.loops -= 1;
432        }
433        body
434    }
435
436    /// `for (init; cond; step) body`, whose first clause is in a scope of its own.
437    fn for_loop(
438        &mut self,
439        init: ForInit,
440        cond: Option<ast::ExprId>,
441        step: Option<ast::ExprId>,
442        body: ast::StmtId,
443    ) -> Stmt {
444        // The scope is the loop's rather than the body's, which is what makes the `i` in
445        // `for (int i = 0; ...)` visible to the condition and gone after the loop.
446        self.scopes.push();
447        let init = match init {
448            ForInit::None => None,
449            ForInit::Expr(value) => {
450                let span = self.ast.expr_span(value);
451                let value = self.expr(value);
452                let value = self.value(value);
453                Some(self.tast.stmt(Stmt::Expr(value), span))
454            }
455            ForInit::Decl(decl) => {
456                let span = self.ast.decl_span(decl);
457                let decls = self.check_decl(decl);
458                self.check_loop_declaration(decl);
459                Some(self.tast.stmt(Stmt::Decls(decls), span))
460            }
461        };
462        let cond = cond.map(|cond| self.controlling(cond));
463        let step = step.map(|step| {
464            let step = self.expr(step);
465            self.value(step)
466        });
467        let body = self.loop_body(body);
468        self.scopes.pop();
469        Stmt::For { init, cond, step, body }
470    }
471
472    /// What a `for` loop's first clause is not allowed to declare.
473    ///
474    /// C99 6.8.5p3 says the declaration there declares objects with automatic storage and nothing
475    /// else, which rules out a `static`, an `extern` and a `typedef`. The point of the rule is
476    /// that the clause scopes to the loop, and a name that outlives the loop has no business
477    /// being written where it looks like it does not.
478    ///
479    /// gcc accepts all three without a word unless `-pedantic` is on, and enough code declares a
480    /// `static` counter there that following the letter of the rule by default would reject
481    /// programs everyone else builds.
482    fn check_loop_declaration(&mut self, decl: ast::DeclId) {
483        if !self.cx.pedantic {
484            return;
485        }
486        let ast::Decl::Var { specs, declarators } = self.ast[decl] else {
487            return;
488        };
489        let specs = self.ast[specs];
490        let word = match specs.storage {
491            _ if specs.is_typedef() => "non-variable",
492            Some(StorageClass::Static) => "static variable",
493            Some(StorageClass::Extern) => "'extern' variable",
494            _ => return,
495        };
496        let ast = self.ast;
497        for &item in &ast[declarators] {
498            let node = ast[item.declarator];
499            let Some(name) = node.name else { continue };
500            let spelled = self.text(name).to_owned();
501            self.report(
502                Diagnostic::warning(
503                    format!("declaration of {word} '{spelled}' in 'for' loop initial declaration"),
504                    node.name_span,
505                )
506                .with_code("E0619"),
507            );
508        }
509    }
510
511    /// `switch (cond) body`, with the case table collected while the body is walked.
512    fn switch(&mut self, scrutinee: ast::ExprId, body: ast::StmtId) -> Stmt {
513        let at = self.ast.expr_span(scrutinee);
514        let cond = self.expr(scrutinee);
515        let cond = self.value(cond);
516        // Read before the promotion and not after it, because the range a case value is measured
517        // against is the one that was written. `switch (c)` on a `char` and `case 300` is worth
518        // saying, and by the time the promotion has run there is nothing left to say it about.
519        let range = eval::int_shape(&self.types, self.tast[cond].ty, self.cx.target);
520        let cond = self.conv().promote(cond);
521        let ty = self.tast[cond].ty;
522        let cond = if self.is_poisoned(cond) || is_integer(&self.types, ty) {
523            cond
524        } else {
525            self.report(Diagnostic::error("switch quantity not an integer", at).with_code("E0620"));
526            self.poison(at)
527        };
528        // The controlling type is the promoted one even where it was not an integer, so that the
529        // cases in the body are still folded and checked against each other rather than being
530        // reported a second time for something the `switch` itself already answered for.
531        let ty = if is_integer(&self.types, ty) { ty } else { self.int() };
532        if let Some(state) = self.body.as_mut() {
533            state.switches.push(Switch {
534                ty,
535                range,
536                cases: Vec::new(),
537                spans: Vec::new(),
538                labels: Vec::new(),
539                default: None,
540            });
541        }
542        let body = self.stmt(body);
543        let Some(switch) = self.body.as_mut().and_then(|state| state.switches.pop()) else {
544            return Stmt::Error;
545        };
546        let cases = self.tast.add_cases(&switch.cases);
547        for &labelled in &switch.labels {
548            let Stmt::Case { case: entry, body } = self.tast[labelled] else {
549                continue;
550            };
551            // The node is holding the place its label took in the table, which is where the
552            // label was written. It is not where the node was checked: two labels on one
553            // statement are checked inside out.
554            let case = cases.iter().nth(entry.index()).expect("a case for every label");
555            self.tast.set_stmt(labelled, Stmt::Case { case, body });
556        }
557        Stmt::Switch { cond, body, cases, default: switch.default.map(|(stmt, _)| stmt) }
558    }
559
560    /// `case lo:`, or GNU's `case lo ... hi:`.
561    fn case(
562        &mut self,
563        lo: ast::ExprId,
564        hi: Option<ast::ExprId>,
565        body: Option<ast::StmtId>,
566        span: Span,
567    ) -> Stmt {
568        // The label joins the table before the statement it labels is checked, so that the
569        // table comes out in the order the labels were written. `case 1: case 2: s;` is one
570        // labelled statement nested inside another, and checking inside out would leave the
571        // table holding 2 before 1.
572        let entry = self.enter_case(lo, hi, span);
573        let body = self.labelled_body(body, span);
574        let Some(entry) = entry else {
575            return Stmt::Error;
576        };
577        self.switches().expect("a switch").cases[entry].body = body;
578        // The node holds its place in the table until the `switch` knows where the table went,
579        // which is what the walk over its body ends with. The node this becomes is registered
580        // by [`Checker::stmt`], since that is where it is written into the arena and only the
581        // node that ends up in the body is worth patching.
582        Stmt::Case { case: rucc_base::Idx::from_usize(entry), body }
583    }
584
585    /// The place in the enclosing switch's table that this label takes, with a body for
586    /// [`Checker::case`] to fill in, or `None` for a label the switch cannot have.
587    fn enter_case(
588        &mut self,
589        lo: ast::ExprId,
590        hi: Option<ast::ExprId>,
591        span: Span,
592    ) -> Option<usize> {
593        if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
594            self.report(
595                Diagnostic::error("case label not within a switch statement", span)
596                    .with_code("E0621"),
597            );
598            return None;
599        }
600        let low = self.case_value(lo, span)?;
601        let high = match hi {
602            Some(hi) => self.case_value(hi, span)?,
603            None => low,
604        };
605        if high < low {
606            self.report(Diagnostic::warning("empty range specified", span).with_code("E0622"));
607            return None;
608        }
609        if let Some(at) = self.overlapping_case(low, high) {
610            self.report(
611                Diagnostic::error("duplicate case value", span)
612                    .with_code("E0623")
613                    .note("previously used here".to_owned(), at),
614            );
615            return None;
616        }
617        let switch = self.switches().expect("a switch");
618        let entry = switch.cases.len();
619        // The body is filled in by the caller once it has been checked. Nothing reads it in
620        // between: the table is only looked at for overlap, which is a question about values.
621        switch.cases.push(Case { low, high, body: rucc_base::Idx::from_usize(0) });
622        switch.spans.push(span);
623        Some(entry)
624    }
625
626    /// The value of one case label, folded and converted to the controlling type.
627    fn case_value(&mut self, value: ast::ExprId, span: Span) -> Option<i128> {
628        let at = self.ast.expr_span(value);
629        let value = self.expr(value);
630        let value = self.value(value);
631        let folded = match self.eval_integer(value) {
632            Ok(folded) => folded,
633            Err(failed) => {
634                if !failed.poisoned {
635                    self.report(
636                        Diagnostic::error("case label does not reduce to an integer constant", at)
637                            .with_code("E0624"),
638                    );
639                }
640                return None;
641            }
642        };
643        let switch = self.switches()?;
644        let (ty, range) = (switch.ty, switch.range);
645        if let Some(range) = range {
646            if eval::overflows(Const::Int(folded), range) {
647                self.report(
648                    Diagnostic::warning("case label value exceeds maximum value for type", span)
649                        .with_code("E0625"),
650                );
651            }
652        }
653        let info = eval::int_shape(&self.types, ty, self.cx.target)?;
654        Some(eval::narrowed(Const::Int(folded), info))
655    }
656
657    /// Where a case that already covers part of this range was written, if there is one.
658    fn overlapping_case(&mut self, low: i128, high: i128) -> Option<Span> {
659        let switch = self.switches()?;
660        switch
661            .cases
662            .iter()
663            .position(|case| case.low <= high && low <= case.high)
664            .map(|index| switch.spans[index])
665    }
666
667    /// `default:`.
668    fn default(&mut self, body: Option<ast::StmtId>, span: Span) -> Stmt {
669        let body = self.labelled_body(body, span);
670        if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
671            self.report(
672                Diagnostic::error("'default' label not within a switch statement", span)
673                    .with_code("E0626"),
674            );
675            return Stmt::Error;
676        }
677        if let Some((_, at)) = self.switches().expect("a switch").default {
678            self.report(
679                Diagnostic::error("multiple default labels in one switch", span)
680                    .with_code("E0627")
681                    .note("this is the first default label".to_owned(), at),
682            );
683            return Stmt::Error;
684        }
685        self.switches().expect("a switch").default = Some((body, span));
686        Stmt::Default { body }
687    }
688
689    /// `name: body`, which defines a label.
690    fn labelled(&mut self, name: Symbol, body: Option<ast::StmtId>, span: Span) -> Stmt {
691        let body = self.labelled_body(body, span);
692        let label = self.label(name, span);
693        let defined = self.body.as_ref().and_then(|state| state.labels[&name].defined);
694        if let Some(at) = defined {
695            let spelled = self.text(name).to_owned();
696            self.report(
697                Diagnostic::error(format!("duplicate label '{spelled}'"), span)
698                    .with_code("E0628")
699                    .note(format!("previous definition of '{spelled}' with type 'void'"), at),
700            );
701            return Stmt::Error;
702        }
703        if let Some(state) = self.body.as_mut() {
704            state.labels.entry(name).and_modify(|known| known.defined = Some(span));
705        }
706        self.tast.define_label(label, body);
707        Stmt::Label { label, body }
708    }
709
710    /// The statement a label labels, which C23 allows to be absent at the end of a block.
711    fn labelled_body(&mut self, body: Option<ast::StmtId>, span: Span) -> StmtId {
712        match body {
713            Some(body) => self.stmt(body),
714            None => self.tast.stmt(Stmt::Empty, span),
715        }
716    }
717
718    /// `__label__ a, b;`, which declares labels local to the block it is written in.
719    fn local_labels(&mut self, names: ast::SymbolList, span: Span) {
720        let ast = self.ast;
721        for &name in &ast[names] {
722            let id = self.tast.add_label(Label { name, stmt: None });
723            let local = Labelled { id, defined: None, at: span };
724            if let Some(state) = self.body.as_mut() {
725                let previous = state.labels.insert(name, local);
726                state.shadowed.push((name, previous));
727            }
728        }
729    }
730
731    /// The label of a name, made where the name is first met.
732    fn label(&mut self, name: Symbol, span: Span) -> LabelId {
733        if let Some(known) = self.body.as_ref().and_then(|state| state.labels.get(&name)) {
734            return known.id;
735        }
736        let id = self.tast.add_label(Label { name, stmt: None });
737        if let Some(state) = self.body.as_mut() {
738            state.labels.insert(name, Labelled { id, defined: None, at: span });
739        }
740        id
741    }
742
743    /// The diagnostic for a label that something jumped to and nothing defined.
744    ///
745    /// gcc points at the function rather than at the jump, which is a choice about a message
746    /// written at the end of a function and not about which one is the mistake. This points at
747    /// the jump, since that is what has to be changed and since a `__label__` is reported at the
748    /// end of a block that a function has no way to name.
749    fn undefined_label(&mut self, label: Labelled) {
750        let name = self.tast[label.id].name;
751        let spelled = self.text(name).to_owned();
752        self.report(
753            Diagnostic::error(format!("label '{spelled}' used but not defined"), label.at)
754                .with_code("E0629"),
755        );
756    }
757
758    /// `goto *expr;`, GNU's computed goto.
759    fn computed_goto(&mut self, target: ast::ExprId) -> Stmt {
760        let at = self.ast.expr_span(target);
761        let target = self.expr(target);
762        let target = self.value(target);
763        if self.is_poisoned(target) {
764            return Stmt::Error;
765        }
766        let ty = self.tast[target].ty;
767        // An integer is allowed through because a null pointer constant is one, and `goto *0;`
768        // is what a macro expands to where the target is decided elsewhere.
769        if !is_pointer(&self.types, ty) && !is_integer(&self.types, ty) {
770            self.report(
771                Diagnostic::error("computed goto must be pointer type", at).with_code("E0630"),
772            );
773            return Stmt::Error;
774        }
775        let void = self.types.pointer(self.types.void());
776        let target = self.conv().to_type(target, void);
777        Stmt::IndirectGoto(target)
778    }
779
780    /// `asm(...)`, GNU's inline assembly.
781    ///
782    /// Nothing here reads a constraint the way a target will. What is checked is the part that
783    /// belongs to the language rather than to the machine: an output has to be something the
784    /// program is allowed to assign to, an output constraint has to say it is one with `=` or
785    /// `+`, an input constraint has to not say it, and the labels of an `asm goto` are labels of
786    /// the function it is in. Whether the target has a register that fits a `"r"` is a question
787    /// for the backend, which is the only place a wrong answer to it can be given.
788    ///
789    /// The operands keep the order they were written in, because the template names them by
790    /// position: the outputs are numbered from zero and the inputs carry on from there, which is
791    /// the numbering `%0` counts in.
792    fn asm(&mut self, id: ast::AsmId, span: Span) -> Stmt {
793        let node = self.ast[id];
794        let outputs = self.asm_operands(node.outputs, 0, true);
795        let first_input = self.ast[node.outputs].len();
796        let inputs = self.asm_operands(node.inputs, first_input, false);
797
798        let mut clobbers = Vec::with_capacity(self.ast[node.clobbers].len());
799        for index in 0..self.ast[node.clobbers].len() {
800            let clobber = self.ast[node.clobbers][index];
801            clobbers.push(self.asm_string(clobber, span));
802        }
803        let clobbers = self.tast.add_str_refs(&clobbers);
804
805        let mut labels = Vec::with_capacity(self.ast[node.labels].len());
806        for index in 0..self.ast[node.labels].len() {
807            let name = self.ast[node.labels][index];
808            labels.push(self.label(name, span));
809        }
810        let labels = self.tast.add_label_refs(&labels);
811        let template = self.asm_template(node.template, outputs, inputs, labels, span);
812
813        // A statement with no outputs is `volatile` whether it said so or not, since one whose
814        // results nothing reads is otherwise one that may be dropped, and an `asm goto` is
815        // volatile for the same reason: what it does is jump, and no output records that.
816        let mut quals = node.quals;
817        if self.ast[node.outputs].is_empty() || quals.has(AsmQuals::GOTO) {
818            quals = quals.with(AsmQuals::VOLATILE);
819        }
820        Stmt::Asm(self.tast.add_asm(Asm { template, outputs, inputs, clobbers, labels, quals }))
821    }
822
823    /// One section of operands, numbered from `first` for the messages that count them.
824    fn asm_operands(
825        &mut self,
826        list: ast::AsmOperandList,
827        first: usize,
828        output: bool,
829    ) -> AsmOperandList {
830        let mut operands = Vec::with_capacity(self.ast[list].len());
831        for index in 0..self.ast[list].len() {
832            let operand = self.ast[list][index];
833            let operand = self.asm_operand(operand, first + index, output);
834            operands.push(operand);
835        }
836        self.tast.add_asm_operands(&operands)
837    }
838
839    /// One operand, checked against what its constraint says it is.
840    fn asm_operand(&mut self, operand: ast::AsmOperand, number: usize, output: bool) -> AsmOperand {
841        let span = operand.span;
842        let constraint = self.asm_string(operand.constraint, span);
843        let text = spelling(&self.tast[constraint]);
844        let value = self.expr(operand.value);
845        let ty = self.tast[value].ty;
846        let lvalue = matches!(self.tast[value].category, Category::Lvalue | Category::Bitfield);
847
848        // A structure has no register to sit in, so it travels the only way it can whatever the
849        // constraint says, and a constraint that does not allow memory is turned down rather
850        // than lowered to an address the backend has no reason to expect.
851        let record = is_record(&self.types, ty);
852        let memory = memory_only(&text) || record;
853        if record && !memory_only(&text) {
854            self.statement_unsupported("a structure or a union in a register constraint", span);
855        }
856
857        if output {
858            if !text.starts_with(['=', '+']) {
859                self.report(
860                    Diagnostic::error("output operand constraint lacks '='", span)
861                        .with_code("E0653"),
862                );
863            }
864            if !lvalue {
865                self.report(
866                    Diagnostic::error("lvalue required in 'asm' statement", span)
867                        .with_code("E0654"),
868                );
869            } else if self.types.quals(ty).has(Qualifiers::CONST) {
870                let what = self.read_only(value);
871                self.report(
872                    Diagnostic::error(format!("read-only {what} used as 'asm' output"), span)
873                        .with_code("E0655"),
874                );
875            }
876        } else {
877            if let Some(sign) = text.chars().find(|&ch| ch == '=' || ch == '+') {
878                self.report(
879                    Diagnostic::error(format!("input operand constraint contains '{sign}'"), span)
880                        .with_code("E0656"),
881                );
882            }
883            if memory && !lvalue {
884                self.report(
885                    Diagnostic::error(
886                        format!("memory input {number} is not directly addressable"),
887                        span,
888                    )
889                    .with_code("E0657"),
890                );
891            }
892        }
893
894        // An output is written through, and an operand in memory is addressed, so both of those
895        // stay the object they name. Everything else is read, which is what turns an array into
896        // a pointer and a variable into its value.
897        let value = if output || memory { value } else { self.value(value) };
898        AsmOperand { name: operand.name, constraint, value, memory }
899    }
900
901    /// One of the strings of an assembly statement, copied into the typed tree.
902    fn asm_string(&mut self, id: ast::StrId, span: Span) -> StrId {
903        let literal = self.ast[id].clone();
904        self.asm_narrow(&literal, span);
905        self.tast.add_string(literal)
906    }
907
908    /// Reports a string of an assembly statement that is not one an assembler can be handed.
909    fn asm_narrow(&mut self, literal: &StringLiteral, span: Span) {
910        if !matches!(literal.encoding, Encoding::Plain) {
911            self.report(Diagnostic::error("wide string literal in 'asm'", span).with_code("E0658"));
912        }
913    }
914
915    /// The template, with every name in it replaced by the number of the thing it names.
916    ///
917    /// gcc numbers the operands and the labels in one sequence, the outputs first and the labels
918    /// last, and `%[name]` is a way of writing one of those numbers without having to count. So
919    /// the numbers are what is kept here: the template that reaches an assembler refers to its
920    /// operands by position, which is what a position is for, and nothing downstream has to
921    /// carry the names around in order to be able to read one.
922    fn asm_template(
923        &mut self,
924        id: ast::StrId,
925        outputs: AsmOperandList,
926        inputs: AsmOperandList,
927        labels: LabelList,
928        span: Span,
929    ) -> StrId {
930        let mut names: Vec<(String, usize)> = Vec::new();
931        let mut number = 0;
932        for list in [outputs, inputs] {
933            for index in 0..self.tast[list].len() {
934                if let Some(name) = self.tast[list][index].name {
935                    names.push((self.text(name).to_owned(), number));
936                }
937                number += 1;
938            }
939        }
940        for index in 0..self.tast[labels].len() {
941            let label = self.tast[labels][index];
942            let name = self.tast[label].name;
943            names.push((self.text(name).to_owned(), number));
944            number += 1;
945        }
946        for at in 1..names.len() {
947            if names[..at].iter().any(|(earlier, _)| *earlier == names[at].0) {
948                let name = names[at].0.clone();
949                self.report(
950                    Diagnostic::error(format!("duplicate asm operand name '{name}'"), span)
951                        .with_code("E0659"),
952                );
953            }
954        }
955
956        let literal = self.ast[id].clone();
957        self.asm_narrow(&literal, span);
958        let text = self.asm_numbers(spelling(&literal), &names, span);
959        let elements = text.chars().map(|ch| ch as u32).collect();
960        self.tast.add_string(StringLiteral { elements, ..literal })
961    }
962
963    /// One template, with the names in it resolved.
964    ///
965    /// A name comes straight after the `%` or after one modifier letter, which is what makes
966    /// `%[x]`, `%w[x]` and `%l[x]` all one reference and the letter in the middle none of this
967    /// walk's business. `%%` is a percent sign and is stepped over whole, so the brackets in
968    /// `%%[x]` are two characters of assembly and not a name.
969    fn asm_numbers(&mut self, text: String, names: &[(String, usize)], span: Span) -> String {
970        let chars: Vec<char> = text.chars().collect();
971        let mut out = String::with_capacity(text.len());
972        let mut index = 0;
973        while index < chars.len() {
974            let ch = chars[index];
975            out.push(ch);
976            index += 1;
977            if ch != '%' {
978                continue;
979            }
980            let letter = chars.get(index).copied();
981            let open = match letter {
982                Some('[') => index,
983                Some(modifier)
984                    if modifier.is_ascii_alphabetic() && chars.get(index + 1) == Some(&'[') =>
985                {
986                    out.push(modifier);
987                    index += 1;
988                    index
989                }
990                // `%%` is the one escape that hides what comes after it.
991                Some('%') => {
992                    out.push('%');
993                    index += 1;
994                    continue;
995                }
996                _ => continue,
997            };
998            let Some(close) = chars[open..].iter().position(|&ch| ch == ']').map(|at| open + at)
999            else {
1000                continue;
1001            };
1002            let name: String = chars[open + 1..close].iter().collect();
1003            index = close + 1;
1004            match names.iter().find(|(known, _)| *known == name) {
1005                Some(&(_, number)) => out.push_str(&number.to_string()),
1006                None => {
1007                    self.report(
1008                        Diagnostic::error(format!("undefined named operand '{name}'"), span)
1009                            .with_code("E0660"),
1010                    );
1011                    out.push_str(&chars[open..=close].iter().collect::<String>());
1012                }
1013            }
1014        }
1015        out
1016    }
1017
1018    /// `break;`, which needs a loop or a `switch` around it.
1019    fn break_stmt(&mut self, span: Span) -> Stmt {
1020        let inside =
1021            self.body.as_ref().is_some_and(|state| state.loops > 0 || !state.switches.is_empty());
1022        if inside {
1023            return Stmt::Break;
1024        }
1025        self.report(
1026            Diagnostic::error("break statement not within loop or switch", span).with_code("E0631"),
1027        );
1028        Stmt::Error
1029    }
1030
1031    /// `continue;`, which needs a loop and is not satisfied by a `switch`.
1032    fn continue_stmt(&mut self, span: Span) -> Stmt {
1033        if self.body.as_ref().is_some_and(|state| state.loops > 0) {
1034            return Stmt::Continue;
1035        }
1036        self.report(
1037            Diagnostic::error("continue statement not within a loop", span).with_code("E0632"),
1038        );
1039        Stmt::Error
1040    }
1041
1042    /// `return;` or `return expr;`, checked against the return type.
1043    ///
1044    /// Both mismatches are errors. They were warnings for as long as C has had prototypes, and
1045    /// gcc 14 turned them into errors along with the rest of `-Wreturn-mismatch`, because a
1046    /// function that returns nothing where a value was promised hands its caller whatever was in
1047    /// the return register.
1048    fn return_stmt(&mut self, value: Option<ast::ExprId>, span: Span) -> Stmt {
1049        let Some((ret, at)) = self.body.as_ref().map(|state| (state.ret, state.at)) else {
1050            return Stmt::Return(None);
1051        };
1052        let void = is_void(&self.types, ret);
1053        // C89 let a function return without the value it promised and let one return a value it
1054        // had no way to give back, and gcc still takes both at that dialect: the first silently
1055        // and the second with a warning. C99 removed them and gcc has made them errors.
1056        let old = self.cx.std < Std::C99;
1057        let Some(value) = value else {
1058            if !void && !old {
1059                self.report(
1060                    Diagnostic::error(
1061                        "'return' with no value, in function returning non-void",
1062                        span,
1063                    )
1064                    .with_code("E0633")
1065                    .note("declared here".to_owned(), at),
1066                );
1067            }
1068            return Stmt::Return(None);
1069        };
1070        let where_from = self.ast.expr_span(value);
1071        let value = self.expr(value);
1072        let value = self.value(value);
1073        if !void {
1074            return Stmt::Return(Some(self.assign_to(ret, value, where_from, Target::Return)));
1075        }
1076        // C23 6.8.6.4 lets a function returning `void` say `return f();` where `f` returns
1077        // `void`, which is what a wrapper does and what gcc has always accepted.
1078        if !is_void(&self.types, self.tast[value].ty) && !self.is_poisoned(value) {
1079            let said = "'return' with a value, in function returning void";
1080            let diagnostic = if old {
1081                Diagnostic::warning(said, where_from)
1082            } else {
1083                Diagnostic::error(said, where_from)
1084            };
1085            self.report(diagnostic.with_code("E0634").note("declared here".to_owned(), at));
1086        }
1087        let value = self.conv().to_void(value);
1088        Stmt::Return(Some(value))
1089    }
1090
1091    /// The controlling expression of an `if`, a `while`, a `do` or a `for`.
1092    fn controlling(&mut self, cond: ast::ExprId) -> ExprId {
1093        let span = self.ast.expr_span(cond);
1094        let cond = self.expr(cond);
1095        self.condition(cond, span)
1096    }
1097
1098    /// The innermost `switch` being checked.
1099    fn switches(&mut self) -> Option<&mut Switch> {
1100        self.body.as_mut()?.switches.last_mut()
1101    }
1102
1103    /// A statement form that is recognised and not checked yet.
1104    fn statement_unsupported(&mut self, what: &str, span: Span) {
1105        self.report(
1106            Diagnostic::error(format!("{what} is not supported yet"), span).with_code("E0519"),
1107        );
1108    }
1109}
1110
1111/// The text of one of the strings of an assembly statement.
1112///
1113/// The elements of a narrow literal are its bytes, which is what the assembler is handed. One
1114/// that is not narrow was reported where it was read, and reading it here as characters rather
1115/// than refusing to read it keeps one mistake from becoming two messages.
1116fn spelling(literal: &StringLiteral) -> String {
1117    literal.elements.iter().filter_map(|&element| char::from_u32(element)).collect()
1118}
1119
1120/// Whether a constraint allows memory and allows nothing else.
1121///
1122/// The letters that mean memory are the machine independent ones, `m`, `o` and `V`, and the two
1123/// that mean an address the instruction modifies. Everything else is a register class, a
1124/// constant, a matching operand or a letter the target invented, and each of those is a value.
1125/// A constraint that allows either, `"rm"`, is a value here, which is the answer gcc reaches for
1126/// as well and which is free to give: a value the target cannot hold in a register is a question
1127/// the backend gets to ask about a machine it knows.
1128fn memory_only(constraint: &str) -> bool {
1129    let letters: Vec<char> =
1130        constraint.chars().filter(|ch| !"=+&%#*!?, \t".contains(*ch)).collect();
1131    !letters.is_empty() && letters.iter().all(|ch| "moV<>".contains(*ch))
1132}
1133
1134#[cfg(test)]
1135mod tests {
1136    use rucc_ast::{
1137        AttrList, Builtin, BuiltinSet, DeclSpecs, DeclSpecsId, Declarator, DeclaratorId, Derived,
1138        TypeSpec,
1139    };
1140    use rucc_base::Interner;
1141    use rucc_lex::{IntConstant, IntConstantType, Remarks};
1142    use rucc_session::Std;
1143    use rucc_target::{TargetInfo, Triple};
1144    use rucc_types::IntKind;
1145
1146    use super::*;
1147    use crate::check::Context;
1148    use crate::print::Printer;
1149
1150    /// The untyped tree a test checks, built by hand.
1151    ///
1152    /// The same shape as the fixtures next door and for the same reason: the checker borrows the
1153    /// interner for as long as it lives, so everything a test needs to name is named before the
1154    /// checker exists.
1155    struct Fixture {
1156        ast: rucc_ast::Ast,
1157        names: Interner,
1158        target: TargetInfo,
1159    }
1160
1161    impl Fixture {
1162        fn new() -> Fixture {
1163            let target =
1164                TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
1165            Fixture { ast: rucc_ast::Ast::new(), names: Interner::new(), target }
1166        }
1167
1168        fn name(&mut self, text: &str) -> Symbol {
1169            self.names.intern(text)
1170        }
1171
1172        fn int(&mut self, value: u128) -> ast::ExprId {
1173            let ty = IntConstantType::Standard(IntKind::Int);
1174            let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
1175            self.ast.expr(ast::Expr::Int(id), Span::DUMMY)
1176        }
1177
1178        fn use_name(&mut self, text: &str) -> ast::ExprId {
1179            let name = self.name(text);
1180            self.ast.expr(ast::Expr::Name(name), Span::DUMMY)
1181        }
1182
1183        /// A specifier list naming a built-in type, as the keywords that were written.
1184        fn keywords(&mut self, written: &[BuiltinSet]) -> DeclSpecsId {
1185            let mut builtin = Builtin::NONE;
1186            for &keyword in written {
1187                builtin = builtin.add(keyword).expect("a keyword written once");
1188            }
1189            let mut specs = DeclSpecs::empty(Span::DUMMY);
1190            specs.ty = TypeSpec::Builtin(builtin);
1191            self.ast.add_specs(specs)
1192        }
1193
1194        /// `int`, which is what most of these declarations are made of.
1195        fn int_specs(&mut self) -> DeclSpecsId {
1196            self.keywords(&[BuiltinSet::INT])
1197        }
1198
1199        fn declarator(&mut self, name: Option<&str>, derived: &[Derived]) -> DeclaratorId {
1200            let name = name.map(|text| self.name(text));
1201            let derived = self.ast.add_derived_list(derived);
1202            self.ast.add_declarator(Declarator {
1203                name,
1204                name_span: Span::DUMMY,
1205                derived,
1206                span: Span::DUMMY,
1207            })
1208        }
1209
1210        /// `int x;` and the like, as a statement.
1211        fn local(&mut self, specs: DeclSpecsId, name: &str) -> ast::DeclId {
1212            let declarator = self.declarator(Some(name), &[]);
1213            let item = ast::InitDeclarator {
1214                declarator,
1215                init: None,
1216                asm_label: None,
1217                attrs: AttrList::EMPTY,
1218                span: Span::DUMMY,
1219            };
1220            let declarators = self.ast.add_init_declarator_list(&[item]);
1221            self.ast.decl(ast::Decl::Var { specs, declarators }, Span::DUMMY)
1222        }
1223
1224        /// `(ty)value`, which is how these tests write an expression of a type they choose.
1225        fn cast(&mut self, specs: DeclSpecsId, value: ast::ExprId) -> ast::ExprId {
1226            let declarator = self.declarator(None, &[]);
1227            let ty = self.ast.add_type_name(ast::TypeName { specs, declarator, span: Span::DUMMY });
1228            self.ast.expr(ast::Expr::Cast { ty, operand: value }, Span::DUMMY)
1229        }
1230
1231        fn stmt(&mut self, stmt: ast::Stmt) -> ast::StmtId {
1232            self.ast.stmt(stmt, Span::DUMMY)
1233        }
1234
1235        /// `{ ... }`, from the statements it holds.
1236        fn block(&mut self, body: &[ast::StmtId]) -> ast::StmtId {
1237            let body = self.ast.add_stmt_list(body);
1238            self.stmt(ast::Stmt::Compound(body))
1239        }
1240
1241        /// `value;`.
1242        fn expr_stmt(&mut self, value: ast::ExprId) -> ast::StmtId {
1243            self.stmt(ast::Stmt::Expr(value))
1244        }
1245
1246        /// `name: body`.
1247        fn labelled(&mut self, text: &str, body: Option<ast::StmtId>) -> ast::StmtId {
1248            let name = self.name(text);
1249            self.stmt(ast::Stmt::Label { name, body, attrs: AttrList::EMPTY })
1250        }
1251
1252        /// `goto name;`.
1253        fn goto(&mut self, text: &str) -> ast::StmtId {
1254            let name = self.name(text);
1255            self.stmt(ast::Stmt::Goto(name))
1256        }
1257
1258        /// `__label__ a, b;`.
1259        fn local_labels(&mut self, names: &[&str]) -> ast::StmtId {
1260            let names: Vec<Symbol> = names.iter().map(|text| self.name(text)).collect();
1261            let names = self.ast.add_symbol_list(&names);
1262            self.stmt(ast::Stmt::LocalLabels(names))
1263        }
1264
1265        /// `case lo: body`, or GNU's `case lo ... hi: body`.
1266        fn case(&mut self, lo: u128, hi: Option<u128>, body: Option<ast::StmtId>) -> ast::StmtId {
1267            let lo = self.int(lo);
1268            let hi = hi.map(|hi| self.int(hi));
1269            self.stmt(ast::Stmt::Case { lo, hi, body })
1270        }
1271
1272        /// `switch (scrutinee) { ... }`.
1273        fn switch(&mut self, scrutinee: ast::ExprId, body: &[ast::StmtId]) -> ast::StmtId {
1274            let body = self.block(body);
1275            self.stmt(ast::Stmt::Switch { scrutinee, body })
1276        }
1277
1278        fn checker(&self) -> Checker<'_> {
1279            Checker::new(&self.ast, Context::new(&self.names, &self.target, Std::C23))
1280        }
1281    }
1282
1283    /// The tree under one statement, which is what most assertions here are about.
1284    fn dump(checker: &Checker<'_>, id: StmtId) -> String {
1285        let mut printer = Printer::new(&checker.tast, &checker.types, checker.cx.names);
1286        printer.stmt(id);
1287        printer.finish()
1288    }
1289
1290    /// What was reported, as the messages alone, notes included.
1291    fn messages(checker: &Checker<'_>) -> Vec<String> {
1292        checker
1293            .errors
1294            .diagnostics()
1295            .iter()
1296            .flat_map(|d| {
1297                std::iter::once(d.message.clone())
1298                    .chain(d.children.iter().map(|n| n.message.clone()))
1299            })
1300            .collect()
1301    }
1302
1303    /// The one message that was reported, which is what most of these tests expect.
1304    fn message(checker: &Checker<'_>) -> String {
1305        let mut reported = messages(checker);
1306        assert_eq!(reported.len(), 1, "expected exactly one diagnostic, got {reported:?}");
1307        reported.pop().expect("one message")
1308    }
1309
1310    /// What was reported, as the severity and the message of each, so that a test can say which
1311    /// of the two a diagnostic is. gcc 14 turned several of these from warnings into errors and
1312    /// the difference is the whole point of some of the tests below.
1313    fn reported(checker: &Checker<'_>) -> Vec<String> {
1314        checker
1315            .errors
1316            .diagnostics()
1317            .iter()
1318            .map(|d| format!("{}: {}", d.severity.as_str(), d.message))
1319            .collect()
1320    }
1321
1322    #[test]
1323    fn a_block_is_a_scope_and_a_name_declared_in_one_is_gone_after_it() {
1324        let mut f = Fixture::new();
1325        let specs = f.int_specs();
1326        let declared = f.local(specs, "x");
1327        let declared = f.stmt(ast::Stmt::Decl(declared));
1328        let inner = f.block(&[declared]);
1329        let use_x = f.use_name("x");
1330        let after = f.expr_stmt(use_x);
1331        let outer = f.block(&[inner, after]);
1332
1333        let mut c = f.checker();
1334        let void = c.types.void();
1335        c.check_stmt(void, outer);
1336
1337        assert_eq!(message(&c), "'x' undeclared (first use in this function)");
1338    }
1339
1340    #[test]
1341    fn a_name_nobody_declared_is_reported_once_per_function_and_not_once_per_use() {
1342        // The wording promises it: `first use in this function` said three times is a sentence
1343        // arguing with itself. A misspelled name written in a loop body is one mistake, and one
1344        // message is what makes the next mistake in the file visible.
1345        let mut f = Fixture::new();
1346        let first = f.use_name("nope");
1347        let first = f.expr_stmt(first);
1348        let second = f.use_name("nope");
1349        let second = f.expr_stmt(second);
1350        let body = f.block(&[first, second]);
1351
1352        let mut c = f.checker();
1353        let void = c.types.void();
1354        let previous = c.open_body(Enclosing::returning(void));
1355        c.check_stmt(void, body);
1356        c.close_body(previous);
1357
1358        assert_eq!(message(&c), "'nope' undeclared (first use in this function)");
1359    }
1360
1361    #[test]
1362    fn an_expression_statement_holds_the_value_and_not_a_conversion_of_it_to_void() {
1363        let mut f = Fixture::new();
1364        let one = f.int(1);
1365        let stmt = f.expr_stmt(one);
1366
1367        let mut c = f.checker();
1368        let void = c.types.void();
1369        let id = c.check_stmt(void, stmt);
1370
1371        assert_eq!(dump(&c, id), "expr\n  const 1 : int\n");
1372        assert!(c.errors.is_empty());
1373    }
1374
1375    #[test]
1376    fn a_statement_expression_has_the_type_of_its_last_statement() {
1377        let mut f = Fixture::new();
1378        let one = f.int(1);
1379        let inner = f.expr_stmt(one);
1380        let body = f.block(&[inner]);
1381        let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1382        let stmt = f.expr_stmt(value);
1383
1384        let mut c = f.checker();
1385        let void = c.types.void();
1386        let id = c.check_stmt(void, stmt);
1387
1388        assert_eq!(
1389            dump(&c, id),
1390            "expr\n  stmt-expr : int\n    block\n      expr\n        const 1 : int\n"
1391        );
1392        assert!(c.errors.is_empty());
1393    }
1394
1395    #[test]
1396    fn a_statement_expression_that_ends_in_something_else_is_void() {
1397        let mut f = Fixture::new();
1398        let body = f.block(&[]);
1399        let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1400        let stmt = f.expr_stmt(value);
1401
1402        let mut c = f.checker();
1403        let void = c.types.void();
1404        let id = c.check_stmt(void, stmt);
1405
1406        assert_eq!(dump(&c, id), "expr\n  stmt-expr : void\n    block\n");
1407        assert!(c.errors.is_empty());
1408    }
1409
1410    #[test]
1411    fn the_declaration_in_a_for_clause_scopes_to_the_loop_and_not_to_what_follows() {
1412        let mut f = Fixture::new();
1413        let specs = f.int_specs();
1414        let declared = f.local(specs, "i");
1415        let empty = f.stmt(ast::Stmt::Empty);
1416        let loop_stmt = f.stmt(ast::Stmt::For {
1417            init: ForInit::Decl(declared),
1418            cond: None,
1419            step: None,
1420            body: empty,
1421        });
1422        let use_i = f.use_name("i");
1423        let after = f.expr_stmt(use_i);
1424        let outer = f.block(&[loop_stmt, after]);
1425
1426        let mut c = f.checker();
1427        let void = c.types.void();
1428        c.check_stmt(void, outer);
1429
1430        assert_eq!(message(&c), "'i' undeclared (first use in this function)");
1431    }
1432
1433    #[test]
1434    fn a_static_in_a_for_clause_is_accepted_and_only_pedantic_says_anything_about_it() {
1435        let mut f = Fixture::new();
1436        let mut specs = DeclSpecs::empty(Span::DUMMY);
1437        let builtin = Builtin::NONE.add(BuiltinSet::INT).expect("a keyword written once");
1438        specs.ty = TypeSpec::Builtin(builtin);
1439        specs.storage = Some(StorageClass::Static);
1440        let specs = f.ast.add_specs(specs);
1441        let declared = f.local(specs, "i");
1442        let empty = f.stmt(ast::Stmt::Empty);
1443        let loop_stmt = f.stmt(ast::Stmt::For {
1444            init: ForInit::Decl(declared),
1445            cond: None,
1446            step: None,
1447            body: empty,
1448        });
1449
1450        let mut c = f.checker();
1451        let void = c.types.void();
1452        c.check_stmt(void, loop_stmt);
1453        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1454
1455        let mut c = f.checker();
1456        c.cx.pedantic = true;
1457        let void = c.types.void();
1458        c.check_stmt(void, loop_stmt);
1459        assert_eq!(
1460            reported(&c),
1461            ["warning: declaration of static variable 'i' in 'for' loop initial declaration"]
1462        );
1463    }
1464
1465    #[test]
1466    fn continue_needs_a_loop_and_is_not_satisfied_by_a_switch() {
1467        let mut f = Fixture::new();
1468        let one = f.int(1);
1469        let go_on = f.stmt(ast::Stmt::Continue);
1470        let case = f.stmt(ast::Stmt::Case { lo: one, hi: None, body: Some(go_on) });
1471        let scrutinee = f.int(0);
1472        let switch = f.switch(scrutinee, &[case]);
1473
1474        let mut c = f.checker();
1475        let void = c.types.void();
1476        c.check_stmt(void, switch);
1477
1478        assert_eq!(message(&c), "continue statement not within a loop");
1479    }
1480
1481    #[test]
1482    fn break_is_satisfied_by_a_switch_and_reported_where_there_is_neither() {
1483        let mut f = Fixture::new();
1484        let stop = f.stmt(ast::Stmt::Break);
1485        let scrutinee = f.int(0);
1486        let switch = f.switch(scrutinee, &[stop]);
1487        let loose = f.stmt(ast::Stmt::Break);
1488
1489        let mut c = f.checker();
1490        let void = c.types.void();
1491        c.check_stmt(void, switch);
1492        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1493
1494        let mut c = f.checker();
1495        let void = c.types.void();
1496        c.check_stmt(void, loose);
1497        assert_eq!(message(&c), "break statement not within loop or switch");
1498    }
1499
1500    #[test]
1501    fn a_goto_resolves_to_a_label_the_function_defines_further_down() {
1502        let mut f = Fixture::new();
1503        let jump = f.goto("done");
1504        let empty = f.stmt(ast::Stmt::Empty);
1505        let target = f.labelled("done", Some(empty));
1506        let body = f.block(&[jump, target]);
1507
1508        let mut c = f.checker();
1509        let void = c.types.void();
1510        let id = c.check_stmt(void, body);
1511
1512        assert_eq!(dump(&c, id), "block\n  goto #0 done\n  label #0 done\n    empty\n");
1513        assert!(c.errors.is_empty());
1514    }
1515
1516    #[test]
1517    fn a_label_that_is_jumped_to_and_never_defined_is_reported_at_the_jump() {
1518        let mut f = Fixture::new();
1519        let jump = f.goto("away");
1520        let body = f.block(&[jump]);
1521
1522        let mut c = f.checker();
1523        let void = c.types.void();
1524        c.check_stmt(void, body);
1525
1526        assert_eq!(message(&c), "label 'away' used but not defined");
1527    }
1528
1529    #[test]
1530    fn the_address_of_a_label_is_a_use_of_it_and_not_a_definition() {
1531        let mut f = Fixture::new();
1532        let away = f.name("away");
1533        let value = f.ast.expr(ast::Expr::LabelAddr(away), Span::DUMMY);
1534        let stmt = f.expr_stmt(value);
1535
1536        let mut c = f.checker();
1537        let void = c.types.void();
1538        let id = c.check_stmt(void, stmt);
1539
1540        assert_eq!(dump(&c, id), "expr\n  label-addr #0 away : void *\n");
1541        assert_eq!(message(&c), "label 'away' used but not defined");
1542    }
1543
1544    #[test]
1545    fn one_label_defined_twice_is_an_error_that_points_at_the_first() {
1546        let mut f = Fixture::new();
1547        let first = f.labelled("here", None);
1548        let second = f.labelled("here", None);
1549        let body = f.block(&[first, second]);
1550
1551        let mut c = f.checker();
1552        let void = c.types.void();
1553        c.check_stmt(void, body);
1554
1555        assert_eq!(
1556            messages(&c),
1557            ["duplicate label 'here'", "previous definition of 'here' with type 'void'",]
1558        );
1559    }
1560
1561    #[test]
1562    fn a_local_label_is_undone_when_its_block_ends_so_two_blocks_may_declare_one_name() {
1563        let mut f = Fixture::new();
1564        let sibling = |f: &mut Fixture| {
1565            let declared = f.local_labels(&["done"]);
1566            let jump = f.goto("done");
1567            let target = f.labelled("done", None);
1568            f.block(&[declared, jump, target])
1569        };
1570        let first = sibling(&mut f);
1571        let second = sibling(&mut f);
1572        let body = f.block(&[first, second]);
1573
1574        let mut c = f.checker();
1575        let void = c.types.void();
1576        let id = c.check_stmt(void, body);
1577
1578        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1579        assert_eq!(
1580            dump(&c, id),
1581            "block\n  block\n    empty\n    goto #0 done\n    label #0 done\n      empty\n  \
1582             block\n    empty\n    goto #1 done\n    label #1 done\n      empty\n"
1583        );
1584    }
1585
1586    #[test]
1587    fn a_local_label_that_nothing_defines_is_reported_when_its_block_ends() {
1588        let mut f = Fixture::new();
1589        let declared = f.local_labels(&["done"]);
1590        let jump = f.goto("done");
1591        let inner = f.block(&[declared, jump]);
1592        let target = f.labelled("done", None);
1593        let body = f.block(&[inner, target]);
1594
1595        let mut c = f.checker();
1596        let void = c.types.void();
1597        c.check_stmt(void, body);
1598
1599        assert_eq!(message(&c), "label 'done' used but not defined");
1600    }
1601
1602    #[test]
1603    fn a_computed_goto_wants_something_that_could_be_an_address() {
1604        let mut f = Fixture::new();
1605        let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1606        let zero = f.int(0);
1607        let target = f.cast(specs, zero);
1608        let stmt = f.stmt(ast::Stmt::GotoExpr(target));
1609
1610        let mut c = f.checker();
1611        let void = c.types.void();
1612        c.check_stmt(void, stmt);
1613
1614        assert_eq!(message(&c), "computed goto must be pointer type");
1615    }
1616
1617    #[test]
1618    fn a_switch_on_something_that_is_not_an_integer_is_an_error() {
1619        let mut f = Fixture::new();
1620        let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1621        let zero = f.int(0);
1622        let scrutinee = f.cast(specs, zero);
1623        let switch = f.switch(scrutinee, &[]);
1624
1625        let mut c = f.checker();
1626        let void = c.types.void();
1627        c.check_stmt(void, switch);
1628
1629        assert_eq!(message(&c), "switch quantity not an integer");
1630    }
1631
1632    #[test]
1633    fn the_cases_of_a_switch_are_one_table_in_the_order_they_were_written() {
1634        let mut f = Fixture::new();
1635        let first = f.case(1, None, None);
1636        let second = f.case(4, Some(6), None);
1637        let default = f.stmt(ast::Stmt::Default { body: None });
1638        let scrutinee = f.int(0);
1639        let switch = f.switch(scrutinee, &[first, second, default]);
1640
1641        let mut c = f.checker();
1642        let void = c.types.void();
1643        let id = c.check_stmt(void, switch);
1644
1645        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1646        assert_eq!(
1647            dump(&c, id),
1648            "switch\n  cond\n    const 0 : int\n  cases\n    case #0 1\n    case #1 4 ... 6\n    \
1649             default\n  body\n    block\n      case #0\n        empty\n      case #1\n        \
1650             empty\n      default\n        empty\n"
1651        );
1652    }
1653
1654    #[test]
1655    fn two_labels_on_one_statement_are_in_the_table_the_way_round_they_were_written() {
1656        // `case 1: case 2: ;` is one labelled statement inside another, so the checking runs
1657        // inside out. The table is a record of what the user wrote and does not follow it.
1658        let mut f = Fixture::new();
1659        let inner = f.case(2, None, None);
1660        let outer = f.case(1, None, Some(inner));
1661        let scrutinee = f.int(0);
1662        let switch = f.switch(scrutinee, &[outer]);
1663
1664        let mut c = f.checker();
1665        let void = c.types.void();
1666        let id = c.check_stmt(void, switch);
1667
1668        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1669        assert_eq!(
1670            dump(&c, id),
1671            "switch\n  cond\n    const 0 : int\n  cases\n    case #0 1\n    case #1 2\n  body\n    \
1672             block\n      case #0\n        case #1\n          empty\n"
1673        );
1674    }
1675
1676    #[test]
1677    fn a_case_that_covers_a_value_an_earlier_one_covers_is_a_duplicate() {
1678        let mut f = Fixture::new();
1679        let first = f.case(1, Some(3), None);
1680        let second = f.case(2, None, None);
1681        let scrutinee = f.int(0);
1682        let switch = f.switch(scrutinee, &[first, second]);
1683
1684        let mut c = f.checker();
1685        let void = c.types.void();
1686        c.check_stmt(void, switch);
1687
1688        assert_eq!(messages(&c), ["duplicate case value", "previously used here"]);
1689    }
1690
1691    #[test]
1692    fn a_case_outside_a_switch_is_an_error_and_so_is_a_default() {
1693        let mut f = Fixture::new();
1694        let case = f.case(1, None, None);
1695        let default = f.stmt(ast::Stmt::Default { body: None });
1696        let body = f.block(&[case, default]);
1697
1698        let mut c = f.checker();
1699        let void = c.types.void();
1700        c.check_stmt(void, body);
1701
1702        assert_eq!(
1703            messages(&c),
1704            [
1705                "case label not within a switch statement",
1706                "'default' label not within a switch statement",
1707            ]
1708        );
1709    }
1710
1711    #[test]
1712    fn a_case_label_that_is_not_a_constant_is_an_error() {
1713        let mut f = Fixture::new();
1714        let specs = f.int_specs();
1715        let declared = f.local(specs, "n");
1716        let declared = f.stmt(ast::Stmt::Decl(declared));
1717        let use_n = f.use_name("n");
1718        let case = f.stmt(ast::Stmt::Case { lo: use_n, hi: None, body: None });
1719        let scrutinee = f.int(0);
1720        let switch = f.switch(scrutinee, &[case]);
1721        let body = f.block(&[declared, switch]);
1722
1723        let mut c = f.checker();
1724        let void = c.types.void();
1725        c.check_stmt(void, body);
1726
1727        assert_eq!(message(&c), "case label does not reduce to an integer constant");
1728    }
1729
1730    #[test]
1731    fn a_case_range_that_runs_backwards_is_empty() {
1732        let mut f = Fixture::new();
1733        let case = f.case(6, Some(4), None);
1734        let scrutinee = f.int(0);
1735        let switch = f.switch(scrutinee, &[case]);
1736
1737        let mut c = f.checker();
1738        let void = c.types.void();
1739        c.check_stmt(void, switch);
1740
1741        assert_eq!(reported(&c), ["warning: empty range specified"]);
1742    }
1743
1744    #[test]
1745    fn a_case_is_measured_against_the_type_that_was_written_and_not_the_promoted_one() {
1746        let mut f = Fixture::new();
1747        let specs = f.keywords(&[BuiltinSet::CHAR]);
1748        let zero = f.int(0);
1749        let scrutinee = f.cast(specs, zero);
1750        let case = f.case(300, None, None);
1751        let switch = f.switch(scrutinee, &[case]);
1752
1753        let mut c = f.checker();
1754        let void = c.types.void();
1755        c.check_stmt(void, switch);
1756
1757        assert_eq!(reported(&c), ["warning: case label value exceeds maximum value for type"]);
1758    }
1759
1760    #[test]
1761    fn two_defaults_in_one_switch_are_an_error_that_points_at_the_first() {
1762        let mut f = Fixture::new();
1763        let first = f.stmt(ast::Stmt::Default { body: None });
1764        let second = f.stmt(ast::Stmt::Default { body: None });
1765        let scrutinee = f.int(0);
1766        let switch = f.switch(scrutinee, &[first, second]);
1767
1768        let mut c = f.checker();
1769        let void = c.types.void();
1770        c.check_stmt(void, switch);
1771
1772        assert_eq!(
1773            messages(&c),
1774            ["multiple default labels in one switch", "this is the first default label"]
1775        );
1776    }
1777
1778    #[test]
1779    fn a_nested_switch_keeps_its_cases_to_itself() {
1780        let mut f = Fixture::new();
1781        let inner_case = f.case(1, None, None);
1782        let inner_scrutinee = f.int(0);
1783        let inner = f.switch(inner_scrutinee, &[inner_case]);
1784        let outer_case = f.case(1, None, Some(inner));
1785        let outer_scrutinee = f.int(0);
1786        let outer = f.switch(outer_scrutinee, &[outer_case]);
1787
1788        let mut c = f.checker();
1789        let void = c.types.void();
1790        let id = c.check_stmt(void, outer);
1791
1792        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1793        assert_eq!(
1794            dump(&c, id),
1795            "switch\n  cond\n    const 0 : int\n  cases\n    case #1 1\n  body\n    block\n      \
1796             case #1\n        switch\n          cond\n            const 0 : int\n          \
1797             cases\n            case #0 1\n          body\n            block\n              case \
1798             #0\n                empty\n"
1799        );
1800    }
1801
1802    #[test]
1803    fn a_bare_return_from_a_function_that_promised_a_value_is_an_error() {
1804        let mut f = Fixture::new();
1805        let stmt = f.stmt(ast::Stmt::Return(None));
1806
1807        let mut c = f.checker();
1808        let int = c.int();
1809        c.check_stmt(int, stmt);
1810
1811        assert_eq!(reported(&c), ["error: 'return' with no value, in function returning non-void"]);
1812        assert_eq!(messages(&c).len(), 2, "the note is attached to it");
1813    }
1814
1815    #[test]
1816    fn a_value_returned_from_a_function_returning_void_is_an_error() {
1817        let mut f = Fixture::new();
1818        let one = f.int(1);
1819        let stmt = f.stmt(ast::Stmt::Return(Some(one)));
1820
1821        let mut c = f.checker();
1822        let void = c.types.void();
1823        c.check_stmt(void, stmt);
1824
1825        assert_eq!(reported(&c), ["error: 'return' with a value, in function returning void"]);
1826    }
1827
1828    #[test]
1829    fn a_void_value_returned_from_a_function_returning_void_is_what_a_wrapper_writes() {
1830        let mut f = Fixture::new();
1831        let specs = f.keywords(&[BuiltinSet::VOID]);
1832        let one = f.int(1);
1833        let value = f.cast(specs, one);
1834        let stmt = f.stmt(ast::Stmt::Return(Some(value)));
1835
1836        let mut c = f.checker();
1837        let void = c.types.void();
1838        c.check_stmt(void, stmt);
1839
1840        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1841    }
1842
1843    #[test]
1844    fn a_returned_value_is_converted_to_the_return_type() {
1845        let mut f = Fixture::new();
1846        let one = f.int(1);
1847        let stmt = f.stmt(ast::Stmt::Return(Some(one)));
1848
1849        let mut c = f.checker();
1850        let long = c.types.int(IntKind::Long);
1851        let id = c.check_stmt(long, stmt);
1852
1853        assert_eq!(dump(&c, id), "return\n  convert arithmetic : long\n    const 1 : int\n");
1854        assert!(c.errors.is_empty());
1855    }
1856}