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 /// `__extension__ operand`, which turns the pedantic diagnostics off inside it.
186 Extension(ExprId),
187}
188
189/// An operator with one operand.
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191pub enum UnaryOp {
192 /// `+`, which is not a no-op: it promotes.
193 Plus,
194 /// `-`.
195 Minus,
196 /// `!`.
197 Not,
198 /// `~`.
199 BitNot,
200 /// `*`.
201 Deref,
202 /// `&`.
203 AddrOf,
204 /// `++x`.
205 PreInc,
206 /// `--x`.
207 PreDec,
208 /// `x++`.
209 PostInc,
210 /// `x--`.
211 PostDec,
212 /// `__real__ x`, GNU.
213 Real,
214 /// `__imag__ x`, GNU.
215 Imag,
216}
217
218impl UnaryOp {
219 /// The spelling, for the printer and for diagnostics.
220 #[must_use]
221 pub const fn spelling(self) -> &'static str {
222 match self {
223 UnaryOp::Plus => "+",
224 UnaryOp::Minus => "-",
225 UnaryOp::Not => "!",
226 UnaryOp::BitNot => "~",
227 UnaryOp::Deref => "*",
228 UnaryOp::AddrOf => "&",
229 UnaryOp::PreInc | UnaryOp::PostInc => "++",
230 UnaryOp::PreDec | UnaryOp::PostDec => "--",
231 UnaryOp::Real => "__real__",
232 UnaryOp::Imag => "__imag__",
233 }
234 }
235
236 /// Whether the operator is written after its operand.
237 #[must_use]
238 pub const fn is_postfix(self) -> bool {
239 matches!(self, UnaryOp::PostInc | UnaryOp::PostDec)
240 }
241}
242
243/// An operator with two operands.
244///
245/// The comma operator is not here; it is [`Expr::Comma`]. Assignment is not here either, and
246/// the operator part of a compound assignment reuses this enum, which is why the values that
247/// cannot appear in one ([`BinaryOp::LogAnd`] and the comparisons) are simply never built by
248/// the parser rather than being a second enum.
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250pub enum BinaryOp {
251 /// `*`.
252 Mul,
253 /// `/`.
254 Div,
255 /// `%`.
256 Rem,
257 /// `+`.
258 Add,
259 /// `-`.
260 Sub,
261 /// `<<`.
262 Shl,
263 /// `>>`.
264 Shr,
265 /// `<`.
266 Lt,
267 /// `>`.
268 Gt,
269 /// `<=`.
270 Le,
271 /// `>=`.
272 Ge,
273 /// `==`.
274 Eq,
275 /// `!=`.
276 Ne,
277 /// `&`.
278 BitAnd,
279 /// `^`.
280 BitXor,
281 /// `|`.
282 BitOr,
283 /// `&&`, which does not evaluate its right operand unless it has to.
284 LogAnd,
285 /// `||`, likewise.
286 LogOr,
287}
288
289impl BinaryOp {
290 /// The spelling, for the printer and for diagnostics.
291 #[must_use]
292 pub const fn spelling(self) -> &'static str {
293 match self {
294 BinaryOp::Mul => "*",
295 BinaryOp::Div => "/",
296 BinaryOp::Rem => "%",
297 BinaryOp::Add => "+",
298 BinaryOp::Sub => "-",
299 BinaryOp::Shl => "<<",
300 BinaryOp::Shr => ">>",
301 BinaryOp::Lt => "<",
302 BinaryOp::Gt => ">",
303 BinaryOp::Le => "<=",
304 BinaryOp::Ge => ">=",
305 BinaryOp::Eq => "==",
306 BinaryOp::Ne => "!=",
307 BinaryOp::BitAnd => "&",
308 BinaryOp::BitXor => "^",
309 BinaryOp::BitOr => "|",
310 BinaryOp::LogAnd => "&&",
311 BinaryOp::LogOr => "||",
312 }
313 }
314
315 /// Whether the operator sequences its left operand before its right, which only the two
316 /// short-circuiting ones do.
317 #[must_use]
318 pub const fn is_short_circuit(self) -> bool {
319 matches!(self, BinaryOp::LogAnd | BinaryOp::LogOr)
320 }
321}
322
323/// One arm of a `_Generic` selection.
324#[derive(Debug, Clone, Copy, PartialEq, Eq)]
325pub struct GenericAssoc {
326 /// The type this arm matches, and `None` for the `default` arm.
327 pub ty: Option<TypeNameId>,
328 /// The expression the arm gives, which is only evaluated if the arm is chosen.
329 pub value: ExprId,
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 #[test]
337 fn an_expression_is_sixteen_bytes() {
338 // The arena is the biggest array in the frontend and the one every pass walks. If this
339 // fails, a variant grew and the fix is a side table, not a bigger node.
340 assert_eq!(size_of::<Expr>(), 16);
341 }
342
343 #[test]
344 fn an_expression_id_is_four_bytes_even_when_optional() {
345 assert_eq!(size_of::<ExprId>(), 4);
346 assert_eq!(size_of::<Option<ExprId>>(), 4);
347 }
348
349 #[test]
350 fn postfix_increment_is_the_only_kind_that_is_postfix() {
351 assert!(UnaryOp::PostInc.is_postfix());
352 assert!(UnaryOp::PostDec.is_postfix());
353 assert!(!UnaryOp::PreInc.is_postfix());
354 assert_eq!(UnaryOp::PostInc.spelling(), UnaryOp::PreInc.spelling());
355 }
356
357 #[test]
358 fn only_the_logical_operators_short_circuit() {
359 assert!(BinaryOp::LogAnd.is_short_circuit());
360 assert!(BinaryOp::LogOr.is_short_circuit());
361 assert!(!BinaryOp::BitAnd.is_short_circuit());
362 }
363}