diff --git a/compiler/ast/src/expressions/mod.rs b/compiler/ast/src/expressions/mod.rs index 3c15365ad3d..5f46d997750 100644 --- a/compiler/ast/src/expressions/mod.rs +++ b/compiler/ast/src/expressions/mod.rs @@ -35,6 +35,9 @@ pub use async_::*; mod array; pub use array::*; +mod slice; +pub use slice::*; + mod binary; pub use binary::*; @@ -90,6 +93,8 @@ pub enum Expression { Async(AsyncExpression), /// An array expression, e.g., `[true, false, true, false]`. Array(ArrayExpression), + /// A slice expression, e.g., `arr[2..=4]``. + Slice(Box), /// A binary expression, e.g., `42 + 24`. Binary(Box), /// A call expression, e.g., `my_fun(args)`. @@ -135,6 +140,7 @@ impl Node for Expression { match self { ArrayAccess(n) => n.span(), Array(n) => n.span(), + Slice(n) => n.span(), AssociatedConstant(n) => n.span(), AssociatedFunction(n) => n.span(), Async(n) => n.span(), @@ -161,6 +167,7 @@ impl Node for Expression { match self { ArrayAccess(n) => n.set_span(span), Array(n) => n.set_span(span), + Slice(n) => n.set_span(span), AssociatedConstant(n) => n.set_span(span), AssociatedFunction(n) => n.set_span(span), Async(n) => n.set_span(span), @@ -187,6 +194,7 @@ impl Node for Expression { match self { Array(n) => n.id(), ArrayAccess(n) => n.id(), + Slice(n) => n.id(), AssociatedConstant(n) => n.id(), AssociatedFunction(n) => n.id(), Async(n) => n.id(), @@ -213,6 +221,7 @@ impl Node for Expression { match self { Array(n) => n.set_id(id), ArrayAccess(n) => n.set_id(id), + Slice(n) => n.set_id(id), AssociatedConstant(n) => n.set_id(id), AssociatedFunction(n) => n.set_id(id), Async(n) => n.set_id(id), @@ -241,6 +250,7 @@ impl fmt::Display for Expression { match &self { Array(n) => n.fmt(f), ArrayAccess(n) => n.fmt(f), + Slice(n) => n.fmt(f), AssociatedConstant(n) => n.fmt(f), AssociatedFunction(n) => n.fmt(f), Async(n) => n.fmt(f), @@ -279,6 +289,7 @@ impl Expression { Ternary(_) => 0, Array(_) | ArrayAccess(_) + | Slice(_) | AssociatedConstant(_) | AssociatedFunction(_) | Async(_) diff --git a/compiler/ast/src/expressions/slice.rs b/compiler/ast/src/expressions/slice.rs new file mode 100644 index 00000000000..ed0901b7515 --- /dev/null +++ b/compiler/ast/src/expressions/slice.rs @@ -0,0 +1,61 @@ +// 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 std::fmt::{self, Debug}; + +use leo_span::Span; +use serde::{Deserialize, Serialize}; + +use crate::{Expression, Node, NodeID}; + +/// The slice expression, e.g., `arr[2..=4]``. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Slice { + /// The expression evaluating to some array type, e.g., `[false, true]`. + pub source_array: Expression, + /// The lower_bound to use, if any, when slicing the array. + pub start: Option, + /// The upper_bound to use, if any, when slicing the array. + pub stop: Option, + /// Whether `stop` is inclusive or not. + /// Signified with `=` when parsing. + pub clusivity: bool, + /// The span for the entire expression `foo[start..=stop]`. + pub span: Span, + /// The ID of the node. + pub id: NodeID, +} + +impl fmt::Display for Slice { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}[{}..{}{}]", + self.source_array, + self.start.clone().map(|start| start.to_string()).unwrap_or("".into()), + if self.clusivity { "=" } else { "" }, + self.stop.clone().map(|stop| stop.to_string()).unwrap_or("".into()) + ) + } +} + +impl From for Expression { + fn from(value: Slice) -> Self { + Expression::Slice(Box::new(value)) + } +} + +crate::simple_node_impl!(Slice); diff --git a/compiler/ast/src/interpreter_value/evaluate.rs b/compiler/ast/src/interpreter_value/evaluate.rs index ca106b26e42..bb6c0503008 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 6d5103ecfd7..a7d25f6e88e 100644 --- a/compiler/ast/src/interpreter_value/value.rs +++ b/compiler/ast/src/interpreter_value/value.rs @@ -601,6 +601,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()?; @@ -614,6 +633,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..27d53759b02 100644 --- a/compiler/ast/src/passes/consumer.rs +++ b/compiler/ast/src/passes/consumer.rs @@ -27,6 +27,7 @@ pub trait ExpressionConsumer { match input { Expression::Array(array) => self.consume_array(array), Expression::ArrayAccess(access) => self.consume_array_access(*access), + Expression::Slice(slice) => self.consume_slice(*slice), Expression::AssociatedConstant(constant) => self.consume_associated_constant(constant), Expression::AssociatedFunction(function) => self.consume_associated_function(function), Expression::Async(async_) => self.consume_async(async_), @@ -62,6 +63,8 @@ pub trait ExpressionConsumer { fn consume_array(&mut self, _input: ArrayExpression) -> Self::Output; + fn consume_slice(&mut self, _input: Slice) -> Self::Output; + fn consume_binary(&mut self, _input: BinaryExpression) -> Self::Output; fn consume_call(&mut self, _input: CallExpression) -> Self::Output; diff --git a/compiler/ast/src/passes/reconstructor.rs b/compiler/ast/src/passes/reconstructor.rs index fe7bce82035..19dcd5ef71a 100644 --- a/compiler/ast/src/passes/reconstructor.rs +++ b/compiler/ast/src/passes/reconstructor.rs @@ -119,29 +119,34 @@ pub trait AstReconstructor { fn reconstruct_expression( &mut self, input: Expression, - additional: &Self::AdditionalInput, + _additional: &Self::AdditionalInput, ) -> (Expression, Self::AdditionalOutput) { match input { - Expression::AssociatedConstant(constant) => self.reconstruct_associated_constant(constant, additional), - Expression::AssociatedFunction(function) => self.reconstruct_associated_function(function, additional), - Expression::Async(async_) => self.reconstruct_async(async_, additional), - Expression::Array(array) => self.reconstruct_array(array, additional), - Expression::ArrayAccess(access) => self.reconstruct_array_access(*access, additional), - Expression::Binary(binary) => self.reconstruct_binary(*binary, additional), - Expression::Call(call) => self.reconstruct_call(*call, additional), - Expression::Cast(cast) => self.reconstruct_cast(*cast, additional), - Expression::Struct(struct_) => self.reconstruct_struct_init(struct_, additional), - Expression::Err(err) => self.reconstruct_err(err, additional), - Expression::Path(path) => self.reconstruct_path(path, additional), - Expression::Literal(value) => self.reconstruct_literal(value, additional), - 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::Ternary(ternary) => self.reconstruct_ternary(*ternary, additional), - Expression::Tuple(tuple) => self.reconstruct_tuple(tuple, additional), - Expression::TupleAccess(access) => self.reconstruct_tuple_access(*access, additional), - Expression::Unary(unary) => self.reconstruct_unary(*unary, additional), - Expression::Unit(unit) => self.reconstruct_unit(unit, additional), + Expression::AssociatedConstant(constant) => { + self.reconstruct_associated_constant(constant, &Default::default()) + } + Expression::AssociatedFunction(function) => { + self.reconstruct_associated_function(function, &Default::default()) + } + Expression::Async(async_) => self.reconstruct_async(async_, &Default::default()), + Expression::Slice(slice) => self.reconstruct_slice(*slice, &Default::default()), + Expression::Array(array) => self.reconstruct_array(array, &Default::default()), + Expression::ArrayAccess(access) => self.reconstruct_array_access(*access, &Default::default()), + Expression::Binary(binary) => self.reconstruct_binary(*binary, &Default::default()), + Expression::Call(call) => self.reconstruct_call(*call, &Default::default()), + Expression::Cast(cast) => self.reconstruct_cast(*cast, &Default::default()), + Expression::Struct(struct_) => self.reconstruct_struct_init(struct_, &Default::default()), + Expression::Err(err) => self.reconstruct_err(err, &Default::default()), + Expression::Path(path) => self.reconstruct_path(path, &Default::default()), + Expression::Literal(value) => self.reconstruct_literal(value, &Default::default()), + Expression::Locator(locator) => self.reconstruct_locator(locator, &Default::default()), + Expression::MemberAccess(access) => self.reconstruct_member_access(*access, &Default::default()), + Expression::Repeat(repeat) => self.reconstruct_repeat(*repeat, &Default::default()), + Expression::Ternary(ternary) => self.reconstruct_ternary(*ternary, &Default::default()), + Expression::Tuple(tuple) => self.reconstruct_tuple(tuple, &Default::default()), + Expression::TupleAccess(access) => self.reconstruct_tuple_access(*access, &Default::default()), + Expression::Unary(unary) => self.reconstruct_unary(*unary, &Default::default()), + Expression::Unit(unit) => self.reconstruct_unit(unit, &Default::default()), } } @@ -253,6 +258,23 @@ pub trait AstReconstructor { ) } + fn reconstruct_slice( + &mut self, + input: Slice, + _additional: &Self::AdditionalInput, + ) -> (Expression, Self::AdditionalOutput) { + ( + Slice { + source_array: self.reconstruct_expression(input.source_array, &Default::default()).0, + start: input.start.map(|expr| self.reconstruct_expression(expr, &Default::default()).0), + stop: input.stop.map(|expr| self.reconstruct_expression(expr, &Default::default()).0), + ..input + } + .into(), + Default::default(), + ) + } + fn reconstruct_binary( &mut self, input: BinaryExpression, diff --git a/compiler/ast/src/passes/visitor.rs b/compiler/ast/src/passes/visitor.rs index e5720df6f93..106567f2bba 100644 --- a/compiler/ast/src/passes/visitor.rs +++ b/compiler/ast/src/passes/visitor.rs @@ -109,6 +109,7 @@ pub trait AstVisitor { match input { Expression::Array(array) => self.visit_array(array, additional), Expression::ArrayAccess(access) => self.visit_array_access(access, additional), + Expression::Slice(slice) => self.visit_slice(slice, additional), Expression::AssociatedConstant(constant) => self.visit_associated_constant(constant, additional), Expression::AssociatedFunction(function) => self.visit_associated_function(function, additional), Expression::Async(async_) => self.visit_async(async_, additional), @@ -153,6 +154,13 @@ pub trait AstVisitor { Default::default() } + fn visit_slice(&mut self, input: &Slice, additional: &Self::AdditionalInput) -> Self::Output { + self.visit_expression(&input.source_array, additional); + input.start.as_ref().map(|start| self.visit_expression(start, additional)); + input.stop.as_ref().map(|stop| self.visit_expression(stop, additional)); + Default::default() + } + fn visit_associated_constant( &mut self, _input: &AssociatedConstantExpression, diff --git a/compiler/parser-lossless/src/grammar.lalrpop b/compiler/parser-lossless/src/grammar.lalrpop index 63ebd2a8734..127fea01b4d 100644 --- a/compiler/parser-lossless/src/grammar.lalrpop +++ b/compiler/parser-lossless/src/grammar.lalrpop @@ -364,6 +364,28 @@ Expr1: SyntaxNode<'a> = { > > > => { SyntaxNode::new(ExpressionKind::TupleAccess, [x, d, i]) }, + // Array slice. + > > > ?> > => { + let inclusive_range_without_an_end = eq.is_some() && stop.is_none(); + let node = SyntaxNode::new( + ExpressionKind::Slice, + [x, l] + .into_iter() + .chain(start) + .chain(std::iter::once(d)) + .chain(eq) + .chain(stop) + .chain(std::iter::once(r)) + ); + + // Emit error for inclusive slices without an end + if inclusive_range_without_an_end { + handler.emit_err(ParserError::inclusive_range_with_no_end(node.span)); + } + + node + }, + // todo: Maybe a seperate Range expression can be introduced here along with a new DotDotEq token, which can also be used with the loops. // Array access. > > > => { SyntaxNode::new(ExpressionKind::ArrayAccess, [x, l, index, r]) diff --git a/compiler/parser-lossless/src/lib.rs b/compiler/parser-lossless/src/lib.rs index 563c7c4486f..468b8bf8922 100644 --- a/compiler/parser-lossless/src/lib.rs +++ b/compiler/parser-lossless/src/lib.rs @@ -152,6 +152,7 @@ pub enum ExpressionKind { AssociatedFunctionCall, Async, Array, + Slice, Binary, Call, Cast, diff --git a/compiler/parser/src/conversions.rs b/compiler/parser/src/conversions.rs index b7db7121e14..9d12f74a6fa 100644 --- a/compiler/parser/src/conversions.rs +++ b/compiler/parser/src/conversions.rs @@ -474,6 +474,32 @@ pub fn to_expression(node: &SyntaxNode<'_>, builder: &NodeBuilder, handler: &Han .collect::>>()?; leo_ast::ArrayExpression { elements, span, id }.into() } + ExpressionKind::Slice => { + let mut slice_iter = node.children.iter(); + + let mut next_token = || slice_iter.next().expect("Can't happen"); + + let array = to_expression(next_token(), builder, handler)?; + let _left = next_token(); + + let token = next_token(); + let (start, _d) = if token.text != ".." { + (Some(to_expression(token, builder, handler)?), next_token()) + } else { + (None, token) + }; + + let token = next_token(); + let (clusivity, stop) = if token.text == "=" { + (true, Some(to_expression(next_token(), builder, handler)?)) + } else if token.text != "]" { + (false, Some(to_expression(token, builder, handler)?)) + } else { + (false, None) + }; + + leo_ast::Slice { source_array: array, start, stop, clusivity, span, id }.into() + } ExpressionKind::Binary => { let [lhs, op, rhs] = &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 a72e8aae063..fbd7367854a 100644 --- a/compiler/passes/src/code_generation/expression.rs +++ b/compiler/passes/src/code_generation/expression.rs @@ -63,6 +63,7 @@ impl CodeGeneratingVisitor<'_> { match input { Expression::Array(expr) => self.visit_array(expr), Expression::ArrayAccess(expr) => self.visit_array_access(expr), + Expression::Slice(_) => panic!("Slices should not appear in the AST at this point of compilation"), Expression::AssociatedConstant(expr) => self.visit_associated_constant(expr), Expression::AssociatedFunction(expr) => self.visit_associated_function(expr), Expression::Async(expr) => self.visit_async(expr), @@ -147,6 +148,19 @@ impl CodeGeneratingVisitor<'_> { let (left_operand, left_instructions) = self.visit_expression(&input.left); let (right_operand, right_instructions) = self.visit_expression(&input.right); + // Check that the types are not arrays. + // This is a sanity check. + // Below types may note be in the type table + // as common subexpression elimination can change the AST quite aggresively + // and also we are/should not be reconstructing the type table after it unless really need. + if let Some(left_type) = self.state.type_table.get(&input.left.id()) { + assert!(!matches!(left_type, Type::Array(_))); + } + + if let Some(right_type) = self.state.type_table.get(&input.right.id()) { + assert!(!matches!(right_type, Type::Array(_))); + } + let opcode = match input.op { BinaryOperation::Add => String::from("add"), BinaryOperation::AddWrapped => String::from("add.w"), diff --git a/compiler/passes/src/common/replacer/mod.rs b/compiler/passes/src/common/replacer/mod.rs index dc9dfe451dc..73bc8e3af4e 100644 --- a/compiler/passes/src/common/replacer/mod.rs +++ b/compiler/passes/src/common/replacer/mod.rs @@ -72,6 +72,7 @@ where Expression::Async(async_) => self.reconstruct_async(async_, &()), Expression::Array(array) => self.reconstruct_array(array, &()), Expression::ArrayAccess(access) => self.reconstruct_array_access(*access, &()), + Expression::Slice(slice) => self.reconstruct_slice(*slice, &()), Expression::Binary(binary) => self.reconstruct_binary(*binary, &()), Expression::Call(call) => self.reconstruct_call(*call, &()), Expression::Cast(cast) => self.reconstruct_cast(*cast, &()), 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/visitor.rs b/compiler/passes/src/common_subexpression_elimination/visitor.rs index e828450272b..4267b0725e0 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: Option, stop: Option }, Ternary { condition: Atom, if_true: Atom, if_false: Atom }, Unary { op: UnaryOperation, receiver: Atom }, } @@ -110,6 +111,7 @@ impl CommonSubexpressionEliminatingVisitor<'_> { | Expression::AssociatedFunction(_) | Expression::Async(_) | Expression::Array(_) + | Expression::Slice(_) | Expression::Binary(_) | Expression::Call(_) | Expression::Cast(_) @@ -151,6 +153,17 @@ impl CommonSubexpressionEliminatingVisitor<'_> { .collect::>>()?; Expr::Array(atoms) } + Expression::Slice(slice_expression) => { + let array = self.try_atom(&mut slice_expression.source_array)?; + + let start = + if let Some(start) = slice_expression.start.as_mut() { Some(self.try_atom(start)?) } else { None }; + + let stop = + if let Some(stop) = slice_expression.stop.as_mut() { Some(self.try_atom(stop)?) } else { None }; + + Expr::Slice { array, start, stop } + } Expression::Binary(binary_expression) => { let left = self.try_atom(&mut binary_expression.left)?; let right = self.try_atom(&mut binary_expression.right)?; diff --git a/compiler/passes/src/const_propagation/ast.rs b/compiler/passes/src/const_propagation/ast.rs index 9492e0af28e..9d9bcc160e6 100644 --- a/compiler/passes/src/const_propagation/ast.rs +++ b/compiler/passes/src/const_propagation/ast.rs @@ -49,10 +49,13 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { /* Expressions */ fn reconstruct_expression(&mut self, input: Expression, _additional: &()) -> (Expression, Self::AdditionalOutput) { - let opt_old_type = self.state.type_table.get(&input.id()); + // Get the input ID. + let input_id = input.id(); + let (new_expr, opt_value) = match input { Expression::Array(array) => self.reconstruct_array(array, &()), Expression::ArrayAccess(access) => self.reconstruct_array_access(*access, &()), + Expression::Slice(slice) => self.reconstruct_slice(*slice, &()), Expression::AssociatedConstant(constant) => self.reconstruct_associated_constant(constant, &()), Expression::AssociatedFunction(function) => self.reconstruct_associated_function(function, &()), Expression::Async(async_) => self.reconstruct_async(async_, &()), @@ -73,8 +76,9 @@ 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. - if let Some(old_type) = opt_old_type { + // 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) = self.state.type_table.get(&input_id) { self.state.type_table.insert(new_expr.id(), old_type); } @@ -153,7 +157,13 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { let Some(Type::Array(array_ty)) = ty else { panic!("Type checking guaranteed that this is an array."); }; - let len = array_ty.length.as_u32(); + + let len = if let Expression::Slice(slice) = *array_ty.length { + let (reconstructed_array, _) = self.reconstruct_slice(*slice, &()); + if let Expression::Array(arr) = reconstructed_array { Some(arr.elements.len() as u32) } else { None } + } else { + array_ty.length.as_u32() + }; if let Some(len) = len { let index_in_bounds = matches!(index_value.as_u32(), Some(index) if index < len); @@ -311,6 +321,96 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { } } + fn reconstruct_slice(&mut self, input: Slice, _additional: &()) -> (Expression, Self::AdditionalOutput) { + let span = input.span(); + let id = input.id(); + let array_id = input.source_array.id(); + let (array, _array_opt) = self.reconstruct_expression(input.source_array.clone(), &()); + + let ty = self.state.type_table.get(&array_id); + let Some(Type::Array(array_ty)) = ty else { + panic!("Type checking guaranteed that this is an array."); + }; + let (_, count_value) = self.reconstruct_expression(*array_ty.length, &()); + + // Since this array comes strictly before the slice definition, array length should + // have been evaluated at this point, otherwise we must have already raised an error. + let Some(len) = count_value.and_then(|value| value.as_u32()) else { return (input.into(), None) }; + + let start = match input.start.clone().map(|expr| self.reconstruct_expression(expr, &()).1) { + None => 0u32, + Some(start_opt) => { + let Some(start) = start_opt.and_then(|val| val.as_u32()) else { + self.slice_bounds_not_evaluated = Some(span); + return (input.into(), None); + }; + + start + } + }; + + let stop = match input.stop.clone().map(|expr| self.reconstruct_expression(expr, &()).1) { + None => len, + Some(stop_opt) => { + let clusivity = if input.clusivity { 1 } else { 0 }; + let Some(stop) = stop_opt.and_then(|val| val.as_u32()) else { + self.slice_bounds_not_evaluated = Some(span); + return (input.into(), None); + }; + + stop + clusivity + } + }; + + let bounds_are_incorrect = stop > len || start > stop; + if bounds_are_incorrect { + if !self.state.handler.had_errors() { + self.emit_err(StaticAnalyzerError::slice_bounds(start, stop, len, span)); + } + } else { + // Everything is correct we can now reconstruct the array + let reconstructed_array = ArrayExpression { + elements: (start..stop) + .map(|index| { + ArrayAccess { + array: array.clone(), + index: Literal::integer( + IntegerType::U32, + index.to_string(), + span, + self.state.node_builder.next_id(), + ) + .into(), + span, + id: self.state.node_builder.next_id(), + } + .into() + }) + .collect(), + span, + id, + }; + + self.state.type_table.insert( + id, + ArrayType::new( + array_ty.element_type.as_ref().clone(), + Literal::integer( + IntegerType::U32, + (stop - start).to_string(), + leo_span::Span::default(), + self.state.node_builder.next_id(), + ) + .into(), + ) + .into(), + ); + return self.reconstruct_array(reconstructed_array, &()); + } + + (input.into(), None) + } + fn reconstruct_binary( &mut self, input: leo_ast::BinaryExpression, @@ -320,7 +420,11 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { let input_id = input.id(); let (left, lhs_opt_value) = self.reconstruct_expression(input.left, &()); - let (right, rhs_opt_value) = self.reconstruct_expression(input.right, &()); + let (right, rhs_opt_value) = self.reconstruct_expression(input.right.clone(), &()); + + // 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. @@ -338,6 +442,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..0d2a1558224 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; @@ -38,6 +38,8 @@ pub struct ConstPropagationOutput { pub array_length_not_evaluated: Option, /// A repeat expression count which was not able to be evaluated. pub repeat_count_not_evaluated: Option, + /// Slice expression bounds which were not able to be evaluated. + pub slice_bounds_not_evaluated: Option, } /// A pass to perform const propagation and folding. @@ -66,6 +68,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![], @@ -74,6 +77,7 @@ impl Pass for ConstPropagation { array_index_not_evaluated: None, array_length_not_evaluated: None, repeat_count_not_evaluated: None, + slice_bounds_not_evaluated: None, }; ast.ast = visitor.reconstruct_program(ast.ast); visitor.state.handler.last_err()?; @@ -84,6 +88,7 @@ impl Pass for ConstPropagation { array_index_not_evaluated: visitor.array_index_not_evaluated, array_length_not_evaluated: visitor.array_length_not_evaluated, repeat_count_not_evaluated: visitor.repeat_count_not_evaluated, + slice_bounds_not_evaluated: visitor.slice_bounds_not_evaluated, }) } } @@ -91,6 +96,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![], @@ -99,6 +105,7 @@ impl<'a> ConstPropagationVisitor<'a> { array_index_not_evaluated: None, array_length_not_evaluated: None, repeat_count_not_evaluated: None, + slice_bounds_not_evaluated: None, } } } diff --git a/compiler/passes/src/const_propagation/visitor.rs b/compiler/passes/src/const_propagation/visitor.rs index bdde0c4d4b7..5268e5cf427 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,10 @@ 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, + /// Slice expression bounds which were not able to be evaluated. + pub slice_bounds_not_evaluated: Option, + /// Limits on data type sizes. + pub limits: TypeCheckingInput, } impl ConstPropagationVisitor<'_> { @@ -73,6 +77,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() + && 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..bf464e43057 100644 --- a/compiler/passes/src/dead_code_elimination/visitor.rs +++ b/compiler/passes/src/dead_code_elimination/visitor.rs @@ -49,6 +49,7 @@ impl DeadCodeEliminatingVisitor<'_> { Repeat(repeat) => sef(&repeat.expr) && sef(&repeat.count), TupleAccess(tuple) => sef(&tuple.tuple), Array(array) => array.elements.iter().all(sef), + Slice(slice) => sef(&slice.source_array) && slice.start.iter().all(sef) && slice.stop.iter().all(sef), AssociatedConstant(_) => true, AssociatedFunction(func) => { // CheatCode, Mapping, and Future operations obviously have side effects. diff --git a/compiler/passes/src/monomorphization/ast.rs b/compiler/passes/src/monomorphization/ast.rs index 2e696c4a419..1832ee66593 100644 --- a/compiler/passes/src/monomorphization/ast.rs +++ b/compiler/passes/src/monomorphization/ast.rs @@ -91,6 +91,7 @@ impl AstReconstructor for MonomorphizationVisitor<'_> { let (new_expr, opt_value) = match input { Expression::Array(array) => self.reconstruct_array(array, &()), Expression::ArrayAccess(access) => self.reconstruct_array_access(*access, &()), + Expression::Slice(slice) => self.reconstruct_slice(*slice, &()), Expression::AssociatedConstant(constant) => self.reconstruct_associated_constant(constant, &()), Expression::AssociatedFunction(function) => self.reconstruct_associated_function(function, &()), Expression::Async(async_) => self.reconstruct_async(async_, &()), diff --git a/compiler/passes/src/option_lowering/ast.rs b/compiler/passes/src/option_lowering/ast.rs index 4e9a97becb7..5ee01481ee3 100644 --- a/compiler/passes/src/option_lowering/ast.rs +++ b/compiler/passes/src/option_lowering/ast.rs @@ -91,6 +91,7 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { Expression::Async(e) => self.reconstruct_async(e, additional), Expression::Array(e) => self.reconstruct_array(e, additional), Expression::ArrayAccess(e) => self.reconstruct_array_access(*e, additional), + Expression::Slice(e) => self.reconstruct_slice(*e, additional), Expression::Binary(e) => self.reconstruct_binary(*e, additional), Expression::Call(e) => self.reconstruct_call(*e, additional), Expression::Cast(e) => self.reconstruct_cast(*e, additional), @@ -331,6 +332,47 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { (input.into(), all_stmts) } + fn reconstruct_slice( + &mut self, + mut input: Slice, + 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.source_array.id())); + + // Use the expected type (if available) for `array` + let (array, mut stmts_array) = self.reconstruct_expression(input.source_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.stop { + Some(end_expr) => { + let (expr, stmts) = self.reconstruct_expression(end_expr, &None); + (Some(expr), stmts) + } + None => (None, vec![]), + }; + + // Update the slice expression with reconstructed parts. + input.source_array = array; + input.start = start; + input.stop = end; + + // Merge all side effects. + stmts_array.append(&mut stmts_start); + stmts_array.append(&mut stmts_end); + + (input.into(), stmts_array) + } + fn reconstruct_binary( &mut self, mut input: BinaryExpression, diff --git a/compiler/passes/src/static_analysis/future_checker.rs b/compiler/passes/src/static_analysis/future_checker.rs index bedd53d6b68..6e6721bea77 100644 --- a/compiler/passes/src/static_analysis/future_checker.rs +++ b/compiler/passes/src/static_analysis/future_checker.rs @@ -91,6 +91,7 @@ impl AstVisitor for FutureChecker<'_> { match input { Expression::Array(array) => self.visit_array(array, &Position::Misc), Expression::ArrayAccess(access) => self.visit_array_access(access, &Position::Misc), + Expression::Slice(slice) => self.visit_slice(slice.as_ref(), &Position::Misc), Expression::AssociatedConstant(constant) => self.visit_associated_constant(constant, &Position::Misc), Expression::AssociatedFunction(function) => self.visit_associated_function(function, &Position::Misc), Expression::Async(async_) => self.visit_async(async_, &Position::Misc), diff --git a/compiler/passes/src/static_single_assignment/expression.rs b/compiler/passes/src/static_single_assignment/expression.rs index 4892ee9e744..8f2524ec13b 100644 --- a/compiler/passes/src/static_single_assignment/expression.rs +++ b/compiler/passes/src/static_single_assignment/expression.rs @@ -32,6 +32,7 @@ use leo_ast::{ MemberAccess, Path, RepeatExpression, + Slice, Statement, StructExpression, StructVariableInitializer, @@ -102,6 +103,11 @@ impl ExpressionConsumer for SsaFormingVisitor<'_> { (ArrayExpression { elements, ..input }.into(), statements) } + fn consume_slice(&mut self, input: leo_ast::Slice) -> Self::Output { + let (source_array, statements) = self.consume_expression_and_define(input.source_array); + (Slice { source_array, ..input }.into(), statements) + } + /// Consumes a binary expression, accumulating any statements that are generated. fn consume_binary(&mut self, input: BinaryExpression) -> Self::Output { // Reconstruct the lhs of the binary expression. diff --git a/compiler/passes/src/type_checking/ast.rs b/compiler/passes/src/type_checking/ast.rs index 6b2fd8db52e..9ee1f12a227 100644 --- a/compiler/passes/src/type_checking/ast.rs +++ b/compiler/passes/src/type_checking/ast.rs @@ -460,6 +460,8 @@ impl AstVisitor for TypeCheckingVisitor<'_> { let output = match input { Expression::Array(array) => self.visit_array(array, additional), Expression::ArrayAccess(access) => self.visit_array_access_general(access, false, additional), + // Slices will be converted to normal arrays after the const propogation. + Expression::Slice(slice) => self.visit_slice(slice, additional), Expression::AssociatedConstant(constant) => self.visit_associated_constant(constant, additional), Expression::AssociatedFunction(function) => self.visit_associated_function(function, additional), Expression::Async(async_) => self.visit_async(async_, additional), @@ -557,6 +559,40 @@ impl AstVisitor for TypeCheckingVisitor<'_> { type_ } + fn visit_slice(&mut self, input: &Slice, _additional: &Self::AdditionalInput) -> Self::Output { + let Type::Array(arr) = self.visit_expression(&input.source_array, &None) else { panic!("Can't happen.") }; + + let mut type_checker = |index| { + let mut index_type = self.visit_expression(index, &None); + + if index_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`. + index_type = Type::Integer(IntegerType::U32); + if let Expression::Literal(literal) = index { + self.check_numeric_literal(literal, &index_type); + } + }; + + self.assert_int_type(&index_type, index.span()); + + // Keep track of the type of the index in the type table. + // This is important for when the index is an unsuffixed literal. + self.state.type_table.insert(index.id(), index_type.clone()); + + index_type + }; + + let start = input.start.as_ref().map(&mut type_checker); + let stop = input.stop.as_ref().map(&mut type_checker); + + if let (Some(strt), Some(stp)) = (start, stop) { + self.assert_type(&strt, &stp, input.span()); + } + + ArrayType::new(arr.element_type().clone(), input.clone().into()).into() + } + fn visit_repeat(&mut self, input: &RepeatExpression, additional: &Self::AdditionalInput) -> Self::Output { // Grab the element type from the expected type if the expected type is an array or if it's // an optional array @@ -777,7 +813,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", @@ -789,7 +828,24 @@ 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() => + { + let len_expr = 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(), + })); + + self.state.type_table.insert(len_expr.id(), Type::Integer(IntegerType::U32)); + + Type::Array(ArrayType::new(array_type1.element_type().clone(), len_expr)) + } + _ => assert_same_type(self, &t1, &t2), + }; self.maybe_assert_type(&result_t, destination, input.span()); diff --git a/errors/src/errors/parser/parser_errors.rs b/errors/src/errors/parser/parser_errors.rs index 40731c09a2e..fd9eb340ab3 100644 --- a/errors/src/errors/parser/parser_errors.rs +++ b/errors/src/errors/parser/parser_errors.rs @@ -478,4 +478,11 @@ create_messages!( msg: "Identifiers cannot start with an underscore.", help: Some("Identifiers must start with a letter.".to_string()), } + + @formatted + inclusive_range_with_no_end { + args: (), + msg: "Inclusive ranges must be bounded at the end (`..=b` or `a..=b`)", + help: Some("use `..` instead".to_string()), + } ); diff --git a/errors/src/errors/static_analyzer/static_analyzer_error.rs b/errors/src/errors/static_analyzer/static_analyzer_error.rs index 353a43a2f98..71dbf3c3059 100644 --- a/errors/src/errors/static_analyzer/static_analyzer_error.rs +++ b/errors/src/errors/static_analyzer/static_analyzer_error.rs @@ -109,6 +109,13 @@ create_messages!( help: None, } + @formatted + slice_bounds { + args: (start: impl Display, stop: impl Display, len: impl Display), + msg: format!("Range {start}..{stop} is out of bounds (array length is {len})."), + help: None, + } + @formatted custom_error { args: (msg: impl Display, help: Option), diff --git a/interpreter/src/cursor.rs b/interpreter/src/cursor.rs index 18d93a0a5b2..6447d6fad76 100644 --- a/interpreter/src/cursor.rs +++ b/interpreter/src/cursor.rs @@ -421,6 +421,7 @@ impl Cursor { Expression::ArrayAccess(access) => places.push(&access.array), Expression::TupleAccess(access) => places.push(&access.tuple), Expression::MemberAccess(access) => places.push(&access.inner), + Expression::Slice(slice) => places.push(&slice.source_array), Expression::Path(path_) => { path = path_; break; @@ -1038,6 +1039,47 @@ 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.stop { + push!()(end, &None); + } + push!()(&slice.source_array, &None); + None + } + Expression::Slice(slice) if step == 1 => { + let span = slice.span(); + let (inclusive, end) = + if slice.clusivity { (slice.clusivity, 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..00dadca71ec --- /dev/null +++ b/tests/expectations/compiler/array/array_concat_fail.out @@ -0,0 +1,5 @@ +Error [ESAZ0374013]: The resulting array length exceeds the maximum allowed length: 512. + --> 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..5132dab3e87 --- /dev/null +++ b/tests/expectations/compiler/array/array_concat_fail2.out @@ -0,0 +1,5 @@ +Error [ESAZ0374013]: The resulting array length exceeds the maximum allowed length: 512. + --> 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/constructor/empty_custom_constructor_fail.out b/tests/expectations/compiler/constructor/empty_custom_constructor_fail.out index 2550c9fdd90..3ae7a3aa692 100644 --- a/tests/expectations/compiler/constructor/empty_custom_constructor_fail.out +++ b/tests/expectations/compiler/constructor/empty_custom_constructor_fail.out @@ -1,4 +1,4 @@ -Error [ESAZ0374012]: The `@custom` constructor has no statements after dead code elimination. +Error [ESAZ0374013]: The `@custom` constructor has no statements after dead code elimination. --> compiler-test:5:5 | 5 | @custom diff --git a/tests/expectations/compiler/slice/array_slices.out b/tests/expectations/compiler/slice/array_slices.out new file mode 100644 index 00000000000..08912e43af6 --- /dev/null +++ b/tests/expectations/compiler/slice/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/slice/array_slices_fail.out b/tests/expectations/compiler/slice/array_slices_fail.out new file mode 100644 index 00000000000..163e56c21b2 --- /dev/null +++ b/tests/expectations/compiler/slice/array_slices_fail.out @@ -0,0 +1,5 @@ +Error [ETYC0372076]: An array cannot be empty + --> compiler-test:3:17 + | + 3 | let b = a[0..0]; + | ^^^^^^^ diff --git a/tests/expectations/compiler/slice/array_slices_fail2.out b/tests/expectations/compiler/slice/array_slices_fail2.out new file mode 100644 index 00000000000..def982bd237 --- /dev/null +++ b/tests/expectations/compiler/slice/array_slices_fail2.out @@ -0,0 +1,5 @@ +Error [ESAZ0374012]: Range 0..192 is 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/slice/array_slicing.out b/tests/expectations/compiler/slice/array_slicing.out new file mode 100644 index 00000000000..8162159bf08 --- /dev/null +++ b/tests/expectations/compiler/slice/array_slicing.out @@ -0,0 +1,43 @@ +program test.aleo; + +function full: + cast 1u8 2u8 3u8 4u8 into r0 as [u8; 4u32]; + cast r0[0u32] r0[1u32] r0[2u32] r0[3u32] into r1 as [u8; 4u32]; + is.eq r0[1u32] r1[0u32] into r2; + assert.eq r2 true; + +function end_exclusive: + cast 1u8 2u8 3u8 4u8 into r0 as [u8; 4u32]; + cast r0[0u32] r0[1u32] into r1 as [u8; 2u32]; + is.eq r0[1u32] r1[0u32] into r2; + assert.eq r2 true; + +function end_inclusive: + cast 1u8 2u8 3u8 4u8 into r0 as [u8; 4u32]; + cast r0[0u32] r0[1u32] r0[2u32] into r1 as [u8; 3u32]; + is.eq 1u8 r1[0u32] into r2; + assert.eq r2 true; + +function start: + cast 1u8 2u8 3u8 4u8 into r0 as [u8; 4u32]; + cast r0[2u32] r0[3u32] into r1 as [u8; 2u32]; + is.eq 3u8 r1[0u32] into r2; + assert.eq r2 true; + +function start_end_exclusive: + cast 1u8 2u8 3u8 4u8 into r0 as [u8; 4u32]; + cast r0[1u32] r0[2u32] into r1 as [u8; 2u32]; + is.eq 3u8 r1[1u32] into r2; + assert.eq r2 true; + +function start_end_inclusive: + cast 1u8 2u8 3u8 4u8 into r0 as [u8; 4u32]; + cast r0[1u32] r0[2u32] r0[3u32] into r1 as [u8; 3u32]; + is.eq 4u8 r1[2u32] into r2; + assert.eq r2 true; + +function expression_indexing: + cast 1u8 2u8 3u8 4u8 into r0 as [u8; 4u32]; + cast r0[1u32] r0[2u32] into r1 as [u8; 2u32]; + is.eq 2u8 r1[1u32] into r2; + assert.eq r2 true; diff --git a/tests/expectations/compiler/slice/inclusive_with_start_without_end_fail.out b/tests/expectations/compiler/slice/inclusive_with_start_without_end_fail.out new file mode 100644 index 00000000000..7efde2aa1e1 --- /dev/null +++ b/tests/expectations/compiler/slice/inclusive_with_start_without_end_fail.out @@ -0,0 +1,7 @@ +Error [EPAR0370054]: Inclusive ranges must be bounded at the end (`..=b` or `a..=b`) + --> compiler-test:5:20 + | + 5 | let arr2 = arr[1..=]; + | ^^^^^^^^^ + | + = use `..` instead diff --git a/tests/expectations/compiler/slice/inclusive_without_end_fail.out b/tests/expectations/compiler/slice/inclusive_without_end_fail.out new file mode 100644 index 00000000000..f58fce9c51c --- /dev/null +++ b/tests/expectations/compiler/slice/inclusive_without_end_fail.out @@ -0,0 +1,7 @@ +Error [EPAR0370054]: Inclusive ranges must be bounded at the end (`..=b` or `a..=b`) + --> compiler-test:5:20 + | + 5 | let arr2 = arr[..=]; + | ^^^^^^^^ + | + = use `..` instead diff --git a/tests/expectations/compiler/slice/index_out_of_bounds.out b/tests/expectations/compiler/slice/index_out_of_bounds.out new file mode 100644 index 00000000000..cffdb939304 --- /dev/null +++ b/tests/expectations/compiler/slice/index_out_of_bounds.out @@ -0,0 +1,5 @@ +Error [ESAZ0374010]: Array index 2 out of bounds (array length is 2). + --> compiler-test:6:26 + | + 6 | assert(arr[1] == arr2[2]); + | ^^^^^^^ diff --git a/tests/expectations/compiler/slice/index_out_of_bounds_fail.out b/tests/expectations/compiler/slice/index_out_of_bounds_fail.out new file mode 100644 index 00000000000..cffdb939304 --- /dev/null +++ b/tests/expectations/compiler/slice/index_out_of_bounds_fail.out @@ -0,0 +1,5 @@ +Error [ESAZ0374010]: Array index 2 out of bounds (array length is 2). + --> compiler-test:6:26 + | + 6 | assert(arr[1] == arr2[2]); + | ^^^^^^^ diff --git a/tests/expectations/parser-statement/statement/expression_fail.out b/tests/expectations/parser-statement/statement/expression_fail.out index e115aeb6f80..2cc6037879b 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..693855971db 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,7 +253,7 @@ 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..e1163022a20 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..99a3e0bbace --- /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; 512]; + let b = [1u8; 512]; + let c = a + b; + return c[16..48]; + } +} \ No newline at end of file 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..4285d727c34 --- /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; 256], b: [u8; 257]) -> [u8; 32] { + let c = a + b; + return c[0..32]; + } +} \ No newline at end of file diff --git a/tests/tests/compiler/array/array_concats.leo b/tests/tests/compiler/array/array_concats.leo new file mode 100644 index 00000000000..255f5a37c3e --- /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; + } +} \ No newline at end of file diff --git a/tests/tests/compiler/slice/array_slices.leo b/tests/tests/compiler/slice/array_slices.leo new file mode 100644 index 00000000000..4cc8849bd7e --- /dev/null +++ b/tests/tests/compiler/slice/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]; + } +} \ No newline at end of file diff --git a/tests/tests/compiler/slice/array_slices_fail.leo b/tests/tests/compiler/slice/array_slices_fail.leo new file mode 100644 index 00000000000..45bf54aab85 --- /dev/null +++ b/tests/tests/compiler/slice/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; + } +} \ No newline at end of file diff --git a/tests/tests/compiler/slice/array_slices_fail2.leo b/tests/tests/compiler/slice/array_slices_fail2.leo new file mode 100644 index 00000000000..be007be5c50 --- /dev/null +++ b/tests/tests/compiler/slice/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; + } +} \ No newline at end of file diff --git a/tests/tests/compiler/slice/array_slicing.leo b/tests/tests/compiler/slice/array_slicing.leo new file mode 100644 index 00000000000..12f8cc8a7db --- /dev/null +++ b/tests/tests/compiler/slice/array_slicing.leo @@ -0,0 +1,45 @@ + +program test.aleo { + transition full() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[..]; + assert(arr[1] == arr2[0]); + } + + transition end_exclusive() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[..2]; + let arr3 = arr[..3]; + assert(arr[1] == arr2[0]); + } + + transition end_inclusive() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[..=2]; + assert(1 == arr2[0]); + } + + transition start() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[2..]; + assert(3 == arr2[0]); + } + + transition start_end_exclusive() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[1..3]; + assert(3 == arr2[1]); + } + + transition start_end_inclusive() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[1..=3]; + assert(4 == arr2[2]); + } + + transition expression_indexing() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[(1u32 + 0u32)..=(3u32 - 1u32)]; + assert(2 == arr2[1]); + } +} diff --git a/tests/tests/compiler/slice/inclusive_with_start_without_end_fail.leo b/tests/tests/compiler/slice/inclusive_with_start_without_end_fail.leo new file mode 100644 index 00000000000..476747f391f --- /dev/null +++ b/tests/tests/compiler/slice/inclusive_with_start_without_end_fail.leo @@ -0,0 +1,8 @@ + +program test.aleo { + transition inclusive_with_start_no_end() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[1..=]; + assert(arr[1] == arr2[0]); + } +} diff --git a/tests/tests/compiler/slice/inclusive_without_end_fail.leo b/tests/tests/compiler/slice/inclusive_without_end_fail.leo new file mode 100644 index 00000000000..6171e205b0a --- /dev/null +++ b/tests/tests/compiler/slice/inclusive_without_end_fail.leo @@ -0,0 +1,8 @@ + +program test.aleo { + transition inclusive_without_end() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[..=]; + assert(arr[1] == arr2[0]); + } +} diff --git a/tests/tests/compiler/slice/index_out_of_bounds_fail.leo b/tests/tests/compiler/slice/index_out_of_bounds_fail.leo new file mode 100644 index 00000000000..2e1911d554d --- /dev/null +++ b/tests/tests/compiler/slice/index_out_of_bounds_fail.leo @@ -0,0 +1,8 @@ + +program test.aleo { + transition index_out_of_bounds() { + let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8]; + let arr2 = arr[1..3]; + assert(arr[1] == arr2[2]); + } +}