Skip to main content

toasty_core/stmt/
expr.rs

1use crate::stmt::{ExprExists, Input};
2
3use super::{
4    Entry, EntryMut, EntryPath, ExprAnd, ExprAny, ExprArg, ExprBinaryOp, ExprCast, ExprError,
5    ExprFunc, ExprInList, ExprInSubquery, ExprIsNull, ExprIsVariant, ExprLet, ExprLike, ExprList,
6    ExprMap, ExprMatch, ExprNot, ExprOr, ExprProject, ExprRecord, ExprStartsWith, ExprStmt, Node,
7    Projection, Substitute, Value, Visit, VisitMut, expr_reference::ExprReference,
8};
9use std::fmt;
10
11/// An expression node in Toasty's query AST.
12///
13/// `Expr` is the central type in the statement intermediate representation. Every
14/// filter, projection, value, and computed result in a Toasty query is
15/// represented as an `Expr` tree. The query engine compiles these trees through
16/// several phases (simplify, lower, plan, execute) before they reach a database
17/// driver.
18///
19/// # Examples
20///
21/// ```ignore
22/// use toasty_core::stmt::{Expr, Value};
23///
24/// // Constant value expressions
25/// let t = Expr::TRUE;
26/// assert!(t.is_true());
27///
28/// let n = Expr::null();
29/// assert!(n.is_value_null());
30///
31/// // From conversions
32/// let i: Expr = 42i64.into();
33/// assert!(i.is_value());
34/// ```
35#[derive(Clone, PartialEq)]
36pub enum Expr {
37    /// Logical AND of multiple expressions. See [`ExprAnd`].
38    And(ExprAnd),
39
40    /// Returns `true` if any item in a collection is truthy. See [`ExprAny`].
41    Any(ExprAny),
42
43    /// Positional argument placeholder. See [`ExprArg`].
44    Arg(ExprArg),
45
46    /// Binary comparison or arithmetic operation. See [`ExprBinaryOp`].
47    BinaryOp(ExprBinaryOp),
48
49    /// Type cast. See [`ExprCast`].
50    Cast(ExprCast),
51
52    /// Instructs the database to use its default value for a column. Useful for
53    /// auto-increment fields and other columns with server-side defaults.
54    Default,
55
56    /// An error expression that fails evaluation with a message. See [`ExprError`].
57    Error(ExprError),
58
59    /// `[NOT] EXISTS(SELECT ...)` check. See [`ExprExists`].
60    Exists(ExprExists),
61
62    /// Aggregate or scalar function call. See [`ExprFunc`].
63    Func(ExprFunc),
64
65    /// An **unresolved** reference to a name (e.g. a column name in a DDL
66    /// context).
67    ///
68    /// Unlike [`Expr::Reference`] / [`ExprReference`], which hold **resolved**
69    /// index-based references into the schema, `Ident` carries only the raw
70    /// name string. It is used in contexts where schema resolution is not
71    /// applicable, such as CHECK constraints in CREATE TABLE statements.
72    Ident(String),
73
74    /// `expr IN (list)` membership test. See [`ExprInList`].
75    InList(ExprInList),
76
77    /// `expr IN (SELECT ...)` membership test. See [`ExprInSubquery`].
78    InSubquery(ExprInSubquery),
79
80    /// `IS [NOT] NULL` check. Separate from binary operators because of
81    /// three-valued logic semantics in SQL. See [`ExprIsNull`].
82    IsNull(ExprIsNull),
83
84    /// Tests whether a value is a specific enum variant. See [`ExprIsVariant`].
85    IsVariant(ExprIsVariant),
86
87    /// Scoped binding expression (transient -- inlined before planning).
88    /// See [`ExprLet`].
89    Let(ExprLet),
90
91    /// SQL `LIKE` pattern match: `expr LIKE pattern`. See [`ExprLike`].
92    Like(ExprLike),
93
94    /// Applies a transformation to each item in a collection. See [`ExprMap`].
95    Map(ExprMap),
96
97    /// Pattern-match dispatching on a subject. See [`ExprMatch`].
98    Match(ExprMatch),
99
100    /// Boolean negation. See [`ExprNot`].
101    Not(ExprNot),
102
103    /// Logical OR of multiple expressions. See [`ExprOr`].
104    Or(ExprOr),
105
106    /// Field projection from a composite value. See [`ExprProject`].
107    Project(ExprProject),
108
109    /// Fixed-size heterogeneous tuple of expressions. See [`ExprRecord`].
110    Record(ExprRecord),
111
112    // TODO: delete this
113    /// Reference to a field, column, or model in the current or an outer query
114    /// scope. See [`ExprReference`].
115    Reference(ExprReference),
116
117    /// Ordered, homogeneous collection of expressions. See [`ExprList`].
118    List(ExprList),
119
120    /// String prefix match: `starts_with(expr, prefix)`. See [`ExprStartsWith`].
121    StartsWith(ExprStartsWith),
122
123    /// Embedded sub-statement (e.g., a subquery). See [`ExprStmt`].
124    Stmt(ExprStmt),
125
126    /// Constant value. See [`Value`].
127    Value(Value),
128}
129
130impl Expr {
131    /// The boolean `true` constant expression.
132    pub const TRUE: Expr = Expr::Value(Value::Bool(true));
133
134    /// The boolean `false` constant expression.
135    pub const FALSE: Expr = Expr::Value(Value::Bool(false));
136
137    /// Alias for [`Expr::Default`] as a constant.
138    pub const DEFAULT: Expr = Expr::Default;
139
140    /// Creates a null value expression.
141    pub fn null() -> Self {
142        Self::Value(Value::Null)
143    }
144
145    /// Is a value that evaluates to null
146    pub fn is_value_null(&self) -> bool {
147        matches!(self, Self::Value(Value::Null))
148    }
149
150    /// Returns true if the expression is the `true` boolean expression
151    pub fn is_true(&self) -> bool {
152        matches!(self, Self::Value(Value::Bool(true)))
153    }
154
155    /// Returns `true` if the expression is the `false` boolean expression
156    pub fn is_false(&self) -> bool {
157        matches!(self, Self::Value(Value::Bool(false)))
158    }
159
160    /// Returns `true` if the expression can never evaluate to `true`.
161    ///
162    /// In SQL's three-valued logic, both `false` and `null` are unsatisfiable:
163    /// a filter producing either value will never match any rows.
164    pub fn is_unsatisfiable(&self) -> bool {
165        self.is_false() || self.is_value_null()
166    }
167
168    /// Returns `true` if the expression is the default expression
169    pub fn is_default(&self) -> bool {
170        matches!(self, Self::Default)
171    }
172
173    /// Returns true if the expression is a constant value.
174    pub fn is_value(&self) -> bool {
175        matches!(self, Self::Value(..))
176    }
177
178    /// Returns `true` if the expression is a sub-statement.
179    pub fn is_stmt(&self) -> bool {
180        matches!(self, Self::Stmt(..))
181    }
182
183    /// Returns true if the expression is a binary operation
184    pub fn is_binary_op(&self) -> bool {
185        matches!(self, Self::BinaryOp(..))
186    }
187
188    /// Returns `true` if the expression is an argument placeholder.
189    pub fn is_arg(&self) -> bool {
190        matches!(self, Self::Arg(_))
191    }
192
193    /// Returns true if the expression is always non-nullable.
194    ///
195    /// This method is conservative and only returns true for expressions we can
196    /// prove are non-nullable.
197    pub fn is_always_non_nullable(&self) -> bool {
198        match self {
199            // A constant value is non-nullable if it's not null.
200            Self::Value(value) => !value.is_null(),
201            // Boolean logic expressions always evaluate to true or false.
202            Self::And(_) | Self::Or(_) | Self::Not(_) => true,
203            // ANY returns true if any item matches, always boolean.
204            Self::Any(_) => true,
205            // Comparisons always evaluate to true or false.
206            Self::BinaryOp(_) => true,
207            // IS NULL checks always evaluate to true or false.
208            Self::IsNull(_) => true,
209            // Variant checks always evaluate to true or false.
210            Self::IsVariant(_) => true,
211            // EXISTS checks always evaluate to true or false.
212            Self::Exists(_) => true,
213            // IN expressions always evaluate to true or false.
214            Self::InList(_) | Self::InSubquery(_) => true,
215            // For other expressions, we cannot prove non-nullability.
216            _ => false,
217        }
218    }
219
220    /// Consumes the expression and returns the inner [`Value`].
221    ///
222    /// # Panics
223    ///
224    /// Panics (via `todo!()`) if `self` is not an `Expr::Value`.
225    pub fn into_value(self) -> Value {
226        match self {
227            Self::Value(value) => value,
228            _ => todo!(),
229        }
230    }
231
232    /// Consumes the expression and returns the inner [`ExprStmt`].
233    ///
234    /// # Panics
235    ///
236    /// Panics (via `todo!()`) if `self` is not an `Expr::Stmt`.
237    pub fn into_stmt(self) -> ExprStmt {
238        match self {
239            Self::Stmt(stmt) => stmt,
240            _ => todo!(),
241        }
242    }
243
244    /// Returns `true` if the expression is stable
245    ///
246    /// An expression is stable if it yields the same value each time it is evaluated
247    pub fn is_stable(&self) -> bool {
248        match self {
249            // Always stable - constant values
250            Self::Value(_) => true,
251
252            // Unresolved identifiers refer to external state (e.g. a column)
253            Self::Ident(_) => false,
254
255            // Never stable - generates new values each evaluation
256            Self::Default => false,
257
258            // Error expressions are stable (they always produce the same error)
259            Self::Error(_) => true,
260
261            // Stable if all children are stable
262            Self::Record(expr_record) => expr_record.iter().all(|expr| expr.is_stable()),
263            Self::List(expr_list) => expr_list.items.iter().all(|expr| expr.is_stable()),
264            Self::Cast(expr_cast) => expr_cast.expr.is_stable(),
265            Self::StartsWith(e) => e.expr.is_stable() && e.prefix.is_stable(),
266            Self::Like(e) => e.expr.is_stable() && e.pattern.is_stable(),
267            Self::BinaryOp(expr_binary) => {
268                expr_binary.lhs.is_stable() && expr_binary.rhs.is_stable()
269            }
270            Self::And(expr_and) => expr_and.iter().all(|expr| expr.is_stable()),
271            Self::Any(expr_any) => expr_any.expr.is_stable(),
272            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_stable()),
273            Self::IsNull(expr_is_null) => expr_is_null.expr.is_stable(),
274            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_stable(),
275            Self::Not(expr_not) => expr_not.expr.is_stable(),
276            Self::InList(expr_in_list) => {
277                expr_in_list.expr.is_stable() && expr_in_list.list.is_stable()
278            }
279            Self::Project(expr_project) => expr_project.base.is_stable(),
280            Self::Let(expr_let) => {
281                expr_let.bindings.iter().all(|b| b.is_stable()) && expr_let.body.is_stable()
282            }
283            Self::Map(expr_map) => expr_map.base.is_stable() && expr_map.map.is_stable(),
284            Self::Match(expr_match) => {
285                expr_match.subject.is_stable()
286                    && expr_match.arms.iter().all(|arm| arm.expr.is_stable())
287            }
288
289            // References and statements - stable (they reference existing data)
290            Self::Reference(_) | Self::Arg(_) => true,
291
292            // Subqueries and functions - could be unstable
293            // For now, conservatively mark as unstable
294            Self::Stmt(_) | Self::Func(_) | Self::InSubquery(_) | Self::Exists(_) => false,
295        }
296    }
297
298    /// Returns `true` if `self` and `other` are syntactically identical **and**
299    /// both sides are stable.
300    ///
301    /// This is the soundness-preserving comparison used by simplification
302    /// rules that rewrite on the assumption that two equal sub-expressions
303    /// produce the same value (idempotent, absorption, complement,
304    /// range-to-equality, OR-to-IN, factoring, variant tautology).
305    ///
306    /// Syntactic identity alone is not enough: `LAST_INSERT_ID() =
307    /// LAST_INSERT_ID()` is two independent evaluations and may yield
308    /// different values, so rewriting `a AND a` to `a` would be unsound when
309    /// `a` is non-deterministic. Gating on [`Self::is_stable`] excludes any
310    /// sub-expression whose value may change across evaluations.
311    pub fn is_equivalent_to(&self, other: &Self) -> bool {
312        self == other && self.is_stable()
313    }
314
315    /// Returns `true` if the expression is a constant expression.
316    ///
317    /// A constant expression is one that does not reference any external data.
318    /// This means it contains no `Reference`, `Stmt`, or `Arg` expressions that
319    /// reference external inputs.
320    ///
321    /// `Arg` expressions inside `Map` bodies *with `nesting` less than the current
322    /// map depth* are local bindings (bound to the mapped element), not external
323    /// inputs, and are therefore considered const in that context.
324    pub fn is_const(&self) -> bool {
325        self.is_const_at_depth(0)
326    }
327
328    /// Inner implementation of [`is_const`] that tracks the number of enclosing
329    /// `Map` scopes. An `Arg` with `nesting < map_depth` is a local binding
330    /// introduced by one of those `Map`s and does not count as external input.
331    fn is_const_at_depth(&self, map_depth: usize) -> bool {
332        match self {
333            // Always constant
334            Self::Value(_) => true,
335
336            // Unresolved identifiers reference external data
337            Self::Ident(_) => false,
338
339            // Arg: local if nesting is within map_depth, otherwise external
340            Self::Arg(arg) => arg.nesting < map_depth,
341
342            // Error expressions are constant (no external data)
343            Self::Error(_) => true,
344
345            // Never constant - references external data
346            Self::Reference(_)
347            | Self::Stmt(_)
348            | Self::InSubquery(_)
349            | Self::Exists(_)
350            | Self::Default
351            | Self::Func(_) => false,
352
353            // Const if all children are const at the same depth
354            Self::Record(expr_record) => expr_record
355                .iter()
356                .all(|expr| expr.is_const_at_depth(map_depth)),
357            Self::List(expr_list) => expr_list
358                .items
359                .iter()
360                .all(|expr| expr.is_const_at_depth(map_depth)),
361            Self::Cast(expr_cast) => expr_cast.expr.is_const_at_depth(map_depth),
362            Self::StartsWith(e) => {
363                e.expr.is_const_at_depth(map_depth) && e.prefix.is_const_at_depth(map_depth)
364            }
365            Self::Like(e) => {
366                e.expr.is_const_at_depth(map_depth) && e.pattern.is_const_at_depth(map_depth)
367            }
368            Self::BinaryOp(expr_binary) => {
369                expr_binary.lhs.is_const_at_depth(map_depth)
370                    && expr_binary.rhs.is_const_at_depth(map_depth)
371            }
372            Self::And(expr_and) => expr_and
373                .iter()
374                .all(|expr| expr.is_const_at_depth(map_depth)),
375            Self::Any(expr_any) => expr_any.expr.is_const_at_depth(map_depth),
376            Self::Not(expr_not) => expr_not.expr.is_const_at_depth(map_depth),
377            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_const_at_depth(map_depth)),
378            Self::IsNull(expr_is_null) => expr_is_null.expr.is_const_at_depth(map_depth),
379            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_const_at_depth(map_depth),
380            Self::InList(expr_in_list) => {
381                expr_in_list.expr.is_const_at_depth(map_depth)
382                    && expr_in_list.list.is_const_at_depth(map_depth)
383            }
384            Self::Project(expr_project) => expr_project.base.is_const_at_depth(map_depth),
385
386            // Let: binding is checked at the current depth; the body is checked
387            // at depth+1 so that arg(nesting=0) in the body is treated as local.
388            Self::Let(expr_let) => {
389                expr_let
390                    .bindings
391                    .iter()
392                    .all(|b| b.is_const_at_depth(map_depth))
393                    && expr_let.body.is_const_at_depth(map_depth + 1)
394            }
395            // Map: base is checked at the current depth; the map body is checked
396            // at depth+1 so that arg(nesting=0) in the body is treated as local.
397            Self::Map(expr_map) => {
398                expr_map.base.is_const_at_depth(map_depth)
399                    && expr_map.map.is_const_at_depth(map_depth + 1)
400            }
401            Self::Match(expr_match) => {
402                expr_match.subject.is_const_at_depth(map_depth)
403                    && expr_match
404                        .arms
405                        .iter()
406                        .all(|arm| arm.expr.is_const_at_depth(map_depth))
407            }
408        }
409    }
410
411    /// Returns `true` if the expression can be evaluated.
412    ///
413    /// An expression can be evaluated if it doesn't contain references to external
414    /// data sources like subqueries or references. Args are allowed since they
415    /// represent function parameters that can be bound at evaluation time.
416    pub fn is_eval(&self) -> bool {
417        match self {
418            // Always evaluable
419            Self::Value(_) => true,
420
421            // Unresolved identifiers cannot be evaluated
422            Self::Ident(_) => false,
423
424            // Args are OK for evaluation
425            Self::Arg(_) => true,
426
427            // Error expressions are evaluable (they produce an error)
428            Self::Error(_) => true,
429
430            // Never evaluable - references external data or requires a database driver
431            Self::Default
432            | Self::Reference(_)
433            | Self::Stmt(_)
434            | Self::InSubquery(_)
435            | Self::Exists(_)
436            | Self::StartsWith(_)
437            | Self::Like(_) => false,
438
439            // Evaluable if all children are evaluable
440            Self::Record(expr_record) => expr_record.iter().all(|expr| expr.is_eval()),
441            Self::List(expr_list) => expr_list.items.iter().all(|expr| expr.is_eval()),
442            Self::Cast(expr_cast) => expr_cast.expr.is_eval(),
443            Self::BinaryOp(expr_binary) => expr_binary.lhs.is_eval() && expr_binary.rhs.is_eval(),
444            Self::And(expr_and) => expr_and.iter().all(|expr| expr.is_eval()),
445            Self::Any(expr_any) => expr_any.expr.is_eval(),
446            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_eval()),
447            Self::Not(expr_not) => expr_not.expr.is_eval(),
448            Self::IsNull(expr_is_null) => expr_is_null.expr.is_eval(),
449            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_eval(),
450            Self::InList(expr_in_list) => {
451                expr_in_list.expr.is_eval() && expr_in_list.list.is_eval()
452            }
453            Self::Project(expr_project) => expr_project.base.is_eval(),
454            Self::Let(expr_let) => {
455                expr_let.bindings.iter().all(|b| b.is_eval()) && expr_let.body.is_eval()
456            }
457            Self::Map(expr_map) => expr_map.base.is_eval() && expr_map.map.is_eval(),
458            Self::Match(expr_match) => {
459                expr_match.subject.is_eval() && expr_match.arms.iter().all(|arm| arm.expr.is_eval())
460            }
461            Self::Func(_) => false,
462        }
463    }
464
465    /// Returns a clone of this expression with all [`Projection`] nodes
466    /// transformed by `f`.
467    pub fn map_projections(&self, f: impl FnMut(&Projection) -> Projection) -> Self {
468        struct MapProjections<T>(T);
469
470        impl<T: FnMut(&Projection) -> Projection> VisitMut for MapProjections<T> {
471            fn visit_projection_mut(&mut self, i: &mut Projection) {
472                *i = self.0(i);
473            }
474        }
475
476        let mut mapped = self.clone();
477        MapProjections(f).visit_expr_mut(&mut mapped);
478        mapped
479    }
480
481    /// Navigates into a nested record or list expression by `path` and returns
482    /// a read-only [`Entry`] reference.
483    ///
484    /// Returns `None` if the path cannot be followed (e.g., the expression is
485    /// not a record or list at the expected depth).
486    #[track_caller]
487    pub fn entry(&self, path: impl EntryPath) -> Option<Entry<'_>> {
488        let mut ret = Entry::Expr(self);
489
490        for step in path.step_iter() {
491            ret = match ret {
492                Entry::Expr(Self::Record(expr)) => Entry::Expr(&expr[step]),
493                Entry::Expr(Self::List(expr)) => Entry::Expr(&expr.items[step]),
494                Entry::Value(Value::Record(record))
495                | Entry::Expr(Self::Value(Value::Record(record))) => Entry::Value(&record[step]),
496                Entry::Value(Value::List(items)) | Entry::Expr(Self::Value(Value::List(items))) => {
497                    Entry::Value(&items[step])
498                }
499                _ => return None,
500            }
501        }
502
503        Some(ret)
504    }
505
506    /// Navigates into a nested record or list expression by `path` and returns
507    /// a mutable [`EntryMut`] reference.
508    ///
509    /// # Panics
510    ///
511    /// Panics if the path cannot be followed on the current expression shape.
512    #[track_caller]
513    pub fn entry_mut(&mut self, path: impl EntryPath) -> EntryMut<'_> {
514        let mut ret = EntryMut::Expr(self);
515
516        for step in path.step_iter() {
517            ret = match ret {
518                EntryMut::Expr(Self::Record(expr)) => EntryMut::Expr(&mut expr[step]),
519                EntryMut::Value(Value::Record(record))
520                | EntryMut::Expr(Self::Value(Value::Record(record))) => {
521                    EntryMut::Value(&mut record[step])
522                }
523                _ => todo!("ret={ret:#?}; step={step:#?}"),
524            }
525        }
526
527        ret
528    }
529
530    /// Takes the expression out, leaving `Expr::Value(Value::Null)` in its
531    /// place. Equivalent to `std::mem::replace(self, Expr::null())`.
532    pub fn take(&mut self) -> Self {
533        std::mem::replace(self, Self::Value(Value::Null))
534    }
535
536    /// Replaces every [`ExprArg`] in this expression tree with the
537    /// corresponding value from `input`.
538    pub fn substitute(&mut self, input: impl Input) {
539        Substitute::new(input).visit_expr_mut(self);
540    }
541}
542
543impl Node for Expr {
544    fn visit<V: Visit>(&self, mut visit: V) {
545        visit.visit_expr(self);
546    }
547
548    fn visit_mut<V: VisitMut>(&mut self, mut visit: V) {
549        visit.visit_expr_mut(self);
550    }
551}
552
553// === Conversions ===
554
555impl From<bool> for Expr {
556    fn from(value: bool) -> Self {
557        Self::Value(Value::from(value))
558    }
559}
560
561impl From<i64> for Expr {
562    fn from(value: i64) -> Self {
563        Self::Value(value.into())
564    }
565}
566
567impl From<&i64> for Expr {
568    fn from(value: &i64) -> Self {
569        Self::Value(value.into())
570    }
571}
572
573impl From<String> for Expr {
574    fn from(value: String) -> Self {
575        Self::Value(value.into())
576    }
577}
578
579impl From<&String> for Expr {
580    fn from(value: &String) -> Self {
581        Self::Value(value.into())
582    }
583}
584
585impl From<&str> for Expr {
586    fn from(value: &str) -> Self {
587        Self::Value(value.into())
588    }
589}
590
591impl From<Value> for Expr {
592    fn from(value: Value) -> Self {
593        Self::Value(value)
594    }
595}
596
597impl<E1, E2> From<(E1, E2)> for Expr
598where
599    E1: Into<Self>,
600    E2: Into<Self>,
601{
602    fn from(value: (E1, E2)) -> Self {
603        Self::Record(value.into())
604    }
605}
606
607impl fmt::Debug for Expr {
608    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
609        match self {
610            Self::And(e) => e.fmt(f),
611            Self::Any(e) => e.fmt(f),
612            Self::Arg(e) => e.fmt(f),
613            Self::BinaryOp(e) => e.fmt(f),
614            Self::Cast(e) => e.fmt(f),
615            Self::Default => write!(f, "Default"),
616            Self::Error(e) => e.fmt(f),
617            Self::Exists(e) => e.fmt(f),
618            Self::Func(e) => e.fmt(f),
619            Self::Ident(e) => write!(f, "Ident({e:?})"),
620            Self::InList(e) => e.fmt(f),
621            Self::InSubquery(e) => e.fmt(f),
622            Self::IsNull(e) => e.fmt(f),
623            Self::IsVariant(e) => e.fmt(f),
624            Self::Let(e) => e.fmt(f),
625            Self::Like(e) => e.fmt(f),
626            Self::Map(e) => e.fmt(f),
627            Self::Match(e) => e.fmt(f),
628            Self::Not(e) => e.fmt(f),
629            Self::Or(e) => e.fmt(f),
630            Self::Project(e) => e.fmt(f),
631            Self::Record(e) => e.fmt(f),
632            Self::Reference(e) => e.fmt(f),
633            Self::List(e) => e.fmt(f),
634            Self::StartsWith(e) => e.fmt(f),
635            Self::Stmt(e) => e.fmt(f),
636            Self::Value(e) => e.fmt(f),
637        }
638    }
639}