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