Skip to main content

rucc_ast/
expr.rs

1//! Expressions.
2//!
3//! Design: `spec/06-lexer-and-parser.md` section 6.2.
4//!
5//! Nothing here is desugared. `a[i]` is a subscript and not `*(a + i)`, `a += b` is a compound
6//! assignment and not `a = a + b`, and a cast keeps the type the source wrote rather than the
7//! conversion it turns into. That is what makes a diagnostic able to quote the program back to
8//! the person who wrote it, and it is what makes `--emit=ast` worth looking at. The rewriting
9//! happens when the IR is built, in `spec/08-ir.md`.
10
11use rucc_base::Symbol;
12
13use crate::ast::{CharId, DesignatorList, ExprList, FloatId, GenericList, IntId, StrId};
14use crate::decl::TypeNameId;
15use crate::init::InitId;
16use crate::stmt::StmtId;
17
18/// An expression in the expression arena.
19pub type ExprId = rucc_base::Idx<Expr>;
20
21/// One expression node.
22///
23/// Sixteen bytes, which is what the widest variant needs and what the whole arena therefore
24/// costs per node. Anything that would not fit is an index into a side table on
25/// [`Ast`](crate::Ast), which is why a call holds a range and not a vector.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum Expr {
28    /// A parse that did not work out.
29    ///
30    /// Poisoned, per section 6.8: semantic analysis says nothing about a node that is already
31    /// the result of a diagnostic, which is the mechanism that stops one syntax error becoming
32    /// forty type errors.
33    Error,
34    /// An identifier, before anything has looked it up.
35    ///
36    /// The parser already knows whether the name is a typedef name, because it had to know to
37    /// parse the surrounding text at all, but it does not resolve it to a declaration. That is
38    /// semantic analysis, which has the scopes and the linkage rules.
39    Name(Symbol),
40    /// An integer constant, in the constant table on [`Ast`](crate::Ast).
41    Int(IntId),
42    /// A floating constant.
43    Float(FloatId),
44    /// A character constant.
45    Char(CharId),
46    /// A string literal, with the adjacent ones already joined onto it by phase 7.
47    Str(StrId),
48    /// `true` or `false`, which C23 made constants rather than macros.
49    Bool(bool),
50    /// `nullptr`.
51    Nullptr,
52    /// `base[index]`, in the order it was written, which is not always the pointer first.
53    Index {
54        /// The left operand.
55        base: ExprId,
56        /// The operand in the brackets.
57        index: ExprId,
58    },
59    /// `callee(args)`.
60    Call {
61        /// What is being called, which is an expression and not necessarily a name.
62        callee: ExprId,
63        /// The arguments, in order.
64        args: ExprList,
65    },
66    /// `base.name` or `base->name`.
67    Member {
68        /// The left operand.
69        base: ExprId,
70        /// The member name, which lives in its own namespace and is not looked up here.
71        name: Symbol,
72        /// Whether it was written with an arrow.
73        arrow: bool,
74    },
75    /// A prefix or postfix operator on one operand.
76    Unary {
77        /// Which operator.
78        op: UnaryOp,
79        /// The operand.
80        operand: ExprId,
81    },
82    /// A binary operator, including the ones that do not evaluate both sides.
83    Binary {
84        /// Which operator.
85        op: BinaryOp,
86        /// The left operand.
87        lhs: ExprId,
88        /// The right operand.
89        rhs: ExprId,
90    },
91    /// `lhs = rhs`, or a compound assignment with the operator kept as written.
92    Assign {
93        /// The operator in a compound assignment, and `None` for a plain one.
94        op: Option<BinaryOp>,
95        /// The left operand.
96        lhs: ExprId,
97        /// The right operand.
98        rhs: ExprId,
99    },
100    /// `cond ? then : otherwise`, where `then` is absent in GNU's `cond ?: otherwise`.
101    Cond {
102        /// The condition.
103        cond: ExprId,
104        /// The second operand, absent when the middle was left out.
105        then: Option<ExprId>,
106        /// The third operand.
107        otherwise: ExprId,
108    },
109    /// `lhs, rhs`.
110    ///
111    /// Its own node rather than a [`BinaryOp`], because the comma operator is a sequence point
112    /// with a discarded left side and shares nothing with arithmetic but its spelling.
113    Comma {
114        /// The operand whose value is thrown away.
115        lhs: ExprId,
116        /// The operand whose value the expression has.
117        rhs: ExprId,
118    },
119    /// `(ty)operand`.
120    Cast {
121        /// The type name in the parentheses.
122        ty: TypeNameId,
123        /// What is being converted.
124        operand: ExprId,
125    },
126    /// `(ty){ ... }`, which is an object and not a conversion.
127    CompoundLiteral {
128        /// The type name in the parentheses.
129        ty: TypeNameId,
130        /// The braced initializer.
131        init: InitId,
132    },
133    /// `sizeof operand`, written without parentheses around a type.
134    SizeofExpr(ExprId),
135    /// `sizeof (ty)`.
136    SizeofType(TypeNameId),
137    /// `alignof operand`, which is GNU's `__alignof__` since ISO C only has the type form.
138    AlignofExpr(ExprId),
139    /// `alignof (ty)`.
140    AlignofType(TypeNameId),
141    /// `_Generic(control, ...)`.
142    Generic {
143        /// The controlling expression, which is never evaluated.
144        control: ExprId,
145        /// The associations, in the order they were written, including the default one.
146        assocs: GenericList,
147    },
148    /// `({ ... })`, GNU's statement expression, whose value is its last expression statement.
149    StmtExpr(StmtId),
150    /// `&&label`, GNU's label address.
151    LabelAddr(Symbol),
152    /// `__builtin_offsetof(ty, path)`, where `path` is a member and not an expression.
153    Offsetof {
154        /// The type being measured.
155        ty: TypeNameId,
156        /// The member path, which is a designator list because `a.b[3].c` is legal here.
157        path: DesignatorList,
158    },
159    /// `__builtin_choose_expr(cond, then, otherwise)`.
160    ///
161    /// The whole reason this exists is that the branch not chosen is never type checked, so it
162    /// has to survive to semantic analysis as itself rather than as a conditional.
163    ChooseExpr {
164        /// The condition, which must be a constant expression.
165        cond: ExprId,
166        /// The branch taken when the condition is nonzero.
167        then: ExprId,
168        /// The other branch.
169        otherwise: ExprId,
170    },
171    /// `__builtin_types_compatible_p(a, b)`.
172    TypesCompatible {
173        /// The first type.
174        a: TypeNameId,
175        /// The second type.
176        b: TypeNameId,
177    },
178    /// `__builtin_va_arg(list, ty)`.
179    VaArg {
180        /// The argument list.
181        list: ExprId,
182        /// The type being fetched.
183        ty: TypeNameId,
184    },
185    /// `__builtin_va_start(list, last)`.
186    VaStart {
187        /// The argument list.
188        list: ExprId,
189        /// The last named parameter, which is a name rather than a value: nothing reads the
190        /// object, and what it is there for is to say where the named arguments stopped. C23
191        /// allows it to be left out, and so does gcc in every dialect, so it is optional here.
192        last: Option<ExprId>,
193    },
194    /// `__builtin_va_end(list)`.
195    VaEnd {
196        /// The argument list.
197        list: ExprId,
198    },
199    /// `__builtin_va_copy(dst, src)`.
200    VaCopy {
201        /// The list being written.
202        dst: ExprId,
203        /// The list being read, which is left where it was.
204        src: ExprId,
205    },
206    /// `__extension__ operand`, which turns the pedantic diagnostics off inside it.
207    Extension(ExprId),
208}
209
210/// An operator with one operand.
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub enum UnaryOp {
213    /// `+`, which is not a no-op: it promotes.
214    Plus,
215    /// `-`.
216    Minus,
217    /// `!`.
218    Not,
219    /// `~`.
220    BitNot,
221    /// `*`.
222    Deref,
223    /// `&`.
224    AddrOf,
225    /// `++x`.
226    PreInc,
227    /// `--x`.
228    PreDec,
229    /// `x++`.
230    PostInc,
231    /// `x--`.
232    PostDec,
233    /// `__real__ x`, GNU.
234    Real,
235    /// `__imag__ x`, GNU.
236    Imag,
237}
238
239impl UnaryOp {
240    /// The spelling, for the printer and for diagnostics.
241    #[must_use]
242    pub const fn spelling(self) -> &'static str {
243        match self {
244            UnaryOp::Plus => "+",
245            UnaryOp::Minus => "-",
246            UnaryOp::Not => "!",
247            UnaryOp::BitNot => "~",
248            UnaryOp::Deref => "*",
249            UnaryOp::AddrOf => "&",
250            UnaryOp::PreInc | UnaryOp::PostInc => "++",
251            UnaryOp::PreDec | UnaryOp::PostDec => "--",
252            UnaryOp::Real => "__real__",
253            UnaryOp::Imag => "__imag__",
254        }
255    }
256
257    /// Whether the operator is written after its operand.
258    #[must_use]
259    pub const fn is_postfix(self) -> bool {
260        matches!(self, UnaryOp::PostInc | UnaryOp::PostDec)
261    }
262}
263
264/// An operator with two operands.
265///
266/// The comma operator is not here; it is [`Expr::Comma`]. Assignment is not here either, and
267/// the operator part of a compound assignment reuses this enum, which is why the values that
268/// cannot appear in one ([`BinaryOp::LogAnd`] and the comparisons) are simply never built by
269/// the parser rather than being a second enum.
270#[derive(Debug, Clone, Copy, PartialEq, Eq)]
271pub enum BinaryOp {
272    /// `*`.
273    Mul,
274    /// `/`.
275    Div,
276    /// `%`.
277    Rem,
278    /// `+`.
279    Add,
280    /// `-`.
281    Sub,
282    /// `<<`.
283    Shl,
284    /// `>>`.
285    Shr,
286    /// `<`.
287    Lt,
288    /// `>`.
289    Gt,
290    /// `<=`.
291    Le,
292    /// `>=`.
293    Ge,
294    /// `==`.
295    Eq,
296    /// `!=`.
297    Ne,
298    /// `&`.
299    BitAnd,
300    /// `^`.
301    BitXor,
302    /// `|`.
303    BitOr,
304    /// `&&`, which does not evaluate its right operand unless it has to.
305    LogAnd,
306    /// `||`, likewise.
307    LogOr,
308}
309
310impl BinaryOp {
311    /// The spelling, for the printer and for diagnostics.
312    #[must_use]
313    pub const fn spelling(self) -> &'static str {
314        match self {
315            BinaryOp::Mul => "*",
316            BinaryOp::Div => "/",
317            BinaryOp::Rem => "%",
318            BinaryOp::Add => "+",
319            BinaryOp::Sub => "-",
320            BinaryOp::Shl => "<<",
321            BinaryOp::Shr => ">>",
322            BinaryOp::Lt => "<",
323            BinaryOp::Gt => ">",
324            BinaryOp::Le => "<=",
325            BinaryOp::Ge => ">=",
326            BinaryOp::Eq => "==",
327            BinaryOp::Ne => "!=",
328            BinaryOp::BitAnd => "&",
329            BinaryOp::BitXor => "^",
330            BinaryOp::BitOr => "|",
331            BinaryOp::LogAnd => "&&",
332            BinaryOp::LogOr => "||",
333        }
334    }
335
336    /// Whether the operator sequences its left operand before its right, which only the two
337    /// short-circuiting ones do.
338    #[must_use]
339    pub const fn is_short_circuit(self) -> bool {
340        matches!(self, BinaryOp::LogAnd | BinaryOp::LogOr)
341    }
342
343    /// Whether the operator is one of the six relational and equality ones.
344    ///
345    /// What the six have in common is the answer rather than the operands: it is one bit, and
346    /// C makes it an `int` holding zero or one. Everything that has to treat them alike asks
347    /// this rather than spelling the six out again.
348    #[must_use]
349    pub const fn is_comparison(self) -> bool {
350        matches!(
351            self,
352            BinaryOp::Lt | BinaryOp::Gt | BinaryOp::Le | BinaryOp::Ge | BinaryOp::Eq | BinaryOp::Ne
353        )
354    }
355}
356
357/// One arm of a `_Generic` selection.
358#[derive(Debug, Clone, Copy, PartialEq, Eq)]
359pub struct GenericAssoc {
360    /// The type this arm matches, and `None` for the `default` arm.
361    pub ty: Option<TypeNameId>,
362    /// The expression the arm gives, which is only evaluated if the arm is chosen.
363    pub value: ExprId,
364}
365
366#[cfg(test)]
367mod tests {
368    use super::*;
369
370    #[test]
371    fn an_expression_is_sixteen_bytes() {
372        // The arena is the biggest array in the frontend and the one every pass walks. If this
373        // fails, a variant grew and the fix is a side table, not a bigger node.
374        assert_eq!(size_of::<Expr>(), 16);
375    }
376
377    #[test]
378    fn an_expression_id_is_four_bytes_even_when_optional() {
379        assert_eq!(size_of::<ExprId>(), 4);
380        assert_eq!(size_of::<Option<ExprId>>(), 4);
381    }
382
383    #[test]
384    fn postfix_increment_is_the_only_kind_that_is_postfix() {
385        assert!(UnaryOp::PostInc.is_postfix());
386        assert!(UnaryOp::PostDec.is_postfix());
387        assert!(!UnaryOp::PreInc.is_postfix());
388        assert_eq!(UnaryOp::PostInc.spelling(), UnaryOp::PreInc.spelling());
389    }
390
391    #[test]
392    fn the_six_relational_and_equality_operators_are_the_comparisons() {
393        let all =
394            [BinaryOp::Lt, BinaryOp::Gt, BinaryOp::Le, BinaryOp::Ge, BinaryOp::Eq, BinaryOp::Ne];
395        assert!(all.iter().all(|op| op.is_comparison()));
396        assert!(!BinaryOp::Add.is_comparison());
397        assert!(!BinaryOp::LogAnd.is_comparison());
398    }
399
400    #[test]
401    fn only_the_logical_operators_short_circuit() {
402        assert!(BinaryOp::LogAnd.is_short_circuit());
403        assert!(BinaryOp::LogOr.is_short_circuit());
404        assert!(!BinaryOp::BitAnd.is_short_circuit());
405    }
406}