diff --git a/acvm-repo/acir_field/src/field_element.rs b/acvm-repo/acir_field/src/field_element.rs index 76956920c37..771f09437fb 100644 --- a/acvm-repo/acir_field/src/field_element.rs +++ b/acvm-repo/acir_field/src/field_element.rs @@ -62,6 +62,58 @@ impl From for FieldElement { } } +impl From for FieldElement { + fn from(a: i64) -> Self { + // Optimized: Convert directly without string conversion + if a >= 0 { + FieldElement(F::from(a as u64)) + } else { + // Negative case: handle i64::MIN specially to avoid overflow + let abs_value = a.wrapping_neg() as u64; + FieldElement(-F::from(abs_value)) + } + } +} + +impl From for FieldElement { + fn from(a: i32) -> Self { + // Optimized: Convert directly without string conversion + if a >= 0 { + FieldElement(F::from(a as u32)) + } else { + // Negative case: handle i32::MIN specially to avoid overflow + let abs_value = a.wrapping_neg() as u32; + FieldElement(-F::from(abs_value)) + } + } +} + +impl From for FieldElement { + fn from(a: i16) -> Self { + // Optimized: Convert directly without string conversion + if a >= 0 { + FieldElement(F::from(a as u16)) + } else { + // Negative case: handle i16::MIN specially to avoid overflow + let abs_value = a.wrapping_neg() as u16; + FieldElement(-F::from(abs_value)) + } + } +} + +impl From for FieldElement { + fn from(a: i8) -> Self { + // Optimized: Convert directly without string conversion + if a >= 0 { + FieldElement(F::from(a as u8)) + } else { + // Negative case: handle i8::MIN specially to avoid overflow + let abs_value = a.wrapping_neg() as u8; + FieldElement(-F::from(abs_value)) + } + } +} + impl Serialize for FieldElement { fn serialize(&self, serializer: S) -> Result where @@ -105,12 +157,52 @@ impl From for FieldElement { } } +impl From for FieldElement { + fn from(a: u16) -> FieldElement { + FieldElement(F::from(a)) + } +} + +impl From for FieldElement { + fn from(a: u8) -> FieldElement { + FieldElement(F::from(a)) + } +} + impl From for FieldElement { fn from(boolean: bool) -> FieldElement { if boolean { FieldElement::one() } else { FieldElement::zero() } } } +impl TryFrom> for u128 { + type Error = (); + + fn try_from(value: FieldElement) -> Result { + match value.try_into_u128() { + Some(value) => Ok(value), + None => Err(()), + } + } +} + +impl TryFrom> for i128 { + type Error = (); + + fn try_from(value: FieldElement) -> Result { + match value.try_into_i128() { + Some(value) => Ok(value), + None => Err(()), + } + } +} + +impl From> for bool { + fn from(field: FieldElement) -> bool { + !field.is_zero() + } +} + impl FieldElement { /// Constructs a `FieldElement` from the underlying prime field representation. /// diff --git a/compiler/noirc_frontend/proptest-regressions/tests/meta_quote_roundtrip.txt b/compiler/noirc_frontend/proptest-regressions/tests/meta_quote_roundtrip.txt new file mode 100644 index 00000000000..e2bef27a891 --- /dev/null +++ b/compiler/noirc_frontend/proptest-regressions/tests/meta_quote_roundtrip.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc f5d63c630ba2411a6e6740f226f355929fffa298ee81b94463988ded2e96209d # shrinks to n = -128 diff --git a/compiler/noirc_frontend/src/elaborator/enums.rs b/compiler/noirc_frontend/src/elaborator/enums.rs index 1570db4a81a..1dce138fea5 100644 --- a/compiler/noirc_frontend/src/elaborator/enums.rs +++ b/compiler/noirc_frontend/src/elaborator/enums.rs @@ -867,19 +867,9 @@ impl Elaborator<'_> { }); let value = match constant { - Value::Bool(value) => SignedField::positive(value), - Value::Field(value) => value, - Value::I8(value) => SignedField::from_signed(value), - Value::I16(value) => SignedField::from_signed(value), - Value::I32(value) => SignedField::from_signed(value), - Value::I64(value) => SignedField::from_signed(value), - Value::U1(value) => SignedField::positive(value), - Value::U8(value) => SignedField::positive(u128::from(value)), - Value::U16(value) => SignedField::positive(u128::from(value)), - Value::U32(value) => SignedField::positive(value), - Value::U64(value) => SignedField::positive(value), - Value::U128(value) => SignedField::positive(value), - Value::Zeroed(_) => SignedField::positive(0u32), + Value::Bool(value) => value.into(), + Value::Integer(int) => int.as_signed_field(), + Value::Zeroed(_) => SignedField::zero(), _ => { self.push_err(ResolverError::NonIntegerGlobalUsedInPattern { location }); return Pattern::Error; diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 7f829ce6b7b..8f4f38091e7 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -755,7 +755,7 @@ impl Elaborator<'_> { return None; }; - let Some(global_value) = global_value.to_non_negative_signed_field() else { + let Some(global_value) = global_value.as_non_negative_signed_field() else { let global_value = global_value.clone(); if global_value.is_integral() { self.push_err(ResolverError::NegativeGlobalType { location, global_value }); diff --git a/compiler/noirc_frontend/src/hir/comptime/display.rs b/compiler/noirc_frontend/src/hir/comptime/display.rs index 2cc4be4c78a..63a2ab99375 100644 --- a/compiler/noirc_frontend/src/hir/comptime/display.rs +++ b/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -1,6 +1,5 @@ use std::fmt::Display; -use acvm::AcirField; use iter_extended::vecmap; use noirc_errors::Location; @@ -386,21 +385,7 @@ impl Display for ValuePrinter<'_, '_> { let msg = if *value { "true" } else { "false" }; write!(f, "{msg}") } - Value::Field(value) => { - // write!(f, "{value}") // This would display the Field as a number, but it doesn't match the runtime. - write!(f, "{}", value.to_field_element().to_short_hex()) - } - Value::I8(value) => write!(f, "{value}"), - Value::I16(value) => write!(f, "{value}"), - Value::I32(value) => write!(f, "{value}"), - Value::I64(value) => write!(f, "{value}"), - Value::U1(false) => write!(f, "0"), - Value::U1(true) => write!(f, "1"), - Value::U8(value) => write!(f, "{value}"), - Value::U16(value) => write!(f, "{value}"), - Value::U32(value) => write!(f, "{value}"), - Value::U64(value) => write!(f, "{value}"), - Value::U128(value) => write!(f, "{value}"), + Value::Integer(int) => write!(f, "{int}"), Value::String(value) => write!(f, "{value}"), Value::CtString(value) => write!(f, "{value}"), Value::FormatString(fragments, _, _) => { diff --git a/compiler/noirc_frontend/src/hir/comptime/integer.rs b/compiler/noirc_frontend/src/hir/comptime/integer.rs new file mode 100644 index 00000000000..9b37f459507 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/integer.rs @@ -0,0 +1,249 @@ +use std::fmt::Display; + +use acvm::{AcirField, FieldElement}; + +use crate::{ + Type, + ast::{ExpressionKind, IntegerBitSize}, + hir_def::expr::{HirExpression, HirLiteral}, + shared::Signedness, + signed_field::SignedField, + token::{IntegerTypeSuffix, Token}, +}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Integer { + Field(SignedField), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + U1(bool), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + U128(u128), +} + +impl Integer { + /// Convert this [Integer] to a field, returning `None` for negative values. + /// Returns `None` for negative integers. + pub(crate) fn as_non_negative_field(&self) -> Option { + match self { + Integer::Field(value) => Some(value.to_field_element()), + Integer::I8(value) if *value >= 0 => Some((*value).into()), + Integer::I16(value) if *value >= 0 => Some((*value).into()), + Integer::I32(value) if *value >= 0 => Some((*value).into()), + Integer::I64(value) if *value >= 0 => Some((*value).into()), + Integer::U1(value) => Some((*value).into()), + Integer::U8(value) => Some((*value).into()), + Integer::U16(value) => Some((*value).into()), + Integer::U32(value) => Some((*value).into()), + Integer::U64(value) => Some((*value).into()), + Integer::U128(value) => Some((*value).into()), + _ => None, + } + } + + /// Converts this [Integer] to a [SignedField]. + pub(crate) fn as_signed_field(self) -> SignedField { + match self { + Integer::Field(value) => value, + Integer::I8(value) => value.into(), + Integer::I16(value) => value.into(), + Integer::I32(value) => value.into(), + Integer::I64(value) => value.into(), + Integer::U1(value) => value.into(), + Integer::U8(value) => value.into(), + Integer::U16(value) => value.into(), + Integer::U32(value) => value.into(), + Integer::U64(value) => value.into(), + Integer::U128(value) => value.into(), + } + } + + /// Converts this [Integer] to a [FieldElement]. Any negative values are + /// encoded in two's complement such that `-x_iN == 2^N - x`. Note that + /// this is only true for the various signed types. Negative [Integer::Field] + /// values will still be encoded as ordinary negative fields. + pub(crate) fn as_field_twos_complement(self) -> FieldElement { + match self { + Integer::Field(value) => value.to_field_element(), + Integer::I8(value) => (value as u8).into(), + Integer::I16(value) => (value as u16).into(), + Integer::I32(value) => (value as u32).into(), + Integer::I64(value) => (value as u64).into(), + Integer::U1(value) => value.into(), + Integer::U8(value) => value.into(), + Integer::U16(value) => value.into(), + Integer::U32(value) => value.into(), + Integer::U64(value) => value.into(), + Integer::U128(value) => value.into(), + } + } + + pub fn is_negative(&self) -> bool { + match self { + Integer::I8(x) => *x < 0, + Integer::I16(x) => *x < 0, + Integer::I32(x) => *x < 0, + Integer::I64(x) => *x < 0, + _ => false, // Unsigned or Field types are never negative + } + } + + pub fn get_type(&self) -> Type { + match self { + Integer::Field(_) => Type::FieldElement, + Integer::I8(_) => Type::Integer(Signedness::Signed, IntegerBitSize::Eight), + Integer::I16(_) => Type::Integer(Signedness::Signed, IntegerBitSize::Sixteen), + Integer::I32(_) => Type::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo), + Integer::I64(_) => Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour), + Integer::U1(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::One), + Integer::U8(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), + Integer::U16(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen), + Integer::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), + Integer::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), + Integer::U128(_) => { + Type::Integer(Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) + } + } + } + + pub(crate) fn into_expression_kind(self) -> ExpressionKind { + use crate::ast::Literal::Integer as Int; + use ExpressionKind::Literal; + match self { + Integer::Field(value) => Literal(Int(value, Some(IntegerTypeSuffix::Field))), + Integer::I8(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::I8))), + Integer::I16(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::I16))), + Integer::I32(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::I32))), + Integer::I64(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::I64))), + Integer::U1(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::U1))), + Integer::U8(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::U8))), + Integer::U16(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::U16))), + Integer::U32(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::U32))), + Integer::U64(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::U64))), + Integer::U128(value) => Literal(Int(value.into(), Some(IntegerTypeSuffix::U128))), + } + } + + pub(crate) fn into_hir_expression(self) -> HirExpression { + match self { + Integer::Field(value) => HirExpression::Literal(HirLiteral::Integer(value)), + Integer::I8(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::I16(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::I32(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::I64(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::U1(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::U8(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::U16(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::U32(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::U64(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + Integer::U128(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), + } + } + + pub(crate) fn into_tokens(self) -> Vec { + match self { + Integer::U1(bool) => { + vec![Token::Int(bool.into(), Some(IntegerTypeSuffix::U1))] + } + Integer::U8(value) => { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::U8))] + } + Integer::U16(value) => { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::U16))] + } + Integer::U32(value) => { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::U32))] + } + Integer::U64(value) => { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::U64))] + } + Integer::U128(value) => { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::U128))] + } + Integer::I8(value) => { + if value < 0 { + let int = Token::Int(value.unsigned_abs().into(), Some(IntegerTypeSuffix::I8)); + vec![Token::Minus, int] + } else { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::I8))] + } + } + Integer::I16(value) => { + if value < 0 { + let int = Token::Int(value.unsigned_abs().into(), Some(IntegerTypeSuffix::I16)); + vec![Token::Minus, int] + } else { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::I16))] + } + } + Integer::I32(value) => { + if value < 0 { + let int = Token::Int(value.unsigned_abs().into(), Some(IntegerTypeSuffix::I32)); + vec![Token::Minus, int] + } else { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::I32))] + } + } + Integer::I64(value) => { + if value < 0 { + let int = Token::Int(value.unsigned_abs().into(), Some(IntegerTypeSuffix::I64)); + vec![Token::Minus, int] + } else { + vec![Token::Int(value.into(), Some(IntegerTypeSuffix::I64))] + } + } + Integer::Field(value) => { + if value.is_negative() { + vec![Token::Minus, Token::Int(value.absolute_value(), None)] + } else { + vec![Token::Int(value.absolute_value(), None)] + } + } + } + } + + pub fn is_zero(&self) -> bool { + match self { + Integer::Field(field) => field.is_zero(), + Integer::I8(value) => *value == 0, + Integer::I16(value) => *value == 0, + Integer::I32(value) => *value == 0, + Integer::I64(value) => *value == 0, + Integer::U1(value) => !value, + Integer::U8(value) => *value == 0, + Integer::U16(value) => *value == 0, + Integer::U32(value) => *value == 0, + Integer::U64(value) => *value == 0, + Integer::U128(value) => *value == 0, + } + } +} + +impl Display for Integer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Integer::Field(value) if value.is_negative() => { + write!(f, "-{}", value.absolute_value()) + } + Integer::Field(value) => { + write!(f, "{}", value.absolute_value()) + } + Integer::I8(value) => write!(f, "{value}"), + Integer::I16(value) => write!(f, "{value}"), + Integer::I32(value) => write!(f, "{value}"), + Integer::I64(value) => write!(f, "{value}"), + Integer::U1(false) => write!(f, "0"), + Integer::U1(true) => write!(f, "1"), + Integer::U8(value) => write!(f, "{value}"), + Integer::U16(value) => write!(f, "{value}"), + Integer::U32(value) => write!(f, "{value}"), + Integer::U64(value) => write!(f, "{value}"), + Integer::U128(value) => write!(f, "{value}"), + } + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 6ce95184e38..5d22ec8ec8e 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -44,6 +44,7 @@ use rustc_hash::FxHashMap as HashMap; use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, UnaryOp}; use crate::elaborator::{ElaborateReason, Elaborator, ElaboratorOptions}; use crate::hir::Context; +use crate::hir::comptime::Integer; use crate::hir::comptime::value::FormatStringFragment; use crate::hir::def_map::ModuleId; use crate::hir::type_check::TypeCheckError; @@ -1003,7 +1004,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let first_field = fields.iter().next(); match first_field { Some((_, value)) => match &*value.borrow() { - Value::Field(ordering) => Some(*ordering), + Value::Integer(Integer::Field(ordering)) => Some(*ordering), _ => None, }, None => None, @@ -1510,10 +1511,10 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if start_type.is_signed() { let get_index = match start_value { - Value::I8(_) => |i| Value::I8(i as i8), - Value::I16(_) => |i| Value::I16(i as i16), - Value::I32(_) => |i| Value::I32(i as i32), - Value::I64(_) => |i| Value::I64(i as i64), + Value::Integer(Integer::I8(_)) => |i| Value::Integer(Integer::I8(i as i8)), + Value::Integer(Integer::I16(_)) => |i| Value::Integer(Integer::I16(i as i16)), + Value::Integer(Integer::I32(_)) => |i| Value::Integer(Integer::I32(i as i32)), + Value::Integer(Integer::I64(_)) => |i| Value::Integer(Integer::I64(i as i64)), _ => unreachable!("Checked above that value is signed type"), }; @@ -1528,12 +1529,12 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } } else if start_type.is_unsigned() { let get_index = match start_value { - Value::U1(_) => |i| Value::U1(i == 1), - Value::U8(_) => |i| Value::U8(i as u8), - Value::U16(_) => |i| Value::U16(i as u16), - Value::U32(_) => |i| Value::U32(i as u32), - Value::U64(_) => |i| Value::U64(i as u64), - Value::U128(_) => |i| Value::U128(i), + Value::Integer(Integer::U1(_)) => |i| Value::Integer(Integer::U1(i == 1)), + Value::Integer(Integer::U8(_)) => |i| Value::Integer(Integer::U8(i as u8)), + Value::Integer(Integer::U16(_)) => |i| Value::Integer(Integer::U16(i as u16)), + Value::Integer(Integer::U32(_)) => |i| Value::Integer(Integer::U32(i as u32)), + Value::Integer(Integer::U64(_)) => |i| Value::Integer(Integer::U64(i as u64)), + Value::Integer(Integer::U128(_)) => |i| Value::Integer(Integer::U128(i)), _ => unreachable!("Checked above that value is unsigned type"), }; @@ -1759,44 +1760,44 @@ fn evaluate_integer(typ: Type, value: SignedField, location: Location) -> IResul } match typ { - FieldElement => Ok(Value::Field(value)), + FieldElement => Ok(Value::field(value)), Integer(Unsigned, One) => { let field_value = value.to_field_element(); if field_value.is_zero() { - Ok(Value::U1(false)) + Ok(Value::u1(false)) } else if field_value.is_one() { - Ok(Value::U1(true)) + Ok(Value::u1(true)) } else { Err(InterpreterError::IntegerOutOfRangeForType { value, typ, location }) } } Integer(Unsigned, Eight) => { - evaluate_unsigned!(U8) + evaluate_unsigned!(u8) } Integer(Unsigned, Sixteen) => { - evaluate_unsigned!(U16) + evaluate_unsigned!(u16) } Integer(Unsigned, ThirtyTwo) => { - evaluate_unsigned!(U32) + evaluate_unsigned!(u32) } Integer(Unsigned, SixtyFour) => { - evaluate_unsigned!(U64) + evaluate_unsigned!(u64) } Integer(Unsigned, HundredTwentyEight) => { - evaluate_unsigned!(U128) + evaluate_unsigned!(u128) } Integer(Signed, One) => Err(InterpreterError::TypeUnsupported { typ, location }), Integer(Signed, Eight) => { - evaluate_signed!(I8) + evaluate_signed!(i8) } Integer(Signed, Sixteen) => { - evaluate_signed!(I16) + evaluate_signed!(i16) } Integer(Signed, ThirtyTwo) => { - evaluate_signed!(I32) + evaluate_signed!(i32) } Integer(Signed, SixtyFour) => { - evaluate_signed!(I64) + evaluate_signed!(i64) } Integer(Signed, HundredTwentyEight) => { Err(InterpreterError::TypeUnsupported { typ, location }) @@ -1818,7 +1819,7 @@ fn bounds_check(array: Value, index: Value, location: Location) -> IResult<(Vect }; let index = match index { - Value::U32(value) => value as usize, + Value::Integer(Integer::U32(value)) => value as usize, value => { let typ = value.get_type().into_owned(); let expected_type = Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo); @@ -1841,29 +1842,39 @@ fn bounds_check(array: Value, index: Value, location: Location) -> IResult<(Vect fn evaluate_prefix_with_value(rhs: Value, operator: UnaryOp, location: Location) -> IResult { match operator { UnaryOp::Minus => match rhs { - Value::Field(value) => Ok(Value::Field(-value)), - Value::I8(value) => value + Value::Integer(Integer::Field(value)) => Ok(Value::field(-value)), + Value::Integer(Integer::I8(value)) => value .checked_neg() - .map(Value::I8) + .map(Value::i8) .ok_or_else(|| InterpreterError::NegateWithOverflow { location }), - Value::I16(value) => value + Value::Integer(Integer::I16(value)) => value .checked_neg() - .map(Value::I16) + .map(Value::i16) .ok_or_else(|| InterpreterError::NegateWithOverflow { location }), - Value::I32(value) => value + Value::Integer(Integer::I32(value)) => value .checked_neg() - .map(Value::I32) + .map(Value::i32) .ok_or_else(|| InterpreterError::NegateWithOverflow { location }), - Value::I64(value) => value + Value::Integer(Integer::I64(value)) => value .checked_neg() - .map(Value::I64) + .map(Value::i64) .ok_or_else(|| InterpreterError::NegateWithOverflow { location }), - Value::U1(_) => Err(InterpreterError::CannotApplyMinusToType { location, typ: "u1" }), - Value::U8(_) => Err(InterpreterError::CannotApplyMinusToType { location, typ: "u8" }), - Value::U16(_) => Err(InterpreterError::CannotApplyMinusToType { location, typ: "u16" }), - Value::U32(_) => Err(InterpreterError::CannotApplyMinusToType { location, typ: "u32" }), - Value::U64(_) => Err(InterpreterError::CannotApplyMinusToType { location, typ: "u64" }), - Value::U128(_) => { + Value::Integer(Integer::U1(_)) => { + Err(InterpreterError::CannotApplyMinusToType { location, typ: "u1" }) + } + Value::Integer(Integer::U8(_)) => { + Err(InterpreterError::CannotApplyMinusToType { location, typ: "u8" }) + } + Value::Integer(Integer::U16(_)) => { + Err(InterpreterError::CannotApplyMinusToType { location, typ: "u16" }) + } + Value::Integer(Integer::U32(_)) => { + Err(InterpreterError::CannotApplyMinusToType { location, typ: "u32" }) + } + Value::Integer(Integer::U64(_)) => { + Err(InterpreterError::CannotApplyMinusToType { location, typ: "u64" }) + } + Value::Integer(Integer::U128(_)) => { Err(InterpreterError::CannotApplyMinusToType { location, typ: "u128" }) } value => { @@ -1874,16 +1885,16 @@ fn evaluate_prefix_with_value(rhs: Value, operator: UnaryOp, location: Location) }, UnaryOp::Not => match rhs { Value::Bool(value) => Ok(Value::Bool(!value)), - Value::I8(value) => Ok(Value::I8(!value)), - Value::I16(value) => Ok(Value::I16(!value)), - Value::I32(value) => Ok(Value::I32(!value)), - Value::I64(value) => Ok(Value::I64(!value)), - Value::U1(value) => Ok(Value::U1(!value)), - Value::U8(value) => Ok(Value::U8(!value)), - Value::U16(value) => Ok(Value::U16(!value)), - Value::U32(value) => Ok(Value::U32(!value)), - Value::U64(value) => Ok(Value::U64(!value)), - Value::U128(value) => Ok(Value::U128(!value)), + Value::Integer(Integer::I8(value)) => Ok(Value::i8(!value)), + Value::Integer(Integer::I16(value)) => Ok(Value::i16(!value)), + Value::Integer(Integer::I32(value)) => Ok(Value::i32(!value)), + Value::Integer(Integer::I64(value)) => Ok(Value::i64(!value)), + Value::Integer(Integer::U1(value)) => Ok(Value::u1(!value)), + Value::Integer(Integer::U8(value)) => Ok(Value::u8(!value)), + Value::Integer(Integer::U16(value)) => Ok(Value::u16(!value)), + Value::Integer(Integer::U32(value)) => Ok(Value::u32(!value)), + Value::Integer(Integer::U64(value)) => Ok(Value::u64(!value)), + Value::Integer(Integer::U128(value)) => Ok(Value::u128(!value)), value => { let typ = value.get_type().into_owned(); Err(InterpreterError::InvalidValueForUnary { typ, location, operator: "not" }) @@ -1910,22 +1921,22 @@ fn evaluate_prefix_with_value(rhs: Value, operator: UnaryOp, location: Location) fn to_u128(value: Value) -> Option { match value { - Value::U1(value) => Some(u128::from(value)), - Value::U8(value) => Some(u128::from(value)), - Value::U16(value) => Some(u128::from(value)), - Value::U32(value) => Some(u128::from(value)), - Value::U64(value) => Some(u128::from(value)), - Value::U128(value) => Some(value), + Value::Integer(Integer::U1(value)) => Some(u128::from(value)), + Value::Integer(Integer::U8(value)) => Some(u128::from(value)), + Value::Integer(Integer::U16(value)) => Some(u128::from(value)), + Value::Integer(Integer::U32(value)) => Some(u128::from(value)), + Value::Integer(Integer::U64(value)) => Some(u128::from(value)), + Value::Integer(Integer::U128(value)) => Some(value), _ => None, } } fn to_i128(value: Value) -> Option { match value { - Value::I8(value) => Some(i128::from(value)), - Value::I16(value) => Some(i128::from(value)), - Value::I32(value) => Some(i128::from(value)), - Value::I64(value) => Some(i128::from(value)), + Value::Integer(Integer::I8(value)) => Some(i128::from(value)), + Value::Integer(Integer::I16(value)) => Some(i128::from(value)), + Value::Integer(Integer::I32(value)) => Some(i128::from(value)), + Value::Integer(Integer::I64(value)) => Some(i128::from(value)), _ => None, } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 3831eb5dc17..d6892b39eb1 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -76,7 +76,7 @@ impl Interpreter<'_, '_> { "apply_range_constraint" => apply_range_constraint(arguments, location, call_stack), "array_as_str_unchecked" => array_as_str_unchecked(arguments, location), "array_len" => array_len(arguments, location), - "array_refcount" => Ok(Value::U32(0)), + "array_refcount" => Ok(Value::u32(0)), "assert_constant" => Ok(Value::Unit), "as_vector" => as_vector(arguments, location), "as_witness" => as_witness(arguments, location), @@ -189,7 +189,7 @@ impl Interpreter<'_, '_> { "vector_pop_front" => vector_pop_front(arguments, location, call_stack), "vector_push_back" => vector_push_back(arguments, location), "vector_push_front" => vector_push_front(arguments, location), - "vector_refcount" => Ok(Value::U32(0)), + "vector_refcount" => Ok(Value::u32(0)), "vector_remove" => vector_remove(arguments, location, call_stack), "static_assert" => static_assert(interner, arguments, location, call_stack), "str_as_bytes" => str_as_bytes(arguments, location), @@ -288,7 +288,7 @@ fn array_len(arguments: Vec<(Value, Location)>, location: Location) -> IResult Ok(Value::U32(values.len() as u32)), + Value::Array(values, _) | Value::Vector(values, _) => Ok(Value::u32(values.len() as u32)), value => { let expected = "array".to_string(); let actual = value.get_type().into_owned(); @@ -402,7 +402,7 @@ fn str_as_bytes(arguments: Vec<(Value, Location)>, location: Location) -> IResul let string = check_one_argument(arguments, location)?; let string = get_str(string)?; - let bytes: Vector = string.bytes().map(Value::U8).collect(); + let bytes: Vector = string.bytes().map(Value::u8).collect(); let byte_array_type = byte_array_type(bytes.len()); Ok(Value::Array(bytes, byte_array_type)) } @@ -1010,7 +1010,7 @@ fn to_be_bits( call_stack: &Vector, ) -> IResult { let value = check_one_argument(arguments, location)?; - let radix = (Value::U32(2), value.1); + let radix = (Value::u32(2), value.1); to_be_radix(vec![value, radix], return_type, location, call_stack) } @@ -1021,7 +1021,7 @@ fn to_le_bits( call_stack: &Vector, ) -> IResult { let value = check_one_argument(arguments, location)?; - let radix = (Value::U32(2), value.1); + let radix = (Value::u32(2), value.1); to_le_radix(vec![value, radix], return_type, location, call_stack) } @@ -1085,7 +1085,7 @@ fn to_le_radix( None => 0, }; // The only built-ins that use these either return `[u1; N]` or `[u8; N]` - if return_type_is_bits { Value::U1(digit != 0) } else { Value::U8(digit) } + if return_type_is_bits { Value::u1(digit != 0) } else { Value::u8(digit) } }); let len: u32 = decomposed_integer @@ -1136,7 +1136,7 @@ fn type_as_constant( // since arithmetic generics may be `Type::InfixExpr`s which evaluate to // constants but are not actually the `Type::Constant` variant. match typ.evaluate_to_u32(location) { - Ok(constant) => Ok(Some(Value::U32(constant))), + Ok(constant) => Ok(Some(Value::u32(constant))), Err(err) => { // Evaluating to a non-constant returns 'None' in user code if err.is_non_constant_evaluated() { @@ -1159,7 +1159,7 @@ fn type_as_integer( type_as(arguments, return_type, location, |typ| { if let Type::Integer(sign, bits) = typ { let sign = Shared::new(Value::Bool(sign.is_signed())); - let bit_size = Shared::new(Value::U8(bits.bit_size())); + let bit_size = Shared::new(Value::u8(bits.bit_size())); Some(Value::Tuple(vec![sign, bit_size])) } else { None @@ -1561,7 +1561,7 @@ where // fn zeroed() -> T fn zeroed(return_type: Type, location: Location) -> Value { match return_type { - Type::FieldElement => Value::Field(SignedField::zero()), + Type::FieldElement => Value::field(SignedField::zero()), Type::Array(length_type, elem) => { if let Ok(length) = length_type.evaluate_to_u32(location) { let element = zeroed(elem.as_ref().clone(), location); @@ -1574,17 +1574,17 @@ fn zeroed(return_type: Type, location: Location) -> Value { } Type::Vector(_) => Value::Vector(Vector::new(), return_type), Type::Integer(sign, bits) => match (sign, bits) { - (Signedness::Unsigned, IntegerBitSize::One) => Value::U1(false), - (Signedness::Unsigned, IntegerBitSize::Eight) => Value::U8(0), - (Signedness::Unsigned, IntegerBitSize::Sixteen) => Value::U16(0), - (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => Value::U32(0), - (Signedness::Unsigned, IntegerBitSize::SixtyFour) => Value::U64(0), - (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => Value::U128(0), + (Signedness::Unsigned, IntegerBitSize::One) => Value::u1(false), + (Signedness::Unsigned, IntegerBitSize::Eight) => Value::u8(0), + (Signedness::Unsigned, IntegerBitSize::Sixteen) => Value::u16(0), + (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => Value::u32(0), + (Signedness::Unsigned, IntegerBitSize::SixtyFour) => Value::u64(0), + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => Value::u128(0), (Signedness::Signed, IntegerBitSize::One) => unreachable!("invalid type: i1"), - (Signedness::Signed, IntegerBitSize::Eight) => Value::I8(0), - (Signedness::Signed, IntegerBitSize::Sixteen) => Value::I16(0), - (Signedness::Signed, IntegerBitSize::ThirtyTwo) => Value::I32(0), - (Signedness::Signed, IntegerBitSize::SixtyFour) => Value::I64(0), + (Signedness::Signed, IntegerBitSize::Eight) => Value::i8(0), + (Signedness::Signed, IntegerBitSize::Sixteen) => Value::i16(0), + (Signedness::Signed, IntegerBitSize::ThirtyTwo) => Value::i32(0), + (Signedness::Signed, IntegerBitSize::SixtyFour) => Value::i64(0), (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => { unreachable!("invalid type: i128") } @@ -2073,14 +2073,14 @@ fn expr_as_integer( expr_as(interner, arguments, return_type, location, |expr| match expr { ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field, _suffix))) => { Some(Value::Tuple(vec![ - Shared::new(Value::Field(SignedField::positive(field.absolute_value()))), + Shared::new(Value::field(SignedField::positive(field.absolute_value()))), Shared::new(Value::Bool(field.is_negative())), ])) } ExprValue::Expression(ExpressionKind::Resolved(id)) => { if let HirExpression::Literal(HirLiteral::Integer(field)) = interner.expression(&id) { Some(Value::Tuple(vec![ - Shared::new(Value::Field(SignedField::positive(field.absolute_value()))), + Shared::new(Value::field(SignedField::positive(field.absolute_value()))), Shared::new(Value::Bool(field.is_negative())), ])) } else { @@ -3111,7 +3111,7 @@ fn modulus_be_bits(arguments: Vec<(Value, Location)>, location: Location) -> IRe check_argument_count(0, &arguments, location)?; let bits = FieldElement::modulus().to_radix_be(2); - let bits_vector = bits.into_iter().map(|bit| Value::U1(bit != 0)).collect(); + let bits_vector = bits.into_iter().map(|bit| Value::u1(bit != 0)).collect(); let int_type = Type::Integer(Signedness::Unsigned, IntegerBitSize::One); let typ = Type::Vector(Box::new(int_type)); @@ -3122,7 +3122,7 @@ fn modulus_be_bytes(arguments: Vec<(Value, Location)>, location: Location) -> IR check_argument_count(0, &arguments, location)?; let bytes = FieldElement::modulus().to_bytes_be(); - let bytes_vector = bytes.into_iter().map(Value::U8).collect(); + let bytes_vector = bytes.into_iter().map(Value::u8).collect(); let int_type = Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight); let typ = Type::Vector(Box::new(int_type)); @@ -3148,7 +3148,7 @@ fn modulus_le_bytes(arguments: Vec<(Value, Location)>, location: Location) -> IR fn modulus_num_bits(arguments: Vec<(Value, Location)>, location: Location) -> IResult { check_argument_count(0, &arguments, location)?; let bits = FieldElement::max_num_bits().into(); - Ok(Value::U64(bits)) + Ok(Value::u64(bits)) } // fn quoted_eq(_first: Quoted, _second: Quoted) -> bool @@ -3264,9 +3264,9 @@ fn derive_generators( let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); let mut embedded_curve_point_fields = HashMap::default(); embedded_curve_point_fields - .insert(x_field_name.clone(), Shared::new(Value::Field(SignedField::positive(x)))); + .insert(x_field_name.clone(), Shared::new(Value::field(SignedField::positive(x)))); embedded_curve_point_fields - .insert(y_field_name.clone(), Shared::new(Value::Field(SignedField::positive(y)))); + .insert(y_field_name.clone(), Shared::new(Value::field(SignedField::positive(y)))); embedded_curve_point_fields .insert(is_infinite_field_name.clone(), Shared::new(Value::Bool(is_infinite))); let embedded_curve_point_struct = diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index c11802a80b8..e8e6aa4cd3e 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -11,6 +11,7 @@ use noirc_errors::Location; use crate::Shared; use crate::ast::{BinaryOp, ItemVisibility, UnaryOp}; use crate::elaborator::Elaborator; +use crate::hir::comptime::Integer; use crate::hir::comptime::display::tokens_to_string; use crate::hir::comptime::value::unwrap_rc; use crate::hir::comptime::value::{FormatStringFragment, StructFields}; @@ -220,14 +221,14 @@ pub(crate) fn get_tuple((value, location): (Value, Location)) -> IResult IResult { match value { - Value::Field(value) => Ok(value), + Value::Integer(Integer::Field(value)) => Ok(value), value => type_mismatch(value, Type::FieldElement, location), } } pub(crate) fn get_u8((value, location): (Value, Location)) -> IResult { match value { - Value::U8(value) => Ok(value), + Value::Integer(Integer::U8(value)) => Ok(value), value => { let expected = Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight); type_mismatch(value, expected, location) @@ -237,7 +238,7 @@ pub(crate) fn get_u8((value, location): (Value, Location)) -> IResult { pub(crate) fn get_u32((value, location): (Value, Location)) -> IResult { match value { - Value::U32(value) => Ok(value), + Value::Integer(Integer::U32(value)) => Ok(value), value => { let expected = Type::u32(); type_mismatch(value, expected, location) @@ -247,7 +248,7 @@ pub(crate) fn get_u32((value, location): (Value, Location)) -> IResult { pub(crate) fn get_u64((value, location): (Value, Location)) -> IResult { match value { - Value::U64(value) => Ok(value), + Value::Integer(Integer::U64(value)) => Ok(value), value => { let expected = Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour); type_mismatch(value, expected, location) @@ -681,7 +682,7 @@ pub(super) fn hash_item( let mut hasher = std::collections::hash_map::DefaultHasher::new(); item.hash(&mut hasher); let hash = hasher.finish(); - Ok(Value::Field(SignedField::positive(u128::from(hash)))) + Ok(Value::field(SignedField::positive(u128::from(hash)))) } pub(super) fn eq_item( @@ -706,7 +707,7 @@ pub(crate) fn byte_array_type(len: usize) -> Type { /// Create a `Value::Array` from bytes. pub(crate) fn to_byte_array(values: &[u8]) -> Value { - Value::Array(values.iter().copied().map(Value::U8).collect(), byte_array_type(values.len())) + Value::Array(values.iter().copied().map(Value::u8).collect(), byte_array_type(values.len())) } /// Create a `Value::Struct` from fields and the expected return type. @@ -735,7 +736,7 @@ pub(crate) fn new_unary_op(operator: UnaryOp, typ: Type) -> Option { let mut fields = HashMap::default(); fields.insert( Rc::new("op".to_string()), - Shared::new(Value::Field(SignedField::positive(unary_op_value))), + Shared::new(Value::field(SignedField::positive(unary_op_value))), ); Some(Value::Struct(fields, typ)) @@ -748,7 +749,7 @@ pub(crate) fn new_binary_op(operator: BinaryOp, typ: Type) -> Value { let mut fields = HashMap::default(); fields.insert( Rc::new("op".to_string()), - Shared::new(Value::Field(SignedField::positive(binary_op_value))), + Shared::new(Value::field(SignedField::positive(binary_op_value))), ); Value::Struct(fields, typ) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/cast.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/cast.rs index 5589bd7deb6..7d92696ffb2 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/cast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/cast.rs @@ -89,25 +89,12 @@ fn perform_cast(kind: CastType, lhs: FieldElement) -> FieldElement { /// Convert the input value to a field. /// -/// Crucially, this is _not_ equivalent to a `SignedField` because negatives -/// of `U{N}` and `I{N}` types in the field are represented in two's complement -/// instead of their positive absolute values. -fn convert_to_field(value: Value, location: Location) -> IResult { +/// Negatives of `U{N}` and `I{N}` types in the field are represented in two's +/// complement instead of the corresponding field value. +fn convert_to_2s_complement_field(value: Value, location: Location) -> IResult { Ok(match value { - Value::Field(value) => value.to_field_element(), - Value::U1(value) => u128::from(value).into(), - Value::U8(value) => u128::from(value).into(), - Value::U16(value) => u128::from(value).into(), - Value::U32(value) => u128::from(value).into(), - Value::U64(value) => u128::from(value).into(), - Value::U128(value) => value.into(), - // `is_negative` is only used for conversions to Field in which case - // these should always be positive so that `-1 as i8 as Field == 255` - Value::I8(value) => FieldElement::from(i128::from(value as u8)), - Value::I16(value) => FieldElement::from(i128::from(value as u16)), - Value::I32(value) => FieldElement::from(i128::from(value as u32)), - Value::I64(value) => FieldElement::from(i128::from(value as u64)), - Value::Bool(value) => FieldElement::from(value), + Value::Integer(int) => int.as_field_twos_complement(), + Value::Bool(value) => value.into(), value => { let typ = value.get_type().into_owned(); return Err(InterpreterError::NonNumericCasted { typ, location }); @@ -122,35 +109,35 @@ pub(super) fn evaluate_cast_one_step( evaluated_lhs: Value, ) -> IResult { let lhs_type = evaluated_lhs.get_type().into_owned(); - let lhs = convert_to_field(evaluated_lhs, location)?; + let lhs = convert_to_2s_complement_field(evaluated_lhs, location)?; let cast_kind = classify_cast(&lhs_type, output_type); let lhs = perform_cast(cast_kind, lhs); // Now just wrap the Result in a Value match output_type.follow_bindings() { - Type::FieldElement => Ok(Value::Field(SignedField::new(lhs, false))), + Type::FieldElement => Ok(Value::field(SignedField::new(lhs, false))), typ @ Type::Integer(sign, bit_size) => match (sign, bit_size) { // These casts are expected to be no-ops - (Signedness::Unsigned, IntegerBitSize::One) => Ok(Value::U1(lhs.to_u128() != 0)), - (Signedness::Unsigned, IntegerBitSize::Eight) => Ok(Value::U8(lhs.to_u128() as u8)), - (Signedness::Unsigned, IntegerBitSize::Sixteen) => Ok(Value::U16(lhs.to_u128() as u16)), + (Signedness::Unsigned, IntegerBitSize::One) => Ok(Value::u1(lhs.to_u128() != 0)), + (Signedness::Unsigned, IntegerBitSize::Eight) => Ok(Value::u8(lhs.to_u128() as u8)), + (Signedness::Unsigned, IntegerBitSize::Sixteen) => Ok(Value::u16(lhs.to_u128() as u16)), (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { - Ok(Value::U32(lhs.to_u128() as u32)) + Ok(Value::u32(lhs.to_u128() as u32)) } (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { - Ok(Value::U64(lhs.to_u128() as u64)) + Ok(Value::u64(lhs.to_u128() as u64)) } (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => { - Ok(Value::U128(lhs.to_u128())) + Ok(Value::u128(lhs.to_u128())) } (Signedness::Signed, IntegerBitSize::One) => { Err(InterpreterError::TypeUnsupported { typ, location }) } - (Signedness::Signed, IntegerBitSize::Eight) => Ok(Value::I8(lhs.to_u128() as i8)), - (Signedness::Signed, IntegerBitSize::Sixteen) => Ok(Value::I16(lhs.to_u128() as i16)), - (Signedness::Signed, IntegerBitSize::ThirtyTwo) => Ok(Value::I32(lhs.to_u128() as i32)), - (Signedness::Signed, IntegerBitSize::SixtyFour) => Ok(Value::I64(lhs.to_u128() as i64)), + (Signedness::Signed, IntegerBitSize::Eight) => Ok(Value::i8(lhs.to_u128() as i8)), + (Signedness::Signed, IntegerBitSize::Sixteen) => Ok(Value::i16(lhs.to_u128() as i16)), + (Signedness::Signed, IntegerBitSize::ThirtyTwo) => Ok(Value::i32(lhs.to_u128() as i32)), + (Signedness::Signed, IntegerBitSize::SixtyFour) => Ok(Value::i64(lhs.to_u128() as i64)), (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => { Err(InterpreterError::TypeUnsupported { typ, location }) } @@ -174,24 +161,24 @@ mod tests { let typ = Type::FieldElement; let lhs_values = [ - Value::Field(SignedField::one()), + Value::field(SignedField::one()), Value::Bool(true), - Value::U1(true), - Value::U8(1), - Value::U16(1), - Value::U32(1), - Value::U64(1), - Value::U128(1), - Value::I8(1), - Value::I16(1), - Value::I32(1), - Value::I64(1), + Value::u1(true), + Value::u8(1), + Value::u16(1), + Value::u32(1), + Value::u64(1), + Value::u128(1), + Value::i8(1), + Value::i16(1), + Value::i32(1), + Value::i64(1), ]; for lhs in lhs_values { assert_eq!( evaluate_cast_one_step(&typ, location, lhs), - Ok(Value::Field(SignedField::one())) + Ok(Value::field(SignedField::one())) ); } } @@ -205,32 +192,28 @@ mod tests { use IntegerBitSize::*; let tests = [ // Widen - (Value::U8(255), unsigned(SixtyFour), Value::U64(255)), - (Value::U8(255), signed(SixtyFour), Value::I64(255)), - (Value::U64(u64::MAX), unsigned(HundredTwentyEight), Value::U128(u128::from(u64::MAX))), + (Value::u8(255), unsigned(SixtyFour), Value::u64(255)), + (Value::u8(255), signed(SixtyFour), Value::i64(255)), + (Value::u64(u64::MAX), unsigned(HundredTwentyEight), Value::u128(u128::from(u64::MAX))), // Reinterpret as negative - (Value::U8(255), signed(Eight), Value::I8(-1)), - (Value::Field(SignedField::positive(255u32)), signed(Eight), Value::I8(-1)), + (Value::u8(255), signed(Eight), Value::i8(-1)), + (Value::field(255u32.into()), signed(Eight), Value::i8(-1)), // Truncate - (Value::U16(300), unsigned(Eight), Value::U8(44)), - (Value::U16(300), signed(Eight), Value::I8(44)), - (Value::U16(255), signed(Eight), Value::I8(-1)), - (Value::Field(SignedField::positive(300u32)), unsigned(Eight), Value::U8(44)), - (Value::Field(SignedField::positive(300u32)), signed(Eight), Value::I8(44)), - (Value::Field(SignedField::positive(10u32)), unsigned(Sixteen), Value::U16(10)), - (Value::Field(SignedField::positive(256u32)), unsigned(Eight), Value::U8(0)), - (Value::Field(SignedField::positive(255u32)), unsigned(Eight), Value::U8(255)), - (Value::U128(u128::MAX), unsigned(SixtyFour), Value::U64(u64::MAX)), + (Value::u16(300), unsigned(Eight), Value::u8(44)), + (Value::u16(300), signed(Eight), Value::i8(44)), + (Value::u16(255), signed(Eight), Value::i8(-1)), + (Value::field(300u32.into()), unsigned(Eight), Value::u8(44)), + (Value::field(300u32.into()), signed(Eight), Value::i8(44)), + (Value::field(10u32.into()), unsigned(Sixteen), Value::u16(10)), + (Value::field(256u32.into()), unsigned(Eight), Value::u8(0)), + (Value::field(255u32.into()), unsigned(Eight), Value::u8(255)), + (Value::u128(u128::MAX), unsigned(SixtyFour), Value::u64(u64::MAX)), // Casting Field -> Field should be a no-op + (Value::field(4u32.into()), Type::FieldElement, Value::field(4u32.into())), ( - Value::Field(SignedField::positive(4u32)), + Value::field(SignedField::negative(4u32)), Type::FieldElement, - Value::Field(SignedField::positive(4u32)), - ), - ( - Value::Field(SignedField::negative(4u32)), - Type::FieldElement, - Value::Field(SignedField::negative(4u32)), + Value::field(SignedField::negative(4u32)), ), ]; @@ -253,30 +236,30 @@ mod tests { use IntegerBitSize::*; let tests = [ // Widen - (Value::I8(127), unsigned(SixtyFour), Value::U64(127)), - (Value::I8(127), signed(SixtyFour), Value::I64(127)), + (Value::i8(127), unsigned(SixtyFour), Value::u64(127)), + (Value::i8(127), signed(SixtyFour), Value::i64(127)), // Widen signed->unsigned: sign extend - (Value::I8(-1), unsigned(Sixteen), Value::U16(65535)), - (Value::I8(-100), unsigned(Sixteen), Value::U16(65436)), + (Value::i8(-1), unsigned(Sixteen), Value::u16(65535)), + (Value::i8(-100), unsigned(Sixteen), Value::u16(65436)), // Casting a negative integer to a field always results in a positive value // This is the only case we zero-extend signed integers instead of sign-extending them - (Value::I8(-1), Type::FieldElement, Value::Field(SignedField::positive(255u32))), + (Value::i8(-1), Type::FieldElement, Value::field(SignedField::positive(255u32))), // Widen negative: sign extend - (Value::I8(-1), signed(Sixteen), Value::I16(-1)), - (Value::I8(-100), signed(Sixteen), Value::I16(-100)), + (Value::i8(-1), signed(Sixteen), Value::i16(-1)), + (Value::i8(-100), signed(Sixteen), Value::i16(-100)), // Reinterpret as positive - (Value::I8(-100), unsigned(Eight), Value::U8(156)), + (Value::i8(-100), unsigned(Eight), Value::u8(156)), // Truncate - (Value::I16(300), unsigned(Eight), Value::U8(44)), - (Value::I16(300), signed(Eight), Value::I8(44)), - (Value::I16(255), signed(Eight), Value::I8(-1)), - (Value::I16(i16::MIN + 5), signed(Eight), Value::I8(5)), - (Value::I16(i16::MIN + 5), unsigned(Eight), Value::U8(5)), - (Value::Field(SignedField::negative(1u32)), unsigned(Eight), Value::U8(0)), - (Value::Field(SignedField::negative(1u32)), signed(Eight), Value::I8(0)), - (Value::Field(SignedField::negative(2u32)), unsigned(Sixteen), Value::U16(65535)), - (Value::Field(SignedField::negative(2u32)), signed(Sixteen), Value::I16(-1)), - (Value::Field(SignedField::positive(u128::MAX)), signed(Eight), Value::I8(-1)), + (Value::i16(300), unsigned(Eight), Value::u8(44)), + (Value::i16(300), signed(Eight), Value::i8(44)), + (Value::i16(255), signed(Eight), Value::i8(-1)), + (Value::i16(i16::MIN + 5), signed(Eight), Value::i8(5)), + (Value::i16(i16::MIN + 5), unsigned(Eight), Value::u8(5)), + (Value::field(SignedField::negative(1u32)), unsigned(Eight), Value::u8(0)), + (Value::field(SignedField::negative(1u32)), signed(Eight), Value::i8(0)), + (Value::field(SignedField::negative(2u32)), unsigned(Sixteen), Value::u16(65535)), + (Value::field(SignedField::negative(2u32)), signed(Sixteen), Value::i16(-1)), + (Value::field(SignedField::positive(u128::MAX)), signed(Eight), Value::i8(-1)), ]; for (lhs, typ, expected) in tests { @@ -292,7 +275,7 @@ mod tests { #[test] fn bool_cast() { let location = Location::dummy(); - let lhs = Value::Field(SignedField::positive(0u32)); + let lhs = Value::field(SignedField::positive(0u32)); let actual = evaluate_cast_one_step(&Type::Bool, location, lhs); assert!(matches!(actual, Err(InterpreterError::CannotCastNumericToBool { .. }))); } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index 543417404d9..367a207ea31 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -240,7 +240,7 @@ fn poseidon2_permutation(arguments: Vec<(Value, Location)>, location: Location) .poseidon2_permutation(&input) .map_err(|error| InterpreterError::BlackBoxError(error, location))?; - let array = fields.into_iter().map(|f| Value::Field(SignedField::positive(f))).collect(); + let array = fields.into_iter().map(|f| Value::field(SignedField::positive(f))).collect(); Ok(Value::Array(array, typ)) } @@ -253,7 +253,7 @@ fn keccakf1600(arguments: Vec<(Value, Location)>, location: Location) -> IResult let result_lanes = acvm::blackbox_solver::keccakf1600(state) .map_err(|error| InterpreterError::BlackBoxError(error, location))?; - let array: Vector = result_lanes.into_iter().map(Value::U64).collect(); + let array: Vector = result_lanes.into_iter().map(Value::u64).collect(); Ok(Value::Array(array, typ)) } @@ -266,7 +266,7 @@ fn sha256_compression(arguments: Vec<(Value, Location)>, location: Location) -> acvm::blackbox_solver::sha256_compression(&mut state, &input); - let state = state.into_iter().map(Value::U32).collect(); + let state = state.into_iter().map(Value::u32).collect(); Ok(Value::Array(state, typ)) } @@ -303,8 +303,8 @@ fn to_embedded_curve_point( ) -> Value { to_struct( [ - ("x", Value::Field(SignedField::positive(x))), - ("y", Value::Field(SignedField::positive(y))), + ("x", Value::field(SignedField::positive(x))), + ("y", Value::field(SignedField::positive(y))), ("is_infinite", Value::Bool(is_infinite)), ], typ, diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/infix.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/infix.rs index 55a014440f3..25d7055c630 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/infix.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/infix.rs @@ -1,11 +1,11 @@ use acvm::AcirField as _; +use super::{IResult, InterpreterError, Value}; use crate::ast::BinaryOpKind; use crate::hir::Location; +use crate::hir::comptime::Integer; use crate::hir_def::expr::HirBinaryOp; -use super::{IResult, InterpreterError, Value}; - pub(super) fn evaluate_infix( lhs_value: Value, rhs_value: Value, @@ -39,15 +39,21 @@ pub(super) fn evaluate_infix( $( ($lhs_var:ident, $rhs_var:ident) to $res_var:ident => $expr:expr ),* + , $(Bool case to $res_var2:ident => $expr2:expr)? $(,)? } ) => { match ($lhs_value, $rhs_value) { $( - (Value::$lhs_var($lhs), Value::$rhs_var($rhs)) => { + (Value::Integer(Integer::$lhs_var($lhs)), Value::Integer(Integer::$rhs_var($rhs))) => { Ok(Value::$res_var(($expr).ok_or(math_error($op))?)) }, )* + $( + (Value::Bool($lhs), Value::Bool($rhs)) => { + Ok(Value::$res_var2(($expr2).ok_or(math_error($op))?)) + }, + )? (_, _) => { Err(error($op)) }, @@ -60,17 +66,17 @@ pub(super) fn evaluate_infix( (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) { field: $field_expr:expr, int: $int_expr:expr, u1: $u1_expr:expr, }) => { match_values! { ($lhs_value as $lhs $op $rhs_value as $rhs) { - (Field, Field) to Field => Some($field_expr), - (I8, I8) to I8 => $int_expr, - (I16, I16) to I16 => $int_expr, - (I32, I32) to I32 => $int_expr, - (I64, I64) to I64 => $int_expr, - (U1, U1) to U1 => $u1_expr, - (U8, U8) to U8 => $int_expr, - (U16, U16) to U16 => $int_expr, - (U32, U32) to U32 => $int_expr, - (U64, U64) to U64 => $int_expr, - (U128, U128) to U128 => $int_expr, + (Field, Field) to field => Some($field_expr), + (I8, I8) to i8 => $int_expr, + (I16, I16) to i16 => $int_expr, + (I32, I32) to i32 => $int_expr, + (I64, I64) to i64 => $int_expr, + (U1, U1) to u1 => $u1_expr, + (U8, U8) to u8 => $int_expr, + (U16, U16) to u16 => $int_expr, + (U32, U32) to u32 => $int_expr, + (U64, U64) to u64 => $int_expr, + (U128, U128) to u128 => $int_expr, } } }; @@ -82,7 +88,6 @@ pub(super) fn evaluate_infix( match_values! { ($lhs_value as $lhs $op $rhs_value as $rhs) { (Field, Field) to Bool => Some($expr), - (Bool, Bool) to Bool => Some($expr), (I8, I8) to Bool => Some($expr), (I16, I16) to Bool => Some($expr), (I32, I32) to Bool => Some($expr), @@ -93,6 +98,7 @@ pub(super) fn evaluate_infix( (U32, U32) to Bool => Some($expr), (U64, U64) to Bool => Some($expr), (U128, U128) to Bool => Some($expr), + Bool case to Bool => Some($expr), } } }; @@ -103,17 +109,17 @@ pub(super) fn evaluate_infix( (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) => $expr:expr) => { match_values! { ($lhs_value as $lhs $op $rhs_value as $rhs) { - (Bool, Bool) to Bool => Some($expr), - (I8, I8) to I8 => Some($expr), - (I16, I16) to I16 => Some($expr), - (I32, I32) to I32 => Some($expr), - (I64, I64) to I64 => Some($expr), - (U1, U1) to U1 => Some($expr), - (U8, U8) to U8 => Some($expr), - (U16, U16) to U16 => Some($expr), - (U32, U32) to U32 => Some($expr), - (U64, U64) to U64 => Some($expr), - (U128, U128) to U128 => Some($expr), + (I8, I8) to i8 => Some($expr), + (I16, I16) to i16 => Some($expr), + (I32, I32) to i32 => Some($expr), + (I64, I64) to i64 => Some($expr), + (U1, U1) to u1 => Some($expr), + (U8, U8) to u8 => Some($expr), + (U16, U16) to u16 => Some($expr), + (U32, U32) to u32 => Some($expr), + (U64, U64) to u64 => Some($expr), + (U128, U128) to u128 => Some($expr), + Bool case to Bool => Some($expr), } } }; @@ -124,16 +130,16 @@ pub(super) fn evaluate_infix( (($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) { int: $int_expr:expr, u1: $u1_expr:expr, }) => { match_values! { ($lhs_value as $lhs $op $rhs_value as $rhs) { - (I8, I8) to I8 => $int_expr, - (I16, I16) to I16 => $int_expr, - (I32, I32) to I32 => $int_expr, - (I64, I64) to I64 => $int_expr, - (U1, U1) to U1 => $u1_expr, - (U8, U8) to U8 => $int_expr, - (U16, U16) to U16 => $int_expr, - (U32, U32) to U32 => $int_expr, - (U64, U64) to U64 => $int_expr, - (U128, U128) to U128 => $int_expr, + (I8, I8) to i8 => $int_expr, + (I16, I16) to i16 => $int_expr, + (I32, I32) to i32 => $int_expr, + (I64, I64) to i64 => $int_expr, + (U1, U1) to u1 => $u1_expr, + (U8, U8) to u8 => $int_expr, + (U16, U16) to u16 => $int_expr, + (U32, U32) to u32 => $int_expr, + (U64, U64) to u64 => $int_expr, + (U128, U128) to u128 => $int_expr, } } }; @@ -259,19 +265,19 @@ mod tests { #[test] /// See: https://github.com/noir-lang/noir/issues/8391 fn regression_8391() { - let lhs = Value::U128(340282366920938463463374607431768211455); - let rhs = Value::U128(2); + let lhs = Value::u128(340282366920938463463374607431768211455); + let rhs = Value::u128(2); let operator = HirBinaryOp { kind: BinaryOpKind::Divide, location: Location::dummy() }; let location = Location::dummy(); let result = evaluate_infix(lhs, rhs, operator, location).unwrap(); - assert_eq!(result, Value::U128(170141183460469231731687303715884105727)); + assert_eq!(result, Value::u128(170141183460469231731687303715884105727)); } #[test] fn regression_9336() { - let lhs = Value::I8(-128); - let rhs = Value::I8(-1); + let lhs = Value::i8(-128); + let rhs = Value::i8(-1); let operator = HirBinaryOp { kind: BinaryOpKind::Modulo, location: Location::dummy() }; let location = Location::dummy(); let err = evaluate_infix(lhs, rhs, operator, location).unwrap_err(); @@ -286,7 +292,7 @@ mod tests { } "#; let result = interpret(src); - assert_eq!(result, Value::U64(48)); + assert_eq!(result, Value::u64(48)); } #[test] @@ -297,7 +303,7 @@ mod tests { } "#; let result = interpret(src); - assert_eq!(result, Value::I64(16)); + assert_eq!(result, Value::i64(16)); } #[test] @@ -338,7 +344,7 @@ mod tests { } "#; let result = interpret(src); - assert_eq!(result, Value::U64(32)); + assert_eq!(result, Value::u64(32)); } #[test] @@ -349,7 +355,7 @@ mod tests { } "#; let result = interpret(src); - assert_eq!(result, Value::U64(0)); + assert_eq!(result, Value::u64(0)); let src = r#" comptime fn main() -> pub u64 { @@ -376,7 +382,7 @@ mod tests { } "; let result = interpret(src); - assert_eq!(result, Value::I64(-1)); + assert_eq!(result, Value::i64(-1)); let src = " comptime fn main() -> pub i64 { @@ -403,7 +409,7 @@ mod tests { } "; let result = interpret(src); - assert_eq!(result, Value::I64(0)); + assert_eq!(result, Value::i64(0)); let src = " comptime fn main() -> pub i64 { @@ -430,7 +436,7 @@ mod tests { } "#; let result = interpret(src); - assert_eq!(result, Value::I64(-32)); + assert_eq!(result, Value::i64(-32)); } #[test] diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs index 509e7b978a6..6f8d365b744 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -13,11 +13,13 @@ mod display; mod errors; mod hir_to_display_ast; +mod integer; mod interpreter; mod tests; mod value; pub use display::{tokens_to_string, tokens_to_string_with_indent}; pub use errors::{ComptimeError, InterpreterError}; +pub use integer::Integer; pub use interpreter::Interpreter; pub use value::{FormatStringFragment, Value}; diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs index f3ef5c1da8f..fbaafc95b0c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -104,14 +104,14 @@ pub(super) fn interpret_expect_error(src: &str) -> InterpreterError { fn interpreter_works() { let program = "comptime fn main() -> pub Field { 3 }"; let result = interpret(program); - assert_eq!(result, Value::Field(SignedField::positive(3u128))); + assert_eq!(result, Value::field(SignedField::positive(3u128))); } #[test] fn interpreter_type_checking_works() { let program = "comptime fn main() -> pub u8 { 3 }"; let result = interpret(program); - assert_eq!(result, Value::U8(3u8)); + assert_eq!(result, Value::u8(3u8)); } #[test] @@ -121,7 +121,7 @@ fn let_statement_works() { x }"; let result = interpret(program); - assert_eq!(result, Value::I8(4)); + assert_eq!(result, Value::i8(4)); } #[test] @@ -132,7 +132,7 @@ fn mutation_works() { x }"; let result = interpret(program); - assert_eq!(result, Value::I8(4)); + assert_eq!(result, Value::i8(4)); } #[test] @@ -143,7 +143,7 @@ fn mutating_references() { *x }"; let result = interpret(program); - assert_eq!(result, Value::I32(4)); + assert_eq!(result, Value::i32(4)); } #[test] @@ -154,7 +154,7 @@ fn mutating_mutable_references() { *x }"; let result = interpret(program); - assert_eq!(result, Value::I64(4)); + assert_eq!(result, Value::i64(4)); } #[test] @@ -166,7 +166,7 @@ fn mutation_leaks() { x }"; let result = interpret(program); - assert_eq!(result, Value::I8(5)); + assert_eq!(result, Value::i8(5)); } #[test] @@ -177,7 +177,7 @@ fn mutating_arrays() { a1[1] }"; let result = interpret(program); - assert_eq!(result, Value::U8(22)); + assert_eq!(result, Value::u8(22)); } #[test] @@ -191,7 +191,7 @@ fn mutate_in_new_scope() { x }"; let result = interpret(program); - assert_eq!(result, Value::U8(2)); + assert_eq!(result, Value::u8(2)); } #[test] @@ -204,7 +204,7 @@ fn for_loop() { x }"; let result = interpret(program); - assert_eq!(result, Value::U8(15)); + assert_eq!(result, Value::u8(15)); } #[test] @@ -217,7 +217,7 @@ fn for_loop_inclusive() { x }"; let result = interpret(program); - assert_eq!(result, Value::U8(21)); + assert_eq!(result, Value::u8(21)); } #[test] @@ -230,7 +230,7 @@ fn for_loop_u16() { x }"; let result = interpret(program); - assert_eq!(result, Value::U16(15)); + assert_eq!(result, Value::u16(15)); } #[test] @@ -246,7 +246,7 @@ fn for_loop_with_break() { x }"; let result = interpret(program); - assert_eq!(result, Value::U32(6)); + assert_eq!(result, Value::u32(6)); } #[test] @@ -262,7 +262,7 @@ fn for_loop_with_continue() { x }"; let result = interpret(program); - assert_eq!(result, Value::U64(11)); + assert_eq!(result, Value::u64(11)); } #[test] @@ -290,7 +290,7 @@ fn lambda() { f(1) }"; let result = interpret(program); - assert!(matches!(result, Value::U8(2))); + assert_eq!(result, Value::u8(2)); } #[test] @@ -308,7 +308,7 @@ fn non_deterministic_recursion() { } }"; let result = interpret(program); - assert_eq!(result, Value::U64(55)); + assert_eq!(result, Value::u64(55)); } #[test] @@ -323,7 +323,7 @@ fn generic_functions() { } "; let result = interpret(program); - assert_eq!(result, Value::U8(2)); + assert_eq!(result, Value::u8(2)); } #[test] diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index bdb8429d522..c48deeffa05 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -11,13 +11,14 @@ use crate::{ Kind, QuotedType, Shared, Type, TypeBindings, TypeVariable, ast::{ ArrayLiteral, BlockExpression, CallExpression, ConstructorExpression, Expression, - ExpressionKind, Ident, IntegerBitSize, LValue, LetStatement, Literal, Path, PathKind, - PathSegment, Pattern, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, + ExpressionKind, Ident, LValue, LetStatement, Path, PathKind, PathSegment, Pattern, + Statement, StatementKind, UnresolvedType, UnresolvedTypeData, }, elaborator::Elaborator, hir::{ - comptime::interpreter::builtin_helpers::fragments_to_string, - def_collector::dc_crate::CompilationError, def_map::ModuleId, + comptime::{Integer, interpreter::builtin_helpers::fragments_to_string}, + def_collector::dc_crate::CompilationError, + def_map::ModuleId, type_check::generics::TraitGenerics, }, hir_def::expr::{ @@ -26,9 +27,8 @@ use crate::{ }, node_interner::{ExprId, FuncId, NodeInterner, StmtId, TraitId, TraitImplId, TypeId}, parser::{Item, Parser}, - shared::Signedness, signed_field::SignedField, - token::{FmtStrFragment, IntegerTypeSuffix, LocatedToken, Token, Tokens}, + token::{FmtStrFragment, LocatedToken, Token, Tokens}, }; use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashSet as HashSet; @@ -43,17 +43,7 @@ use super::{ pub enum Value { Unit, Bool(bool), - Field(SignedField), - I8(i8), - I16(i16), - I32(i32), - I64(i64), - U1(bool), - U8(u8), - U16(u16), - U32(u32), - U64(u64), - U128(u128), + Integer(Integer), String(Rc), FormatString(Rc>, Type, u32 /* length */), CtString(Rc), @@ -125,7 +115,33 @@ pub enum TypedExpr { StmtId(StmtId), } +macro_rules! int_constructor { + ($name: ident, $capitalized: ident) => { + pub fn $name(x: $name) -> Self { + Value::Integer(Integer::$capitalized(x)) + } + }; +} + impl Value { + pub fn field(x: SignedField) -> Self { + Value::Integer(Integer::Field(x)) + } + + pub fn u1(x: bool) -> Self { + Value::Integer(Integer::U1(x)) + } + + int_constructor!(u8, U8); + int_constructor!(u16, U16); + int_constructor!(u32, U32); + int_constructor!(u64, U64); + int_constructor!(u128, U128); + int_constructor!(i8, I8); + int_constructor!(i16, I16); + int_constructor!(i32, I32); + int_constructor!(i64, I64); + pub(crate) fn expression(expr: ExpressionKind) -> Self { Value::Expr(Box::new(ExprValue::Expression(expr))) } @@ -148,19 +164,7 @@ impl Value { Cow::Owned(match self { Value::Unit => Type::Unit, Value::Bool(_) => Type::Bool, - Value::Field(_) => Type::FieldElement, - Value::I8(_) => Type::Integer(Signedness::Signed, IntegerBitSize::Eight), - Value::I16(_) => Type::Integer(Signedness::Signed, IntegerBitSize::Sixteen), - Value::I32(_) => Type::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo), - Value::I64(_) => Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour), - Value::U1(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::One), - Value::U8(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), - Value::U16(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen), - Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), - Value::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), - Value::U128(_) => { - Type::Integer(Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) - } + Value::Integer(value) => value.get_type(), Value::String(value) => { let length: u32 = value .len() @@ -212,53 +216,12 @@ impl Value { elaborator: &mut Elaborator, location: Location, ) -> IResult { + use crate::ast::Literal::*; let kind = match self { - Value::Unit => ExpressionKind::Literal(Literal::Unit), - Value::Bool(value) => ExpressionKind::Literal(Literal::Bool(value)), - Value::Field(value) => { - ExpressionKind::Literal(Literal::Integer(value, Some(IntegerTypeSuffix::Field))) - } - Value::I8(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::from_signed(value), - Some(IntegerTypeSuffix::I8), - )), - Value::I16(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::from_signed(value), - Some(IntegerTypeSuffix::I16), - )), - Value::I32(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::from_signed(value), - Some(IntegerTypeSuffix::I32), - )), - Value::I64(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::from_signed(value), - Some(IntegerTypeSuffix::I64), - )), - Value::U1(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::positive(value), - Some(IntegerTypeSuffix::U1), - )), - Value::U8(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::positive(u128::from(value)), - Some(IntegerTypeSuffix::U8), - )), - Value::U16(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::positive(u128::from(value)), - Some(IntegerTypeSuffix::U16), - )), - Value::U32(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::positive(value), - Some(IntegerTypeSuffix::U32), - )), - Value::U64(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::positive(value), - Some(IntegerTypeSuffix::U64), - )), - Value::U128(value) => ExpressionKind::Literal(Literal::Integer( - SignedField::positive(value), - Some(IntegerTypeSuffix::U128), - )), - Value::String(value) => ExpressionKind::Literal(Literal::Str(unwrap_rc(value))), + Value::Unit => ExpressionKind::Literal(Unit), + Value::Bool(value) => ExpressionKind::Literal(Bool(value)), + Value::Integer(value) => value.into_expression_kind(), + Value::String(value) => ExpressionKind::Literal(Str(unwrap_rc(value))), Value::CtString(value) => { // Lower to `std::meta::AsCtString::as_ctstring(contents)` let ident = |name: &str| Ident::new(name.to_string(), location); @@ -286,8 +249,8 @@ impl Value { segment("AsCtString"), segment("as_ctstring"), ]); - let contents = Literal::Str(unwrap_rc(value)); - let contents = Expression { kind: ExpressionKind::Literal(contents), location }; + let kind = ExpressionKind::Literal(Str(unwrap_rc(value))); + let contents = Expression { kind, location }; call(as_ctstring, vec![contents]) } Value::FormatString(fragments, _, length) => { @@ -337,7 +300,7 @@ impl Value { }; new_fragments.push(new_fragment); } - let fmtstr = ExpressionKind::Literal(Literal::FmtStr(new_fragments, length)); + let fmtstr = ExpressionKind::Literal(FmtStr(new_fragments, length)); if has_values { statements.push(Statement { kind: StatementKind::Expression(Expression { kind: fmtstr, location }), @@ -392,12 +355,12 @@ impl Value { Value::Array(elements, _) => { let elements = try_vecmap(elements, |element| element.into_expression(elaborator, location))?; - ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(elements))) + ExpressionKind::Literal(Array(ArrayLiteral::Standard(elements))) } Value::Vector(elements, _) => { let elements = try_vecmap(elements, |element| element.into_expression(elaborator, location))?; - ExpressionKind::Literal(Literal::Vector(ArrayLiteral::Standard(elements))) + ExpressionKind::Literal(Vector(ArrayLiteral::Standard(elements))) } Value::Quoted(tokens) => { // Wrap the tokens in '{' and '}' so that we can parse statements as well. @@ -487,37 +450,7 @@ impl Value { let expression = match self { Value::Unit => HirExpression::Literal(HirLiteral::Unit), Value::Bool(value) => HirExpression::Literal(HirLiteral::Bool(value)), - Value::Field(value) => HirExpression::Literal(HirLiteral::Integer(value)), - Value::I8(value) => { - HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) - } - Value::I16(value) => { - HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) - } - Value::I32(value) => { - HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) - } - Value::I64(value) => { - HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) - } - Value::U1(value) => { - HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) - } - Value::U8(value) => HirExpression::Literal(HirLiteral::Integer(SignedField::positive( - u128::from(value), - ))), - Value::U16(value) => HirExpression::Literal(HirLiteral::Integer( - SignedField::positive(u128::from(value)), - )), - Value::U32(value) => { - HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) - } - Value::U64(value) => { - HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) - } - Value::U128(value) => { - HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) - } + Value::Integer(int) => int.into_hir_expression(), Value::String(value) => HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))), Value::FormatString(fragments, _typ, length) => { let mut captures = Vec::new(); @@ -640,6 +573,8 @@ impl Value { Value::Unit => { vec![Token::LeftParen, Token::RightParen] } + Value::Bool(bool) => vec![Token::Bool(bool)], + Value::Integer(value) => value.into_tokens(), Value::Quoted(tokens) => return Ok(unwrap_rc(tokens)), Value::Type(typ) => vec![Token::QuotedType(interner.push_quoted_type(typ))], Value::Expr(expr) => match *expr { @@ -668,84 +603,6 @@ impl Value { vec![Token::QuotedType(interner.push_quoted_type(typ))] } Value::TypedExpr(TypedExpr::ExprId(expr_id)) => vec![Token::UnquoteMarker(expr_id)], - Value::Bool(bool) => vec![Token::Bool(bool)], - Value::U1(bool) => { - vec![Token::Int(u128::from(bool).into(), Some(IntegerTypeSuffix::U1))] - } - Value::U8(value) => { - vec![Token::Int(u128::from(value).into(), Some(IntegerTypeSuffix::U8))] - } - Value::U16(value) => { - vec![Token::Int(u128::from(value).into(), Some(IntegerTypeSuffix::U16))] - } - Value::U32(value) => { - vec![Token::Int(u128::from(value).into(), Some(IntegerTypeSuffix::U32))] - } - Value::U64(value) => { - vec![Token::Int(u128::from(value).into(), Some(IntegerTypeSuffix::U64))] - } - Value::U128(value) => { - vec![Token::Int(value.into(), Some(IntegerTypeSuffix::U128))] - } - Value::I8(value) => { - if value < 0 { - vec![ - Token::Minus, - Token::Int( - u128::from(value.unsigned_abs()).into(), - Some(IntegerTypeSuffix::I8), - ), - ] - } else { - vec![Token::Int((value as u128).into(), Some(IntegerTypeSuffix::I8))] - } - } - Value::I16(value) => { - if value < 0 { - vec![ - Token::Minus, - Token::Int( - u128::from(value.unsigned_abs()).into(), - Some(IntegerTypeSuffix::I16), - ), - ] - } else { - vec![Token::Int((value as u128).into(), Some(IntegerTypeSuffix::I16))] - } - } - Value::I32(value) => { - if value < 0 { - vec![ - Token::Minus, - Token::Int( - u128::from(value.unsigned_abs()).into(), - Some(IntegerTypeSuffix::I32), - ), - ] - } else { - vec![Token::Int((value as u128).into(), Some(IntegerTypeSuffix::I32))] - } - } - Value::I64(value) => { - if value < 0 { - vec![ - Token::Minus, - Token::Int( - u128::from(value.unsigned_abs()).into(), - Some(IntegerTypeSuffix::I64), - ), - ] - } else { - vec![Token::Int((value as u128).into(), Some(IntegerTypeSuffix::I64))] - } - } - Value::Field(value) => { - if value.is_negative() { - vec![Token::Minus, Token::Int(value.absolute_value(), None)] - } else { - vec![Token::Int(value.absolute_value(), None)] - } - } Value::String(value) | Value::CtString(value) => { vec![Token::Str(unwrap_rc(value))] } @@ -764,37 +621,13 @@ impl Value { /// Returns false for non-integral `Value`s. pub(crate) fn is_integral(&self) -> bool { - use Value::*; - matches!( - self, - Field(_) - | I8(_) - | I16(_) - | I32(_) - | I64(_) - | U1(_) - | U8(_) - | U16(_) - | U32(_) - | U64(_) - | U128(_) - ) + matches!(self, Value::Integer(_)) } pub(crate) fn is_zero(&self) -> bool { use Value::*; match self { - Field(value) => value.is_zero(), - I8(value) => *value == 0, - I16(value) => *value == 0, - I32(value) => *value == 0, - I64(value) => *value == 0, - U1(value) => !*value, - U8(value) => *value == 0, - U16(value) => *value == 0, - U32(value) => *value == 0, - U64(value) => *value == 0, - U128(value) => *value == 0, + Integer(value) => value.is_zero(), _ => false, } } @@ -821,17 +654,7 @@ impl Value { Value::Pointer(shared, _, _) => shared.borrow().contains_function_or_closure(), Value::Unit | Value::Bool(_) - | Value::Field(_) - | Value::I8(_) - | Value::I16(_) - | Value::I32(_) - | Value::I64(_) - | Value::U1(_) - | Value::U8(_) - | Value::U16(_) - | Value::U32(_) - | Value::U64(_) - | Value::U128(_) + | Value::Integer(_) | Value::String(_) | Value::FormatString(_, _, _) | Value::CtString(_) @@ -850,28 +673,20 @@ impl Value { } } - /// Converts any integral `Value` into a `SignedField`. + /// Converts any non-negative integer `Value` into a `SignedField`. /// Returns `None` for non-integral `Value`s and negative numbers. - pub(crate) fn to_non_negative_signed_field(&self) -> Option { - let value = self.to_signed_field()?; - value.is_positive().then_some(value) + pub(crate) fn as_non_negative_signed_field(&self) -> Option { + match self { + Value::Integer(int) => int.as_non_negative_field().map(SignedField::positive), + _ => None, + } } /// Converts any integral `Value` into a `SignedField`. - /// Returns `None` for non-integral `Value`s - pub(crate) fn to_signed_field(&self) -> Option { + #[cfg(test)] + pub(crate) fn as_signed_field(&self) -> Option { match self { - Value::Field(value) => Some(value.into()), - Value::I8(value) => Some(value.into()), - Value::I16(value) => Some(value.into()), - Value::I32(value) => Some(value.into()), - Value::I64(value) => Some(value.into()), - Value::U1(value) => Some(value.into()), - Value::U8(value) => Some(value.into()), - Value::U16(value) => Some(value.into()), - Value::U32(value) => Some(value.into()), - Value::U64(value) => Some(value.into()), - Value::U128(value) => Some(value.into()), + Value::Integer(int) => Some(int.as_signed_field()), _ => None, } } @@ -899,13 +714,12 @@ impl Value { } } + /// True if this value is negative. + /// Defaults to false if this value is not negative or is not an integer. pub fn is_negative(&self) -> bool { match self { - Value::I8(x) => *x < 0, - Value::I16(x) => *x < 0, - Value::I32(x) => *x < 0, - Value::I64(x) => *x < 0, - _ => false, // Unsigned or Field types are never negative + Value::Integer(int) => int.is_negative(), + _ => false, } } diff --git a/compiler/noirc_frontend/src/hir/printer/mod.rs b/compiler/noirc_frontend/src/hir/printer/mod.rs index c2502a4bc99..ea0c788183d 100644 --- a/compiler/noirc_frontend/src/hir/printer/mod.rs +++ b/compiler/noirc_frontend/src/hir/printer/mod.rs @@ -870,17 +870,7 @@ impl<'context, 'string> ItemPrinter<'context, 'string> { match value { Value::Unit => self.push_str("()"), Value::Bool(bool) => self.push_str(&bool.to_string()), - Value::Field(value) => self.push_str(&value.to_string()), - Value::I8(value) => self.push_str(&value.to_string()), - Value::I16(value) => self.push_str(&value.to_string()), - Value::I32(value) => self.push_str(&value.to_string()), - Value::I64(value) => self.push_str(&value.to_string()), - Value::U1(value) => self.push_str(&value.to_string()), - Value::U8(value) => self.push_str(&value.to_string()), - Value::U16(value) => self.push_str(&value.to_string()), - Value::U32(value) => self.push_str(&value.to_string()), - Value::U64(value) => self.push_str(&value.to_string()), - Value::U128(value) => self.push_str(&value.to_string()), + Value::Integer(int) => self.push_str(&int.to_string()), Value::String(string) => self.push_str(&format!("{string:?}")), Value::FormatString(fragments, _typ, _) => { let has_values = fragments diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index ae856329ebd..acb778faa68 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -744,7 +744,7 @@ mod proptests { fn numeric_value_to_type(value: Value) -> Type { let kind_type = value.get_type(); let kind = Kind::numeric(kind_type.into_owned()); - let value = value.to_signed_field().expect("ICE: numeric_value_to_type: expected a "); + let value = value.as_signed_field().expect("ICE: numeric_value_to_type: expected a field"); Type::Constant(value, kind) } diff --git a/tooling/lsp/src/requests/hover/from_reference.rs b/tooling/lsp/src/requests/hover/from_reference.rs index 60870e7c470..0b64f45e7fe 100644 --- a/tooling/lsp/src/requests/hover/from_reference.rs +++ b/tooling/lsp/src/requests/hover/from_reference.rs @@ -1006,17 +1006,7 @@ fn append_value_to_string(value: &Value, string: &mut String) -> Option<()> { match value { Value::Unit => string.push_str("()"), Value::Bool(value) => string.push_str(&value.to_string()), - Value::Field(field_element) => string.push_str(&field_element.to_string()), - Value::I8(value) => string.push_str(&value.to_string()), - Value::I16(value) => string.push_str(&value.to_string()), - Value::I32(value) => string.push_str(&value.to_string()), - Value::I64(value) => string.push_str(&value.to_string()), - Value::U1(value) => string.push_str(&value.to_string()), - Value::U8(value) => string.push_str(&value.to_string()), - Value::U16(value) => string.push_str(&value.to_string()), - Value::U32(value) => string.push_str(&value.to_string()), - Value::U64(value) => string.push_str(&value.to_string()), - Value::U128(value) => string.push_str(&value.to_string()), + Value::Integer(value) => string.push_str(&value.to_string()), Value::String(value) | Value::CtString(value) => string.push_str(&value.to_string()), Value::Tuple(values) => { let len = values.iter().len(); diff --git a/tooling/nargo_cli/src/cli/execute_cmd/interpret.rs b/tooling/nargo_cli/src/cli/execute_cmd/interpret.rs index e93867dbf3e..b2228aa9237 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd/interpret.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd/interpret.rs @@ -167,41 +167,41 @@ fn input_value_to_comptime_value(input: &InputValue, typ: &Type, location: Locat .expect("Could not convert field value to integer"); match numeric_value { NumericValue::Field(_) => panic!("Field should not happen here"), - NumericValue::U1(value) => Value::U1(value), + NumericValue::U1(value) => Value::u1(value), NumericValue::U8(fitted) => match fitted { - Fitted::Fit(value) => Value::U8(value), + Fitted::Fit(value) => Value::u8(value), Fitted::Unfit(..) => panic!("input value does not fit in u8"), }, NumericValue::U16(fitted) => match fitted { - Fitted::Fit(value) => Value::U16(value), + Fitted::Fit(value) => Value::u16(value), Fitted::Unfit(..) => panic!("input value does not fit in u16"), }, NumericValue::U32(fitted) => match fitted { - Fitted::Fit(value) => Value::U32(value), + Fitted::Fit(value) => Value::u32(value), Fitted::Unfit(..) => panic!("input value does not fit in u32"), }, NumericValue::U64(fitted) => match fitted { - Fitted::Fit(value) => Value::U64(value), + Fitted::Fit(value) => Value::u64(value), Fitted::Unfit(..) => panic!("input value does not fit in u64"), }, NumericValue::U128(fitted) => match fitted { - Fitted::Fit(value) => Value::U128(value), + Fitted::Fit(value) => Value::u128(value), Fitted::Unfit(..) => panic!("input value does not fit in u128"), }, NumericValue::I8(fitted) => match fitted { - Fitted::Fit(value) => Value::I8(value), + Fitted::Fit(value) => Value::i8(value), Fitted::Unfit(..) => panic!("input value does not fit in i8"), }, NumericValue::I16(fitted) => match fitted { - Fitted::Fit(value) => Value::I16(value), + Fitted::Fit(value) => Value::i16(value), Fitted::Unfit(..) => panic!("input value does not fit in i16"), }, NumericValue::I32(fitted) => match fitted { - Fitted::Fit(value) => Value::I32(value), + Fitted::Fit(value) => Value::i32(value), Fitted::Unfit(..) => panic!("input value does not fit in i32"), }, NumericValue::I64(fitted) => match fitted { - Fitted::Fit(value) => Value::I64(value), + Fitted::Fit(value) => Value::i64(value), Fitted::Unfit(..) => panic!("input value does not fit in i64"), }, } @@ -210,7 +210,7 @@ fn input_value_to_comptime_value(input: &InputValue, typ: &Type, location: Locat let InputValue::Field(value) = input else { panic!("expected field input for field element type"); }; - Value::Field(SignedField::positive(*value)) + Value::field(SignedField::positive(*value)) } Type::Array(length, element_typ) => { let length = @@ -297,18 +297,7 @@ fn output_value_to_string(value: &Value, context: &Context) -> String { match value { Value::Unit => "()".to_string(), Value::Bool(value) => value.to_string(), - Value::Field(signed_field) => signed_field.to_field_element().to_short_hex(), - Value::I8(value) => value.to_string(), - Value::I16(value) => value.to_string(), - Value::I32(value) => value.to_string(), - Value::I64(value) => value.to_string(), - Value::U1(false) => "0".to_string(), - Value::U1(true) => "1".to_string(), - Value::U8(value) => value.to_string(), - Value::U16(value) => value.to_string(), - Value::U32(value) => value.to_string(), - Value::U64(value) => value.to_string(), - Value::U128(value) => value.to_string(), + Value::Integer(value) => value.to_string(), Value::String(string) => { format!("{string:?}") }