leo_ast/expressions/
mod.rs

1// Copyright (C) 2019-2025 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, Node, NodeBuilder, NodeID, Path, Type};
18use leo_span::{Span, Symbol};
19
20use serde::{Deserialize, 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 err;
42pub use err::*;
43
44mod member_access;
45pub use member_access::*;
46
47mod intrinsic;
48pub use intrinsic::*;
49
50mod repeat;
51pub use repeat::*;
52
53mod struct_init;
54pub use struct_init::*;
55
56mod ternary;
57pub use ternary::*;
58
59mod tuple;
60pub use tuple::*;
61
62mod tuple_access;
63pub use tuple_access::*;
64
65mod unary;
66pub use unary::*;
67
68mod unit;
69pub use unit::*;
70
71mod literal;
72pub use literal::*;
73
74pub mod locator;
75pub use locator::*;
76
77/// Expression that evaluates to a value.
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
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 cast expression, e.g., `42u32 as u8`.
93    Cast(Box<CastExpression>),
94    /// An expression of type "error".
95    /// Will result in a compile error eventually.
96    Err(ErrExpression),
97    /// A path to some item, e.g., `foo::bar::x`.
98    Path(Path),
99    /// A literal expression.
100    Literal(Literal),
101    /// A locator expression, e.g., `hello.aleo/foo`.
102    Locator(LocatorExpression),
103    /// An access of a struct member, e.g. `struc.member`.
104    MemberAccess(Box<MemberAccess>),
105    /// An array expression constructed from one repeated element, e.g., `[1u32; 5]`.
106    Repeat(Box<RepeatExpression>),
107    /// An expression constructing a struct like `Foo { bar: 42, baz }`.
108    Struct(StructExpression),
109    /// A ternary conditional expression `cond ? if_expr : else_expr`.
110    Ternary(Box<TernaryExpression>),
111    /// A tuple expression e.g., `(foo, 42, true)`.
112    Tuple(TupleExpression),
113    /// A tuple access expression e.g., `foo.2`.
114    TupleAccess(Box<TupleAccess>),
115    /// An unary expression.
116    Unary(Box<UnaryExpression>),
117    /// A unit expression e.g. `()`
118    Unit(UnitExpression),
119}
120
121impl Default for Expression {
122    fn default() -> Self {
123        Expression::Err(Default::default())
124    }
125}
126
127impl Node for Expression {
128    fn span(&self) -> Span {
129        use Expression::*;
130        match self {
131            ArrayAccess(n) => n.span(),
132            Array(n) => n.span(),
133            Async(n) => n.span(),
134            Binary(n) => n.span(),
135            Call(n) => n.span(),
136            Cast(n) => n.span(),
137            Err(n) => n.span(),
138            Intrinsic(n) => n.span(),
139            Path(n) => n.span(),
140            Literal(n) => n.span(),
141            Locator(n) => n.span(),
142            MemberAccess(n) => n.span(),
143            Repeat(n) => n.span(),
144            Struct(n) => n.span(),
145            Ternary(n) => n.span(),
146            Tuple(n) => n.span(),
147            TupleAccess(n) => n.span(),
148            Unary(n) => n.span(),
149            Unit(n) => n.span(),
150        }
151    }
152
153    fn set_span(&mut self, span: Span) {
154        use Expression::*;
155        match self {
156            ArrayAccess(n) => n.set_span(span),
157            Array(n) => n.set_span(span),
158            Async(n) => n.set_span(span),
159            Binary(n) => n.set_span(span),
160            Call(n) => n.set_span(span),
161            Cast(n) => n.set_span(span),
162            Err(n) => n.set_span(span),
163            Intrinsic(n) => n.set_span(span),
164            Path(n) => n.set_span(span),
165            Literal(n) => n.set_span(span),
166            Locator(n) => n.set_span(span),
167            MemberAccess(n) => n.set_span(span),
168            Repeat(n) => n.set_span(span),
169            Struct(n) => n.set_span(span),
170            Ternary(n) => n.set_span(span),
171            Tuple(n) => n.set_span(span),
172            TupleAccess(n) => n.set_span(span),
173            Unary(n) => n.set_span(span),
174            Unit(n) => n.set_span(span),
175        }
176    }
177
178    fn id(&self) -> NodeID {
179        use Expression::*;
180        match self {
181            Array(n) => n.id(),
182            ArrayAccess(n) => n.id(),
183            Async(n) => n.id(),
184            Binary(n) => n.id(),
185            Call(n) => n.id(),
186            Cast(n) => n.id(),
187            Path(n) => n.id(),
188            Literal(n) => n.id(),
189            Locator(n) => n.id(),
190            MemberAccess(n) => n.id(),
191            Repeat(n) => n.id(),
192            Err(n) => n.id(),
193            Intrinsic(n) => n.id(),
194            Struct(n) => n.id(),
195            Ternary(n) => n.id(),
196            Tuple(n) => n.id(),
197            TupleAccess(n) => n.id(),
198            Unary(n) => n.id(),
199            Unit(n) => n.id(),
200        }
201    }
202
203    fn set_id(&mut self, id: NodeID) {
204        use Expression::*;
205        match self {
206            Array(n) => n.set_id(id),
207            ArrayAccess(n) => n.set_id(id),
208            Async(n) => n.set_id(id),
209            Binary(n) => n.set_id(id),
210            Call(n) => n.set_id(id),
211            Cast(n) => n.set_id(id),
212            Path(n) => n.set_id(id),
213            Literal(n) => n.set_id(id),
214            Locator(n) => n.set_id(id),
215            MemberAccess(n) => n.set_id(id),
216            Repeat(n) => n.set_id(id),
217            Err(n) => n.set_id(id),
218            Intrinsic(n) => n.set_id(id),
219            Struct(n) => n.set_id(id),
220            Ternary(n) => n.set_id(id),
221            Tuple(n) => n.set_id(id),
222            TupleAccess(n) => n.set_id(id),
223            Unary(n) => n.set_id(id),
224            Unit(n) => n.set_id(id),
225        }
226    }
227}
228
229impl fmt::Display for Expression {
230    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231        use Expression::*;
232        match &self {
233            Array(n) => n.fmt(f),
234            ArrayAccess(n) => n.fmt(f),
235            Async(n) => n.fmt(f),
236            Binary(n) => n.fmt(f),
237            Call(n) => n.fmt(f),
238            Cast(n) => n.fmt(f),
239            Err(n) => n.fmt(f),
240            Intrinsic(n) => n.fmt(f),
241            Path(n) => n.fmt(f),
242            Literal(n) => n.fmt(f),
243            Locator(n) => n.fmt(f),
244            MemberAccess(n) => n.fmt(f),
245            Repeat(n) => n.fmt(f),
246            Struct(n) => n.fmt(f),
247            Ternary(n) => n.fmt(f),
248            Tuple(n) => n.fmt(f),
249            TupleAccess(n) => n.fmt(f),
250            Unary(n) => n.fmt(f),
251            Unit(n) => n.fmt(f),
252        }
253    }
254}
255
256#[derive(Clone, Copy, Eq, PartialEq)]
257pub(crate) enum Associativity {
258    Left,
259    Right,
260    None,
261}
262
263impl Expression {
264    pub(crate) fn precedence(&self) -> u32 {
265        use Expression::*;
266        match self {
267            Binary(e) => e.precedence(),
268            Cast(_) => 12,
269            Ternary(_) => 0,
270            Array(_) | ArrayAccess(_) | Async(_) | Call(_) | Err(_) | Intrinsic(_) | Path(_) | Literal(_)
271            | Locator(_) | MemberAccess(_) | Repeat(_) | Struct(_) | Tuple(_) | TupleAccess(_) | Unary(_) | Unit(_) => {
272                20
273            }
274        }
275    }
276
277    pub(crate) fn associativity(&self) -> Associativity {
278        if let Expression::Binary(bin) = self { bin.associativity() } else { Associativity::None }
279    }
280
281    /// Returns `self` as a known `u32` if possible. Otherwise, returns a `None`. This allows for large and/or signed
282    /// types but only if they can be safely cast to a `u32`.
283    pub fn as_u32(&self) -> Option<u32> {
284        if let Expression::Literal(literal) = &self {
285            if let LiteralVariant::Integer(int_type, s, ..) = &literal.variant {
286                use crate::IntegerType::*;
287                let s = s.replace("_", "");
288
289                return match int_type {
290                    U8 => u8::from_str_by_radix(&s).map(|v| v as u32).ok(),
291                    U16 => u16::from_str_by_radix(&s).map(|v| v as u32).ok(),
292                    U32 => u32::from_str_by_radix(&s).ok(),
293                    U64 => u64::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
294                    U128 => u128::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
295                    I8 => i8::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
296                    I16 => i16::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
297                    I32 => i32::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
298                    I64 => i64::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
299                    I128 => i128::from_str_by_radix(&s).ok().and_then(|v| u32::try_from(v).ok()),
300                };
301            } else if let LiteralVariant::Unsuffixed(s) = &literal.variant {
302                // Assume unsuffixed literals are `u32`. The type checker should enforce that as the default type.
303                let s = s.replace("_", "");
304                return u32::from_str_by_radix(&s).ok();
305            }
306        }
307        None
308    }
309
310    pub fn is_none_expr(&self) -> bool {
311        matches!(self, Expression::Literal(Literal { variant: LiteralVariant::None, .. }))
312    }
313
314    /// Returns true if we can confidently say evaluating this expression has no side effects, false otherwise
315    pub fn is_pure(&self, get_type: &impl Fn(NodeID) -> Type) -> bool {
316        match self {
317            // Discriminate intrinsics
318            Expression::Intrinsic(intr) => {
319                if let Some(intrinsic) = Intrinsic::from_symbol(intr.name, &intr.type_parameters) {
320                    intrinsic.is_pure()
321                } else {
322                    false
323                }
324            }
325
326            // We may be indirectly referring to an impure item
327            // This analysis could be more granular
328            Expression::Call(..) | Expression::Err(..) | Expression::Async(..) | Expression::Cast(..) => false,
329
330            Expression::Binary(expr) => {
331                use BinaryOperation::*;
332                match expr.op {
333                    // These can halt for any of their operand types.
334                    Div | Mod | Rem | Shl | Shr => false,
335                    // These can only halt for integers.
336                    Add | Mul | Pow => !matches!(get_type(expr.id()), Type::Integer(..)),
337                    _ => expr.left.is_pure(get_type) && expr.right.is_pure(get_type),
338                }
339            }
340            Expression::Unary(expr) => {
341                use UnaryOperation::*;
342                match expr.op {
343                    // These can halt for any of their operand types.
344                    Abs | Inverse | SquareRoot => false,
345                    // Negate can only halt for integers.
346                    Negate => !matches!(get_type(expr.id()), Type::Integer(..)),
347                    _ => expr.receiver.is_pure(get_type),
348                }
349            }
350
351            // Always pure
352            Expression::Locator(..) | Expression::Literal(..) | Expression::Path(..) | Expression::Unit(..) => true,
353
354            // Recurse
355            Expression::ArrayAccess(expr) => expr.array.is_pure(get_type) && expr.index.is_pure(get_type),
356            Expression::MemberAccess(expr) => expr.inner.is_pure(get_type),
357            Expression::Repeat(expr) => expr.expr.is_pure(get_type) && expr.count.is_pure(get_type),
358            Expression::TupleAccess(expr) => expr.tuple.is_pure(get_type),
359            Expression::Array(expr) => expr.elements.iter().all(|e| e.is_pure(get_type)),
360            Expression::Struct(expr) => {
361                expr.const_arguments.iter().all(|e| e.is_pure(get_type))
362                    && expr.members.iter().all(|init| init.expression.as_ref().is_none_or(|e| e.is_pure(get_type)))
363            }
364            Expression::Ternary(expr) => {
365                expr.condition.is_pure(get_type) && expr.if_true.is_pure(get_type) && expr.if_false.is_pure(get_type)
366            }
367            Expression::Tuple(expr) => expr.elements.iter().all(|e| e.is_pure(get_type)),
368        }
369    }
370
371    /// Returns the *zero value expression* for a given type, if one exists.
372    ///
373    /// This is used during lowering and reconstruction to provide default or
374    /// placeholder values (e.g., for `get_or_use` calls or struct initialization).
375    ///
376    /// Supported types:
377    /// - **Integers** (`i8`–`i128`, `u8`–`u128`): literal `0`
378    /// - **Boolean**: literal `false`
379    /// - **Field**, **Group**, **Scalar**: zero literals `"0"`
380    /// - **Structs**: recursively constructs a struct with all members zeroed
381    /// - **Arrays**: repeats a zero element for the array length
382    ///
383    /// Returns `None` if the type has no well-defined zero representation
384    /// (e.g. mapping, Future).
385    ///
386    /// The `struct_lookup` callback provides member definitions for composite types.
387    #[allow(clippy::type_complexity)]
388    pub fn zero(
389        ty: &Type,
390        span: Span,
391        node_builder: &NodeBuilder,
392        struct_lookup: &dyn Fn(&[Symbol]) -> Vec<(Symbol, Type)>,
393    ) -> Option<Self> {
394        let id = node_builder.next_id();
395
396        match ty {
397            // Numeric types
398            Type::Integer(IntegerType::I8) => Some(Literal::integer(IntegerType::I8, "0".to_string(), span, id).into()),
399            Type::Integer(IntegerType::I16) => {
400                Some(Literal::integer(IntegerType::I16, "0".to_string(), span, id).into())
401            }
402            Type::Integer(IntegerType::I32) => {
403                Some(Literal::integer(IntegerType::I32, "0".to_string(), span, id).into())
404            }
405            Type::Integer(IntegerType::I64) => {
406                Some(Literal::integer(IntegerType::I64, "0".to_string(), span, id).into())
407            }
408            Type::Integer(IntegerType::I128) => {
409                Some(Literal::integer(IntegerType::I128, "0".to_string(), span, id).into())
410            }
411            Type::Integer(IntegerType::U8) => Some(Literal::integer(IntegerType::U8, "0".to_string(), span, id).into()),
412            Type::Integer(IntegerType::U16) => {
413                Some(Literal::integer(IntegerType::U16, "0".to_string(), span, id).into())
414            }
415            Type::Integer(IntegerType::U32) => {
416                Some(Literal::integer(IntegerType::U32, "0".to_string(), span, id).into())
417            }
418            Type::Integer(IntegerType::U64) => {
419                Some(Literal::integer(IntegerType::U64, "0".to_string(), span, id).into())
420            }
421            Type::Integer(IntegerType::U128) => {
422                Some(Literal::integer(IntegerType::U128, "0".to_string(), span, id).into())
423            }
424
425            // Boolean
426            Type::Boolean => Some(Literal::boolean(false, span, id).into()),
427
428            // Address: addresses don't have a well defined _zero_ but this value is often used as
429            // the "zero" address in practical applications. It really should never be used directly though.
430            // It should only be used as a placeholder for representating `none` for example.
431            Type::Address => Some(
432                Literal::address(
433                    "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc".to_string(),
434                    span,
435                    id,
436                )
437                .into(),
438            ),
439
440            // Field, Group, Scalar
441            Type::Field => Some(Literal::field("0".to_string(), span, id).into()),
442            Type::Group => Some(Literal::group("0".to_string(), span, id).into()),
443            Type::Scalar => Some(Literal::scalar("0".to_string(), span, id).into()),
444
445            // Structs (composite types)
446            Type::Composite(composite_type) => {
447                let path = &composite_type.path;
448                let members = struct_lookup(&path.absolute_path());
449
450                let struct_members = members
451                    .into_iter()
452                    .map(|(symbol, member_type)| {
453                        let member_id = node_builder.next_id();
454                        let zero_expr = Self::zero(&member_type, span, node_builder, struct_lookup)?;
455
456                        Some(StructVariableInitializer {
457                            span,
458                            id: member_id,
459                            identifier: Identifier::new(symbol, node_builder.next_id()),
460                            expression: Some(zero_expr),
461                        })
462                    })
463                    .collect::<Option<Vec<_>>>()?;
464
465                Some(Expression::Struct(StructExpression {
466                    span,
467                    id,
468                    path: path.clone(),
469                    const_arguments: composite_type.const_arguments.clone(),
470                    members: struct_members,
471                }))
472            }
473
474            // Arrays
475            Type::Array(array_type) => {
476                let element_ty = &array_type.element_type;
477
478                let element_expr = Self::zero(element_ty, span, node_builder, struct_lookup)?;
479
480                Some(Expression::Repeat(
481                    RepeatExpression { span, id, expr: element_expr, count: *array_type.length.clone() }.into(),
482                ))
483            }
484
485            // Other types are not expected or supported just yet
486            _ => None,
487        }
488    }
489}