Skip to main content

rucc_ast/
stmt.rs

1//! Statements.
2//!
3//! Design: `spec/06-lexer-and-parser.md` section 6.2.
4//!
5//! Like the expressions, these are not desugared. A `for` loop stays a `for` loop instead of
6//! becoming a `while`, a `switch` keeps its cases as a tree instead of a jump table, and a
7//! `do` loop is not a `while` with the test moved. All of that happens when the IR is built.
8//!
9//! A declaration inside a block is [`Stmt::Decl`], holding the whole declaration and so all of
10//! its declarators, which keeps `int a, b;` one statement instead of two and keeps the shape
11//! the same at block scope as at file scope.
12
13use rucc_base::Symbol;
14
15use crate::asm::AsmId;
16use crate::ast::{AttrList, StmtList, SymbolList};
17use crate::decl::DeclId;
18use crate::expr::ExprId;
19
20/// A statement in the statement arena.
21pub type StmtId = rucc_base::Idx<Stmt>;
22
23/// One statement node.
24///
25/// Twenty bytes, set by [`Stmt::For`], which is the only variant with four operands and which
26/// gets away with it because [`ForInit`] has spare tag values for the statement's own tag to
27/// live in. Splitting the loop's clauses into a side table would buy four bytes a statement and
28/// cost an indirection on the most common loop in C, so they stay inline.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum Stmt {
31    /// A parse that did not work out. Poisoned, as [`Expr::Error`](crate::Expr::Error) is.
32    Error,
33    /// `;`.
34    Empty,
35    /// An expression evaluated for its effect.
36    Expr(ExprId),
37    /// A declaration at block scope, with all of its declarators.
38    Decl(DeclId),
39    /// `{ ... }`, which is also a scope.
40    Compound(StmtList),
41    /// `if (cond) then else otherwise`.
42    If {
43        /// The controlling expression.
44        cond: ExprId,
45        /// The statement taken when it is nonzero.
46        then: StmtId,
47        /// The `else` branch, if there was one.
48        otherwise: Option<StmtId>,
49    },
50    /// `switch (scrutinee) body`.
51    Switch {
52        /// The controlling expression.
53        scrutinee: ExprId,
54        /// The body, whose cases are found by walking it.
55        body: StmtId,
56    },
57    /// `while (cond) body`.
58    While {
59        /// The controlling expression, tested before each iteration.
60        cond: ExprId,
61        /// The body.
62        body: StmtId,
63    },
64    /// `do body while (cond);`.
65    DoWhile {
66        /// The body, which runs at least once.
67        body: StmtId,
68        /// The controlling expression, tested after each iteration.
69        cond: ExprId,
70    },
71    /// `for (init; cond; step) body`.
72    For {
73        /// The first clause, which may declare something and so may open a scope.
74        init: ForInit,
75        /// The controlling expression, absent when the clause was left empty, which means the
76        /// loop runs forever.
77        cond: Option<ExprId>,
78        /// The third clause, evaluated after each iteration.
79        step: Option<ExprId>,
80        /// The body.
81        body: StmtId,
82    },
83    /// `goto name;`.
84    Goto(Symbol),
85    /// `goto *expr;`, GNU's computed goto, which Postgres and the kernel both use.
86    GotoExpr(ExprId),
87    /// `continue;`.
88    Continue,
89    /// `break;`.
90    Break,
91    /// `return expr;`, or `return;`.
92    Return(Option<ExprId>),
93    /// `name: body`.
94    Label {
95        /// The label, which lives in a namespace of its own and is scoped to the function.
96        name: Symbol,
97        /// The statement it labels, absent when the label is the last thing in a block, which
98        /// C23 allows and which everybody wrote as `name: ;` before it.
99        body: Option<StmtId>,
100        /// Attributes on the label, such as `[[gnu::hot]]`.
101        attrs: AttrList,
102    },
103    /// `case lo: body`, or GNU's `case lo ... hi: body`.
104    Case {
105        /// The value, or the first value of a range.
106        lo: ExprId,
107        /// The last value of a GNU case range, which is included.
108        hi: Option<ExprId>,
109        /// The statement it labels, absent for the same reason as on a label.
110        body: Option<StmtId>,
111    },
112    /// `default: body`.
113    Default {
114        /// The statement it labels.
115        body: Option<StmtId>,
116    },
117    /// `__label__ a, b;`, GNU's block-local labels, which a macro needs so that two expansions
118    /// in one function do not collide.
119    LocalLabels(SymbolList),
120    /// An `asm` statement.
121    Asm(AsmId),
122}
123
124/// The first clause of a `for` statement.
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub enum ForInit {
127    /// Nothing was written.
128    None,
129    /// An expression.
130    Expr(ExprId),
131    /// A declaration, which C99 allowed and which scopes to the loop.
132    Decl(DeclId),
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn a_statement_is_twenty_bytes() {
141        assert_eq!(size_of::<Stmt>(), 20);
142    }
143
144    #[test]
145    fn a_statement_id_is_four_bytes_even_when_optional() {
146        assert_eq!(size_of::<StmtId>(), 4);
147        assert_eq!(size_of::<Option<StmtId>>(), 4);
148    }
149
150    #[test]
151    fn a_for_clause_is_eight_bytes() {
152        assert_eq!(size_of::<ForInit>(), 8);
153    }
154}