diff --git a/compiler/ast/src/expressions/mod.rs b/compiler/ast/src/expressions/mod.rs index eab01f7efed..3baa2bc17b0 100644 --- a/compiler/ast/src/expressions/mod.rs +++ b/compiler/ast/src/expressions/mod.rs @@ -53,6 +53,9 @@ pub use member_access::*; mod repeat; pub use repeat::*; +mod slice; +pub use slice::*; + mod struct_init; pub use struct_init::*; @@ -109,6 +112,8 @@ pub enum Expression { MemberAccess(Box), /// An array expression constructed from one repeated element, e.g., `[1u32; 5]`. Repeat(Box), + /// An array expression constructed from a slice of another array, e.g., `arr[1..4]`. + Slice(Box), /// An expression constructing a struct like `Foo { bar: 42, baz }`. Struct(StructExpression), /// A ternary conditional expression `cond ? if_expr : else_expr`. @@ -147,6 +152,7 @@ impl Node for Expression { Locator(n) => n.span(), MemberAccess(n) => n.span(), Repeat(n) => n.span(), + Slice(n) => n.span(), Struct(n) => n.span(), Ternary(n) => n.span(), Tuple(n) => n.span(), @@ -173,6 +179,7 @@ impl Node for Expression { Locator(n) => n.set_span(span), MemberAccess(n) => n.set_span(span), Repeat(n) => n.set_span(span), + Slice(n) => n.set_span(span), Struct(n) => n.set_span(span), Ternary(n) => n.set_span(span), Tuple(n) => n.set_span(span), @@ -198,6 +205,7 @@ impl Node for Expression { Locator(n) => n.id(), MemberAccess(n) => n.id(), Repeat(n) => n.id(), + Slice(n) => n.id(), Err(n) => n.id(), Struct(n) => n.id(), Ternary(n) => n.id(), @@ -224,6 +232,7 @@ impl Node for Expression { Locator(n) => n.set_id(id), MemberAccess(n) => n.set_id(id), Repeat(n) => n.set_id(id), + Slice(n) => n.set_id(id), Err(n) => n.set_id(id), Struct(n) => n.set_id(id), Ternary(n) => n.set_id(id), @@ -253,6 +262,7 @@ impl fmt::Display for Expression { Locator(n) => n.fmt(f), MemberAccess(n) => n.fmt(f), Repeat(n) => n.fmt(f), + Slice(n) => n.fmt(f), Struct(n) => n.fmt(f), Ternary(n) => n.fmt(f), Tuple(n) => n.fmt(f), @@ -289,6 +299,7 @@ impl Expression { | Locator(_) | MemberAccess(_) | Repeat(_) + | Slice(_) | Struct(_) | Tuple(_) | TupleAccess(_) diff --git a/compiler/ast/src/expressions/slice.rs b/compiler/ast/src/expressions/slice.rs new file mode 100644 index 00000000000..ce01184694d --- /dev/null +++ b/compiler/ast/src/expressions/slice.rs @@ -0,0 +1,60 @@ +// Copyright (C) 2019-2025 Provable Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use super::*; + +/// An array constructed from slicing another. +/// +/// E.g., `arr[2..5]`, `arr[0..=3]`, `arr[1..]`, `arr[..4]` +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SliceExpression { + /// The array being sliced. + pub array: Expression, + /// The optional starting index. + pub start: Option, + /// The optional ending index and whether it's inclusive. + pub end: Option<(bool, Expression)>, + /// The span. + pub span: Span, + /// The ID of the node. + pub id: NodeID, +} + +impl fmt::Display for SliceExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Format the start expression. + let start = match &self.start { + Some(expr) => expr.to_string(), + None => "".to_string(), + }; + // Format the end expression. + let end = match &self.end { + Some((true, expr)) => format!("={expr}"), + Some((false, expr)) => expr.to_string(), + None => "".to_string(), + }; + + write!(f, "{}[{start}..{end}]", self.array) + } +} + +impl From for Expression { + fn from(value: SliceExpression) -> Self { + Expression::Slice(Box::new(value)) + } +} + +crate::simple_node_impl!(SliceExpression); diff --git a/compiler/ast/src/interpreter_value/evaluate.rs b/compiler/ast/src/interpreter_value/evaluate.rs index fccffa566d2..1e99d403483 100644 --- a/compiler/ast/src/interpreter_value/evaluate.rs +++ b/compiler/ast/src/interpreter_value/evaluate.rs @@ -389,6 +389,7 @@ fn resolve_unsuffixed_binary_op_operands( } /// Evaluate a binary operation. +#[rustfmt::skip] pub fn evaluate_binary( span: Span, op: BinaryOperation, @@ -404,28 +405,33 @@ pub fn evaluate_binary( _ => {} } - let ValueVariants::Svm(SvmValueParam::Plaintext(Plaintext::Literal(lhs, ..))) = &lhs.contents else { + let ValueVariants::Svm(SvmValueParam::Plaintext(lhs)) = &lhs.contents else { halt2!(span, "Type error"); }; - let ValueVariants::Svm(SvmValueParam::Plaintext(Plaintext::Literal(rhs, ..))) = &rhs.contents else { + let ValueVariants::Svm(SvmValueParam::Plaintext(rhs)) = &rhs.contents else { halt2!(span, "Type error"); }; let value = match op { BinaryOperation::Add => { let Some(value): Option = (match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (*x).checked_add(**y).map(|z| z.into()), - (SvmLiteralParam::Group(x), SvmLiteralParam::Group(y)) => Some((*x + y).into()), - (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) => Some((*x + y).into()), - (SvmLiteralParam::Scalar(x), SvmLiteralParam::Scalar(y)) => Some((*x + y).into()), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (*x).checked_add(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::Group(x), _), Plaintext::Literal(SvmLiteralParam::Group(y), _)) => Some((*x + y).into()), + (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) => Some((*x + y).into()), + (Plaintext::Literal(SvmLiteralParam::Scalar(x), _), Plaintext::Literal(SvmLiteralParam::Scalar(y), _)) => Some((*x + y).into()), + (Plaintext::Array(x, _), Plaintext::Array(y, _)) => { + let mut elements = x.clone(); + elements.extend(y.clone()); + Some(ValueVariants::Svm(SvmValueParam::Plaintext(Plaintext::Array(elements, Default::default()))).into()) + } _ => halt2!(span, "Type error"), }) else { halt2!(span, "add overflow"); @@ -433,49 +439,49 @@ pub fn evaluate_binary( value } BinaryOperation::AddWrapped => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (*x).wrapping_add(**y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (*x).wrapping_add(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (*x).wrapping_add(**y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::And => match (lhs, rhs) { - (SvmLiteralParam::Boolean(x), SvmLiteralParam::Boolean(y)) => (**x && **y).into(), + (Plaintext::Literal(SvmLiteralParam::Boolean(x), _), Plaintext::Literal(SvmLiteralParam::Boolean(y), _)) => (**x && **y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::BitwiseAnd => match (lhs, rhs) { - (SvmLiteralParam::Boolean(x), SvmLiteralParam::Boolean(y)) => (**x & **y).into(), - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (**x & **y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (**x & **y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (**x & **y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (**x & **y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (**x & **y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (**x & **y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (**x & **y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (**x & **y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (**x & **y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::Boolean(x), _), Plaintext::Literal(SvmLiteralParam::Boolean(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (**x & **y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (**x & **y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Div => { let Some(value) = (match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (*x).checked_div(**y).map(|z| z.into()), - (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) => y.inverse().map(|y| (*x * y).into()).ok(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (*x).checked_div(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) => y.inverse().map(|y| (*x * y).into()).ok(), _ => halt2!(span, "Type error"), }) else { halt2!(span, "div overflow"); @@ -483,86 +489,86 @@ pub fn evaluate_binary( value } BinaryOperation::DivWrapped => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) if **y != 0 => (*x).wrapping_div(**y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) if **y != 0 => (*x).wrapping_div(**y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Eq => unreachable!("This case was handled above"), BinaryOperation::Gte => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (*x >= *y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (*x >= *y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (*x >= *y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (*x >= *y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (*x >= *y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (*x >= *y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (*x >= *y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (*x >= *y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (*x >= *y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (*x >= *y).into(), - (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) => (*x >= *y).into(), - (SvmLiteralParam::Scalar(x), SvmLiteralParam::Scalar(y)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) => (*x >= *y).into(), + (Plaintext::Literal(SvmLiteralParam::Scalar(x), _), Plaintext::Literal(SvmLiteralParam::Scalar(y), _)) => (*x >= *y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Gt => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (*x > *y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (*x > *y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (*x > *y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (*x > *y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (*x > *y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (*x > *y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (*x > *y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (*x > *y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (*x > *y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (*x > *y).into(), - (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) => (*x > *y).into(), - (SvmLiteralParam::Scalar(x), SvmLiteralParam::Scalar(y)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) => (*x > *y).into(), + (Plaintext::Literal(SvmLiteralParam::Scalar(x), _), Plaintext::Literal(SvmLiteralParam::Scalar(y), _)) => (*x > *y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Lte => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (*x <= *y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (*x <= *y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (*x <= *y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (*x <= *y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (*x <= *y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (*x <= *y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (*x <= *y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (*x <= *y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (*x <= *y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (*x <= *y).into(), - (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) => (*x <= *y).into(), - (SvmLiteralParam::Scalar(x), SvmLiteralParam::Scalar(y)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) => (*x <= *y).into(), + (Plaintext::Literal(SvmLiteralParam::Scalar(x), _), Plaintext::Literal(SvmLiteralParam::Scalar(y), _)) => (*x <= *y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Lt => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (*x < *y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (*x < *y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (*x < *y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (*x < *y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (*x < *y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (*x < *y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (*x < *y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (*x < *y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (*x < *y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (*x < *y).into(), - (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) => (*x < *y).into(), - (SvmLiteralParam::Scalar(x), SvmLiteralParam::Scalar(y)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) => (*x < *y).into(), + (Plaintext::Literal(SvmLiteralParam::Scalar(x), _), Plaintext::Literal(SvmLiteralParam::Scalar(y), _)) => (*x < *y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Mod => { let Some(value) = (match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => x.checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => x.checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => x.checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => x.checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => x.checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => x.checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => x.checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => x.checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => x.checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => x.checked_rem(**y).map(|z| z.into()), _ => halt2!(span, "Type error"), }) else { halt2!(span, "mod overflow"); @@ -571,19 +577,19 @@ pub fn evaluate_binary( } BinaryOperation::Mul => { let Some(value) = (match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => x.checked_mul(**y).map(|z| z.into()), - (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) => Some((*x * y).into()), - (SvmLiteralParam::Group(x), SvmLiteralParam::Scalar(y)) => Some((*x * y).into()), - (SvmLiteralParam::Scalar(x), SvmLiteralParam::Group(y)) => Some((*x * y).into()), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => x.checked_mul(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) => Some((*x * y).into()), + (Plaintext::Literal(SvmLiteralParam::Group(x), _), Plaintext::Literal(SvmLiteralParam::Scalar(y), _)) => Some((*x * y).into()), + (Plaintext::Literal(SvmLiteralParam::Scalar(x), _), Plaintext::Literal(SvmLiteralParam::Group(y), _)) => Some((*x * y).into()), _ => halt2!(span, "Type error"), }) else { halt2!(span, "mul overflow"); @@ -591,73 +597,73 @@ pub fn evaluate_binary( value } BinaryOperation::MulWrapped => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => x.wrapping_mul(**y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => x.wrapping_mul(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => x.wrapping_mul(**y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Nand => match (lhs, rhs) { - (SvmLiteralParam::Boolean(x), SvmLiteralParam::Boolean(y)) => (!(**x & **y)).into(), + (Plaintext::Literal(SvmLiteralParam::Boolean(x), _), Plaintext::Literal(SvmLiteralParam::Boolean(y), _)) => (!(**x & **y)).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Neq => unreachable!("This case was handled above"), BinaryOperation::Nor => match (lhs, rhs) { - (SvmLiteralParam::Boolean(x), SvmLiteralParam::Boolean(y)) => (!(**x | **y)).into(), + (Plaintext::Literal(SvmLiteralParam::Boolean(x), _), Plaintext::Literal(SvmLiteralParam::Boolean(y), _)) => (!(**x | **y)).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Or => match (lhs, rhs) { - (SvmLiteralParam::Boolean(x), SvmLiteralParam::Boolean(y)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::Boolean(x), _), Plaintext::Literal(SvmLiteralParam::Boolean(y), _)) => (**x | **y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::BitwiseOr => match (lhs, rhs) { - (SvmLiteralParam::Boolean(x), SvmLiteralParam::Boolean(y)) => (**x | **y).into(), - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (**x | **y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (**x | **y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (**x | **y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (**x | **y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (**x | **y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (**x | **y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (**x | **y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (**x | **y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (**x | **y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::Boolean(x), _), Plaintext::Literal(SvmLiteralParam::Boolean(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (**x | **y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (**x | **y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Pow => { - if let (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) = (&lhs, &rhs) { + if let (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) = (&lhs, &rhs) { x.pow(y).into() } else { let rhs: u32 = match rhs { - SvmLiteralParam::U8(y) => (**y).into(), - SvmLiteralParam::U16(y) => (**y).into(), - SvmLiteralParam::U32(y) => **y, + Plaintext::Literal(SvmLiteralParam::U8(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U16(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U32(y), _) => **y, _ => tc_fail2!(), }; let Some(value) = (match lhs { - SvmLiteralParam::U8(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::U16(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::U32(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::U64(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::U128(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::I8(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::I16(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::I32(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::I64(x) => x.checked_pow(rhs).map(|z| z.into()), - SvmLiteralParam::I128(x) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::U8(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::U16(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::U32(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::U64(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::U128(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::I8(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::I16(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::I32(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::I64(x), _) => x.checked_pow(rhs).map(|z| z.into()), + Plaintext::Literal(SvmLiteralParam::I128(x), _) => x.checked_pow(rhs).map(|z| z.into()), _ => halt2!(span, "Type error"), }) else { halt2!(span, "pow overflow"); @@ -667,39 +673,39 @@ pub fn evaluate_binary( } BinaryOperation::PowWrapped => { let rhs: u32 = match rhs { - SvmLiteralParam::U8(y) => (**y).into(), - SvmLiteralParam::U16(y) => (**y).into(), - SvmLiteralParam::U32(y) => **y, + Plaintext::Literal(SvmLiteralParam::U8(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U16(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U32(y), _) => **y, _ => halt2!(span, "Type error"), }; match lhs { - SvmLiteralParam::U8(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::U16(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::U32(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::U64(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::U128(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::I8(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::I16(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::I32(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::I64(x) => x.wrapping_pow(rhs).into(), - SvmLiteralParam::I128(x) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U8(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U16(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U32(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U64(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U128(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I8(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I16(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I32(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I64(x), _) => x.wrapping_pow(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I128(x), _) => x.wrapping_pow(rhs).into(), _ => halt2!(span, "Type error"), } } BinaryOperation::Rem => { let Some(value) = (match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (**x).checked_rem(**y).map(|z| z.into()), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (**x).checked_rem(**y).map(|z| z.into()), _ => halt2!(span, "Type error"), }) else { halt2!(span, "rem error"); @@ -708,33 +714,33 @@ pub fn evaluate_binary( } BinaryOperation::RemWrapped => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) if **y != 0 => (*x).wrapping_rem(**y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Shl => { let rhs: u32 = match rhs { - SvmLiteralParam::U8(y) => (**y).into(), - SvmLiteralParam::U16(y) => (**y).into(), - SvmLiteralParam::U32(y) => **y, + Plaintext::Literal(SvmLiteralParam::U8(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U16(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U32(y), _) => **y, _ => halt2!(span, "Type error"), }; match lhs { - SvmLiteralParam::U8(_) | SvmLiteralParam::I8(_) if rhs >= 8 => halt2!(span, "shl overflow"), - SvmLiteralParam::U16(_) | SvmLiteralParam::I16(_) if rhs >= 16 => halt2!(span, "shl overflow"), - SvmLiteralParam::U32(_) | SvmLiteralParam::I32(_) if rhs >= 32 => halt2!(span, "shl overflow"), - SvmLiteralParam::U64(_) | SvmLiteralParam::I64(_) if rhs >= 64 => halt2!(span, "shl overflow"), - SvmLiteralParam::U128(_) | SvmLiteralParam::I128(_) if rhs >= 128 => halt2!(span, "shl overflow"), - SvmLiteralParam::U8(x) => { + Plaintext::Literal(SvmLiteralParam::U8(_), _) | Plaintext::Literal(SvmLiteralParam::I8(_), _) if rhs >= 8 => halt2!(span, "shl overflow"), + Plaintext::Literal(SvmLiteralParam::U16(_), _) | Plaintext::Literal(SvmLiteralParam::I16(_), _) if rhs >= 16 => halt2!(span, "shl overflow"), + Plaintext::Literal(SvmLiteralParam::U32(_), _) | Plaintext::Literal(SvmLiteralParam::I32(_), _) if rhs >= 32 => halt2!(span, "shl overflow"), + Plaintext::Literal(SvmLiteralParam::U64(_), _) | Plaintext::Literal(SvmLiteralParam::I64(_), _) if rhs >= 64 => halt2!(span, "shl overflow"), + Plaintext::Literal(SvmLiteralParam::U128(_), _) | Plaintext::Literal(SvmLiteralParam::I128(_), _) if rhs >= 128 => halt2!(span, "shl overflow"), + Plaintext::Literal(SvmLiteralParam::U8(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -743,7 +749,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::U16(x) => { + Plaintext::Literal(SvmLiteralParam::U16(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -752,7 +758,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::U32(x) => { + Plaintext::Literal(SvmLiteralParam::U32(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -761,7 +767,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::U64(x) => { + Plaintext::Literal(SvmLiteralParam::U64(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -770,7 +776,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::U128(x) => { + Plaintext::Literal(SvmLiteralParam::U128(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -779,7 +785,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::I8(x) => { + Plaintext::Literal(SvmLiteralParam::I8(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -788,7 +794,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::I16(x) => { + Plaintext::Literal(SvmLiteralParam::I16(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -797,7 +803,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::I32(x) => { + Plaintext::Literal(SvmLiteralParam::I32(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -806,7 +812,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::I64(x) => { + Plaintext::Literal(SvmLiteralParam::I64(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -815,7 +821,7 @@ pub fn evaluate_binary( } shifted.into() } - SvmLiteralParam::I128(x) => { + Plaintext::Literal(SvmLiteralParam::I128(x), _) => { let before_ones = x.count_ones(); let shifted = (**x) << rhs; let after_ones = x.count_ones(); @@ -830,91 +836,91 @@ pub fn evaluate_binary( BinaryOperation::ShlWrapped => { let rhs: u32 = match rhs { - SvmLiteralParam::U8(y) => (**y).into(), - SvmLiteralParam::U16(y) => (**y).into(), - SvmLiteralParam::U32(y) => **y, + Plaintext::Literal(SvmLiteralParam::U8(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U16(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U32(y), _) => **y, _ => halt2!(span, "Type error"), }; match lhs { - SvmLiteralParam::U8(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::U16(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::U32(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::U64(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::U128(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::I8(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::I16(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::I32(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::I64(x) => x.wrapping_shl(rhs).into(), - SvmLiteralParam::I128(x) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U8(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U16(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U32(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U64(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::U128(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I8(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I16(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I32(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I64(x), _) => x.wrapping_shl(rhs).into(), + Plaintext::Literal(SvmLiteralParam::I128(x), _) => x.wrapping_shl(rhs).into(), _ => halt2!(span, "Type error"), } } BinaryOperation::Shr => { let rhs: u32 = match rhs { - SvmLiteralParam::U8(y) => (**y).into(), - SvmLiteralParam::U16(y) => (**y).into(), - SvmLiteralParam::U32(y) => **y, + Plaintext::Literal(SvmLiteralParam::U8(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U16(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U32(y), _) => **y, _ => halt2!(span, "Type error"), }; match lhs { - SvmLiteralParam::U8(_) | SvmLiteralParam::I8(_) if rhs >= 8 => halt2!(span, "shr overflow"), - SvmLiteralParam::U16(_) | SvmLiteralParam::I16(_) if rhs >= 16 => halt2!(span, "shr overflow"), - SvmLiteralParam::U32(_) | SvmLiteralParam::I32(_) if rhs >= 32 => halt2!(span, "shr overflow"), - SvmLiteralParam::U64(_) | SvmLiteralParam::I64(_) if rhs >= 64 => halt2!(span, "shr overflow"), - SvmLiteralParam::U128(_) | SvmLiteralParam::I128(_) if rhs >= 128 => halt2!(span, "shr overflow"), - SvmLiteralParam::U8(x) => (**x >> rhs).into(), - SvmLiteralParam::U16(x) => (**x >> rhs).into(), - SvmLiteralParam::U32(x) => (**x >> rhs).into(), - SvmLiteralParam::U64(x) => (**x >> rhs).into(), - SvmLiteralParam::U128(x) => (**x >> rhs).into(), - SvmLiteralParam::I8(x) => (**x >> rhs).into(), - SvmLiteralParam::I16(x) => (**x >> rhs).into(), - SvmLiteralParam::I32(x) => (**x >> rhs).into(), - SvmLiteralParam::I64(x) => (**x >> rhs).into(), - SvmLiteralParam::I128(x) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::U8(_), _) | Plaintext::Literal(SvmLiteralParam::I8(_), _) if rhs >= 8 => halt2!(span, "shr overflow"), + Plaintext::Literal(SvmLiteralParam::U16(_), _) | Plaintext::Literal(SvmLiteralParam::I16(_), _) if rhs >= 16 => halt2!(span, "shr overflow"), + Plaintext::Literal(SvmLiteralParam::U32(_), _) | Plaintext::Literal(SvmLiteralParam::I32(_), _) if rhs >= 32 => halt2!(span, "shr overflow"), + Plaintext::Literal(SvmLiteralParam::U64(_), _) | Plaintext::Literal(SvmLiteralParam::I64(_), _) if rhs >= 64 => halt2!(span, "shr overflow"), + Plaintext::Literal(SvmLiteralParam::U128(_), _) | Plaintext::Literal(SvmLiteralParam::I128(_), _) if rhs >= 128 => halt2!(span, "shr overflow"), + Plaintext::Literal(SvmLiteralParam::U8(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::U16(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::U32(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::U64(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::U128(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::I8(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::I16(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::I32(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::I64(x), _) => (**x >> rhs).into(), + Plaintext::Literal(SvmLiteralParam::I128(x), _) => (**x >> rhs).into(), _ => tc_fail2!(), } } BinaryOperation::ShrWrapped => { let rhs: u32 = match rhs { - SvmLiteralParam::U8(y) => (**y).into(), - SvmLiteralParam::U16(y) => (**y).into(), - SvmLiteralParam::U32(y) => **y, + Plaintext::Literal(SvmLiteralParam::U8(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U16(y), _) => (**y).into(), + Plaintext::Literal(SvmLiteralParam::U32(y), _) => **y, _ => halt2!(span, "Type error"), }; match lhs { - SvmLiteralParam::U8(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::U16(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::U32(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::U64(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::U128(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::I8(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::I16(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::I32(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::I64(x) => (x.wrapping_shr(rhs)).into(), - SvmLiteralParam::I128(x) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::U8(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::U16(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::U32(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::U64(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::U128(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::I8(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::I16(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::I32(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::I64(x), _) => (x.wrapping_shr(rhs)).into(), + Plaintext::Literal(SvmLiteralParam::I128(x), _) => (x.wrapping_shr(rhs)).into(), _ => halt2!(span, "Type error"), } } BinaryOperation::Sub => { let Some(value) = (match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (**x).checked_sub(**y).map(|z| z.into()), - (SvmLiteralParam::Group(x), SvmLiteralParam::Group(y)) => Some((*x - *y).into()), - (SvmLiteralParam::Field(x), SvmLiteralParam::Field(y)) => Some((*x - *y).into()), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (**x).checked_sub(**y).map(|z| z.into()), + (Plaintext::Literal(SvmLiteralParam::Group(x), _), Plaintext::Literal(SvmLiteralParam::Group(y), _)) => Some((*x - *y).into()), + (Plaintext::Literal(SvmLiteralParam::Field(x), _), Plaintext::Literal(SvmLiteralParam::Field(y), _)) => Some((*x - *y).into()), _ => halt2!(span, "Type error"), }) else { halt2!(span, "sub overflow"); @@ -923,30 +929,30 @@ pub fn evaluate_binary( } BinaryOperation::SubWrapped => match (lhs, rhs) { - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (**x).wrapping_sub(**y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (**x).wrapping_sub(**y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (**x).wrapping_sub(**y).into(), _ => halt2!(span, "Type error"), }, BinaryOperation::Xor => match (lhs, rhs) { - (SvmLiteralParam::Boolean(x), SvmLiteralParam::Boolean(y)) => (**x ^ **y).into(), - (SvmLiteralParam::U8(x), SvmLiteralParam::U8(y)) => (**x ^ **y).into(), - (SvmLiteralParam::U16(x), SvmLiteralParam::U16(y)) => (**x ^ **y).into(), - (SvmLiteralParam::U32(x), SvmLiteralParam::U32(y)) => (**x ^ **y).into(), - (SvmLiteralParam::U64(x), SvmLiteralParam::U64(y)) => (**x ^ **y).into(), - (SvmLiteralParam::U128(x), SvmLiteralParam::U128(y)) => (**x ^ **y).into(), - (SvmLiteralParam::I8(x), SvmLiteralParam::I8(y)) => (**x ^ **y).into(), - (SvmLiteralParam::I16(x), SvmLiteralParam::I16(y)) => (**x ^ **y).into(), - (SvmLiteralParam::I32(x), SvmLiteralParam::I32(y)) => (**x ^ **y).into(), - (SvmLiteralParam::I64(x), SvmLiteralParam::I64(y)) => (**x ^ **y).into(), - (SvmLiteralParam::I128(x), SvmLiteralParam::I128(y)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::Boolean(x), _), Plaintext::Literal(SvmLiteralParam::Boolean(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::U8(x), _), Plaintext::Literal(SvmLiteralParam::U8(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::U16(x), _), Plaintext::Literal(SvmLiteralParam::U16(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::U32(x), _), Plaintext::Literal(SvmLiteralParam::U32(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::U64(x), _), Plaintext::Literal(SvmLiteralParam::U64(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::U128(x), _), Plaintext::Literal(SvmLiteralParam::U128(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::I8(x), _), Plaintext::Literal(SvmLiteralParam::I8(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::I16(x), _), Plaintext::Literal(SvmLiteralParam::I16(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::I32(x), _), Plaintext::Literal(SvmLiteralParam::I32(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::I64(x), _), Plaintext::Literal(SvmLiteralParam::I64(y), _)) => (**x ^ **y).into(), + (Plaintext::Literal(SvmLiteralParam::I128(x), _), Plaintext::Literal(SvmLiteralParam::I128(y), _)) => (**x ^ **y).into(), _ => halt2!(span, "Type error"), }, }; diff --git a/compiler/ast/src/interpreter_value/value.rs b/compiler/ast/src/interpreter_value/value.rs index 9cad57608f3..cd389197b75 100644 --- a/compiler/ast/src/interpreter_value/value.rs +++ b/compiler/ast/src/interpreter_value/value.rs @@ -571,6 +571,25 @@ impl Value { Some(array[i].clone().into()) } + pub fn array_slice(&self, start: usize, end_exclusive: Option) -> Option { + let plaintext: &SvmPlaintext = self.try_as_ref()?; + let Plaintext::Array(array, ..) = plaintext else { + return None; + }; + + let range = match end_exclusive { + Some(end) if end <= start || end > array.len() => return None, + Some(end) => start..end, + None if start > array.len() => return None, + None => start..array.len(), + }; + + Some(Value::from(ValueVariants::Svm(SvmValue::Plaintext(SvmPlaintext::Array( + array[range].to_vec(), + Default::default(), + ))))) + } + pub fn array_index_set(&mut self, i: usize, value: Self) -> Option<()> { let plaintext_rhs: SvmPlaintext = value.try_into().ok()?; @@ -584,6 +603,15 @@ impl Value { Some(()) } + pub fn array_len(&self) -> Option { + let plaintext: &SvmPlaintext = self.try_as_ref()?; + let Plaintext::Array(array, ..) = plaintext else { + return None; + }; + + Some(array.len()) + } + pub fn tuple_len(&self) -> Option { let ValueVariants::Tuple(tuple) = &self.contents else { return None; diff --git a/compiler/ast/src/passes/consumer.rs b/compiler/ast/src/passes/consumer.rs index 79fee072630..2cdb771ee17 100644 --- a/compiler/ast/src/passes/consumer.rs +++ b/compiler/ast/src/passes/consumer.rs @@ -40,6 +40,7 @@ pub trait ExpressionConsumer { Expression::Locator(locator) => self.consume_locator(locator), Expression::MemberAccess(access) => self.consume_member_access(*access), Expression::Repeat(repeat) => self.consume_repeat(*repeat), + Expression::Slice(slice) => self.consume_slice(*slice), Expression::Ternary(ternary) => self.consume_ternary(*ternary), Expression::Tuple(tuple) => self.consume_tuple(tuple), Expression::TupleAccess(access) => self.consume_tuple_access(*access), @@ -82,6 +83,8 @@ pub trait ExpressionConsumer { fn consume_repeat(&mut self, _input: RepeatExpression) -> Self::Output; + fn consume_slice(&mut self, _input: SliceExpression) -> Self::Output; + fn consume_ternary(&mut self, _input: TernaryExpression) -> Self::Output; fn consume_tuple(&mut self, _input: TupleExpression) -> Self::Output; diff --git a/compiler/ast/src/passes/reconstructor.rs b/compiler/ast/src/passes/reconstructor.rs index 35cd8a827e7..1545e7bf781 100644 --- a/compiler/ast/src/passes/reconstructor.rs +++ b/compiler/ast/src/passes/reconstructor.rs @@ -129,6 +129,7 @@ pub trait AstReconstructor { Expression::Locator(locator) => self.reconstruct_locator(locator, additional), Expression::MemberAccess(access) => self.reconstruct_member_access(*access, additional), Expression::Repeat(repeat) => self.reconstruct_repeat(*repeat, additional), + Expression::Slice(slice) => self.reconstruct_slice(*slice, additional), Expression::Ternary(ternary) => self.reconstruct_ternary(*ternary, additional), Expression::Tuple(tuple) => self.reconstruct_tuple(tuple, additional), Expression::TupleAccess(access) => self.reconstruct_tuple_access(*access, additional), @@ -215,6 +216,25 @@ pub trait AstReconstructor { ) } + fn reconstruct_slice( + &mut self, + input: SliceExpression, + _additional: &Self::AdditionalInput, + ) -> (Expression, Self::AdditionalOutput) { + ( + SliceExpression { + array: self.reconstruct_expression(input.array, &Default::default()).0, + start: input.start.map(|start| self.reconstruct_expression(start, &Default::default()).0), + end: input + .end + .map(|(inclusive, end)| (inclusive, self.reconstruct_expression(end, &Default::default()).0)), + ..input + } + .into(), + Default::default(), + ) + } + fn reconstruct_tuple_access( &mut self, input: TupleAccess, diff --git a/compiler/ast/src/passes/visitor.rs b/compiler/ast/src/passes/visitor.rs index 1aa1f104341..e65375a3735 100644 --- a/compiler/ast/src/passes/visitor.rs +++ b/compiler/ast/src/passes/visitor.rs @@ -117,6 +117,7 @@ pub trait AstVisitor { Expression::Locator(locator) => self.visit_locator(locator, additional), Expression::MemberAccess(access) => self.visit_member_access(access, additional), Expression::Repeat(repeat) => self.visit_repeat(repeat, additional), + Expression::Slice(slice) => self.visit_slice(slice, additional), Expression::Ternary(ternary) => self.visit_ternary(ternary, additional), Expression::Tuple(tuple) => self.visit_tuple(tuple, additional), Expression::TupleAccess(access) => self.visit_tuple_access(access, additional), @@ -227,6 +228,17 @@ pub trait AstVisitor { Default::default() } + fn visit_slice(&mut self, input: &SliceExpression, _additional: &Self::AdditionalInput) -> Self::Output { + self.visit_expression(&input.array, &Default::default()); + if let Some(start) = input.start.as_ref() { + self.visit_expression(start, &Default::default()); + } + if let Some((_, end)) = input.end.as_ref() { + self.visit_expression(end, &Default::default()); + } + Default::default() + } + fn visit_ternary(&mut self, input: &TernaryExpression, _additional: &Self::AdditionalInput) -> Self::Output { self.visit_expression(&input.condition, &Default::default()); self.visit_expression(&input.if_true, &Default::default()); diff --git a/compiler/parser-lossless/src/grammar.lalrpop b/compiler/parser-lossless/src/grammar.lalrpop index 6addac5d644..91b1d794364 100644 --- a/compiler/parser-lossless/src/grammar.lalrpop +++ b/compiler/parser-lossless/src/grammar.lalrpop @@ -358,6 +358,22 @@ Expr1: SyntaxNode<'a> = { > > > => { SyntaxNode::new(ExpressionKind::ArrayAccess, [x, l, index, r]) }, + // Array slice with `[a..b]` + > > > => { + SyntaxNode::new(ExpressionKind::SliceBoth, [x, l, start, op, end, r]) + }, + // Array slice with `[a..]` + > > > > => { + SyntaxNode::new(ExpressionKind::SliceFirst, [x, l, start, op, r]) + }, + // Array slice with `[..=b]` + > > > => { + SyntaxNode::new(ExpressionKind::SliceLast, [x, l, op, end, r]) + }, + // Array slice with `[..]` + > > > > => { + SyntaxNode::new(ExpressionKind::SliceNone, [x, l, op, r]) + }, // Member access. > > > => { SyntaxNode::new(ExpressionKind::MemberAccess, [x, d, i]) @@ -368,6 +384,11 @@ Expr1: SyntaxNode<'a> = { }, } +RangeOp: SyntaxNode<'a> = { + WithTrivia, + WithTrivia, +} + Op2: SyntaxNode<'a> = { WithTrivia, WithTrivia, @@ -875,6 +896,7 @@ extern { Comma => LalrToken { token: Token::Comma, .. }, Dot => LalrToken { token: Token::Dot, .. }, DotDot => LalrToken { token: Token::DotDot, .. }, + DotDotEq => LalrToken { token: Token::DotDotEq, .. }, Semicolon => LalrToken { token: Token::Semicolon, .. }, Colon => LalrToken { token: Token::Colon, .. }, DoubleColon => LalrToken{ token: Token::DoubleColon, .. }, diff --git a/compiler/parser-lossless/src/lib.rs b/compiler/parser-lossless/src/lib.rs index 5ef2bcb7723..9c7c67d7806 100644 --- a/compiler/parser-lossless/src/lib.rs +++ b/compiler/parser-lossless/src/lib.rs @@ -158,6 +158,10 @@ pub enum ExpressionKind { MethodCall, Parenthesized, Repeat, + SliceBoth, + SliceFirst, + SliceLast, + SliceNone, // program.id, block.height, etc SpecialAccess, Struct, diff --git a/compiler/parser-lossless/src/tokens.rs b/compiler/parser-lossless/src/tokens.rs index 7bcf4100b5b..00483f65779 100644 --- a/compiler/parser-lossless/src/tokens.rs +++ b/compiler/parser-lossless/src/tokens.rs @@ -216,6 +216,8 @@ pub enum Token { Dot, #[token("..")] DotDot, + #[token("..=")] + DotDotEq, #[token(";")] Semicolon, #[token(":")] @@ -411,6 +413,7 @@ impl Token { "Comma" => "','", "Dot" => "'.'", "DotDot" => "'..'", + "DotDotEq" => "'..='", "Semicolon" => "';'", "Colon" => "':'", "DoubleColon" => "'::'", diff --git a/compiler/parser/src/conversions.rs b/compiler/parser/src/conversions.rs index d00940f0a30..78027dd2265 100644 --- a/compiler/parser/src/conversions.rs +++ b/compiler/parser/src/conversions.rs @@ -719,6 +719,52 @@ pub fn to_expression(node: &SyntaxNode<'_>, builder: &NodeBuilder, handler: &Han let count = to_expression(count, builder, handler)?; leo_ast::RepeatExpression { expr, count, span, id }.into() } + ExpressionKind::SliceBoth => { + let [array, _left, start, c, end, _right] = &node.children[..] else { + panic!("Can't happen"); + }; + let array = to_expression(array, builder, handler)?; + let start = Some(to_expression(start, builder, handler)?); + let inclusive = match c.text { + ".." => false, + "..=" => true, + _ => panic!("Can't happen"), + }; + let end = Some((inclusive, to_expression(end, builder, handler)?)); + leo_ast::SliceExpression { array, start, end, span, id }.into() + } + ExpressionKind::SliceFirst => { + let [array, _left, start, _c, _right] = &node.children[..] else { + panic!("Can't happen"); + }; + let array = to_expression(array, builder, handler)?; + let start = Some(to_expression(start, builder, handler)?); + let end = None; + leo_ast::SliceExpression { array, start, end, span, id }.into() + } + ExpressionKind::SliceLast => { + let [array, _left, _c, end, _right] = &node.children[..] else { + panic!("Can't happen"); + }; + let array = to_expression(array, builder, handler)?; + let start = None; + let inclusive = match _c.text { + ".." => false, + "..=" => true, + _ => panic!("Can't happen"), + }; + let end = Some((inclusive, to_expression(end, builder, handler)?)); + leo_ast::SliceExpression { array, start, end, span, id }.into() + } + ExpressionKind::SliceNone => { + let [array, _left, _c, _right] = &node.children[..] else { + panic!("Can't happen"); + }; + let array = to_expression(array, builder, handler)?; + let start = None; + let end = None; + leo_ast::SliceExpression { array, start, end, span, id }.into() + } ExpressionKind::SpecialAccess => { let [qualifier, _dot, name] = &node.children[..] else { panic!("Can't happen"); diff --git a/compiler/passes/src/code_generation/expression.rs b/compiler/passes/src/code_generation/expression.rs index 8323237c9b8..d6bd82fedfb 100644 --- a/compiler/passes/src/code_generation/expression.rs +++ b/compiler/passes/src/code_generation/expression.rs @@ -70,6 +70,7 @@ impl CodeGeneratingVisitor<'_> { Expression::Locator(expr) => self.visit_locator(expr), Expression::MemberAccess(expr) => self.visit_member_access(expr), Expression::Repeat(expr) => self.visit_repeat(expr), + Expression::Slice(expr) => self.visit_slice(expr), Expression::Ternary(expr) => self.visit_ternary(expr), Expression::Tuple(expr) => self.visit_tuple(expr), Expression::TupleAccess(_) => panic!("Tuple accesses should not appear in the AST at this point."), @@ -141,6 +142,23 @@ impl CodeGeneratingVisitor<'_> { let (left_operand, left_instructions) = self.visit_expression(&input.left); let (right_operand, right_instructions) = self.visit_expression(&input.right); + // Get the types of the expressions. + let left_type = self + .state + .type_table + .get(&input.left.id()) + .expect("All types should be known at this phase of compilation"); + let right_type = self + .state + .type_table + .get(&input.right.id()) + .expect("All types should be known at this phase of compilation"); + + // Check that the types are not arrays. + // This is a sanity check. + assert!(!matches!(left_type, Type::Array(_))); + assert!(!matches!(right_type, Type::Array(_))); + let opcode = match input.op { BinaryOperation::Add => String::from("add"), BinaryOperation::AddWrapped => String::from("add.w"), @@ -397,6 +415,52 @@ impl CodeGeneratingVisitor<'_> { (destination_register, operand_instructions) } + fn visit_slice(&mut self, input: &leo_ast::SliceExpression) -> (String, String) { + let (array_operand, mut operand_instructions) = self.visit_expression(&input.array); + + // Get the start index. + let start = match &input.start { + Some(Expression::Literal(Literal { + variant: LiteralVariant::Integer(_, s) | LiteralVariant::Unsuffixed(s), + .. + })) => s.parse::().expect("constant folding ensures that this is a valid integer"), + _ => panic!("constant folding ensures that the start index is a literal"), + }; + + // Get the end index. + let end = match &input.end { + Some(( + _, + Expression::Literal(Literal { + variant: LiteralVariant::Integer(_, s) | LiteralVariant::Unsuffixed(s), + .. + }), + )) => s.parse::().expect("constant folding ensures that this is a valid integer"), + _ => panic!("constant folding ensures that the end index is a literal"), + }; + + // Construct the expression operands. + let expression_operands = + (start..end).map(|i| format!("{array_operand}[{i}u32]")).collect::>().join(" "); + + // Construct the destination register. + let destination_register = self.next_register(); + + // Get the array type. + let Some(array_type @ Type::Array(..)) = self.state.type_table.get(&input.id) else { + panic!("All types should be known at this phase of compilation"); + }; + let array_type: String = Self::visit_type(&array_type); + + let array_instruction = + format!(" cast {expression_operands} into {destination_register} as {array_type};\n"); + + // Concatenate the instructions. + operand_instructions.push_str(&array_instruction); + + (destination_register, operand_instructions) + } + // group::GEN -> group::GEN fn visit_associated_constant(&mut self, input: &AssociatedConstantExpression) -> (String, String) { (format!("{input}"), String::new()) diff --git a/compiler/passes/src/common/replacer/mod.rs b/compiler/passes/src/common/replacer/mod.rs index dc9dfe451dc..930eca35559 100644 --- a/compiler/passes/src/common/replacer/mod.rs +++ b/compiler/passes/src/common/replacer/mod.rs @@ -82,6 +82,7 @@ where Expression::Locator(locator) => self.reconstruct_locator(locator, &()), Expression::MemberAccess(access) => self.reconstruct_member_access(*access, &()), Expression::Repeat(repeat) => self.reconstruct_repeat(*repeat, &()), + Expression::Slice(slice) => self.reconstruct_slice(*slice, &()), Expression::Ternary(ternary) => self.reconstruct_ternary(*ternary, &()), Expression::Tuple(tuple) => self.reconstruct_tuple(tuple, &()), Expression::TupleAccess(access) => self.reconstruct_tuple_access(*access, &()), diff --git a/compiler/passes/src/common/type_table/mod.rs b/compiler/passes/src/common/type_table/mod.rs index e8533148d29..59cc41c9bd4 100644 --- a/compiler/passes/src/common/type_table/mod.rs +++ b/compiler/passes/src/common/type_table/mod.rs @@ -38,3 +38,12 @@ impl TypeTable { self.inner.borrow_mut().insert(index, value); } } + +impl std::fmt::Display for TypeTable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (node_id, type_) in self.inner.borrow().iter() { + writeln!(f, "{node_id}: {type_}")?; + } + Ok(()) + } +} diff --git a/compiler/passes/src/common_subexpression_elimination/ast.rs b/compiler/passes/src/common_subexpression_elimination/ast.rs index e8f6c722969..15740fefc23 100644 --- a/compiler/passes/src/common_subexpression_elimination/ast.rs +++ b/compiler/passes/src/common_subexpression_elimination/ast.rs @@ -25,7 +25,7 @@ impl AstReconstructor for CommonSubexpressionEliminatingVisitor<'_> { fn reconstruct_expression(&mut self, input: Expression, _additional: &()) -> (Expression, Self::AdditionalOutput) { // We simply forward every expression to `try_expr` rather than using the individual reconstruct // functions from the `AstReconstructor` trait. - (self.try_expr(input, None).expect("CSE Error").0, Default::default()) + (self.try_expr(input, None).expect("CSE Error: Error reconstructing expression").0, Default::default()) } fn reconstruct_block(&mut self, mut block: Block) -> (Block, Self::AdditionalOutput) { @@ -38,7 +38,9 @@ impl AstReconstructor for CommonSubexpressionEliminatingVisitor<'_> { fn reconstruct_definition(&mut self, mut input: DefinitionStatement) -> (Statement, Self::AdditionalOutput) { match input.place { DefinitionPlace::Single(place) => { - let (value, definition_not_needed) = self.try_expr(input.value, Some(place.name)).expect("CSE Error"); + let (value, definition_not_needed) = self + .try_expr(input.value, Some(place.name)) + .expect("CSE Error: Error reconstructing single definition"); if definition_not_needed { // We don't need this definition - everywhere its variable is referred to, we'll map it to some other @@ -50,7 +52,8 @@ impl AstReconstructor for CommonSubexpressionEliminatingVisitor<'_> { } } DefinitionPlace::Multiple(_) => { - let (value, _) = self.try_expr(input.value, None).expect("CSE Error"); + let (value, _) = + self.try_expr(input.value, None).expect("CSE Error: Error reconstructing multiple definitions"); input.value = value; (input.into(), Default::default()) } diff --git a/compiler/passes/src/common_subexpression_elimination/visitor.rs b/compiler/passes/src/common_subexpression_elimination/visitor.rs index d4e7a974e66..a1ff2e6ca88 100644 --- a/compiler/passes/src/common_subexpression_elimination/visitor.rs +++ b/compiler/passes/src/common_subexpression_elimination/visitor.rs @@ -41,6 +41,7 @@ pub enum Expr { ArrayAccess { array: Atom, index: Atom }, Binary { op: BinaryOperation, left: Atom, right: Atom }, Repeat { value: Atom, count: Atom }, + Slice { array: Atom, start: Atom, end: Option<(bool, Atom)> }, Ternary { condition: Atom, if_true: Atom, if_false: Atom }, Unary { op: UnaryOperation, receiver: Atom }, } @@ -73,6 +74,9 @@ impl CommonSubexpressionEliminatingVisitor<'_> { /// Turn `expression` into an `Atom` if possible, looking it up in the expression /// tables when it's a path. Also changes `expression` into the found value. fn try_atom(&self, expression: &mut Expression) -> Option { + // Get the ID of the expression. + let id = expression.id(); + // Modify the expression in place if it's a path that can be replaced. let value = match expression { Expression::Literal(literal) => Atom::Literal(literal.variant.clone()), Expression::Path(path) => { @@ -80,8 +84,10 @@ impl CommonSubexpressionEliminatingVisitor<'_> { Atom::Path(path.qualifier().iter().map(|id| id.name).chain([path.identifier().name]).collect()); let expr = Expr::Atom(atom_path); if let Some(name) = self.scopes.iter().rev().find_map(|scope| scope.expressions.get(&expr)) { - // This path is mapped to some name already, so replace it. - *path = Path::new( + // Get the type of the expression. + let type_ = self.state.type_table.get(&id)?; + // Construct a new path for this identifier. + let p = Path::new( Vec::new(), Identifier::new(*name, self.state.node_builder.next_id()), true, @@ -89,6 +95,10 @@ impl CommonSubexpressionEliminatingVisitor<'_> { path.span(), self.state.node_builder.next_id(), ); + // Assign the type of the path. + self.state.type_table.insert(p.id(), type_); + // This path is mapped to some name already, so replace it. + *path = p; Atom::Path(vec![*name]) } else { let Expr::Atom(atom_path) = expr else { unreachable!() }; @@ -108,6 +118,7 @@ impl CommonSubexpressionEliminatingVisitor<'_> { | Expression::Locator(_) | Expression::MemberAccess(_) | Expression::Repeat(_) + | Expression::Slice(_) | Expression::Struct(_) | Expression::Ternary(_) | Expression::Tuple(_) @@ -172,6 +183,15 @@ impl CommonSubexpressionEliminatingVisitor<'_> { let count = self.try_atom(&mut repeat_expression.count)?; Expr::Repeat { value, count } } + Expression::Slice(slice_expression) => { + let array = self.try_atom(&mut slice_expression.array)?; + let start = slice_expression.start.as_mut().and_then(|expr| self.try_atom(expr))?; + let end = slice_expression + .end + .as_mut() + .map(|(inclusive, expr)| self.try_atom(expr).map(|end| (*inclusive, end)))?; + Expr::Slice { array, start, end } + } Expression::Ternary(ternary_expression) => { let condition = self.try_atom(&mut ternary_expression.condition)?; let if_true = self.try_atom(&mut ternary_expression.if_true)?; @@ -245,6 +265,10 @@ impl CommonSubexpressionEliminatingVisitor<'_> { if let Some(name) = map.expressions.get(&expr).cloned() { // We already have a symbol whose value is this expression. let identifier = Identifier { name, span, id: self.state.node_builder.next_id() }; + // Get the type of the expression. + let type_ = self.state.type_table.get(&expression.id())?.clone(); + // Assign the type of the new expression. + self.state.type_table.insert(identifier.id, type_.clone()); if let Some(place) = place { // We were defining a new variable, whose right hand side is already defined, so map // this variable to the previous variable. diff --git a/compiler/passes/src/const_propagation/ast.rs b/compiler/passes/src/const_propagation/ast.rs index 9468e61e55b..9b8383cd578 100644 --- a/compiler/passes/src/const_propagation/ast.rs +++ b/compiler/passes/src/const_propagation/ast.rs @@ -49,7 +49,9 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { /* Expressions */ fn reconstruct_expression(&mut self, input: Expression, _additional: &()) -> (Expression, Self::AdditionalOutput) { + // Look up the old type before reconstructing the expression. let opt_old_type = self.state.type_table.get(&input.id()); + let (new_expr, opt_value) = match input { Expression::Array(array) => self.reconstruct_array(array, &()), Expression::ArrayAccess(access) => self.reconstruct_array_access(*access, &()), @@ -66,6 +68,7 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { Expression::Locator(locator) => self.reconstruct_locator(locator, &()), Expression::MemberAccess(access) => self.reconstruct_member_access(*access, &()), Expression::Repeat(repeat) => self.reconstruct_repeat(*repeat, &()), + Expression::Slice(slice) => self.reconstruct_slice(*slice, &()), Expression::Ternary(ternary) => self.reconstruct_ternary(*ternary, &()), Expression::Tuple(tuple) => self.reconstruct_tuple(tuple, &()), Expression::TupleAccess(access) => self.reconstruct_tuple_access(*access, &()), @@ -73,7 +76,8 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { Expression::Unit(unit) => self.reconstruct_unit(unit, &()), }; - // If the expression was in the type table before, make an entry for the new expression. + // Update the type table to reflect that the new expression has the same type as the old one. + // Note. The old type must be looked up after reconstructing the expression. if let Some(old_type) = opt_old_type { self.state.type_table.insert(new_expr.id(), old_type); } @@ -249,7 +253,6 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { let member_name = input.name.name; if let Some(struct_) = value_opt { let value_result = struct_.member_access(member_name).expect("Type checking guarantees the member exists."); - (self.value_to_expression(&value_result, span, id).expect(VALUE_ERROR), Some(value_result.clone())) } else { (MemberAccess { inner, ..input }.into(), None) @@ -277,6 +280,130 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { } } + fn reconstruct_slice(&mut self, input: SliceExpression, _additional: &()) -> (Expression, Self::AdditionalOutput) { + let span = input.span(); + let id = input.id(); + let array_id = input.array.id(); + + // Reconstruct the array expression. + let (array, array_opt) = self.reconstruct_expression(input.array, &()); + + // Reconstruct the array type. + let array_type = match self.state.type_table.get(&array_id) { + Some(Type::Array(array_type)) => match self.reconstruct_array_type(array_type.clone()) { + (Type::Array(array_type), _) => array_type, + _ => panic!("`reconstruct_array_type` always returns an array"), + }, + _ => panic!("Type checking guaranteed that this is an array"), + }; + + // Reconstruct the start expression, if it was provided. Otherwise, default to 0. + let (start, start_value) = if let Some(start_expr) = input.start { + self.reconstruct_expression(start_expr, &()) + } else { + ( + Literal::integer(IntegerType::U32, "0".to_string(), Default::default(), Default::default()).into(), + interpreter_value::literal_to_value( + &Literal { + variant: LiteralVariant::Integer(IntegerType::U32, "0".to_string()), + id: Default::default(), + span, + }, + &Some(Type::Integer(IntegerType::U32)), + ) + .ok(), + ) + }; + + // Reconstruct the end expression if it was provided, otherwise, default to the array length. + let (end, end_value) = if let Some((end_inclusive, end_expr)) = input.end { + // Get the span of the end expression. + let end_expr_span = end_expr.span(); + // If the end is inclusive, we need to add 1 to it to make it exclusive. + let expression = if end_inclusive { + // Create a new integer literal. + let literal = Expression::Literal(Literal::integer( + IntegerType::U32, + "1".to_string(), + end_expr_span, + self.state.node_builder.next_id(), + )); + // Assign its type. + self.state.type_table.insert(literal.id(), Type::Integer(IntegerType::U32)); + // Create a new expression. + let expression = Expression::Binary(Box::new(BinaryExpression { + left: end_expr, + op: BinaryOperation::Add, + right: literal, + id: self.state.node_builder.next_id(), + span: end_expr_span, + })); + // Assign its type. + self.state.type_table.insert(expression.id(), Type::Integer(IntegerType::U32)); + + expression + } else { + end_expr + }; + let (end_expr, end_value) = self.reconstruct_expression(expression, &()); + (end_expr, end_value.and_then(|v| v.as_u32())) + } else { + (*array_type.length.clone(), array_type.length.as_u32()) + }; + + // Check the indices. + match (start_value.as_ref().and_then(|v| v.as_u32()), end_value, array_type.length.as_u32()) { + (Some(start_value), Some(end_value), Some(array_length)) => { + // A helper to check that a value is in bounds. + let check_in_bounds = |value: u32| { + if value > array_length { + // Only emit a bounds error if we have no other errors yet. + // This prevents a chain of redundant error messages when a loop is unrolled. + if !self.state.handler.had_errors() { + self.emit_err(StaticAnalyzerError::array_bounds(value.to_string(), array_length, span)); + } + false + } else { + true + } + }; + + // Check that the start value is in bounds. + check_in_bounds(start_value); + + // Check that the end value is in bounds. + check_in_bounds(end_value); + + // Check that the start value is strictly less than the end value. + if start_value >= end_value { + // Only emit a bounds error if we have no other errors yet. + if !self.state.handler.had_errors() { + self.emit_err(StaticAnalyzerError::custom_error( + "The slice's starting index must be strictly less than the ending index.", + None::, + span, + )); + } + } else if let Some(array_value) = array_opt { + // If the array is determined, evaluate the slice at compile time. + let result_value = array_value + .array_slice(start_value as usize, Some(end_value as usize)) + .expect("Type checking checked bounds."); + return (self.value_to_expression(&result_value, span, id).expect(VALUE_ERROR), Some(result_value)); + } + } + _ => { + if end_value.is_none() { + self.array_index_not_evaluated = Some(end.span()); + } + if start_value.is_none() { + self.array_index_not_evaluated = Some(start.span()); + } + } + } + (SliceExpression { array, start: Some(start), end: Some((false, end)), ..input }.into(), None) + } + fn reconstruct_tuple_access( &mut self, input: TupleAccess, @@ -324,6 +451,10 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { let (left, lhs_opt_value) = self.reconstruct_expression(input.left, &()); let (right, rhs_opt_value) = self.reconstruct_expression(input.right, &()); + // Get the types of the left and right operands. + let left_type = self.state.type_table.get(&left.id()); + let right_type = self.state.type_table.get(&right.id()); + if let (Some(lhs_value), Some(rhs_value)) = (lhs_opt_value, rhs_opt_value) { // We were able to evaluate both operands, so we can evaluate this expression. match interpreter_value::evaluate_binary( @@ -340,6 +471,96 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { Err(err) => self .emit_err(StaticAnalyzerError::compile_time_binary_op(lhs_value, rhs_value, input.op, err, span)), } + } else if let (BinaryOperation::Add, Some(Type::Array(left_array_type)), Some(Type::Array(right_array_type))) = + (input.op, left_type, right_type) + { + // Reconstruct the array types. + let (Type::Array(left_array_type), _) = self.reconstruct_array_type(left_array_type.clone()) else { + panic!("`reconstruct_array_type` always returns an array"); + }; + let (Type::Array(right_array_type), _) = self.reconstruct_array_type(right_array_type.clone()) else { + panic!("`reconstruct_array_type` always returns an array"); + }; + + // If the lengths of both arrays are known at compile time, we can rewrite the addition as an array expression. + if let (Some(left_length), Some(right_length)) = + (left_array_type.length.as_u32(), right_array_type.length.as_u32()) + { + // Check that the new array length does not exceed the maximum allowed length. + let total_length = left_length as usize + right_length as usize; + if total_length > self.limits.max_array_elements { + self.emit_err(StaticAnalyzerError::custom_error( + format!( + "The resulting array length exceeds the maximum allowed length: {}.", + self.limits.max_array_elements, + ), + None::, + span, + )); + return (BinaryExpression { left, right, ..input }.into(), None); + } + + // The new array length is the sum of the two lengths. + let new_length = Expression::Literal(Literal::integer( + IntegerType::U32, + (left_length + right_length).to_string(), + Default::default(), + self.state.node_builder.next_id(), + )); + // Assign its type. + self.state.type_table.insert(new_length.id(), Type::Integer(IntegerType::U32)); + // Create the elements. + let mut elements = Vec::with_capacity(total_length); + + // A helper function to add the elements to the new array. + let mut add_elements = |length: u32, array: &Expression, element_type: &Type| { + elements.extend((0..length as usize).map(|i| { + // Create the index. + let index = Expression::Literal(Literal::integer( + IntegerType::U32, + i.to_string(), + Default::default(), + self.state.node_builder.next_id(), + )); + // Assign its type. + self.state.type_table.insert(index.id(), Type::Integer(IntegerType::U32)); + // Create the access expression. + let element: Expression = ArrayAccess { + array: array.clone(), + index, + id: self.state.node_builder.next_id(), + span: Default::default(), + } + .into(); + // Assign its type. + self.state.type_table.insert(element.id(), element_type.clone()); + // Reconstruct and return the element. + self.reconstruct_expression(element, &()).0 + })) + }; + + // Add the elements from the left array. + add_elements(left_length, &left, &left_array_type.element_type); + // Add the elements from the right array. + add_elements(right_length, &right, &right_array_type.element_type); + + // Create the new array expression. + let new_array: Expression = + ArrayExpression { elements, id: self.state.node_builder.next_id(), span: Default::default() } + .into(); + + // Assign its type. + self.state.type_table.insert( + new_array.id(), + Type::Array(leo_ast::ArrayType { + element_type: left_array_type.element_type.clone(), + length: Box::new(new_length.clone()), + }), + ); + + // Reconstruct and return the new array expression. + return self.reconstruct_expression(new_array, &()); + } } (BinaryExpression { left, right, ..input }.into(), None) diff --git a/compiler/passes/src/const_propagation/mod.rs b/compiler/passes/src/const_propagation/mod.rs index e059ca6dbde..ad887933770 100644 --- a/compiler/passes/src/const_propagation/mod.rs +++ b/compiler/passes/src/const_propagation/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::Pass; +use crate::{Pass, TypeCheckingInput}; use leo_ast::ProgramReconstructor as _; use leo_errors::Result; @@ -66,6 +66,7 @@ impl Pass for ConstPropagation { fn do_pass(_input: Self::Input, state: &mut crate::CompilerState) -> Result { let mut ast = std::mem::take(&mut state.ast); let mut visitor = ConstPropagationVisitor { + limits: TypeCheckingInput::new(state.network), state, program: Symbol::intern(""), module: vec![], @@ -91,6 +92,7 @@ impl Pass for ConstPropagation { impl<'a> ConstPropagationVisitor<'a> { pub fn new(state: &'a mut crate::CompilerState, program: Symbol) -> Self { ConstPropagationVisitor { + limits: TypeCheckingInput::new(state.network), state, program, module: vec![], diff --git a/compiler/passes/src/const_propagation/visitor.rs b/compiler/passes/src/const_propagation/visitor.rs index bdde0c4d4b7..d436a437845 100644 --- a/compiler/passes/src/const_propagation/visitor.rs +++ b/compiler/passes/src/const_propagation/visitor.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::CompilerState; +use crate::{CompilerState, TypeCheckingInput}; use leo_ast::{Expression, Node, NodeID, interpreter_value::Value}; use leo_errors::StaticAnalyzerError; @@ -36,6 +36,8 @@ pub struct ConstPropagationVisitor<'a> { pub array_length_not_evaluated: Option, /// A repeat expression count which was not able to be evaluated. pub repeat_count_not_evaluated: Option, + /// Limits on data type sizes. + pub limits: TypeCheckingInput, } impl ConstPropagationVisitor<'_> { @@ -73,6 +75,20 @@ impl ConstPropagationVisitor<'_> { .map(|mem| (mem.identifier.name, mem.type_.clone())) .collect() }; + // If the value is an array, check that the limits are not exceeded. + if let Some(length) = value.array_len() { + if length > self.limits.max_array_elements { + self.emit_err(StaticAnalyzerError::custom_error( + format!( + "The resulting array length exceeds the maximum allowed length: {}.", + self.limits.max_array_elements, + ), + None::, + span, + )); + return None; + } + } value.to_expression(span, &self.state.node_builder, &ty, &struct_lookup) } diff --git a/compiler/passes/src/dead_code_elimination/visitor.rs b/compiler/passes/src/dead_code_elimination/visitor.rs index 37d3f65ed96..1d4f95a03fc 100644 --- a/compiler/passes/src/dead_code_elimination/visitor.rs +++ b/compiler/passes/src/dead_code_elimination/visitor.rs @@ -47,6 +47,11 @@ impl DeadCodeEliminatingVisitor<'_> { ArrayAccess(array) => sef(&array.array) && sef(&array.index), MemberAccess(mem) => sef(&mem.inner), Repeat(repeat) => sef(&repeat.expr) && sef(&repeat.count), + Slice(slice) => { + sef(&slice.array) + && slice.start.as_ref().is_none_or(sef) + && slice.end.as_ref().is_none_or(|(_, e)| sef(e)) + } TupleAccess(tuple) => sef(&tuple.tuple), Array(array) => array.elements.iter().all(sef), AssociatedConstant(_) => true, diff --git a/compiler/passes/src/monomorphization/ast.rs b/compiler/passes/src/monomorphization/ast.rs index 2e696c4a419..855df882424 100644 --- a/compiler/passes/src/monomorphization/ast.rs +++ b/compiler/passes/src/monomorphization/ast.rs @@ -104,6 +104,7 @@ impl AstReconstructor for MonomorphizationVisitor<'_> { Expression::Locator(locator) => self.reconstruct_locator(locator, &()), Expression::MemberAccess(access) => self.reconstruct_member_access(*access, &()), Expression::Repeat(repeat) => self.reconstruct_repeat(*repeat, &()), + Expression::Slice(slice) => self.reconstruct_slice(*slice, &()), Expression::Ternary(ternary) => self.reconstruct_ternary(*ternary, &()), Expression::Tuple(tuple) => self.reconstruct_tuple(tuple, &()), Expression::TupleAccess(access) => self.reconstruct_tuple_access(*access, &()), diff --git a/compiler/passes/src/option_lowering/ast.rs b/compiler/passes/src/option_lowering/ast.rs index 1d7639ab93a..713729a590b 100644 --- a/compiler/passes/src/option_lowering/ast.rs +++ b/compiler/passes/src/option_lowering/ast.rs @@ -100,6 +100,7 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { Expression::Locator(e) => self.reconstruct_locator(e, additional), Expression::MemberAccess(e) => self.reconstruct_member_access(*e, additional), Expression::Repeat(e) => self.reconstruct_repeat(*e, additional), + Expression::Slice(e) => self.reconstruct_slice(*e, additional), Expression::Ternary(e) => self.reconstruct_ternary(*e, additional), Expression::Tuple(e) => self.reconstruct_tuple(e, additional), Expression::TupleAccess(e) => self.reconstruct_tuple_access(*e, additional), @@ -258,17 +259,8 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { mut input: RepeatExpression, additional: &Self::AdditionalInput, ) -> (Expression, Self::AdditionalOutput) { - // Derive expected element type from the type of the whole expression - let expected_element_type = - additional.clone().or_else(|| self.state.type_table.get(&input.id)).and_then(|mut ty| { - if let Type::Optional(inner) = ty { - ty = *inner.inner; - } - match ty { - Type::Array(array_ty) => Some(*array_ty.element_type), - _ => None, - } - }); + // Get the expected type of the element expression. + let expected_element_type = additional.clone().or_else(|| self.state.type_table.get(&input.expr.id())); // Use expected type (if available) for `expr` let (expr, mut stmts_expr) = self.reconstruct_expression(input.expr, &expected_element_type); @@ -283,6 +275,47 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { (input.into(), stmts_expr) } + fn reconstruct_slice( + &mut self, + mut input: SliceExpression, + additional: &Self::AdditionalInput, + ) -> (Expression, Self::AdditionalOutput) { + // Get the expected type of array expression. + let expected_array_type = additional.clone().or_else(|| self.state.type_table.get(&input.array.id())); + + // Use the expected type (if available) for `array` + let (array, mut stmts_array) = self.reconstruct_expression(input.array, &expected_array_type); + + // Reconstruct the `start` expression if it exists. + let (start, mut stmts_start) = match input.start { + Some(start_expr) => { + let (expr, stmts) = self.reconstruct_expression(start_expr, &None); + (Some(expr), stmts) + } + None => (None, vec![]), + }; + + // Reconstruct the `end` expression if it exists. + let (end, mut stmts_end) = match input.end { + Some((inclusive, end_expr)) => { + let (expr, stmts) = self.reconstruct_expression(end_expr, &None); + (Some((inclusive, expr)), stmts) + } + None => (None, vec![]), + }; + + // Update the slice expression with reconstructed parts. + input.array = array; + input.start = start; + input.end = end; + + // Merge all side effects. + stmts_array.append(&mut stmts_start); + stmts_array.append(&mut stmts_end); + + (input.into(), stmts_array) + } + fn reconstruct_tuple_access( &mut self, mut input: TupleAccess, diff --git a/compiler/passes/src/static_analysis/future_checker.rs b/compiler/passes/src/static_analysis/future_checker.rs index abd3cd00ed4..b1b32500653 100644 --- a/compiler/passes/src/static_analysis/future_checker.rs +++ b/compiler/passes/src/static_analysis/future_checker.rs @@ -104,6 +104,7 @@ impl AstVisitor for FutureChecker<'_> { Expression::Locator(locator) => self.visit_locator(locator, &Position::Misc), Expression::MemberAccess(access) => self.visit_member_access(access, &Position::Misc), Expression::Repeat(repeat) => self.visit_repeat(repeat, &Position::Misc), + Expression::Slice(slice) => self.visit_slice(slice, &Position::Misc), Expression::Ternary(ternary) => self.visit_ternary(ternary, &Position::Misc), Expression::Tuple(tuple) => self.visit_tuple(tuple, additional), Expression::TupleAccess(access) => self.visit_tuple_access(access, &Position::Misc), diff --git a/compiler/passes/src/static_single_assignment/expression.rs b/compiler/passes/src/static_single_assignment/expression.rs index 9e4ec039cb1..3d0b1e9fbd2 100644 --- a/compiler/passes/src/static_single_assignment/expression.rs +++ b/compiler/passes/src/static_single_assignment/expression.rs @@ -234,6 +234,13 @@ impl ExpressionConsumer for SsaFormingVisitor<'_> { (RepeatExpression { expr, ..input }.into(), statements) } + fn consume_slice(&mut self, input: leo_ast::SliceExpression) -> Self::Output { + let (array, statements) = self.consume_expression_and_define(input.array); + + // By now, the start and end expressions should be literals. So we just ignore them. There is no need to SSA them. + (leo_ast::SliceExpression { array, ..input }.into(), statements) + } + /// Consumes a ternary expression, accumulating any statements that are generated. fn consume_ternary(&mut self, input: TernaryExpression) -> Self::Output { // Reconstruct the condition of the ternary expression. diff --git a/compiler/passes/src/type_checking/ast.rs b/compiler/passes/src/type_checking/ast.rs index de8ba9b6863..9219bb8bf1c 100644 --- a/compiler/passes/src/type_checking/ast.rs +++ b/compiler/passes/src/type_checking/ast.rs @@ -442,6 +442,7 @@ impl AstVisitor for TypeCheckingVisitor<'_> { Expression::Locator(locator) => self.visit_locator(locator, additional), Expression::MemberAccess(access) => self.visit_member_access_general(access, false, additional), Expression::Repeat(repeat) => self.visit_repeat(repeat, additional), + Expression::Slice(slice) => self.visit_slice(slice, additional), Expression::Ternary(ternary) => self.visit_ternary(ternary, additional), Expression::Tuple(tuple) => self.visit_tuple(tuple, additional), Expression::TupleAccess(access) => self.visit_tuple_access_general(access, false, additional), @@ -563,6 +564,135 @@ impl AstVisitor for TypeCheckingVisitor<'_> { type_ } + fn visit_slice(&mut self, input: &SliceExpression, additional: &Self::AdditionalInput) -> Self::Output { + // Check that the expression is an array. + let array_type = match self.visit_expression(&input.array, &None) { + Type::Array(array_type) => array_type, + Type::Err => return Type::Err, + type_ => { + self.emit_err(TypeCheckerError::type_should_be2(type_, "an array", input.array.span())); + return Type::Err; + } + }; + + // A helper function to get check the start or end index. + let mut check_index = |expression: &Expression| -> Option { + let mut type_ = self.visit_expression(expression, &None); + + if type_ == Type::Numeric { + // If the index has type `Numeric`, then it's an unsuffixed literal. Just infer its type to be `u32` and + // then check it's validity as a `u32`. + type_ = Type::Integer(IntegerType::U32); + if let Expression::Literal(literal) = expression { + self.check_numeric_literal(literal, &type_); + } + } + + self.assert_int_type(&type_, expression.span()); + + // Keep track of the type of the start index in the type table. + // This is important for when the index is an unsuffixed literal. + self.state.type_table.insert(expression.id(), type_); + + // Get the value of the index if it's a literal. + expression.as_u32() + }; + + // Get an expression for the start index. + let (start, start_value_opt) = if let Some(start) = &input.start { + // If the start index is provided, check it. + (start.clone(), check_index(start)) + } else { + (Expression::Literal(Literal::unsuffixed("0".to_string(), Default::default(), Default::default())), Some(0)) + }; + + // Get an expression for the end index. + let (end, end_value_opt) = if let Some((inclusive, end)) = &input.end { + // If the end index is provided, check it. + let value_opt = check_index(end); + + // If the end index is inclusive, then we need to add 1 to it. + if *inclusive { + if let Some(value) = value_opt { + let expression = Expression::Literal(Literal::integer( + IntegerType::U32, + (value + 1).to_string(), + Default::default(), + Default::default(), + )); + (expression, Some(value + 1)) + } else { + let expression = Expression::Binary(Box::new(BinaryExpression { + left: end.clone(), + op: BinaryOperation::Add, + right: Expression::Literal(Literal::unsuffixed( + "1".to_string(), + Default::default(), + Default::default(), + )), + id: Default::default(), + span: Default::default(), + })); + (expression, None) + } + } else { + (end.clone(), value_opt) + } + } else { + (*array_type.length.clone(), array_type.length.as_u32()) + }; + + // Create an expression for the length of the slice: `end - start`. + let length = match (start_value_opt, end_value_opt, array_type.length.as_u32()) { + (Some(start_value), Some(end_value), Some(array_length)) => { + // Check that `start` is in bounds. + if start_value > array_length { + self.emit_err(TypeCheckerError::custom( + format!("The index `{start_value}` is out of bounds. The array length is `{array_length}`."), + input.start.as_ref().map(|e| e.span()).unwrap_or(input.span()), + )); + return Type::Err; + } + // Check that `end` is in bounds. + if end_value > array_length { + self.emit_err(TypeCheckerError::custom( + format!("The index `{end_value}` is out of bounds. The array length is `{array_length}`."), + input.end.as_ref().map(|(_, e)| e.span()).unwrap_or(input.span()), + )); + return Type::Err; + } + // Check that start is strictly less than end. + if start_value >= end_value { + println!("{input}"); + self.emit_err(TypeCheckerError::custom( + format!( + "The start index `{start_value}` must be strictly less than the end index `{end_value}`." + ), + input.start.as_ref().map(|e| e.span()).unwrap_or(input.span()), + )); + return Type::Err; + } + Expression::Literal(Literal::integer( + IntegerType::U32, + (end_value - start_value).to_string(), + Default::default(), + Default::default(), + )) + } + _ => Expression::Binary(Box::new(BinaryExpression { + left: end, + op: BinaryOperation::Sub, + right: start, + id: Default::default(), + span: Default::default(), + })), + }; + + let type_ = Type::Array(ArrayType::new(array_type.element_type().clone(), length)); + self.maybe_assert_type(&type_, additional, input.span()); + type_ + } + fn visit_associated_constant( &mut self, input: &AssociatedConstantExpression, @@ -788,7 +918,10 @@ impl AstVisitor for TypeCheckingVisitor<'_> { // Now sanity check everything let assert_add_type = |type_: &Type, span: Span| { - if !matches!(type_, Type::Err | Type::Field | Type::Group | Type::Scalar | Type::Integer(_)) { + if !matches!( + type_, + Type::Err | Type::Field | Type::Group | Type::Scalar | Type::Integer(_) | Type::Array(_) + ) { self.emit_err(TypeCheckerError::type_should_be2( type_, "a field, group, scalar, or integer", @@ -800,7 +933,25 @@ impl AstVisitor for TypeCheckingVisitor<'_> { assert_add_type(&t1, input.left.span()); assert_add_type(&t2, input.right.span()); - let result_t = assert_same_type(self, &t1, &t2); + let result_t = match (&t1, &t2) { + (Type::Array(array_type1), Type::Array(array_type2)) + if array_type1.element_type() == array_type2.element_type() => + { + // Define the length expression. + let length = Expression::Binary(Box::new(BinaryExpression { + left: *array_type1.length.clone(), + op: BinaryOperation::Add, + right: *array_type2.length.clone(), + id: self.state.node_builder.next_id(), + span: Default::default(), + })); + // Set the type of the length expression to `U32` in the type table. + self.state.type_table.insert(length.id(), Type::Integer(IntegerType::U32)); + + Type::Array(ArrayType::new(array_type1.element_type().clone(), length)) + } + _ => assert_same_type(self, &t1, &t2), + }; self.maybe_assert_type(&result_t, destination, input.span()); diff --git a/interpreter/src/cursor.rs b/interpreter/src/cursor.rs index 0d4d8189bc1..b850b9e4a14 100644 --- a/interpreter/src/cursor.rs +++ b/interpreter/src/cursor.rs @@ -394,6 +394,7 @@ impl Cursor { | Expression::Literal(..) | Expression::Locator(..) | Expression::Repeat(..) + | Expression::Slice(..) | Expression::Struct(..) | Expression::Ternary(..) | Expression::Tuple(..) @@ -971,6 +972,50 @@ impl Cursor { count_resolved.as_u32().expect_tc(repeat.span())? as usize, ))) } + Expression::Slice(slice) if step == 0 => { + if let Some(start) = &slice.start { + push!()(start, &None); + } + if let Some((_, end)) = &slice.end { + push!()(end, &None); + } + push!()(&slice.array, &None); + None + } + Expression::Slice(slice) if step == 1 => { + let span = slice.span(); + let (inclusive, end) = if let Some((inclusive, _)) = &slice.end { + (*inclusive, Some(self.pop_value()?)) + } else { + (false, None) + }; + let start = if slice.start.is_some() { self.pop_value()? } else { Value::from(0u32) }; + let array = self.pop_value()?; + + // Local helper function to convert a Value into usize + fn to_usize(value: &Value, span: Span) -> Result { + let value = value.resolve_if_unsuffixed(&Some(Type::Integer(leo_ast::IntegerType::U32)), span)?; + Ok(value.as_u32().expect_tc(span)? as usize) + } + + let start_usize = to_usize(&start, span)?; + let end_usize = match end { + Some(v) => { + let mut end_usize = to_usize(&v, span)?; + if inclusive { + if end_usize == usize::MAX { + halt!(span, "Slice end index overflow") + } else { + end_usize += 1; + } + } + Some(end_usize) + } + None => None, + }; + + Some(array.array_slice(start_usize, end_usize).expect_tc(span)?) + } Expression::AssociatedConstant(constant) if step == 0 => { let Type::Identifier(type_ident) = constant.ty else { tc_fail!(); diff --git a/tests/expectations/compiler/array/array_concat_fail.out b/tests/expectations/compiler/array/array_concat_fail.out new file mode 100644 index 00000000000..bd21503d736 --- /dev/null +++ b/tests/expectations/compiler/array/array_concat_fail.out @@ -0,0 +1,5 @@ +Error [ESAZ0374012]: The resulting array length exceeds the maximum allowed length: 32. + --> compiler-test:5:13 + | + 5 | let c = a + b; + | ^^^^^ diff --git a/tests/expectations/compiler/array/array_concat_fail2.out b/tests/expectations/compiler/array/array_concat_fail2.out new file mode 100644 index 00000000000..1fcf19cfe72 --- /dev/null +++ b/tests/expectations/compiler/array/array_concat_fail2.out @@ -0,0 +1,5 @@ +Error [ESAZ0374012]: The resulting array length exceeds the maximum allowed length: 32. + --> compiler-test:3:13 + | + 3 | let c = a + b; + | ^^^^^ diff --git a/tests/expectations/compiler/array/array_concats.out b/tests/expectations/compiler/array/array_concats.out new file mode 100644 index 00000000000..f9320b92fd6 --- /dev/null +++ b/tests/expectations/compiler/array/array_concats.out @@ -0,0 +1,9 @@ +program test.aleo; + +function flatten: + input r0 as [[boolean; 2u32]; 2u32].private; + cast r0[0u32][0u32] r0[0u32][1u32] r0[1u32][0u32] r0[1u32][1u32] into r1 as [boolean; 4u32]; + assert.eq r1 r1; + assert.eq r1 r1; + assert.eq r1 r1; + output r1 as [boolean; 4u32].private; diff --git a/tests/expectations/compiler/array/array_slices.out b/tests/expectations/compiler/array/array_slices.out new file mode 100644 index 00000000000..08912e43af6 --- /dev/null +++ b/tests/expectations/compiler/array/array_slices.out @@ -0,0 +1,22 @@ +program test.aleo; + +function simple_slices: + input r0 as [boolean; 1u32].private; + cast r0[0u32] into r1 as [boolean; 1u32]; + assert.eq r1 r1; + assert.eq r1 r1; + assert.eq r1 r1; + assert.eq r1 r1; + assert.eq r1 r1; + output r0[0u32] as boolean.private; + +function nested_slices: + input r0 as [[boolean; 1u32]; 1u32].private; + cast r0[0u32] into r1 as [[boolean; 1u32]; 1u32]; + cast r1[0u32] into r2 as [[boolean; 1u32]; 1u32]; + cast r2[0u32] into r3 as [[boolean; 1u32]; 1u32]; + cast r3[0u32] into r4 as [[boolean; 1u32]; 1u32]; + cast r4[0u32] into r5 as [[boolean; 1u32]; 1u32]; + cast r5[0u32] into r6 as [[boolean; 1u32]; 1u32]; + assert.eq r6 r6; + output r0[0u32][0u32] as boolean.private; diff --git a/tests/expectations/compiler/array/array_slices_fail.out b/tests/expectations/compiler/array/array_slices_fail.out new file mode 100644 index 00000000000..5ad93b26cab --- /dev/null +++ b/tests/expectations/compiler/array/array_slices_fail.out @@ -0,0 +1,5 @@ +Error [ETYC0372156]: The start index `0` must be strictly less than the end index `0`. + --> compiler-test:3:19 + | + 3 | let b = a[0..0]; + | ^ diff --git a/tests/expectations/compiler/array/array_slices_fail2.out b/tests/expectations/compiler/array/array_slices_fail2.out new file mode 100644 index 00000000000..5a01561a9d4 --- /dev/null +++ b/tests/expectations/compiler/array/array_slices_fail2.out @@ -0,0 +1,5 @@ +Error [ESAZ0374010]: Array index 192 out of bounds (array length is 32). + --> compiler-test:3:17 + | + 3 | let b = a[(2u32 - 1 - 1)..(8u32 * 8 * 3)]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/expectations/compiler/statements/all_loops_fail.out b/tests/expectations/compiler/statements/all_loops_fail.out index 721e1247a85..9def44c4cd4 100644 --- a/tests/expectations/compiler/statements/all_loops_fail.out +++ b/tests/expectations/compiler/statements/all_loops_fail.out @@ -1,5 +1,5 @@ -Error [EPAR0370005]: expected an identifier, a program id, an address literal, an integer literal, a static string, '!', '-', '(', '[', 'true', 'false', 'async', 'block', 'network', 'self' -- found '=' - --> compiler-test:15:30 +Error [EPAR0370005]: expected '&&', '||', '&', '|', '^', '==', '!=', '<', '<=', '>', '>=', '+', '-', '*', '/', '**', '%', '<<', '>>', '[', '.', '..', '?', 'as' -- found '..=' + --> compiler-test:15:28 | 15 | for a: u32 in 10u32..=0u32 { - | ^ + | ^^^ diff --git a/tests/expectations/parser-statement/statement/expression_fail.out b/tests/expectations/parser-statement/statement/expression_fail.out index e115aeb6f80..fbc6cac515d 100644 --- a/tests/expectations/parser-statement/statement/expression_fail.out +++ b/tests/expectations/parser-statement/statement/expression_fail.out @@ -19,7 +19,7 @@ Error [EPAR0370005]: expected an identifier, a program id, an address literal, a | 1 | (x,y|; | ^ -Error [EPAR0370005]: expected an identifier, a program id, an address literal, an integer literal, a static string, '!', '-', '(', '[', 'true', 'false', 'async', 'block', 'network', 'self' -- found '}' +Error [EPAR0370005]: expected an identifier, a program id, an address literal, an integer literal, a static string, '!', '-', '(', '[', '..', '..=', 'true', 'false', 'async', 'block', 'network', 'self' -- found '}' --> test_4:1:3 | 1 | x[}; diff --git a/tests/expectations/parser-statement/unreachable/eat_int.out b/tests/expectations/parser-statement/unreachable/eat_int.out index 2418346df2d..c135e5005df 100644 --- a/tests/expectations/parser-statement/unreachable/eat_int.out +++ b/tests/expectations/parser-statement/unreachable/eat_int.out @@ -133,7 +133,7 @@ Error [EPAR0370003]: unexpected EOF | 1 | x.0_> | ^ -Error [EPAR0370005]: expected '=', '&&', '||', '&&=', '||=', '&', '&=', '|', '|=', '^', '&=', '==', '!=', '<', '<=', '>', '>=', '+', '+=', '-', '-=', '*', '*=', '/', '/=', '**', '**=', '%', '%=', '<<', '<<=', '>>', '>>=', '[', '.', ';', '?', 'as' -- found '..' +Error [EPAR0370005]: expected '=', '&&=', '||=', '&=', '|=', '&=', '+=', '-=', '*=', '/=', '**=', '%=', '<<=', '>>=', ';' -- found '..' --> test_30:1:5 | 1 | x.0_.. diff --git a/tests/expectations/parser-statement/unreachable/math_op_fail.out b/tests/expectations/parser-statement/unreachable/math_op_fail.out index 3b682687524..350ae446b6a 100644 --- a/tests/expectations/parser-statement/unreachable/math_op_fail.out +++ b/tests/expectations/parser-statement/unreachable/math_op_fail.out @@ -13,7 +13,7 @@ Error [EPAR0370005]: expected ';' -- found ',' | 1 | let x = a , b; | ^ -Error [EPAR0370005]: expected ']' -- found ';' +Error [EPAR0370005]: expected ']', '..', '..=' -- found ';' --> test_3:1:14 | 1 | let x = a [ b; @@ -68,7 +68,7 @@ Error [EPAR0370005]: expected '&&', '||', '&', '|', '^', '==', '!=', '<', '<=', | 1 | let x = a ! b; | ^ -Error [EPAR0370005]: expected '&&', '||', '&', '|', '^', '==', '!=', '<', '<=', '>', '>=', '+', '-', '*', '/', '**', '%', '<<', '>>', '[', '.', ';', '?', 'as' -- found '..' +Error [EPAR0370005]: expected ';' -- found '..' --> test_14:1:11 | 1 | let x = a .. b; @@ -193,7 +193,7 @@ Error [EPAR0370005]: expected '=', '&&=', '||=', '&=', '|=', '&=', '+=', '-=', ' | 1 | x,=b; // 43 | ^ -Error [EPAR0370005]: expected an identifier, a program id, an address literal, an integer literal, a static string, '!', '-', '(', '[', 'true', 'false', 'async', 'block', 'network', 'self' -- found '=' +Error [EPAR0370005]: expected an identifier, a program id, an address literal, an integer literal, a static string, '!', '-', '(', '[', '..', '..=', 'true', 'false', 'async', 'block', 'network', 'self' -- found '=' --> test_40:1:3 | 1 | x[=b; @@ -253,8 +253,8 @@ Error [EPAR0370005]: expected an identifier, a program id, an address literal, a | 1 | x<==b; | ^ -Error [EPAR0370005]: expected '=', '&&', '||', '&&=', '||=', '&', '&=', '|', '|=', '^', '&=', '==', '!=', '<', '<=', '>', '>=', '+', '+=', '-', '-=', '*', '*=', '/', '/=', '**', '**=', '%', '%=', '<<', '<<=', '>>', '>>=', '[', '.', ';', '?', 'as' -- found '..' +Error [EPAR0370005]: expected '=', '&&=', '||=', '&=', '|=', '&=', '+=', '-=', '*=', '/=', '**=', '%=', '<<=', '>>=', ';' -- found '..=' --> test_52:1:2 | 1 | x..=b; - | ^^ + | ^^^ diff --git a/tests/expectations/parser-statement/unreachable/postfix_fail.out b/tests/expectations/parser-statement/unreachable/postfix_fail.out index a0cc6e2fba2..6f767f34cbd 100644 --- a/tests/expectations/parser-statement/unreachable/postfix_fail.out +++ b/tests/expectations/parser-statement/unreachable/postfix_fail.out @@ -13,7 +13,7 @@ Error [EPAR0370005]: expected ';' -- found ',' | 1 | let x = a,; | ^ -Error [EPAR0370005]: expected an identifier, a program id, an address literal, an integer literal, a static string, '!', '-', '(', '[', 'true', 'false', 'async', 'block', 'network', 'self' -- found ';' +Error [EPAR0370005]: expected an identifier, a program id, an address literal, an integer literal, a static string, '!', '-', '(', '[', '..', '..=', 'true', 'false', 'async', 'block', 'network', 'self' -- found ';' --> test_3:1:11 | 1 | let x = a[; @@ -93,7 +93,7 @@ Error [EPAR0370005]: expected an identifier, a program id, an address literal, a | 1 | let x = a>; | ^ -Error [EPAR0370005]: expected '&&', '||', '&', '|', '^', '==', '!=', '<', '<=', '>', '>=', '+', '-', '*', '/', '**', '%', '<<', '>>', '[', '.', ';', '?', 'as' -- found '..' +Error [EPAR0370005]: expected ';' -- found '..' --> test_19:1:10 | 1 | let x = a..; diff --git a/tests/tests/compiler/array/array_concat_fail.leo b/tests/tests/compiler/array/array_concat_fail.leo new file mode 100644 index 00000000000..b574eaa706c --- /dev/null +++ b/tests/tests/compiler/array/array_concat_fail.leo @@ -0,0 +1,8 @@ +program test.aleo { + transition concat_past_array_max() -> [u8; 32] { + let a = [0u8; 32]; + let b = [1u8; 32]; + let c = a + b; + return c[16..48]; + } +} diff --git a/tests/tests/compiler/array/array_concat_fail2.leo b/tests/tests/compiler/array/array_concat_fail2.leo new file mode 100644 index 00000000000..76879c44921 --- /dev/null +++ b/tests/tests/compiler/array/array_concat_fail2.leo @@ -0,0 +1,6 @@ +program test.aleo { + transition concat_past_array_max(a: [u8; 16], b: [u8; 17]) -> [u8; 32] { + let c = a + b; + return c[0..32]; + } +} diff --git a/tests/tests/compiler/array/array_concats.leo b/tests/tests/compiler/array/array_concats.leo new file mode 100644 index 00000000000..bae7b534a58 --- /dev/null +++ b/tests/tests/compiler/array/array_concats.leo @@ -0,0 +1,29 @@ +program test.aleo { + transition flatten(a: [[bool; 2]; 2]) -> [bool; 4] { + let b = a[0] + a[1]; + let c = [a[0][0], a[0][1], a[1][0], a[1][1]]; + let d = flatten2::[2](a); + let e = generic_flatten::[2, 2](a); + + assert_eq(b, c); + assert_eq(b, d); + assert_eq(b, e); + + return b; + + } + + inline flatten2::[M: u32](bits: [[bool; M]; 2]) -> [bool; 2 * M] { + return bits[0] + bits[1]; + } + + inline generic_flatten::[M: u32, N: u32](bits: [[bool; M]; N]) -> [bool; M * N] { + let flattened = [false; M * N]; + for i in 0u32..N { + for j in 0u32..M { + flattened[i * M + j] = bits[i][j]; + } + } + return flattened; + } +} diff --git a/tests/tests/compiler/array/array_slices.leo b/tests/tests/compiler/array/array_slices.leo new file mode 100644 index 00000000000..19a23acac79 --- /dev/null +++ b/tests/tests/compiler/array/array_slices.leo @@ -0,0 +1,27 @@ +program test.aleo { + transition simple_slices(a: [bool; 1]) -> bool { + let b = a[..]; + let c = a[0..1]; + let d = a[..1]; + let e = a[0..]; + let f = a[0..=0]; + let g = a[..=0]; + + assert_eq(b, c); + assert_eq(b, d); + assert_eq(b, e); + assert_eq(b, f); + assert_eq(b, g); + + return a[0]; + } + + transition nested_slices(a: [[bool; 1]; 1]) -> bool { + let b = a[..][0..][..][0..][..][0..]; + let c = a[0..1][0..1][0..1][0..1][0..1][0..1]; + + assert_eq(b, c); + + return a[0][0]; + } +} diff --git a/tests/tests/compiler/array/array_slices_fail.leo b/tests/tests/compiler/array/array_slices_fail.leo new file mode 100644 index 00000000000..c1ea12e4111 --- /dev/null +++ b/tests/tests/compiler/array/array_slices_fail.leo @@ -0,0 +1,6 @@ +program test.aleo { + transition empty_slice(a: [bool; 1]) -> bool { + let b = a[0..0]; + return true; + } +} diff --git a/tests/tests/compiler/array/array_slices_fail2.leo b/tests/tests/compiler/array/array_slices_fail2.leo new file mode 100644 index 00000000000..9ad09175941 --- /dev/null +++ b/tests/tests/compiler/array/array_slices_fail2.leo @@ -0,0 +1,6 @@ +program test.aleo { + transition slice_out_of_bounds(a: [bool; 32]) -> bool { + let b = a[(2u32 - 1 - 1)..(8u32 * 8 * 3)]; + return true; + } +}