diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index 43eb7df303f..219569d1a23 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -2760,8 +2760,6 @@ impl<'a> Context<'a> { unreachable!("Expected static_assert to be removed by this point") } Intrinsic::StrAsBytes => unreachable!("Expected as_bytes to be removed by this point"), - Intrinsic::FromField => unreachable!("Expected from_field to be removed by this point"), - Intrinsic::AsField => unreachable!("Expected as_field to be removed by this point"), Intrinsic::IsUnconstrained => { unreachable!("Expected is_unconstrained to be removed by this point") } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 0b02cd29f0f..5bcddc21275 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -635,9 +635,7 @@ impl<'block> BrilligBlock<'block> { let array = array.extract_register(); self.brillig_context.load_instruction(destination, array); } - Intrinsic::FromField - | Intrinsic::AsField - | Intrinsic::IsUnconstrained + Intrinsic::IsUnconstrained | Intrinsic::DerivePedersenGenerators | Intrinsic::ApplyRangeConstraint | Intrinsic::StrAsBytes diff --git a/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs index 01d1d51c730..40a13fa144f 100644 --- a/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs +++ b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs @@ -294,11 +294,9 @@ impl DependencyContext { Intrinsic::ArrayLen | Intrinsic::ArrayRefCount | Intrinsic::ArrayAsStrUnchecked - | Intrinsic::AsField | Intrinsic::AsSlice | Intrinsic::BlackBox(..) | Intrinsic::DerivePedersenGenerators - | Intrinsic::FromField | Intrinsic::Hint(..) | Intrinsic::SlicePushBack | Intrinsic::SlicePushFront @@ -574,12 +572,10 @@ impl Context { Intrinsic::ArrayLen | Intrinsic::ArrayAsStrUnchecked | Intrinsic::ArrayRefCount - | Intrinsic::AsField | Intrinsic::AsSlice | Intrinsic::BlackBox(..) | Intrinsic::Hint(Hint::BlackBox) | Intrinsic::DerivePedersenGenerators - | Intrinsic::FromField | Intrinsic::SliceInsert | Intrinsic::SlicePushBack | Intrinsic::SlicePushFront diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 3072bb1d72d..8ff75cce6bc 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -65,8 +65,6 @@ pub(crate) enum Intrinsic { ToRadix(Endian), BlackBox(BlackBoxFunc), Hint(Hint), - FromField, - AsField, AsWitness, IsUnconstrained, DerivePedersenGenerators, @@ -97,8 +95,6 @@ impl std::fmt::Display for Intrinsic { Intrinsic::ToRadix(Endian::Little) => write!(f, "to_le_radix"), Intrinsic::BlackBox(function) => write!(f, "{function}"), Intrinsic::Hint(Hint::BlackBox) => write!(f, "black_box"), - Intrinsic::FromField => write!(f, "from_field"), - Intrinsic::AsField => write!(f, "as_field"), Intrinsic::AsWitness => write!(f, "as_witness"), Intrinsic::IsUnconstrained => write!(f, "is_unconstrained"), Intrinsic::DerivePedersenGenerators => write!(f, "derive_pedersen_generators"), @@ -140,8 +136,6 @@ impl Intrinsic { | Intrinsic::SlicePushFront | Intrinsic::SliceInsert | Intrinsic::StrAsBytes - | Intrinsic::FromField - | Intrinsic::AsField | Intrinsic::IsUnconstrained | Intrinsic::DerivePedersenGenerators | Intrinsic::FieldLessThan => false, @@ -213,8 +207,6 @@ impl Intrinsic { "to_be_radix" => Some(Intrinsic::ToRadix(Endian::Big)), "to_le_bits" => Some(Intrinsic::ToBits(Endian::Little)), "to_be_bits" => Some(Intrinsic::ToBits(Endian::Big)), - "from_field" => Some(Intrinsic::FromField), - "as_field" => Some(Intrinsic::AsField), "as_witness" => Some(Intrinsic::AsWitness), "is_unconstrained" => Some(Intrinsic::IsUnconstrained), "derive_pedersen_generators" => Some(Intrinsic::DerivePedersenGenerators), diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index fa7d314ff33..6da4c7702c8 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -330,31 +330,6 @@ pub(super) fn simplify_call( Intrinsic::BlackBox(bb_func) => { simplify_black_box_func(bb_func, arguments, dfg, block, call_stack) } - Intrinsic::AsField => { - let instruction = Instruction::Cast(arguments[0], NumericType::NativeField); - SimplifyResult::SimplifiedToInstruction(instruction) - } - Intrinsic::FromField => { - let incoming_type = Type::field(); - let target_type = return_type.clone().unwrap(); - - let truncate = Instruction::Truncate { - value: arguments[0], - bit_size: target_type.bit_size(), - max_bit_size: incoming_type.bit_size(), - }; - let truncated_value = dfg - .insert_instruction_and_results( - truncate, - block, - Some(vec![incoming_type]), - call_stack, - ) - .first(); - - let instruction = Instruction::Cast(truncated_value, target_type.unwrap_numeric()); - SimplifyResult::SimplifiedToInstruction(instruction) - } Intrinsic::AsWitness => SimplifyResult::None, Intrinsic::IsUnconstrained => SimplifyResult::None, Intrinsic::DerivePedersenGenerators => { diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index e85e2c4a441..e99f239e82e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -176,8 +176,6 @@ impl Context { | Intrinsic::ToRadix(_) | Intrinsic::BlackBox(_) | Intrinsic::Hint(Hint::BlackBox) - | Intrinsic::FromField - | Intrinsic::AsField | Intrinsic::AsSlice | Intrinsic::AsWitness | Intrinsic::IsUnconstrained diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index 79f2354aff6..9a931e36ea2 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -235,8 +235,6 @@ fn slice_capacity_change( | Intrinsic::StrAsBytes | Intrinsic::BlackBox(_) | Intrinsic::Hint(Hint::BlackBox) - | Intrinsic::FromField - | Intrinsic::AsField | Intrinsic::AsWitness | Intrinsic::IsUnconstrained | Intrinsic::DerivePedersenGenerators diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 2e672e3d0d5..3d8ccf78926 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -64,7 +64,6 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_len" => array_len(interner, arguments, location), "array_refcount" => Ok(Value::U32(0)), "assert_constant" => Ok(Value::Bool(true)), - "as_field" => as_field(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), "ctstring_eq" => ctstring_eq(arguments, location), "ctstring_hash" => ctstring_hash(arguments, location), @@ -117,7 +116,6 @@ impl<'local, 'context> Interpreter<'local, 'context> { "field_less_than" => field_less_than(arguments, location), "fmtstr_as_ctstring" => fmtstr_as_ctstring(interner, arguments, location), "fmtstr_quoted_contents" => fmtstr_quoted_contents(interner, arguments, location), - "from_field" => from_field(interner, arguments, return_type, location), "fresh_type_variable" => fresh_type_variable(interner), "function_def_add_attribute" => function_def_add_attribute(self, arguments, location), "function_def_body" => function_def_body(interner, arguments, location), @@ -292,16 +290,6 @@ fn array_as_str_unchecked( Ok(Value::String(Rc::new(string))) } -// fn as_field(x: T) -> Field {} -fn as_field( - interner: &NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { - let (value, value_location) = check_one_argument(arguments, location)?; - Interpreter::evaluate_cast_one_step(&Type::FieldElement, value_location, value, interner) -} - fn as_slice( interner: &NodeInterner, arguments: Vec<(Value, Location)>, @@ -2236,17 +2224,6 @@ fn fmtstr_quoted_contents( Ok(Value::Quoted(Rc::new(tokens))) } -// fn from_field(x: Field) -> T {} -fn from_field( - interner: &NodeInterner, - arguments: Vec<(Value, Location)>, - return_type: Type, - location: Location, -) -> IResult { - let (value, value_location) = check_one_argument(arguments, location)?; - Interpreter::evaluate_cast_one_step(&return_type, value_location, value, interner) -} - // fn fresh_type_variable() -> Type fn fresh_type_variable(interner: &NodeInterner) -> IResult { Ok(Value::Type(interner.next_type_variable_with_kind(Kind::Any))) diff --git a/noir_stdlib/src/convert.nr b/noir_stdlib/src/convert.nr index d4bea8b6390..1e5c1484b5f 100644 --- a/noir_stdlib/src/convert.nr +++ b/noir_stdlib/src/convert.nr @@ -117,3 +117,51 @@ impl From for Field { } } // docs:end:from-impls + +/// A generic interface for casting between primitive types, +/// equivalent of using the `as` keyword between values. +/// +/// # Example +/// +/// ``` +/// let x: Field = 1234567890; +/// let y: u8 = x as u8; +/// let z: u8 = x.as_(); +/// assert_eq(y, z); +/// ``` +pub trait AsPrimitive { + /// The equivalent of doing `self as T`. + fn as_(self) -> T; +} + +#[generate_as_primitive_impls] +comptime fn generate_as_primitive_impls(_: FunctionDefinition) -> Quoted { + let types = [ + quote { bool }, + quote { u8 }, + quote { u16 }, + quote { u32 }, + quote { u64 }, + quote { i8 }, + quote { i16 }, + quote { i32 }, + quote { i64 }, + quote { Field }, + ]; + + let mut impls = &[]; + for type1 in types { + for type2 in types { + impls = impls.push_back( + quote { + impl AsPrimitive<$type1> for $type2 { + fn as_(self) -> $type1 { + self as $type1 + } + } + }, + ); + } + } + impls.join(quote {}) +} diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index c3a289a8fa2..1cb8f7fc2dd 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -28,6 +28,8 @@ pub mod mem; pub mod panic; pub mod hint; +use convert::AsPrimitive; + // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident #[oracle(print)] @@ -89,28 +91,29 @@ pub fn assert_constant(x: T) {} #[builtin(static_assert)] pub fn static_assert(predicate: bool, message: str) {} -// from_field and as_field are private since they are not valid for every type. -// `as` should be the default for users to cast between primitive types, and in the future -// traits can be used to work with generic types. -#[builtin(from_field)] -fn from_field(x: Field) -> T {} - -#[builtin(as_field)] -fn as_field(x: T) -> Field {} - -pub fn wrapping_add(x: T, y: T) -> T { - crate::from_field(crate::as_field(x) + crate::as_field(y)) +pub fn wrapping_add(x: T, y: T) -> T +where + T: AsPrimitive, + Field: AsPrimitive, +{ + AsPrimitive::as_(x.as_() + y.as_()) } -pub fn wrapping_sub(x: T, y: T) -> T { +pub fn wrapping_sub(x: T, y: T) -> T +where + T: AsPrimitive, + Field: AsPrimitive, +{ //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow - crate::from_field( - crate::as_field(x) + 340282366920938463463374607431768211456 - crate::as_field(y), - ) + AsPrimitive::as_(x.as_() + 340282366920938463463374607431768211456 - y.as_()) } -pub fn wrapping_mul(x: T, y: T) -> T { - crate::from_field(crate::as_field(x) * crate::as_field(y)) +pub fn wrapping_mul(x: T, y: T) -> T +where + T: AsPrimitive, + Field: AsPrimitive, +{ + AsPrimitive::as_(x.as_() * y.as_()) } #[builtin(as_witness)] diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index 06febb3ee00..b448e11acb1 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -1,5 +1,6 @@ use crate::cmp::{Eq, Ord, Ordering}; use crate::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub}; +use super::convert::AsPrimitive; global pow64: Field = 18446744073709551616; //2^64; global pow63: Field = 9223372036854775808; // 2^63; @@ -139,8 +140,11 @@ impl U128 { } } - pub fn from_integer(i: T) -> U128 { - let f = crate::as_field(i); + pub fn from_integer(i: T) -> U128 + where + T: AsPrimitive, + { + let f = i.as_(); // Reject values which would overflow a u128 f.assert_max_bit_size::<128>(); let lo = f as u64 as Field; @@ -148,8 +152,11 @@ impl U128 { U128 { lo, hi } } - pub fn to_integer(self) -> T { - crate::from_field(self.lo + self.hi * pow64) + pub fn to_integer(self) -> T + where + Field: AsPrimitive, + { + AsPrimitive::as_(self.lo + self.hi * pow64) } fn wrapping_mul(self: Self, b: U128) -> U128 {