Skip to main content

leo_ast/expressions/
mod.rs

1// Copyright (C) 2019-2026 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use crate::{Identifier, IntegerType, Intrinsic, Location, Mode, Node, NodeBuilder, NodeID, Path, TypeKind};
18use leo_span::{Span, Symbol, sym};
19
20use serde::Serialize;
21use std::fmt;
22
23mod array_access;
24pub use array_access::*;
25
26mod async_;
27pub use async_::*;
28
29mod array;
30pub use array::*;
31
32mod binary;
33pub use binary::*;
34
35mod call;
36pub use call::*;
37
38mod cast;
39pub use cast::*;
40
41mod composite_init;
42pub use composite_init::*;
43
44mod dynamic_op;
45pub use dynamic_op::*;
46
47mod err;
48pub use err::*;
49
50mod member_access;
51pub use member_access::*;
52
53mod intrinsic;
54pub use intrinsic::*;
55
56mod repeat;
57pub use repeat::*;
58
59mod ternary;
60pub use ternary::*;
61
62mod tuple;
63pub use tuple::*;
64
65mod tuple_access;
66pub use tuple_access::*;
67
68mod unary;
69pub use unary::*;
70
71mod unit;
72pub use unit::*;
73
74mod literal;
75pub use literal::*;
76
77/// Expression that evaluates to a value.
78#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
79pub enum Expression {
80    /// An array access, e.g. `arr[i]`.
81    ArrayAccess(Box<ArrayAccess>),
82    /// An `async` block: e.g. `async { my_mapping.set(1, 2); }`.
83    Async(AsyncExpression),
84    /// An array expression, e.g., `[true, false, true, false]`.
85    Array(ArrayExpression),
86    /// A binary expression, e.g., `42 + 24`.
87    Binary(Box<BinaryExpression>),
88    /// An intrinsic expression, e.g., `_my_intrinsic(args)`.
89    Intrinsic(Box<IntrinsicExpression>),
90    /// A call expression, e.g., `my_fun(args)`.
91    Call(Box<CallExpression>),
92    /// A dynamic operation against an interface, e.g., `MyInterface @ (target) :: foobar(args)`,
93    /// `MyInterface @ (target) :: counter`, or `MyInterface @ (target) :: entries.get(i)`.
94    DynamicOp(Box<DynamicOpExpression>),
95    /// A cast expression, e.g., `42u32 as u8`.
96    Cast(Box<CastExpression>),
97    /// An expression of type "error".
98    /// Will result in a compile error eventually.
99    /// An expression constructing a composite like `Foo { bar: 42, baz }`.
100    Composite(CompositeExpression),
101    Err(ErrExpression),
102    /// A path to some item, e.g., `foo::bar::x`.
103    Path(Path),
104    /// A literal expression.
105    Literal(Literal),
106    /// An access of a composite member, e.g. `composite.member`.
107    MemberAccess(Box<MemberAccess>),
108    /// An array expression constructed from one repeated element, e.g., `[1u32; 5]`.
109    Repeat(Box<RepeatExpression>),
110    /// A ternary conditional expression `cond ? if_expr : else_expr`.
111    Ternary(Box<TernaryExpression>),
112    /// A tuple expression e.g., `(foo, 42, true)`.
113    Tuple(TupleExpression),
114    /// A tuple access expression e.g., `foo.2`.
115    TupleAccess(Box<TupleAccess>),
116    /// An unary expression.
117    Unary(Box<UnaryExpression>),
118    /// A unit expression e.g. `()`
119    Unit(UnitExpression),
120}
121
122impl Default for Expression {
123    fn default() -> Self {
124        Expression::Err(Default::default())
125    }
126}
127
128impl Node for Expression {
129    fn span(&self) -> Span {
130        use Expression::*;
131        match self {
132            ArrayAccess(n) => n.span(),
133            Array(n) => n.span(),
134            Async(n) => n.span(),
135            Binary(n) => n.span(),
136            Call(n) => n.span(),
137            DynamicOp(n) => n.span(),
138            Cast(n) => n.span(),
139            Composite(n) => n.span(),
140            Err(n) => n.span(),
141            Intrinsic(n) => n.span(),
142            Path(n) => n.span(),
143            Literal(n) => n.span(),
144            MemberAccess(n) => n.span(),
145            Repeat(n) => n.span(),
146            Ternary(n) => n.span(),
147            Tuple(n) => n.span(),
148            TupleAccess(n) => n.span(),
149            Unary(n) => n.span(),
150            Unit(n) => n.span(),
151        }
152    }
153
154    fn set_span(&mut self, span: Span) {
155        use Expression::*;
156        match self {
157            ArrayAccess(n) => n.set_span(span),
158            Array(n) => n.set_span(span),
159            Async(n) => n.set_span(span),
160            Binary(n) => n.set_span(span),
161            Call(n) => n.set_span(span),
162            DynamicOp(n) => n.set_span(span),
163            Cast(n) => n.set_span(span),
164            Composite(n) => n.set_span(span),
165            Err(n) => n.set_span(span),
166            Intrinsic(n) => n.set_span(span),
167            Path(n) => n.set_span(span),
168            Literal(n) => n.set_span(span),
169            MemberAccess(n) => n.set_span(span),
170            Repeat(n) => n.set_span(span),
171            Ternary(n) => n.set_span(span),
172            Tuple(n) => n.set_span(span),
173            TupleAccess(n) => n.set_span(span),
174            Unary(n) => n.set_span(span),
175            Unit(n) => n.set_span(span),
176        }
177    }
178
179    fn id(&self) -> NodeID {
180        use Expression::*;
181        match self {
182            Array(n) => n.id(),
183            ArrayAccess(n) => n.id(),
184            Async(n) => n.id(),
185            Binary(n) => n.id(),
186            Call(n) => n.id(),
187            DynamicOp(n) => n.id(),
188            Cast(n) => n.id(),
189            Composite(n) => n.id(),
190            Path(n) => n.id(),
191            Literal(n) => n.id(),
192            MemberAccess(n) => n.id(),
193            Repeat(n) => n.id(),
194            Err(n) => n.id(),
195            Intrinsic(n) => n.id(),
196            Ternary(n) => n.id(),
197            Tuple(n) => n.id(),
198            TupleAccess(n) => n.id(),
199            Unary(n) => n.id(),
200            Unit(n) => n.id(),
201        }
202    }
203
204    fn set_id(&mut self, id: NodeID) {
205        use Expression::*;
206        match self {
207            Array(n) => n.set_id(id),
208            ArrayAccess(n) => n.set_id(id),
209            Async(n) => n.set_id(id),
210            Binary(n) => n.set_id(id),
211            Call(n) => n.set_id(id),
212            DynamicOp(n) => n.set_id(id),
213            Cast(n) => n.set_id(id),
214            Composite(n) => n.set_id(id),
215            Path(n) => n.set_id(id),
216            Literal(n) => n.set_id(id),
217            MemberAccess(n) => n.set_id(id),
218            Repeat(n) => n.set_id(id),
219            Err(n) => n.set_id(id),
220            Intrinsic(n) => n.set_id(id),
221            Ternary(n) => n.set_id(id),
222            Tuple(n) => n.set_id(id),
223            TupleAccess(n) => n.set_id(id),
224            Unary(n) => n.set_id(id),
225            Unit(n) => n.set_id(id),
226        }
227    }
228}
229
230impl fmt::Display for Expression {
231    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232        use Expression::*;
233        match &self {
234            Array(n) => n.fmt(f),
235            ArrayAccess(n) => n.fmt(f),
236            Async(n) => n.fmt(f),
237            Binary(n) => n.fmt(f),
238            Call(n) => n.fmt(f),
239            DynamicOp(n) => n.fmt(f),
240            Cast(n) => n.fmt(f),
241            Composite(n) => n.fmt(f),
242            Err(n) => n.fmt(f),
243            Intrinsic(n) => n.fmt(f),
244            Path(n) => n.fmt(f),
245            Literal(n) => n.fmt(f),
246            MemberAccess(n) => n.fmt(f),
247            Repeat(n) => n.fmt(f),
248            Ternary(n) => n.fmt(f),
249            Tuple(n) => n.fmt(f),
250            TupleAccess(n) => n.fmt(f),
251            Unary(n) => n.fmt(f),
252            Unit(n) => n.fmt(f),
253        }
254    }
255}
256
257#[derive(Clone, Copy, Eq, PartialEq)]
258pub(crate) enum Associativity {
259    Left,
260    Right,
261    None,
262}
263
264impl Expression {
265    pub(crate) fn precedence(&self) -> u32 {
266        use Expression::*;
267        match self {
268            Binary(e) => e.precedence(),
269            Cast(_) => 12,
270            Ternary(_) => 0,
271            Array(_) | ArrayAccess(_) | Async(_) | Call(_) | DynamicOp(_) | Composite(_) | Err(_) | Intrinsic(_)
272            | Path(_) | Literal(_) | MemberAccess(_) | Repeat(_) | Tuple(_) | TupleAccess(_) | Unary(_) | Unit(_) => 20,
273        }
274    }
275
276    pub(crate) fn associativity(&self) -> Associativity {
277        if let Expression::Binary(bin) = self { bin.associativity() } else { Associativity::None }
278    }
279
280    /// Returns `self` as a known `u32` if possible. Otherwise, returns a `None`. This allows for large and/or signed
281    /// types but only if they can be safely cast to a `u32`.
282    pub fn as_u32(&self) -> Option<u32> {
283        if let Expression::Literal(literal) = &self {
284            if let LiteralVariant::Integer(int_type, s, ..) = &literal.variant {
285                use crate::IntegerType::*;
286                let s = s.replace("_", "");
287
288                return match int_type {
289                    U8 => u8::from_str_by_radix(&s).map(|v| v as u32).ok(),
290                    U16 => u16::from_str_by_radix(&s).map(|v| v as u32).ok(),
291                    U32 => u32::from_str_by_radix(&s).ok(),
292                    U64 => u64::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
293                    U128 => u128::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
294                    I8 => i8::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
295                    I16 => i16::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
296                    I32 => i32::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
297                    I64 => i64::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
298                    I128 => i128::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
299                };
300            } else if let LiteralVariant::Unsuffixed(s) = &literal.variant {
301                // Assume unsuffixed literals are `u32`. The type checker should enforce that as the default type.
302                let s = s.replace("_", "");
303                return u32::from_str_by_radix(&s).ok();
304            }
305        }
306        None
307    }
308
309    pub fn is_none_expr(&self) -> bool {
310        matches!(self, Expression::Literal(Literal { variant: LiteralVariant::None, .. }))
311    }
312
313    /// Returns true if we can confidently say evaluating this expression has no side effects, false otherwise
314    pub fn is_pure(&self, get_type: &impl Fn(NodeID) -> TypeKind) -> bool {
315        match self {
316            // Discriminate intrinsics
317            Expression::Intrinsic(intr) => {
318                if let Some(intrinsic) = Intrinsic::from_symbol(intr.name, &intr.type_parameters) {
319                    intrinsic.is_pure() && intr.arguments.iter().all(|arg| arg.is_pure(get_type))
320                } else {
321                    false
322                }
323            }
324
325            // We may be indirectly referring to an impure item
326            // This analysis could be more granular
327            Expression::Call(..)
328            | Expression::DynamicOp(..)
329            | Expression::Err(..)
330            | Expression::Async(..)
331            | Expression::Cast(..) => false,
332
333            Expression::Binary(expr) => {
334                use BinaryOperation::*;
335                match expr.op {
336                    // These can halt for any of their operand types.
337                    Div | DivWrapped | Mod | Rem | RemWrapped | Shl | Shr => false,
338                    // These can only halt for integers.
339                    Add | Mul | Pow | Sub => {
340                        !matches!(get_type(expr.id()), TypeKind::Integer(..))
341                            && expr.left.is_pure(get_type)
342                            && expr.right.is_pure(get_type)
343                    }
344                    _ => expr.left.is_pure(get_type) && expr.right.is_pure(get_type),
345                }
346            }
347            Expression::Unary(expr) => {
348                use UnaryOperation::*;
349                match expr.op {
350                    // These can halt for any of their operand types.
351                    Abs | Inverse | SquareRoot => false,
352                    // Negate can only halt for integers.
353                    Negate => expr.receiver.is_pure(get_type) && !matches!(get_type(expr.id()), TypeKind::Integer(..)),
354                    _ => expr.receiver.is_pure(get_type),
355                }
356            }
357
358            // Always pure
359            Expression::Literal(..) | Expression::Path(..) | Expression::Unit(..) => true,
360
361            // Recurse
362            Expression::ArrayAccess(expr) => expr.array.is_pure(get_type) && expr.index.is_pure(get_type),
363            Expression::MemberAccess(expr) => {
364                // Keep dyn record owner handling in sync with type checking and code generation.
365                let is_dynamic_record_read =
366                    matches!(get_type(expr.inner.id()), TypeKind::DynRecord) && expr.name.name != sym::owner;
367                !is_dynamic_record_read && expr.inner.is_pure(get_type)
368            }
369            Expression::Repeat(expr) => expr.expr.is_pure(get_type) && expr.count.is_pure(get_type),
370            Expression::TupleAccess(expr) => expr.tuple.is_pure(get_type),
371            Expression::Array(expr) => expr.elements.iter().all(|e| e.is_pure(get_type)),
372            Expression::Composite(expr) => {
373                expr.const_arguments.iter().all(|e| e.is_pure(get_type))
374                    && expr.members.iter().all(|init| init.expression.as_ref().is_none_or(|e| e.is_pure(get_type)))
375            }
376            Expression::Ternary(expr) => {
377                expr.condition.is_pure(get_type) && expr.if_true.is_pure(get_type) && expr.if_false.is_pure(get_type)
378            }
379            Expression::Tuple(expr) => expr.elements.iter().all(|e| e.is_pure(get_type)),
380        }
381    }
382
383    /// Returns the *zero value expression* for a given type, if one exists.
384    ///
385    /// This is used during lowering and reconstruction to provide default or
386    /// placeholder values (e.g., for `get_or_use` calls or composite initialization).
387    ///
388    /// Supported types:
389    /// - **Integers** (`i8`–`i128`, `u8`–`u128`): literal `0`
390    /// - **Boolean**: literal `false`
391    /// - **Field**, **Group**, **Scalar**: zero literals `"0"`
392    /// - **Composites**: recursively constructs a composite with all members zeroed
393    /// - **Arrays**: repeats a zero element for the array length
394    ///
395    /// Returns `None` if the type has no well-defined zero representation
396    /// (e.g. mapping, Future).
397    ///
398    /// The `composite_lookup` callback provides member definitions for composite types.
399    #[allow(clippy::type_complexity)]
400    pub fn zero(
401        ty: &TypeKind,
402        span: Span,
403        node_builder: &NodeBuilder,
404        composite_lookup: &dyn Fn(&Location) -> Vec<(Symbol, TypeKind)>,
405    ) -> Option<Self> {
406        let id = node_builder.next_id();
407
408        match ty {
409            // Numeric types
410            TypeKind::Integer(IntegerType::I8) => Some(Literal::integer(IntegerType::I8, "0".to_string(), span, id).into()),
411            TypeKind::Integer(IntegerType::I16) => {
412                Some(Literal::integer(IntegerType::I16, "0".to_string(), span, id).into())
413            }
414            TypeKind::Integer(IntegerType::I32) => {
415                Some(Literal::integer(IntegerType::I32, "0".to_string(), span, id).into())
416            }
417            TypeKind::Integer(IntegerType::I64) => {
418                Some(Literal::integer(IntegerType::I64, "0".to_string(), span, id).into())
419            }
420            TypeKind::Integer(IntegerType::I128) => {
421                Some(Literal::integer(IntegerType::I128, "0".to_string(), span, id).into())
422            }
423            TypeKind::Integer(IntegerType::U8) => Some(Literal::integer(IntegerType::U8, "0".to_string(), span, id).into()),
424            TypeKind::Integer(IntegerType::U16) => {
425                Some(Literal::integer(IntegerType::U16, "0".to_string(), span, id).into())
426            }
427            TypeKind::Integer(IntegerType::U32) => {
428                Some(Literal::integer(IntegerType::U32, "0".to_string(), span, id).into())
429            }
430            TypeKind::Integer(IntegerType::U64) => {
431                Some(Literal::integer(IntegerType::U64, "0".to_string(), span, id).into())
432            }
433            TypeKind::Integer(IntegerType::U128) => {
434                Some(Literal::integer(IntegerType::U128, "0".to_string(), span, id).into())
435            }
436
437            // Boolean
438            TypeKind::Boolean => Some(Literal::boolean(false, span, id).into()),
439
440            // Address: addresses don't have a well defined _zero_ but this value is often used as
441            // the "zero" address in practical applications. It really should never be used directly though.
442            // It should only be used as a placeholder for representating `none` for example.
443            TypeKind::Address => Some(
444                Literal::address(
445                    "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc".to_string(),
446                    span,
447                    id,
448                )
449                .into(),
450            ),
451
452            // Field, Group, Scalar
453            TypeKind::Field => Some(Literal::field("0".to_string(), span, id).into()),
454            TypeKind::Group => Some(Literal::group("0".to_string(), span, id).into()),
455            TypeKind::Scalar => Some(Literal::scalar("0".to_string(), span, id).into()),
456
457            // Signature: signatures don't have a well defined _zero_. The value chosen here is arbitrary.
458            // That being said, this value should really never be used directly. It should only be used as a 
459            // placeholder for representing `none` for example.
460            TypeKind::Signature => Some(
461                Literal::signature(
462                    "sign195m229jvzr0wmnshj6f8gwplhkrkhjumgjmad553r997u7pjfgpfz4j2w0c9lp53mcqqdsmut2g3a2zuvgst85w38hv273mwjec3sqjsv9w6uglcy58gjh7x3l55z68zsf24kx7a73ctp8x8klhuw7l2p4s3aq8um5jp304js7qcnwdqj56q5r5088tyvxsgektun0rnmvtsuxpe6sj".to_string(),
463                    span,
464                    id,
465                )
466                .into(),
467            ),
468
469            // Composite types
470            TypeKind::Composite(composite_type) => {
471                let path = &composite_type.path;
472                let members = composite_lookup(path.expect_global_location());
473
474                let composite_members = members
475                    .into_iter()
476                    .map(|(symbol, member_type)| {
477                        let member_id = node_builder.next_id();
478                        let zero_expr = Self::zero(&member_type, span, node_builder, composite_lookup)?;
479
480                        Some(CompositeFieldInitializer {
481                            span,
482                            id: member_id,
483                            identifier: Identifier::new(symbol, node_builder.next_id()),
484                            expression: Some(zero_expr),
485                        })
486                    })
487                    .collect::<Option<Vec<_>>>()?;
488
489                Some(Expression::Composite(CompositeExpression {
490                    span,
491                    id,
492                    path: path.clone(),
493                    const_arguments: composite_type.const_arguments.clone(),
494                    members: composite_members,
495                    base: None,
496                }))
497            }
498
499            // Arrays
500            TypeKind::Array(array_type) => {
501                let element_ty = &array_type.element_type;
502
503                let element_expr = Self::zero(element_ty, span, node_builder, composite_lookup)?;
504
505                Some(Expression::Repeat(
506                    RepeatExpression { span, id, expr: element_expr, count: *array_type.length.clone() }.into(),
507                ))
508            }
509
510            // Other types are not expected or supported just yet
511            _ => None,
512        }
513    }
514}