Skip to main content

rucc_sema/
stmt.rs

1//! Typed statements.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.14.
4//!
5//! The shapes are the ones the parser produced, because a `for` loop that has become a `while`
6//! loop by the time anything reports on it is a `for` loop nobody can be told about. What is
7//! different is that the conditions have been converted to `bool`, the labels and the `goto`s
8//! that reach them have been resolved to each other, and a `switch` carries the table of cases
9//! that the walk to the IR would otherwise have to collect by searching its body.
10
11use rucc_base::{Idx, IdxRange};
12
13use crate::asm::AsmId;
14use crate::decl::DeclList;
15use crate::expr::ExprId;
16use crate::tast::LabelId;
17
18/// One `case` of a `switch`, in the case table.
19pub type CaseId = Idx<Case>;
20
21/// One typed statement in the arena.
22pub type StmtId = Idx<Stmt>;
23
24/// The table of references to statements, which is what a block is a run of.
25#[derive(Debug)]
26pub struct StmtRef;
27
28/// A run of statements.
29pub type StmtList = IdxRange<StmtRef>;
30
31/// A run of the cases of one `switch`.
32pub type CaseList = IdxRange<Case>;
33
34/// A statement.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum Stmt {
37    /// A statement that was already the subject of a diagnostic.
38    Error,
39    /// `;`, which is a statement and does nothing.
40    Empty,
41    /// An expression evaluated for its effects, whose value is discarded.
42    Expr(ExprId),
43    /// `{ ... }`, which is also a scope, though the scope has been resolved by now.
44    Block(StmtList),
45    /// The declarations of one declaration statement, which introduce objects into the block.
46    Decls(DeclList),
47    /// `if (cond) then else otherwise`.
48    If {
49        /// The condition, converted to `bool`.
50        cond: ExprId,
51        /// The statement taken when it is true.
52        then: StmtId,
53        /// The statement taken when it is false, if there is one.
54        otherwise: Option<StmtId>,
55    },
56    /// `while (cond) body`.
57    While {
58        /// The condition, converted to `bool`.
59        cond: ExprId,
60        /// The body.
61        body: StmtId,
62    },
63    /// `do body while (cond);`.
64    DoWhile {
65        /// The body, which runs before the condition is first evaluated.
66        body: StmtId,
67        /// The condition, converted to `bool`.
68        cond: ExprId,
69    },
70    /// `for (init; cond; step) body`.
71    For {
72        /// The initial clause, which is a declaration or an expression or nothing.
73        init: Option<StmtId>,
74        /// The condition, converted to `bool`, or nothing for `for (;;)`.
75        cond: Option<ExprId>,
76        /// The step, evaluated for its effects.
77        step: Option<ExprId>,
78        /// The body.
79        body: StmtId,
80    },
81    /// `switch (cond) body`.
82    Switch {
83        /// The controlling expression, after its integer promotion.
84        cond: ExprId,
85        /// The body, which holds the labelled statements themselves.
86        body: StmtId,
87        /// Where each value goes, collected here so that the walk to the IR builds a jump
88        /// table from a table rather than by searching the body for labels.
89        cases: CaseList,
90        /// Where a value that matches nothing goes, absent when there is no `default`, which
91        /// is the same statement the [`Stmt::Default`] in the body labels.
92        default: Option<StmtId>,
93    },
94    /// `case value:`, which is a label on the statement it precedes.
95    ///
96    /// The value is in the case table rather than here, because two `i128` bounds would make
97    /// every statement in the arena the size of the widest one.
98    Case {
99        /// Which entry of the enclosing `switch`'s table, so that reaching a case gives its
100        /// value without searching the table for the statement that is already in hand.
101        case: CaseId,
102        /// The statement it labels.
103        body: StmtId,
104    },
105    /// `default:`, which is a label and not a case, since it has no value to be in a table.
106    ///
107    /// It is here as well as in the enclosing `switch` because the two say different things:
108    /// this says where in the body it was written, and the field on the `switch` is the entry
109    /// of the jump table. A reader that only had the field could not say where `default:` went.
110    Default {
111        /// The statement it labels.
112        body: StmtId,
113    },
114    /// `name: body`, with the label resolved.
115    Label {
116        /// Which label.
117        label: LabelId,
118        /// The statement it labels.
119        body: StmtId,
120    },
121    /// `goto name;`, with the label resolved.
122    Goto(LabelId),
123    /// `goto *expr;`, GNU's computed goto, whose target is a label address.
124    IndirectGoto(ExprId),
125    /// `asm(...)`, whose operands and labels are in the assembly table.
126    Asm(AsmId),
127    /// `break;`, which leaves the innermost loop or `switch`.
128    Break,
129    /// `continue;`, which starts the next iteration of the innermost loop.
130    Continue,
131    /// `return;` or `return expr;`, with the value already converted to the return type.
132    Return(Option<ExprId>),
133}
134
135/// One `case` of a `switch`, after its value has been folded.
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137pub struct Case {
138    /// The lowest value that reaches this case.
139    ///
140    /// The value is held in the promoted type of the controlling expression, converted there
141    /// once, so that the comparison the IR emits is between two values of one type.
142    pub low: i128,
143    /// The highest value that reaches it, which equals `low` unless this is GNU's `case 1 ... 9`.
144    pub high: i128,
145    /// The statement it labels.
146    pub body: StmtId,
147}