From 2264dba04dcf00d392d5fe01d982ad0bde5b0625 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 17 Dec 2024 11:46:13 -0300 Subject: [PATCH 1/8] Remove the `as_field` and `from_field` built-ins --- compiler/noirc_evaluator/src/acir/mod.rs | 2 -- .../src/brillig/brillig_gen/brillig_block.rs | 4 +-- .../check_for_underconstrained_values.rs | 4 --- .../noirc_evaluator/src/ssa/ir/instruction.rs | 8 ----- .../src/ssa/ir/instruction/call.rs | 25 ------------- .../src/ssa/opt/remove_enable_side_effects.rs | 2 -- .../src/ssa/opt/remove_if_else.rs | 2 -- .../src/hir/comptime/interpreter/builtin.rs | 23 ------------ noir_stdlib/src/convert.nr | 9 +++++ noir_stdlib/src/lib.nr | 35 ++++++++++--------- noir_stdlib/src/uint128.nr | 14 +++++--- 11 files changed, 39 insertions(+), 89 deletions(-) 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..11c170f8e38 100644 --- a/noir_stdlib/src/convert.nr +++ b/noir_stdlib/src/convert.nr @@ -116,4 +116,13 @@ impl From for Field { value as Field } } + +// Field +impl From for u64 { + fn from(value: Field) -> u64 { + value.assert_max_bit_size::<64>(); + value as u64 + } +} + // docs:end:from-impls diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index c3a289a8fa2..f35230355f2 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -89,28 +89,31 @@ 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: convert::Into, + T: convert::From, +{ + T::from(x.into() + y.into()) } -pub fn wrapping_sub(x: T, y: T) -> T { +pub fn wrapping_sub(x: T, y: T) -> T +where + T: convert::Into, + T: convert::From, +{ //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow - crate::from_field( - crate::as_field(x) + 340282366920938463463374607431768211456 - crate::as_field(y), + T::from( + x.into() + 340282366920938463463374607431768211456 - y.into(), ) } -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: convert::Into, + T: convert::From, +{ + T::from(x.into() * y.into()) } #[builtin(as_witness)] diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index 06febb3ee00..48bef85d1d3 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -139,8 +139,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: super::convert::Into, + { + let f = i.into(); // Reject values which would overflow a u128 f.assert_max_bit_size::<128>(); let lo = f as u64 as Field; @@ -148,8 +151,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 + T: super::convert::From, + { + T::from(self.lo + self.hi * pow64) } fn wrapping_mul(self: Self, b: U128) -> U128 { From 585f173fdf6ce9933a52d6b21fa921bd96185a5d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 18 Dec 2024 17:17:20 -0300 Subject: [PATCH 2/8] Unify constraints --- noir_stdlib/src/lib.nr | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index f35230355f2..83e998c8e1b 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -91,16 +91,14 @@ pub fn static_assert(predicate: bool, message: str) {} pub fn wrapping_add(x: T, y: T) -> T where - T: convert::Into, - T: convert::From, + T: convert::Into + convert::From, { T::from(x.into() + y.into()) } pub fn wrapping_sub(x: T, y: T) -> T where - T: convert::Into, - T: convert::From, + T: convert::Into + convert::From, { //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow T::from( @@ -110,8 +108,7 @@ where pub fn wrapping_mul(x: T, y: T) -> T where - T: convert::Into, - T: convert::From, + T: convert::Into + convert::From, { T::from(x.into() * y.into()) } From 43c8fd68b4ce862953f4af32859c60e8c8762175 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 18 Dec 2024 17:22:17 -0300 Subject: [PATCH 3/8] Introduce an UnsafeFromField trait --- noir_stdlib/src/convert.nr | 59 ++++++++++++++++++++++++++++++++++---- noir_stdlib/src/lib.nr | 12 ++++---- noir_stdlib/src/uint128.nr | 4 +-- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/noir_stdlib/src/convert.nr b/noir_stdlib/src/convert.nr index 11c170f8e38..418151540a8 100644 --- a/noir_stdlib/src/convert.nr +++ b/noir_stdlib/src/convert.nr @@ -116,13 +116,60 @@ impl From for Field { value as Field } } +// docs:end:from-impls -// Field -impl From for u64 { - fn from(value: Field) -> u64 { - value.assert_max_bit_size::<64>(); - value as u64 +pub(crate) trait UnsafeFromField { + fn unsafe_from_field(input: Field) -> Self; +} + +impl UnsafeFromField for u8 { + fn unsafe_from_field(input: Field) -> u8 { + input as u8 } } -// docs:end:from-impls +impl UnsafeFromField for u16 { + fn unsafe_from_field(input: Field) -> u16 { + input as u16 + } +} +impl UnsafeFromField for u32 { + fn unsafe_from_field(input: Field) -> u32 { + input as u32 + } +} + +impl UnsafeFromField for u64 { + fn unsafe_from_field(input: Field) -> u64 { + input as u64 + } +} + +impl UnsafeFromField for i8 { + fn unsafe_from_field(input: Field) -> i8 { + input as i8 + } +} + +impl UnsafeFromField for i16 { + fn unsafe_from_field(input: Field) -> i16 { + input as i16 + } +} +impl UnsafeFromField for i32 { + fn unsafe_from_field(input: Field) -> i32 { + input as i32 + } +} + +impl UnsafeFromField for i64 { + fn unsafe_from_field(input: Field) -> i64 { + input as i64 + } +} + +impl UnsafeFromField for Field { + fn unsafe_from_field(input: Field) -> Field { + input + } +} diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 83e998c8e1b..070ad1cd44c 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -91,26 +91,26 @@ pub fn static_assert(predicate: bool, message: str) {} pub fn wrapping_add(x: T, y: T) -> T where - T: convert::Into + convert::From, + T: convert::Into + convert::UnsafeFromField, { - T::from(x.into() + y.into()) + T::unsafe_from_field(x.into() + y.into()) } pub fn wrapping_sub(x: T, y: T) -> T where - T: convert::Into + convert::From, + T: convert::Into + convert::UnsafeFromField, { //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow - T::from( + T::unsafe_from_field( x.into() + 340282366920938463463374607431768211456 - y.into(), ) } pub fn wrapping_mul(x: T, y: T) -> T where - T: convert::Into + convert::From, + T: convert::Into + convert::UnsafeFromField, { - T::from(x.into() * y.into()) + T::unsafe_from_field(x.into() * y.into()) } #[builtin(as_witness)] diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index 48bef85d1d3..a0080bfd474 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -153,9 +153,9 @@ impl U128 { pub fn to_integer(self) -> T where - T: super::convert::From, + T: super::convert::UnsafeFromField, { - T::from(self.lo + self.hi * pow64) + T::unsafe_from_field(self.lo + self.hi * pow64) } fn wrapping_mul(self: Self, b: U128) -> U128 { From 8d910682ac267df30d1e74e970e514c8329dca3b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 18 Dec 2024 17:24:23 -0300 Subject: [PATCH 4/8] unsafe from field -> cast from field --- noir_stdlib/src/convert.nr | 40 +++++++++++++++++++------------------- noir_stdlib/src/lib.nr | 12 ++++++------ noir_stdlib/src/uint128.nr | 4 ++-- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/noir_stdlib/src/convert.nr b/noir_stdlib/src/convert.nr index 418151540a8..8bef42c303d 100644 --- a/noir_stdlib/src/convert.nr +++ b/noir_stdlib/src/convert.nr @@ -118,58 +118,58 @@ impl From for Field { } // docs:end:from-impls -pub(crate) trait UnsafeFromField { - fn unsafe_from_field(input: Field) -> Self; +pub(crate) trait CastFromField { + fn cast_from_field(input: Field) -> Self; } -impl UnsafeFromField for u8 { - fn unsafe_from_field(input: Field) -> u8 { +impl CastFromField for u8 { + fn cast_from_field(input: Field) -> u8 { input as u8 } } -impl UnsafeFromField for u16 { - fn unsafe_from_field(input: Field) -> u16 { +impl CastFromField for u16 { + fn cast_from_field(input: Field) -> u16 { input as u16 } } -impl UnsafeFromField for u32 { - fn unsafe_from_field(input: Field) -> u32 { +impl CastFromField for u32 { + fn cast_from_field(input: Field) -> u32 { input as u32 } } -impl UnsafeFromField for u64 { - fn unsafe_from_field(input: Field) -> u64 { +impl CastFromField for u64 { + fn cast_from_field(input: Field) -> u64 { input as u64 } } -impl UnsafeFromField for i8 { - fn unsafe_from_field(input: Field) -> i8 { +impl CastFromField for i8 { + fn cast_from_field(input: Field) -> i8 { input as i8 } } -impl UnsafeFromField for i16 { - fn unsafe_from_field(input: Field) -> i16 { +impl CastFromField for i16 { + fn cast_from_field(input: Field) -> i16 { input as i16 } } -impl UnsafeFromField for i32 { - fn unsafe_from_field(input: Field) -> i32 { +impl CastFromField for i32 { + fn cast_from_field(input: Field) -> i32 { input as i32 } } -impl UnsafeFromField for i64 { - fn unsafe_from_field(input: Field) -> i64 { +impl CastFromField for i64 { + fn cast_from_field(input: Field) -> i64 { input as i64 } } -impl UnsafeFromField for Field { - fn unsafe_from_field(input: Field) -> Field { +impl CastFromField for Field { + fn cast_from_field(input: Field) -> Field { input } } diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 070ad1cd44c..d833a3de987 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -91,26 +91,26 @@ pub fn static_assert(predicate: bool, message: str) {} pub fn wrapping_add(x: T, y: T) -> T where - T: convert::Into + convert::UnsafeFromField, + T: convert::Into + convert::CastFromField, { - T::unsafe_from_field(x.into() + y.into()) + T::cast_from_field(x.into() + y.into()) } pub fn wrapping_sub(x: T, y: T) -> T where - T: convert::Into + convert::UnsafeFromField, + T: convert::Into + convert::CastFromField, { //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow - T::unsafe_from_field( + T::cast_from_field( x.into() + 340282366920938463463374607431768211456 - y.into(), ) } pub fn wrapping_mul(x: T, y: T) -> T where - T: convert::Into + convert::UnsafeFromField, + T: convert::Into + convert::CastFromField, { - T::unsafe_from_field(x.into() * y.into()) + T::cast_from_field(x.into() * y.into()) } #[builtin(as_witness)] diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index a0080bfd474..3c2c4177091 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -153,9 +153,9 @@ impl U128 { pub fn to_integer(self) -> T where - T: super::convert::UnsafeFromField, + T: super::convert::CastFromField, { - T::unsafe_from_field(self.lo + self.hi * pow64) + T::cast_from_field(self.lo + self.hi * pow64) } fn wrapping_mul(self: Self, b: U128) -> U128 { From b1e92ca67d9be3a2fc34747c77d7ff207e306d29 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 18 Dec 2024 17:43:42 -0300 Subject: [PATCH 5/8] Also introduce CastToField --- noir_stdlib/src/convert.nr | 59 ++++++++++++++++++++++++++++++++++++++ noir_stdlib/src/lib.nr | 12 ++++---- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/noir_stdlib/src/convert.nr b/noir_stdlib/src/convert.nr index 8bef42c303d..aec14e6cc67 100644 --- a/noir_stdlib/src/convert.nr +++ b/noir_stdlib/src/convert.nr @@ -173,3 +173,62 @@ impl CastFromField for Field { input } } + +pub(crate) trait CastToField { + fn cast_to_field(input: Self) -> Field; +} + +impl CastToField for u8 { + fn cast_to_field(input: u8) -> Field { + input as Field + } +} + +impl CastToField for u16 { + fn cast_to_field(input: u16) -> Field { + input as Field + } +} + +impl CastToField for u32 { + fn cast_to_field(input: u32) -> Field { + input as Field + } +} + +impl CastToField for u64 { + fn cast_to_field(input: u64) -> Field { + input as Field + } +} + +impl CastToField for i8 { + fn cast_to_field(input: i8) -> Field { + input as Field + } +} + +impl CastToField for i16 { + fn cast_to_field(input: i16) -> Field { + input as Field + } +} + +impl CastToField for i32 { + fn cast_to_field(input: i32) -> Field { + input as Field + } +} + +impl CastToField for i64 { + fn cast_to_field(input: i64) -> Field { + input as Field + } +} + +impl CastToField for Field { + fn cast_to_field(input: Field) -> Field { + input + } +} + diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index d833a3de987..fe3eb330a87 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -91,26 +91,26 @@ pub fn static_assert(predicate: bool, message: str) {} pub fn wrapping_add(x: T, y: T) -> T where - T: convert::Into + convert::CastFromField, + T: convert::CastToField + convert::CastFromField, { - T::cast_from_field(x.into() + y.into()) + T::cast_from_field(x.cast_to_field() + y.cast_to_field()) } pub fn wrapping_sub(x: T, y: T) -> T where - T: convert::Into + convert::CastFromField, + T: convert::CastToField + convert::CastFromField, { //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow T::cast_from_field( - x.into() + 340282366920938463463374607431768211456 - y.into(), + x.cast_to_field() + 340282366920938463463374607431768211456 - y.cast_to_field(), ) } pub fn wrapping_mul(x: T, y: T) -> T where - T: convert::Into + convert::CastFromField, + T: convert::CastToField + convert::CastFromField, { - T::cast_from_field(x.into() * y.into()) + T::cast_from_field(x.cast_to_field() * y.cast_to_field()) } #[builtin(as_witness)] From 1325463a57b0fd3f4871190ab54387710cefd3ff Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 18 Dec 2024 17:55:24 -0300 Subject: [PATCH 6/8] One more CastToField --- noir_stdlib/src/uint128.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index 3c2c4177091..be1e8cd349e 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -141,9 +141,9 @@ impl U128 { pub fn from_integer(i: T) -> U128 where - T: super::convert::Into, + T: super::convert::CastToField, { - let f = i.into(); + let f = i.cast_to_field(); // Reject values which would overflow a u128 f.assert_max_bit_size::<128>(); let lo = f as u64 as Field; From 3f3ac4a281e514b6cefc6b41e49f8accf9c56cda Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 19 Dec 2024 10:08:09 -0300 Subject: [PATCH 7/8] Introduce an `AsPrimitive` trait --- noir_stdlib/src/convert.nr | 159 +++++++++++-------------------------- noir_stdlib/src/lib.nr | 19 +++-- noir_stdlib/src/uint128.nr | 9 ++- 3 files changed, 62 insertions(+), 125 deletions(-) diff --git a/noir_stdlib/src/convert.nr b/noir_stdlib/src/convert.nr index aec14e6cc67..1e5c1484b5f 100644 --- a/noir_stdlib/src/convert.nr +++ b/noir_stdlib/src/convert.nr @@ -118,117 +118,50 @@ impl From for Field { } // docs:end:from-impls -pub(crate) trait CastFromField { - fn cast_from_field(input: Field) -> Self; +/// 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 {}) } - -impl CastFromField for u8 { - fn cast_from_field(input: Field) -> u8 { - input as u8 - } -} - -impl CastFromField for u16 { - fn cast_from_field(input: Field) -> u16 { - input as u16 - } -} -impl CastFromField for u32 { - fn cast_from_field(input: Field) -> u32 { - input as u32 - } -} - -impl CastFromField for u64 { - fn cast_from_field(input: Field) -> u64 { - input as u64 - } -} - -impl CastFromField for i8 { - fn cast_from_field(input: Field) -> i8 { - input as i8 - } -} - -impl CastFromField for i16 { - fn cast_from_field(input: Field) -> i16 { - input as i16 - } -} -impl CastFromField for i32 { - fn cast_from_field(input: Field) -> i32 { - input as i32 - } -} - -impl CastFromField for i64 { - fn cast_from_field(input: Field) -> i64 { - input as i64 - } -} - -impl CastFromField for Field { - fn cast_from_field(input: Field) -> Field { - input - } -} - -pub(crate) trait CastToField { - fn cast_to_field(input: Self) -> Field; -} - -impl CastToField for u8 { - fn cast_to_field(input: u8) -> Field { - input as Field - } -} - -impl CastToField for u16 { - fn cast_to_field(input: u16) -> Field { - input as Field - } -} - -impl CastToField for u32 { - fn cast_to_field(input: u32) -> Field { - input as Field - } -} - -impl CastToField for u64 { - fn cast_to_field(input: u64) -> Field { - input as Field - } -} - -impl CastToField for i8 { - fn cast_to_field(input: i8) -> Field { - input as Field - } -} - -impl CastToField for i16 { - fn cast_to_field(input: i16) -> Field { - input as Field - } -} - -impl CastToField for i32 { - fn cast_to_field(input: i32) -> Field { - input as Field - } -} - -impl CastToField for i64 { - fn cast_to_field(input: i64) -> Field { - input as Field - } -} - -impl CastToField for Field { - fn cast_to_field(input: Field) -> Field { - input - } -} - diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index fe3eb330a87..ac22f424686 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -1,3 +1,5 @@ +use convert::AsPrimitive; + pub mod hash; pub mod aes128; pub mod array; @@ -91,26 +93,27 @@ pub fn static_assert(predicate: bool, message: str) {} pub fn wrapping_add(x: T, y: T) -> T where - T: convert::CastToField + convert::CastFromField, + T: convert::AsPrimitive, + Field: convert::AsPrimitive, { - T::cast_from_field(x.cast_to_field() + y.cast_to_field()) + AsPrimitive::as_(x.as_() + y.as_()) } pub fn wrapping_sub(x: T, y: T) -> T where - T: convert::CastToField + convert::CastFromField, + T: convert::AsPrimitive, + Field: convert::AsPrimitive, { //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow - T::cast_from_field( - x.cast_to_field() + 340282366920938463463374607431768211456 - y.cast_to_field(), - ) + AsPrimitive::as_(x.as_() + 340282366920938463463374607431768211456 - y.as_()) } pub fn wrapping_mul(x: T, y: T) -> T where - T: convert::CastToField + convert::CastFromField, + T: convert::AsPrimitive, + Field: convert::AsPrimitive, { - T::cast_from_field(x.cast_to_field() * y.cast_to_field()) + AsPrimitive::as_(x.as_() * y.as_()) } #[builtin(as_witness)] diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index be1e8cd349e..b53b829a6bd 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; @@ -141,9 +142,9 @@ impl U128 { pub fn from_integer(i: T) -> U128 where - T: super::convert::CastToField, + T: super::convert::AsPrimitive, { - let f = i.cast_to_field(); + let f = i.as_(); // Reject values which would overflow a u128 f.assert_max_bit_size::<128>(); let lo = f as u64 as Field; @@ -153,9 +154,9 @@ impl U128 { pub fn to_integer(self) -> T where - T: super::convert::CastFromField, + Field: super::convert::AsPrimitive, { - T::cast_from_field(self.lo + self.hi * pow64) + AsPrimitive::as_(self.lo + self.hi * pow64) } fn wrapping_mul(self: Self, b: U128) -> U128 { From b6a119874fc8f11f706a17e250a89de75d56d95a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 19 Dec 2024 10:16:25 -0300 Subject: [PATCH 8/8] Remove redundant qualifications --- noir_stdlib/src/lib.nr | 16 ++++++++-------- noir_stdlib/src/uint128.nr | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index ac22f424686..1cb8f7fc2dd 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -1,5 +1,3 @@ -use convert::AsPrimitive; - pub mod hash; pub mod aes128; pub mod array; @@ -30,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)] @@ -93,16 +93,16 @@ pub fn static_assert(predicate: bool, message: str) {} pub fn wrapping_add(x: T, y: T) -> T where - T: convert::AsPrimitive, - Field: convert::AsPrimitive, + T: AsPrimitive, + Field: AsPrimitive, { AsPrimitive::as_(x.as_() + y.as_()) } pub fn wrapping_sub(x: T, y: T) -> T where - T: convert::AsPrimitive, - Field: convert::AsPrimitive, + T: AsPrimitive, + Field: AsPrimitive, { //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow AsPrimitive::as_(x.as_() + 340282366920938463463374607431768211456 - y.as_()) @@ -110,8 +110,8 @@ where pub fn wrapping_mul(x: T, y: T) -> T where - T: convert::AsPrimitive, - Field: convert::AsPrimitive, + T: AsPrimitive, + Field: AsPrimitive, { AsPrimitive::as_(x.as_() * y.as_()) } diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index b53b829a6bd..b448e11acb1 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -142,7 +142,7 @@ impl U128 { pub fn from_integer(i: T) -> U128 where - T: super::convert::AsPrimitive, + T: AsPrimitive, { let f = i.as_(); // Reject values which would overflow a u128 @@ -154,7 +154,7 @@ impl U128 { pub fn to_integer(self) -> T where - Field: super::convert::AsPrimitive, + Field: AsPrimitive, { AsPrimitive::as_(self.lo + self.hi * pow64) }