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.12.
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, ForInit, StorageClass};
44use rucc_base::Symbol;
45use rucc_diag::{Diagnostic, Span};
46use rucc_types::{IntegerInfo, TypeId, is_integer, is_pointer, is_void};
47
48use crate::check::Checker;
49use crate::check::expr::Target;
50use crate::eval;
51use crate::expr::{Category, Expr, ExprId, ExprKind};
52use crate::stmt::{Case, Stmt, StmtId};
53use crate::tast::{Const, Label, LabelId};
54
55/// What the statements of one function body are checked against.
56#[derive(Debug)]
57pub(in crate::check) struct Body {
58    /// The return type, which every `return` in it answers to.
59    ret: TypeId,
60    /// Where the function was named, for the `declared here` note under a `return` that
61    /// disagrees with the return type.
62    at: Span,
63    /// The labels of the function, by the name they were written with.
64    labels: HashMap<Symbol, Labelled>,
65    /// What the enclosing blocks bound the names of their `__label__` declarations to, so that a
66    /// block-local label can be undone when the block ends.
67    shadowed: Vec<(Symbol, Option<Labelled>)>,
68    /// Where each enclosing block's run of those starts.
69    blocks: Vec<usize>,
70    /// The `switch` statements this one is inside, innermost last.
71    switches: Vec<Switch>,
72    /// How many loops it is inside, which is what `continue` asks and half of what `break` asks.
73    loops: usize,
74    /// The names this function has already been told about, so that a name nobody declared is
75    /// reported once rather than once per use. The message says `first use in this function`
76    /// and gcc means it: a typo in a loop body is one mistake however many times it is written.
77    undeclared: HashSet<Symbol>,
78}
79
80/// One label of a function.
81#[derive(Debug, Clone, Copy)]
82struct Labelled {
83    /// The label in the typed tree, made where the name was first met.
84    id: LabelId,
85    /// Whether the statement it names has been seen, and where the label was written.
86    defined: Option<Span>,
87    /// Where the name was first met, which is what an undefined label is reported at.
88    at: Span,
89}
90
91/// One `switch` being checked, and the case table it is collecting.
92#[derive(Debug)]
93struct Switch {
94    /// The promoted type of the controlling expression, which every case value is held in.
95    ty: TypeId,
96    /// The shape of the type before that promotion, which is the range a case value is warned
97    /// about for leaving. gcc measures against what was written rather than against what the
98    /// promotion widened it to, so `case 300` on a `char` is worth saying even though 300 is a
99    /// perfectly good `int`.
100    range: Option<IntegerInfo>,
101    /// The cases so far, in the order they were written.
102    cases: Vec<Case>,
103    /// Where each of them was written, for the note under a duplicate.
104    spans: Vec<Span>,
105    /// The statements those cases label, which are patched with their table entries once the
106    /// table exists. Each one says which entry is its own, so the order here does not matter.
107    labels: Vec<StmtId>,
108    /// The `default`, and where it was written, once one has been seen.
109    default: Option<(StmtId, Span)>,
110}
111
112impl Checker<'_> {
113    /// Checks one statement, as though it were the body of a function returning `ret`.
114    ///
115    /// The entry for a caller that has a statement rather than a translation unit, which is what
116    /// the tests here are built on. A body is opened around it and closed after, so that the
117    /// labels are resolved and reported the way they are in a real function.
118    pub fn check_stmt(&mut self, ret: TypeId, id: ast::StmtId) -> StmtId {
119        let previous = self.open_body(ret, Span::DUMMY);
120        let stmt = self.stmt(id);
121        self.close_body(previous);
122        stmt
123    }
124
125    /// Checks one statement and gives back the node it became.
126    pub(in crate::check) fn stmt(&mut self, id: ast::StmtId) -> StmtId {
127        let span = self.ast.stmt_span(id);
128        let node = match self.ast[id] {
129            ast::Stmt::Error => Stmt::Error,
130            ast::Stmt::Empty => Stmt::Empty,
131            ast::Stmt::Expr(value) => {
132                let value = self.expr(value);
133                Stmt::Expr(self.value(value))
134            }
135            ast::Stmt::Decl(decl) => Stmt::Decls(self.check_decl(decl)),
136            ast::Stmt::Compound(body) => Stmt::Block(self.block(body)),
137            ast::Stmt::If { cond, then, otherwise } => {
138                let cond = self.controlling(cond);
139                let then = self.stmt(then);
140                Stmt::If { cond, then, otherwise: otherwise.map(|id| self.stmt(id)) }
141            }
142            ast::Stmt::Switch { scrutinee, body } => self.switch(scrutinee, body),
143            ast::Stmt::While { cond, body } => {
144                let cond = self.controlling(cond);
145                Stmt::While { cond, body: self.loop_body(body) }
146            }
147            ast::Stmt::DoWhile { body, cond } => {
148                let body = self.loop_body(body);
149                Stmt::DoWhile { body, cond: self.controlling(cond) }
150            }
151            ast::Stmt::For { init, cond, step, body } => self.for_loop(init, cond, step, body),
152            ast::Stmt::Goto(name) => Stmt::Goto(self.label(name, span)),
153            ast::Stmt::GotoExpr(target) => self.computed_goto(target),
154            ast::Stmt::Continue => self.continue_stmt(span),
155            ast::Stmt::Break => self.break_stmt(span),
156            ast::Stmt::Return(value) => self.return_stmt(value, span),
157            ast::Stmt::Label { name, body, .. } => self.labelled(name, body, span),
158            ast::Stmt::Case { lo, hi, body } => self.case(lo, hi, body, span),
159            ast::Stmt::Default { body } => self.default(body, span),
160            ast::Stmt::LocalLabels(names) => {
161                self.local_labels(names, span);
162                Stmt::Empty
163            }
164            ast::Stmt::Asm(_) => {
165                self.statement_unsupported("an assembler statement", span);
166                Stmt::Error
167            }
168        };
169        let stmt = self.tast.stmt(node, span);
170        // The `switch` patches its cases once it has a table, and what it has to patch is the
171        // node that ended up in the body rather than the one the arm above built, so the case
172        // is registered here where that node exists.
173        if matches!(node, Stmt::Case { .. }) {
174            if let Some(switch) = self.switches() {
175                switch.labels.push(stmt);
176            }
177        }
178        stmt
179    }
180
181    /// `({ ... })`, GNU's statement expression, whose value is its last statement's.
182    ///
183    /// The type is the last statement's if that statement is an expression, and `void` otherwise,
184    /// which is gcc's rule and which makes `({ })` and `({ int x; })` both `void`. This works
185    /// because an expression statement holds the value of its expression rather than a conversion
186    /// of it to `void`: the statement is what discards the value, and here is where the value is
187    /// wanted instead.
188    pub(in crate::check) fn stmt_expr(&mut self, id: ast::StmtId, span: Span) -> ExprId {
189        let stmt = self.stmt(id);
190        let ty = match self.tast[stmt] {
191            Stmt::Block(body) => match self.tast[body].last() {
192                Some(&last) => match self.tast[last] {
193                    Stmt::Expr(value) => self.tast[value].ty,
194                    _ => self.types.void(),
195                },
196                None => self.types.void(),
197            },
198            _ => self.types.void(),
199        };
200        self.tast.expr(Expr::new(ExprKind::StmtExpr(stmt), ty, Category::Rvalue), span)
201    }
202
203    /// `&&name`, GNU's label address, whose type is `void *` and whose target is a label.
204    ///
205    /// Mentioning a label here is a use of it and not a definition, so a function that takes the
206    /// address of a label it never defines is reported the same way a `goto` to one is.
207    pub(in crate::check) fn label_addr(&mut self, name: Symbol, span: Span) -> ExprId {
208        let label = self.label(name, span);
209        let ty = self.types.pointer(self.types.void());
210        self.tast.expr(Expr::new(ExprKind::LabelAddr(label), ty, Category::Rvalue), span)
211    }
212
213    /// Opens a body, and gives back the one it displaced so that it can be put back.
214    ///
215    /// Displaced rather than asserted absent, because GNU's nested functions are a body inside a
216    /// body and each has its own labels, its own return type and its own loops.
217    pub(in crate::check) fn open_body(&mut self, ret: TypeId, at: Span) -> Option<Body> {
218        let body = Body {
219            ret,
220            at,
221            labels: HashMap::new(),
222            shadowed: Vec::new(),
223            blocks: Vec::new(),
224            switches: Vec::new(),
225            loops: 0,
226            undeclared: HashSet::new(),
227        };
228        self.body.replace(body)
229    }
230
231    /// Whether this is the first time the function being checked has used the undeclared name
232    /// `name`, and records it either way.
233    ///
234    /// Always true outside a function body, where there is nothing to remember it in and where
235    /// each declaration is its own context anyway.
236    pub(in crate::check) fn first_undeclared_use(&mut self, name: Symbol) -> bool {
237        match &mut self.body {
238            Some(body) => body.undeclared.insert(name),
239            None => true,
240        }
241    }
242
243    /// Closes a body, reporting the labels that were used and never defined.
244    pub(in crate::check) fn close_body(&mut self, previous: Option<Body>) {
245        let Some(body) = mem::replace(&mut self.body, previous) else {
246            return;
247        };
248        // Sorted, because a map has no order and a compiler whose diagnostics come out in a
249        // different order on two runs of the same input is one nobody can write a test against.
250        let mut undefined: Vec<Labelled> =
251            body.labels.into_values().filter(|label| label.defined.is_none()).collect();
252        undefined.sort_by_key(|label| label.at.lo);
253        for label in undefined {
254            self.undefined_label(label);
255        }
256    }
257
258    /// The body of a function definition, walked in the scope its parameters are already in.
259    ///
260    /// A function body is one scope with the parameters, which is why this exists rather than
261    /// the caller reaching [`Checker::stmt`]: that would open a second scope and make
262    /// `void f(int a) { int a; }` two declarations of `a` that never meet.
263    pub(in crate::check) fn body_block(&mut self, body: ast::StmtId) -> StmtId {
264        let span = self.ast.stmt_span(body);
265        let ast::Stmt::Compound(list) = self.ast[body] else {
266            return self.stmt(body);
267        };
268        let list = self.statements(list);
269        self.tast.stmt(Stmt::Block(list), span)
270    }
271
272    /// A block, which is a scope.
273    fn block(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
274        self.scopes.push();
275        let list = self.statements(body);
276        self.scopes.pop();
277        list
278    }
279
280    /// The statements of a block, with the block-local labels undone at the end of it.
281    fn statements(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
282        if let Some(state) = self.body.as_mut() {
283            let mark = state.shadowed.len();
284            state.blocks.push(mark);
285        }
286        let ids = self.ast[body].to_vec();
287        let mut stmts = Vec::with_capacity(ids.len());
288        for id in ids {
289            stmts.push(self.stmt(id));
290        }
291        self.end_block();
292        self.tast.add_stmt_refs(&stmts)
293    }
294
295    /// Undoes what `__label__` declared in the block that is ending.
296    fn end_block(&mut self) {
297        let Some(body) = self.body.as_mut() else {
298            return;
299        };
300        let Some(mark) = body.blocks.pop() else {
301            return;
302        };
303        let mut gone = Vec::new();
304        while body.shadowed.len() > mark {
305            let (name, previous) = body.shadowed.pop().expect("a saved binding");
306            let local = match previous {
307                Some(previous) => body.labels.insert(name, previous),
308                None => body.labels.remove(&name),
309            };
310            if let Some(local) = local {
311                if local.defined.is_none() {
312                    gone.push(local);
313                }
314            }
315        }
316        gone.sort_by_key(|label| label.at.lo);
317        for label in gone {
318            self.undefined_label(label);
319        }
320    }
321
322    /// The body of a loop, inside which `break` and `continue` both mean something.
323    fn loop_body(&mut self, body: ast::StmtId) -> StmtId {
324        if let Some(state) = self.body.as_mut() {
325            state.loops += 1;
326        }
327        let body = self.stmt(body);
328        if let Some(state) = self.body.as_mut() {
329            state.loops -= 1;
330        }
331        body
332    }
333
334    /// `for (init; cond; step) body`, whose first clause is in a scope of its own.
335    fn for_loop(
336        &mut self,
337        init: ForInit,
338        cond: Option<ast::ExprId>,
339        step: Option<ast::ExprId>,
340        body: ast::StmtId,
341    ) -> Stmt {
342        // The scope is the loop's rather than the body's, which is what makes the `i` in
343        // `for (int i = 0; ...)` visible to the condition and gone after the loop.
344        self.scopes.push();
345        let init = match init {
346            ForInit::None => None,
347            ForInit::Expr(value) => {
348                let span = self.ast.expr_span(value);
349                let value = self.expr(value);
350                let value = self.value(value);
351                Some(self.tast.stmt(Stmt::Expr(value), span))
352            }
353            ForInit::Decl(decl) => {
354                let span = self.ast.decl_span(decl);
355                let decls = self.check_decl(decl);
356                self.check_loop_declaration(decl);
357                Some(self.tast.stmt(Stmt::Decls(decls), span))
358            }
359        };
360        let cond = cond.map(|cond| self.controlling(cond));
361        let step = step.map(|step| {
362            let step = self.expr(step);
363            self.value(step)
364        });
365        let body = self.loop_body(body);
366        self.scopes.pop();
367        Stmt::For { init, cond, step, body }
368    }
369
370    /// What a `for` loop's first clause is not allowed to declare.
371    ///
372    /// C99 6.8.5p3 says the declaration there declares objects with automatic storage and nothing
373    /// else, which rules out a `static`, an `extern` and a `typedef`. The point of the rule is
374    /// that the clause scopes to the loop, and a name that outlives the loop has no business
375    /// being written where it looks like it does not.
376    ///
377    /// gcc accepts all three without a word unless `-pedantic` is on, and enough code declares a
378    /// `static` counter there that following the letter of the rule by default would reject
379    /// programs everyone else builds.
380    fn check_loop_declaration(&mut self, decl: ast::DeclId) {
381        if !self.cx.pedantic {
382            return;
383        }
384        let ast::Decl::Var { specs, declarators } = self.ast[decl] else {
385            return;
386        };
387        let specs = self.ast[specs];
388        let word = match specs.storage {
389            _ if specs.is_typedef() => "non-variable",
390            Some(StorageClass::Static) => "static variable",
391            Some(StorageClass::Extern) => "'extern' variable",
392            _ => return,
393        };
394        let ast = self.ast;
395        for &item in &ast[declarators] {
396            let node = ast[item.declarator];
397            let Some(name) = node.name else { continue };
398            let spelled = self.text(name).to_owned();
399            self.report(
400                Diagnostic::warning(
401                    format!("declaration of {word} '{spelled}' in 'for' loop initial declaration"),
402                    node.name_span,
403                )
404                .with_code("E0619"),
405            );
406        }
407    }
408
409    /// `switch (cond) body`, with the case table collected while the body is walked.
410    fn switch(&mut self, scrutinee: ast::ExprId, body: ast::StmtId) -> Stmt {
411        let at = self.ast.expr_span(scrutinee);
412        let cond = self.expr(scrutinee);
413        let cond = self.value(cond);
414        // Read before the promotion and not after it, because the range a case value is measured
415        // against is the one that was written. `switch (c)` on a `char` and `case 300` is worth
416        // saying, and by the time the promotion has run there is nothing left to say it about.
417        let range = eval::int_shape(&self.types, self.tast[cond].ty, self.cx.target);
418        let cond = self.conv().promote(cond);
419        let ty = self.tast[cond].ty;
420        let cond = if self.is_poisoned(cond) || is_integer(&self.types, ty) {
421            cond
422        } else {
423            self.report(Diagnostic::error("switch quantity not an integer", at).with_code("E0620"));
424            self.poison(at)
425        };
426        // The controlling type is the promoted one even where it was not an integer, so that the
427        // cases in the body are still folded and checked against each other rather than being
428        // reported a second time for something the `switch` itself already answered for.
429        let ty = if is_integer(&self.types, ty) { ty } else { self.int() };
430        if let Some(state) = self.body.as_mut() {
431            state.switches.push(Switch {
432                ty,
433                range,
434                cases: Vec::new(),
435                spans: Vec::new(),
436                labels: Vec::new(),
437                default: None,
438            });
439        }
440        let body = self.stmt(body);
441        let Some(switch) = self.body.as_mut().and_then(|state| state.switches.pop()) else {
442            return Stmt::Error;
443        };
444        let cases = self.tast.add_cases(&switch.cases);
445        for &labelled in &switch.labels {
446            let Stmt::Case { case: entry, body } = self.tast[labelled] else {
447                continue;
448            };
449            // The node is holding the place its label took in the table, which is where the
450            // label was written. It is not where the node was checked: two labels on one
451            // statement are checked inside out.
452            let case = cases.iter().nth(entry.index()).expect("a case for every label");
453            self.tast.set_stmt(labelled, Stmt::Case { case, body });
454        }
455        Stmt::Switch { cond, body, cases, default: switch.default.map(|(stmt, _)| stmt) }
456    }
457
458    /// `case lo:`, or GNU's `case lo ... hi:`.
459    fn case(
460        &mut self,
461        lo: ast::ExprId,
462        hi: Option<ast::ExprId>,
463        body: Option<ast::StmtId>,
464        span: Span,
465    ) -> Stmt {
466        // The label joins the table before the statement it labels is checked, so that the
467        // table comes out in the order the labels were written. `case 1: case 2: s;` is one
468        // labelled statement nested inside another, and checking inside out would leave the
469        // table holding 2 before 1.
470        let entry = self.enter_case(lo, hi, span);
471        let body = self.labelled_body(body, span);
472        let Some(entry) = entry else {
473            return Stmt::Error;
474        };
475        self.switches().expect("a switch").cases[entry].body = body;
476        // The node holds its place in the table until the `switch` knows where the table went,
477        // which is what the walk over its body ends with. The node this becomes is registered
478        // by [`Checker::stmt`], since that is where it is written into the arena and only the
479        // node that ends up in the body is worth patching.
480        Stmt::Case { case: rucc_base::Idx::from_usize(entry), body }
481    }
482
483    /// The place in the enclosing switch's table that this label takes, with a body for
484    /// [`Checker::case`] to fill in, or `None` for a label the switch cannot have.
485    fn enter_case(
486        &mut self,
487        lo: ast::ExprId,
488        hi: Option<ast::ExprId>,
489        span: Span,
490    ) -> Option<usize> {
491        if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
492            self.report(
493                Diagnostic::error("case label not within a switch statement", span)
494                    .with_code("E0621"),
495            );
496            return None;
497        }
498        let low = self.case_value(lo, span)?;
499        let high = match hi {
500            Some(hi) => self.case_value(hi, span)?,
501            None => low,
502        };
503        if high < low {
504            self.report(Diagnostic::warning("empty range specified", span).with_code("E0622"));
505            return None;
506        }
507        if let Some(at) = self.overlapping_case(low, high) {
508            self.report(
509                Diagnostic::error("duplicate case value", span)
510                    .with_code("E0623")
511                    .note("previously used here".to_owned(), at),
512            );
513            return None;
514        }
515        let switch = self.switches().expect("a switch");
516        let entry = switch.cases.len();
517        // The body is filled in by the caller once it has been checked. Nothing reads it in
518        // between: the table is only looked at for overlap, which is a question about values.
519        switch.cases.push(Case { low, high, body: rucc_base::Idx::from_usize(0) });
520        switch.spans.push(span);
521        Some(entry)
522    }
523
524    /// The value of one case label, folded and converted to the controlling type.
525    fn case_value(&mut self, value: ast::ExprId, span: Span) -> Option<i128> {
526        let at = self.ast.expr_span(value);
527        let value = self.expr(value);
528        let value = self.value(value);
529        let folded = match self.eval_integer(value) {
530            Ok(folded) => folded,
531            Err(failed) => {
532                if !failed.poisoned {
533                    self.report(
534                        Diagnostic::error("case label does not reduce to an integer constant", at)
535                            .with_code("E0624"),
536                    );
537                }
538                return None;
539            }
540        };
541        let switch = self.switches()?;
542        let (ty, range) = (switch.ty, switch.range);
543        if let Some(range) = range {
544            if eval::overflows(Const::Int(folded), range) {
545                self.report(
546                    Diagnostic::warning("case label value exceeds maximum value for type", span)
547                        .with_code("E0625"),
548                );
549            }
550        }
551        let info = eval::int_shape(&self.types, ty, self.cx.target)?;
552        Some(eval::narrowed(Const::Int(folded), info))
553    }
554
555    /// Where a case that already covers part of this range was written, if there is one.
556    fn overlapping_case(&mut self, low: i128, high: i128) -> Option<Span> {
557        let switch = self.switches()?;
558        switch
559            .cases
560            .iter()
561            .position(|case| case.low <= high && low <= case.high)
562            .map(|index| switch.spans[index])
563    }
564
565    /// `default:`.
566    fn default(&mut self, body: Option<ast::StmtId>, span: Span) -> Stmt {
567        let body = self.labelled_body(body, span);
568        if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
569            self.report(
570                Diagnostic::error("'default' label not within a switch statement", span)
571                    .with_code("E0626"),
572            );
573            return Stmt::Error;
574        }
575        if let Some((_, at)) = self.switches().expect("a switch").default {
576            self.report(
577                Diagnostic::error("multiple default labels in one switch", span)
578                    .with_code("E0627")
579                    .note("this is the first default label".to_owned(), at),
580            );
581            return Stmt::Error;
582        }
583        self.switches().expect("a switch").default = Some((body, span));
584        Stmt::Default { body }
585    }
586
587    /// `name: body`, which defines a label.
588    fn labelled(&mut self, name: Symbol, body: Option<ast::StmtId>, span: Span) -> Stmt {
589        let body = self.labelled_body(body, span);
590        let label = self.label(name, span);
591        let defined = self.body.as_ref().and_then(|state| state.labels[&name].defined);
592        if let Some(at) = defined {
593            let spelled = self.text(name).to_owned();
594            self.report(
595                Diagnostic::error(format!("duplicate label '{spelled}'"), span)
596                    .with_code("E0628")
597                    .note(format!("previous definition of '{spelled}' with type 'void'"), at),
598            );
599            return Stmt::Error;
600        }
601        if let Some(state) = self.body.as_mut() {
602            state.labels.entry(name).and_modify(|known| known.defined = Some(span));
603        }
604        self.tast.define_label(label, body);
605        Stmt::Label { label, body }
606    }
607
608    /// The statement a label labels, which C23 allows to be absent at the end of a block.
609    fn labelled_body(&mut self, body: Option<ast::StmtId>, span: Span) -> StmtId {
610        match body {
611            Some(body) => self.stmt(body),
612            None => self.tast.stmt(Stmt::Empty, span),
613        }
614    }
615
616    /// `__label__ a, b;`, which declares labels local to the block it is written in.
617    fn local_labels(&mut self, names: ast::SymbolList, span: Span) {
618        let ast = self.ast;
619        for &name in &ast[names] {
620            let id = self.tast.add_label(Label { name, stmt: None });
621            let local = Labelled { id, defined: None, at: span };
622            if let Some(state) = self.body.as_mut() {
623                let previous = state.labels.insert(name, local);
624                state.shadowed.push((name, previous));
625            }
626        }
627    }
628
629    /// The label of a name, made where the name is first met.
630    fn label(&mut self, name: Symbol, span: Span) -> LabelId {
631        if let Some(known) = self.body.as_ref().and_then(|state| state.labels.get(&name)) {
632            return known.id;
633        }
634        let id = self.tast.add_label(Label { name, stmt: None });
635        if let Some(state) = self.body.as_mut() {
636            state.labels.insert(name, Labelled { id, defined: None, at: span });
637        }
638        id
639    }
640
641    /// The diagnostic for a label that something jumped to and nothing defined.
642    ///
643    /// gcc points at the function rather than at the jump, which is a choice about a message
644    /// written at the end of a function and not about which one is the mistake. This points at
645    /// the jump, since that is what has to be changed and since a `__label__` is reported at the
646    /// end of a block that a function has no way to name.
647    fn undefined_label(&mut self, label: Labelled) {
648        let name = self.tast[label.id].name;
649        let spelled = self.text(name).to_owned();
650        self.report(
651            Diagnostic::error(format!("label '{spelled}' used but not defined"), label.at)
652                .with_code("E0629"),
653        );
654    }
655
656    /// `goto *expr;`, GNU's computed goto.
657    fn computed_goto(&mut self, target: ast::ExprId) -> Stmt {
658        let at = self.ast.expr_span(target);
659        let target = self.expr(target);
660        let target = self.value(target);
661        if self.is_poisoned(target) {
662            return Stmt::Error;
663        }
664        let ty = self.tast[target].ty;
665        // An integer is allowed through because a null pointer constant is one, and `goto *0;`
666        // is what a macro expands to where the target is decided elsewhere.
667        if !is_pointer(&self.types, ty) && !is_integer(&self.types, ty) {
668            self.report(
669                Diagnostic::error("computed goto must be pointer type", at).with_code("E0630"),
670            );
671            return Stmt::Error;
672        }
673        let void = self.types.pointer(self.types.void());
674        let target = self.conv().to_type(target, void);
675        Stmt::IndirectGoto(target)
676    }
677
678    /// `break;`, which needs a loop or a `switch` around it.
679    fn break_stmt(&mut self, span: Span) -> Stmt {
680        let inside =
681            self.body.as_ref().is_some_and(|state| state.loops > 0 || !state.switches.is_empty());
682        if inside {
683            return Stmt::Break;
684        }
685        self.report(
686            Diagnostic::error("break statement not within loop or switch", span).with_code("E0631"),
687        );
688        Stmt::Error
689    }
690
691    /// `continue;`, which needs a loop and is not satisfied by a `switch`.
692    fn continue_stmt(&mut self, span: Span) -> Stmt {
693        if self.body.as_ref().is_some_and(|state| state.loops > 0) {
694            return Stmt::Continue;
695        }
696        self.report(
697            Diagnostic::error("continue statement not within a loop", span).with_code("E0632"),
698        );
699        Stmt::Error
700    }
701
702    /// `return;` or `return expr;`, checked against the return type.
703    ///
704    /// Both mismatches are errors. They were warnings for as long as C has had prototypes, and
705    /// gcc 14 turned them into errors along with the rest of `-Wreturn-mismatch`, because a
706    /// function that returns nothing where a value was promised hands its caller whatever was in
707    /// the return register.
708    fn return_stmt(&mut self, value: Option<ast::ExprId>, span: Span) -> Stmt {
709        let Some((ret, at)) = self.body.as_ref().map(|state| (state.ret, state.at)) else {
710            return Stmt::Return(None);
711        };
712        let void = is_void(&self.types, ret);
713        let Some(value) = value else {
714            if !void {
715                self.report(
716                    Diagnostic::error(
717                        "'return' with no value, in function returning non-void",
718                        span,
719                    )
720                    .with_code("E0633")
721                    .note("declared here".to_owned(), at),
722                );
723            }
724            return Stmt::Return(None);
725        };
726        let where_from = self.ast.expr_span(value);
727        let value = self.expr(value);
728        let value = self.value(value);
729        if !void {
730            return Stmt::Return(Some(self.assign_to(ret, value, where_from, Target::Return)));
731        }
732        // C23 6.8.6.4 lets a function returning `void` say `return f();` where `f` returns
733        // `void`, which is what a wrapper does and what gcc has always accepted.
734        if !is_void(&self.types, self.tast[value].ty) && !self.is_poisoned(value) {
735            self.report(
736                Diagnostic::error("'return' with a value, in function returning void", where_from)
737                    .with_code("E0634")
738                    .note("declared here".to_owned(), at),
739            );
740        }
741        let value = self.conv().to_void(value);
742        Stmt::Return(Some(value))
743    }
744
745    /// The controlling expression of an `if`, a `while`, a `do` or a `for`.
746    fn controlling(&mut self, cond: ast::ExprId) -> ExprId {
747        let span = self.ast.expr_span(cond);
748        let cond = self.expr(cond);
749        self.condition(cond, span)
750    }
751
752    /// The innermost `switch` being checked.
753    fn switches(&mut self) -> Option<&mut Switch> {
754        self.body.as_mut()?.switches.last_mut()
755    }
756
757    /// A statement form that is recognised and not checked yet.
758    fn statement_unsupported(&mut self, what: &str, span: Span) {
759        self.report(
760            Diagnostic::error(format!("{what} is not supported yet"), span).with_code("E0519"),
761        );
762    }
763}
764
765#[cfg(test)]
766mod tests {
767    use rucc_ast::{
768        AttrList, Builtin, BuiltinSet, DeclSpecs, DeclSpecsId, Declarator, DeclaratorId, Derived,
769        TypeSpec,
770    };
771    use rucc_base::Interner;
772    use rucc_lex::{IntConstant, IntConstantType, Remarks};
773    use rucc_session::Std;
774    use rucc_target::{TargetInfo, Triple};
775    use rucc_types::IntKind;
776
777    use super::*;
778    use crate::check::Context;
779    use crate::print::Printer;
780
781    /// The untyped tree a test checks, built by hand.
782    ///
783    /// The same shape as the fixtures next door and for the same reason: the checker borrows the
784    /// interner for as long as it lives, so everything a test needs to name is named before the
785    /// checker exists.
786    struct Fixture {
787        ast: rucc_ast::Ast,
788        names: Interner,
789        target: TargetInfo,
790    }
791
792    impl Fixture {
793        fn new() -> Fixture {
794            let target =
795                TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
796            Fixture { ast: rucc_ast::Ast::new(), names: Interner::new(), target }
797        }
798
799        fn name(&mut self, text: &str) -> Symbol {
800            self.names.intern(text)
801        }
802
803        fn int(&mut self, value: u128) -> ast::ExprId {
804            let ty = IntConstantType::Standard(IntKind::Int);
805            let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
806            self.ast.expr(ast::Expr::Int(id), Span::DUMMY)
807        }
808
809        fn use_name(&mut self, text: &str) -> ast::ExprId {
810            let name = self.name(text);
811            self.ast.expr(ast::Expr::Name(name), Span::DUMMY)
812        }
813
814        /// A specifier list naming a built-in type, as the keywords that were written.
815        fn keywords(&mut self, written: &[BuiltinSet]) -> DeclSpecsId {
816            let mut builtin = Builtin::NONE;
817            for &keyword in written {
818                builtin = builtin.add(keyword).expect("a keyword written once");
819            }
820            let mut specs = DeclSpecs::empty(Span::DUMMY);
821            specs.ty = TypeSpec::Builtin(builtin);
822            self.ast.add_specs(specs)
823        }
824
825        /// `int`, which is what most of these declarations are made of.
826        fn int_specs(&mut self) -> DeclSpecsId {
827            self.keywords(&[BuiltinSet::INT])
828        }
829
830        fn declarator(&mut self, name: Option<&str>, derived: &[Derived]) -> DeclaratorId {
831            let name = name.map(|text| self.name(text));
832            let derived = self.ast.add_derived_list(derived);
833            self.ast.add_declarator(Declarator {
834                name,
835                name_span: Span::DUMMY,
836                derived,
837                span: Span::DUMMY,
838            })
839        }
840
841        /// `int x;` and the like, as a statement.
842        fn local(&mut self, specs: DeclSpecsId, name: &str) -> ast::DeclId {
843            let declarator = self.declarator(Some(name), &[]);
844            let item = ast::InitDeclarator {
845                declarator,
846                init: None,
847                asm_label: None,
848                attrs: AttrList::EMPTY,
849                span: Span::DUMMY,
850            };
851            let declarators = self.ast.add_init_declarator_list(&[item]);
852            self.ast.decl(ast::Decl::Var { specs, declarators }, Span::DUMMY)
853        }
854
855        /// `(ty)value`, which is how these tests write an expression of a type they choose.
856        fn cast(&mut self, specs: DeclSpecsId, value: ast::ExprId) -> ast::ExprId {
857            let declarator = self.declarator(None, &[]);
858            let ty = self.ast.add_type_name(ast::TypeName { specs, declarator, span: Span::DUMMY });
859            self.ast.expr(ast::Expr::Cast { ty, operand: value }, Span::DUMMY)
860        }
861
862        fn stmt(&mut self, stmt: ast::Stmt) -> ast::StmtId {
863            self.ast.stmt(stmt, Span::DUMMY)
864        }
865
866        /// `{ ... }`, from the statements it holds.
867        fn block(&mut self, body: &[ast::StmtId]) -> ast::StmtId {
868            let body = self.ast.add_stmt_list(body);
869            self.stmt(ast::Stmt::Compound(body))
870        }
871
872        /// `value;`.
873        fn expr_stmt(&mut self, value: ast::ExprId) -> ast::StmtId {
874            self.stmt(ast::Stmt::Expr(value))
875        }
876
877        /// `name: body`.
878        fn labelled(&mut self, text: &str, body: Option<ast::StmtId>) -> ast::StmtId {
879            let name = self.name(text);
880            self.stmt(ast::Stmt::Label { name, body, attrs: AttrList::EMPTY })
881        }
882
883        /// `goto name;`.
884        fn goto(&mut self, text: &str) -> ast::StmtId {
885            let name = self.name(text);
886            self.stmt(ast::Stmt::Goto(name))
887        }
888
889        /// `__label__ a, b;`.
890        fn local_labels(&mut self, names: &[&str]) -> ast::StmtId {
891            let names: Vec<Symbol> = names.iter().map(|text| self.name(text)).collect();
892            let names = self.ast.add_symbol_list(&names);
893            self.stmt(ast::Stmt::LocalLabels(names))
894        }
895
896        /// `case lo: body`, or GNU's `case lo ... hi: body`.
897        fn case(&mut self, lo: u128, hi: Option<u128>, body: Option<ast::StmtId>) -> ast::StmtId {
898            let lo = self.int(lo);
899            let hi = hi.map(|hi| self.int(hi));
900            self.stmt(ast::Stmt::Case { lo, hi, body })
901        }
902
903        /// `switch (scrutinee) { ... }`.
904        fn switch(&mut self, scrutinee: ast::ExprId, body: &[ast::StmtId]) -> ast::StmtId {
905            let body = self.block(body);
906            self.stmt(ast::Stmt::Switch { scrutinee, body })
907        }
908
909        fn checker(&self) -> Checker<'_> {
910            Checker::new(&self.ast, Context::new(&self.names, &self.target, Std::C23))
911        }
912    }
913
914    /// The tree under one statement, which is what most assertions here are about.
915    fn dump(checker: &Checker<'_>, id: StmtId) -> String {
916        let mut printer = Printer::new(&checker.tast, &checker.types, checker.cx.names);
917        printer.stmt(id);
918        printer.finish()
919    }
920
921    /// What was reported, as the messages alone, notes included.
922    fn messages(checker: &Checker<'_>) -> Vec<String> {
923        checker
924            .errors
925            .diagnostics()
926            .iter()
927            .flat_map(|d| {
928                std::iter::once(d.message.clone())
929                    .chain(d.children.iter().map(|n| n.message.clone()))
930            })
931            .collect()
932    }
933
934    /// The one message that was reported, which is what most of these tests expect.
935    fn message(checker: &Checker<'_>) -> String {
936        let mut reported = messages(checker);
937        assert_eq!(reported.len(), 1, "expected exactly one diagnostic, got {reported:?}");
938        reported.pop().expect("one message")
939    }
940
941    /// What was reported, as the severity and the message of each, so that a test can say which
942    /// of the two a diagnostic is. gcc 14 turned several of these from warnings into errors and
943    /// the difference is the whole point of some of the tests below.
944    fn reported(checker: &Checker<'_>) -> Vec<String> {
945        checker
946            .errors
947            .diagnostics()
948            .iter()
949            .map(|d| format!("{}: {}", d.severity.as_str(), d.message))
950            .collect()
951    }
952
953    #[test]
954    fn a_block_is_a_scope_and_a_name_declared_in_one_is_gone_after_it() {
955        let mut f = Fixture::new();
956        let specs = f.int_specs();
957        let declared = f.local(specs, "x");
958        let declared = f.stmt(ast::Stmt::Decl(declared));
959        let inner = f.block(&[declared]);
960        let use_x = f.use_name("x");
961        let after = f.expr_stmt(use_x);
962        let outer = f.block(&[inner, after]);
963
964        let mut c = f.checker();
965        let void = c.types.void();
966        c.check_stmt(void, outer);
967
968        assert_eq!(message(&c), "'x' undeclared (first use in this function)");
969    }
970
971    #[test]
972    fn a_name_nobody_declared_is_reported_once_per_function_and_not_once_per_use() {
973        // The wording promises it: `first use in this function` said three times is a sentence
974        // arguing with itself. A misspelled name written in a loop body is one mistake, and one
975        // message is what makes the next mistake in the file visible.
976        let mut f = Fixture::new();
977        let first = f.use_name("nope");
978        let first = f.expr_stmt(first);
979        let second = f.use_name("nope");
980        let second = f.expr_stmt(second);
981        let body = f.block(&[first, second]);
982
983        let mut c = f.checker();
984        let void = c.types.void();
985        let previous = c.open_body(void, Span::DUMMY);
986        c.check_stmt(void, body);
987        c.close_body(previous);
988
989        assert_eq!(message(&c), "'nope' undeclared (first use in this function)");
990    }
991
992    #[test]
993    fn an_expression_statement_holds_the_value_and_not_a_conversion_of_it_to_void() {
994        let mut f = Fixture::new();
995        let one = f.int(1);
996        let stmt = f.expr_stmt(one);
997
998        let mut c = f.checker();
999        let void = c.types.void();
1000        let id = c.check_stmt(void, stmt);
1001
1002        assert_eq!(dump(&c, id), "expr\n  const 1 : int\n");
1003        assert!(c.errors.is_empty());
1004    }
1005
1006    #[test]
1007    fn a_statement_expression_has_the_type_of_its_last_statement() {
1008        let mut f = Fixture::new();
1009        let one = f.int(1);
1010        let inner = f.expr_stmt(one);
1011        let body = f.block(&[inner]);
1012        let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1013        let stmt = f.expr_stmt(value);
1014
1015        let mut c = f.checker();
1016        let void = c.types.void();
1017        let id = c.check_stmt(void, stmt);
1018
1019        assert_eq!(
1020            dump(&c, id),
1021            "expr\n  stmt-expr : int\n    block\n      expr\n        const 1 : int\n"
1022        );
1023        assert!(c.errors.is_empty());
1024    }
1025
1026    #[test]
1027    fn a_statement_expression_that_ends_in_something_else_is_void() {
1028        let mut f = Fixture::new();
1029        let body = f.block(&[]);
1030        let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1031        let stmt = f.expr_stmt(value);
1032
1033        let mut c = f.checker();
1034        let void = c.types.void();
1035        let id = c.check_stmt(void, stmt);
1036
1037        assert_eq!(dump(&c, id), "expr\n  stmt-expr : void\n    block\n");
1038        assert!(c.errors.is_empty());
1039    }
1040
1041    #[test]
1042    fn the_declaration_in_a_for_clause_scopes_to_the_loop_and_not_to_what_follows() {
1043        let mut f = Fixture::new();
1044        let specs = f.int_specs();
1045        let declared = f.local(specs, "i");
1046        let empty = f.stmt(ast::Stmt::Empty);
1047        let loop_stmt = f.stmt(ast::Stmt::For {
1048            init: ForInit::Decl(declared),
1049            cond: None,
1050            step: None,
1051            body: empty,
1052        });
1053        let use_i = f.use_name("i");
1054        let after = f.expr_stmt(use_i);
1055        let outer = f.block(&[loop_stmt, after]);
1056
1057        let mut c = f.checker();
1058        let void = c.types.void();
1059        c.check_stmt(void, outer);
1060
1061        assert_eq!(message(&c), "'i' undeclared (first use in this function)");
1062    }
1063
1064    #[test]
1065    fn a_static_in_a_for_clause_is_accepted_and_only_pedantic_says_anything_about_it() {
1066        let mut f = Fixture::new();
1067        let mut specs = DeclSpecs::empty(Span::DUMMY);
1068        let builtin = Builtin::NONE.add(BuiltinSet::INT).expect("a keyword written once");
1069        specs.ty = TypeSpec::Builtin(builtin);
1070        specs.storage = Some(StorageClass::Static);
1071        let specs = f.ast.add_specs(specs);
1072        let declared = f.local(specs, "i");
1073        let empty = f.stmt(ast::Stmt::Empty);
1074        let loop_stmt = f.stmt(ast::Stmt::For {
1075            init: ForInit::Decl(declared),
1076            cond: None,
1077            step: None,
1078            body: empty,
1079        });
1080
1081        let mut c = f.checker();
1082        let void = c.types.void();
1083        c.check_stmt(void, loop_stmt);
1084        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1085
1086        let mut c = f.checker();
1087        c.cx.pedantic = true;
1088        let void = c.types.void();
1089        c.check_stmt(void, loop_stmt);
1090        assert_eq!(
1091            reported(&c),
1092            ["warning: declaration of static variable 'i' in 'for' loop initial declaration"]
1093        );
1094    }
1095
1096    #[test]
1097    fn continue_needs_a_loop_and_is_not_satisfied_by_a_switch() {
1098        let mut f = Fixture::new();
1099        let one = f.int(1);
1100        let go_on = f.stmt(ast::Stmt::Continue);
1101        let case = f.stmt(ast::Stmt::Case { lo: one, hi: None, body: Some(go_on) });
1102        let scrutinee = f.int(0);
1103        let switch = f.switch(scrutinee, &[case]);
1104
1105        let mut c = f.checker();
1106        let void = c.types.void();
1107        c.check_stmt(void, switch);
1108
1109        assert_eq!(message(&c), "continue statement not within a loop");
1110    }
1111
1112    #[test]
1113    fn break_is_satisfied_by_a_switch_and_reported_where_there_is_neither() {
1114        let mut f = Fixture::new();
1115        let stop = f.stmt(ast::Stmt::Break);
1116        let scrutinee = f.int(0);
1117        let switch = f.switch(scrutinee, &[stop]);
1118        let loose = f.stmt(ast::Stmt::Break);
1119
1120        let mut c = f.checker();
1121        let void = c.types.void();
1122        c.check_stmt(void, switch);
1123        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1124
1125        let mut c = f.checker();
1126        let void = c.types.void();
1127        c.check_stmt(void, loose);
1128        assert_eq!(message(&c), "break statement not within loop or switch");
1129    }
1130
1131    #[test]
1132    fn a_goto_resolves_to_a_label_the_function_defines_further_down() {
1133        let mut f = Fixture::new();
1134        let jump = f.goto("done");
1135        let empty = f.stmt(ast::Stmt::Empty);
1136        let target = f.labelled("done", Some(empty));
1137        let body = f.block(&[jump, target]);
1138
1139        let mut c = f.checker();
1140        let void = c.types.void();
1141        let id = c.check_stmt(void, body);
1142
1143        assert_eq!(dump(&c, id), "block\n  goto #0 done\n  label #0 done\n    empty\n");
1144        assert!(c.errors.is_empty());
1145    }
1146
1147    #[test]
1148    fn a_label_that_is_jumped_to_and_never_defined_is_reported_at_the_jump() {
1149        let mut f = Fixture::new();
1150        let jump = f.goto("away");
1151        let body = f.block(&[jump]);
1152
1153        let mut c = f.checker();
1154        let void = c.types.void();
1155        c.check_stmt(void, body);
1156
1157        assert_eq!(message(&c), "label 'away' used but not defined");
1158    }
1159
1160    #[test]
1161    fn the_address_of_a_label_is_a_use_of_it_and_not_a_definition() {
1162        let mut f = Fixture::new();
1163        let away = f.name("away");
1164        let value = f.ast.expr(ast::Expr::LabelAddr(away), Span::DUMMY);
1165        let stmt = f.expr_stmt(value);
1166
1167        let mut c = f.checker();
1168        let void = c.types.void();
1169        let id = c.check_stmt(void, stmt);
1170
1171        assert_eq!(dump(&c, id), "expr\n  label-addr #0 away : void *\n");
1172        assert_eq!(message(&c), "label 'away' used but not defined");
1173    }
1174
1175    #[test]
1176    fn one_label_defined_twice_is_an_error_that_points_at_the_first() {
1177        let mut f = Fixture::new();
1178        let first = f.labelled("here", None);
1179        let second = f.labelled("here", None);
1180        let body = f.block(&[first, second]);
1181
1182        let mut c = f.checker();
1183        let void = c.types.void();
1184        c.check_stmt(void, body);
1185
1186        assert_eq!(
1187            messages(&c),
1188            ["duplicate label 'here'", "previous definition of 'here' with type 'void'",]
1189        );
1190    }
1191
1192    #[test]
1193    fn a_local_label_is_undone_when_its_block_ends_so_two_blocks_may_declare_one_name() {
1194        let mut f = Fixture::new();
1195        let sibling = |f: &mut Fixture| {
1196            let declared = f.local_labels(&["done"]);
1197            let jump = f.goto("done");
1198            let target = f.labelled("done", None);
1199            f.block(&[declared, jump, target])
1200        };
1201        let first = sibling(&mut f);
1202        let second = sibling(&mut f);
1203        let body = f.block(&[first, second]);
1204
1205        let mut c = f.checker();
1206        let void = c.types.void();
1207        let id = c.check_stmt(void, body);
1208
1209        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1210        assert_eq!(
1211            dump(&c, id),
1212            "block\n  block\n    empty\n    goto #0 done\n    label #0 done\n      empty\n  \
1213             block\n    empty\n    goto #1 done\n    label #1 done\n      empty\n"
1214        );
1215    }
1216
1217    #[test]
1218    fn a_local_label_that_nothing_defines_is_reported_when_its_block_ends() {
1219        let mut f = Fixture::new();
1220        let declared = f.local_labels(&["done"]);
1221        let jump = f.goto("done");
1222        let inner = f.block(&[declared, jump]);
1223        let target = f.labelled("done", None);
1224        let body = f.block(&[inner, target]);
1225
1226        let mut c = f.checker();
1227        let void = c.types.void();
1228        c.check_stmt(void, body);
1229
1230        assert_eq!(message(&c), "label 'done' used but not defined");
1231    }
1232
1233    #[test]
1234    fn a_computed_goto_wants_something_that_could_be_an_address() {
1235        let mut f = Fixture::new();
1236        let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1237        let zero = f.int(0);
1238        let target = f.cast(specs, zero);
1239        let stmt = f.stmt(ast::Stmt::GotoExpr(target));
1240
1241        let mut c = f.checker();
1242        let void = c.types.void();
1243        c.check_stmt(void, stmt);
1244
1245        assert_eq!(message(&c), "computed goto must be pointer type");
1246    }
1247
1248    #[test]
1249    fn a_switch_on_something_that_is_not_an_integer_is_an_error() {
1250        let mut f = Fixture::new();
1251        let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1252        let zero = f.int(0);
1253        let scrutinee = f.cast(specs, zero);
1254        let switch = f.switch(scrutinee, &[]);
1255
1256        let mut c = f.checker();
1257        let void = c.types.void();
1258        c.check_stmt(void, switch);
1259
1260        assert_eq!(message(&c), "switch quantity not an integer");
1261    }
1262
1263    #[test]
1264    fn the_cases_of_a_switch_are_one_table_in_the_order_they_were_written() {
1265        let mut f = Fixture::new();
1266        let first = f.case(1, None, None);
1267        let second = f.case(4, Some(6), None);
1268        let default = f.stmt(ast::Stmt::Default { body: None });
1269        let scrutinee = f.int(0);
1270        let switch = f.switch(scrutinee, &[first, second, default]);
1271
1272        let mut c = f.checker();
1273        let void = c.types.void();
1274        let id = c.check_stmt(void, switch);
1275
1276        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1277        assert_eq!(
1278            dump(&c, id),
1279            "switch\n  cond\n    const 0 : int\n  cases\n    case #0 1\n    case #1 4 ... 6\n    \
1280             default\n  body\n    block\n      case #0\n        empty\n      case #1\n        \
1281             empty\n      default\n        empty\n"
1282        );
1283    }
1284
1285    #[test]
1286    fn two_labels_on_one_statement_are_in_the_table_the_way_round_they_were_written() {
1287        // `case 1: case 2: ;` is one labelled statement inside another, so the checking runs
1288        // inside out. The table is a record of what the user wrote and does not follow it.
1289        let mut f = Fixture::new();
1290        let inner = f.case(2, None, None);
1291        let outer = f.case(1, None, Some(inner));
1292        let scrutinee = f.int(0);
1293        let switch = f.switch(scrutinee, &[outer]);
1294
1295        let mut c = f.checker();
1296        let void = c.types.void();
1297        let id = c.check_stmt(void, switch);
1298
1299        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1300        assert_eq!(
1301            dump(&c, id),
1302            "switch\n  cond\n    const 0 : int\n  cases\n    case #0 1\n    case #1 2\n  body\n    \
1303             block\n      case #0\n        case #1\n          empty\n"
1304        );
1305    }
1306
1307    #[test]
1308    fn a_case_that_covers_a_value_an_earlier_one_covers_is_a_duplicate() {
1309        let mut f = Fixture::new();
1310        let first = f.case(1, Some(3), None);
1311        let second = f.case(2, None, None);
1312        let scrutinee = f.int(0);
1313        let switch = f.switch(scrutinee, &[first, second]);
1314
1315        let mut c = f.checker();
1316        let void = c.types.void();
1317        c.check_stmt(void, switch);
1318
1319        assert_eq!(messages(&c), ["duplicate case value", "previously used here"]);
1320    }
1321
1322    #[test]
1323    fn a_case_outside_a_switch_is_an_error_and_so_is_a_default() {
1324        let mut f = Fixture::new();
1325        let case = f.case(1, None, None);
1326        let default = f.stmt(ast::Stmt::Default { body: None });
1327        let body = f.block(&[case, default]);
1328
1329        let mut c = f.checker();
1330        let void = c.types.void();
1331        c.check_stmt(void, body);
1332
1333        assert_eq!(
1334            messages(&c),
1335            [
1336                "case label not within a switch statement",
1337                "'default' label not within a switch statement",
1338            ]
1339        );
1340    }
1341
1342    #[test]
1343    fn a_case_label_that_is_not_a_constant_is_an_error() {
1344        let mut f = Fixture::new();
1345        let specs = f.int_specs();
1346        let declared = f.local(specs, "n");
1347        let declared = f.stmt(ast::Stmt::Decl(declared));
1348        let use_n = f.use_name("n");
1349        let case = f.stmt(ast::Stmt::Case { lo: use_n, hi: None, body: None });
1350        let scrutinee = f.int(0);
1351        let switch = f.switch(scrutinee, &[case]);
1352        let body = f.block(&[declared, switch]);
1353
1354        let mut c = f.checker();
1355        let void = c.types.void();
1356        c.check_stmt(void, body);
1357
1358        assert_eq!(message(&c), "case label does not reduce to an integer constant");
1359    }
1360
1361    #[test]
1362    fn a_case_range_that_runs_backwards_is_empty() {
1363        let mut f = Fixture::new();
1364        let case = f.case(6, Some(4), None);
1365        let scrutinee = f.int(0);
1366        let switch = f.switch(scrutinee, &[case]);
1367
1368        let mut c = f.checker();
1369        let void = c.types.void();
1370        c.check_stmt(void, switch);
1371
1372        assert_eq!(reported(&c), ["warning: empty range specified"]);
1373    }
1374
1375    #[test]
1376    fn a_case_is_measured_against_the_type_that_was_written_and_not_the_promoted_one() {
1377        let mut f = Fixture::new();
1378        let specs = f.keywords(&[BuiltinSet::CHAR]);
1379        let zero = f.int(0);
1380        let scrutinee = f.cast(specs, zero);
1381        let case = f.case(300, None, None);
1382        let switch = f.switch(scrutinee, &[case]);
1383
1384        let mut c = f.checker();
1385        let void = c.types.void();
1386        c.check_stmt(void, switch);
1387
1388        assert_eq!(reported(&c), ["warning: case label value exceeds maximum value for type"]);
1389    }
1390
1391    #[test]
1392    fn two_defaults_in_one_switch_are_an_error_that_points_at_the_first() {
1393        let mut f = Fixture::new();
1394        let first = f.stmt(ast::Stmt::Default { body: None });
1395        let second = f.stmt(ast::Stmt::Default { body: None });
1396        let scrutinee = f.int(0);
1397        let switch = f.switch(scrutinee, &[first, second]);
1398
1399        let mut c = f.checker();
1400        let void = c.types.void();
1401        c.check_stmt(void, switch);
1402
1403        assert_eq!(
1404            messages(&c),
1405            ["multiple default labels in one switch", "this is the first default label"]
1406        );
1407    }
1408
1409    #[test]
1410    fn a_nested_switch_keeps_its_cases_to_itself() {
1411        let mut f = Fixture::new();
1412        let inner_case = f.case(1, None, None);
1413        let inner_scrutinee = f.int(0);
1414        let inner = f.switch(inner_scrutinee, &[inner_case]);
1415        let outer_case = f.case(1, None, Some(inner));
1416        let outer_scrutinee = f.int(0);
1417        let outer = f.switch(outer_scrutinee, &[outer_case]);
1418
1419        let mut c = f.checker();
1420        let void = c.types.void();
1421        let id = c.check_stmt(void, outer);
1422
1423        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1424        assert_eq!(
1425            dump(&c, id),
1426            "switch\n  cond\n    const 0 : int\n  cases\n    case #1 1\n  body\n    block\n      \
1427             case #1\n        switch\n          cond\n            const 0 : int\n          \
1428             cases\n            case #0 1\n          body\n            block\n              case \
1429             #0\n                empty\n"
1430        );
1431    }
1432
1433    #[test]
1434    fn a_bare_return_from_a_function_that_promised_a_value_is_an_error() {
1435        let mut f = Fixture::new();
1436        let stmt = f.stmt(ast::Stmt::Return(None));
1437
1438        let mut c = f.checker();
1439        let int = c.int();
1440        c.check_stmt(int, stmt);
1441
1442        assert_eq!(reported(&c), ["error: 'return' with no value, in function returning non-void"]);
1443        assert_eq!(messages(&c).len(), 2, "the note is attached to it");
1444    }
1445
1446    #[test]
1447    fn a_value_returned_from_a_function_returning_void_is_an_error() {
1448        let mut f = Fixture::new();
1449        let one = f.int(1);
1450        let stmt = f.stmt(ast::Stmt::Return(Some(one)));
1451
1452        let mut c = f.checker();
1453        let void = c.types.void();
1454        c.check_stmt(void, stmt);
1455
1456        assert_eq!(reported(&c), ["error: 'return' with a value, in function returning void"]);
1457    }
1458
1459    #[test]
1460    fn a_void_value_returned_from_a_function_returning_void_is_what_a_wrapper_writes() {
1461        let mut f = Fixture::new();
1462        let specs = f.keywords(&[BuiltinSet::VOID]);
1463        let one = f.int(1);
1464        let value = f.cast(specs, one);
1465        let stmt = f.stmt(ast::Stmt::Return(Some(value)));
1466
1467        let mut c = f.checker();
1468        let void = c.types.void();
1469        c.check_stmt(void, stmt);
1470
1471        assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1472    }
1473
1474    #[test]
1475    fn a_returned_value_is_converted_to_the_return_type() {
1476        let mut f = Fixture::new();
1477        let one = f.int(1);
1478        let stmt = f.stmt(ast::Stmt::Return(Some(one)));
1479
1480        let mut c = f.checker();
1481        let long = c.types.int(IntKind::Long);
1482        let id = c.check_stmt(long, stmt);
1483
1484        assert_eq!(dump(&c, id), "return\n  convert arithmetic : long\n    const 1 : int\n");
1485        assert!(c.errors.is_empty());
1486    }
1487}