diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 1a58386c139..d5eef00d461 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -1059,10 +1059,7 @@ impl Context { // Casting into a Field as a no-op Ok(variable) } - NumericType::Unsigned { bit_size } => { - if incoming_type.is_signed() { - todo!("Cast from unsigned to signed") - } + NumericType::Unsigned { bit_size } | NumericType::Signed { bit_size } => { let max_bit_size = incoming_type.bit_size(); if max_bit_size <= *bit_size { // Incoming variable already fits into target bit size - this is a no-op @@ -1070,7 +1067,6 @@ impl Context { } self.acir_context.truncate_var(variable, *bit_size, max_bit_size) } - NumericType::Signed { .. } => todo!("Cast into signed"), } } diff --git a/tooling/nargo_cli/tests/execution_success/signed_arithmetic/Nargo.toml b/tooling/nargo_cli/tests/execution_success/signed_arithmetic/Nargo.toml new file mode 100644 index 00000000000..952e7d88a5a --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/signed_arithmetic/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "signed_arithmetic" +type = "bin" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/tooling/nargo_cli/tests/execution_success/signed_arithmetic/Prover.toml b/tooling/nargo_cli/tests/execution_success/signed_arithmetic/Prover.toml new file mode 100644 index 00000000000..e0e584b7380 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/signed_arithmetic/Prover.toml @@ -0,0 +1,3 @@ +x = "5" +y = "8" +z = "-15" diff --git a/tooling/nargo_cli/tests/execution_success/signed_arithmetic/src/main.nr b/tooling/nargo_cli/tests/execution_success/signed_arithmetic/src/main.nr new file mode 100644 index 00000000000..c2a1b580f40 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/signed_arithmetic/src/main.nr @@ -0,0 +1,32 @@ +fn main(mut x: i32, mut y: i32, z: i32) { + let mut s1: i8 = 5; + let mut s2: i8 = 8; + assert(s1+s2 == 13); + assert(x + y == 13); + + s2= -8; + y = -y; + assert(s1+s2 == -3); + assert(x + y == -3); + + s1 = -15; + assert(s1-s2 == -7); + assert(z-y == -7); + + s1 = -5; + s2 = 8; + x = -x; + y = -y; + assert(s1-s2 == -13); + assert(x-y == -13); + + s2 = -8; + y = -y; + assert(s1*s2 == 40); + assert(x*y == 40); + + s1 = 1; + s2 = -8; + assert(s1*s2 == -8); + assert(x/x*y == -8); +} diff --git a/tooling/noirc_abi/src/input_parser/mod.rs b/tooling/noirc_abi/src/input_parser/mod.rs index 139f3276179..fc6cd4b3b30 100644 --- a/tooling/noirc_abi/src/input_parser/mod.rs +++ b/tooling/noirc_abi/src/input_parser/mod.rs @@ -1,5 +1,5 @@ -use num_bigint::BigUint; -use num_traits::Num; +use num_bigint::{BigInt, BigUint}; +use num_traits::{Num, Zero}; use std::collections::BTreeMap; use acvm::FieldElement; @@ -201,10 +201,48 @@ fn parse_str_to_field(value: &str) -> Result { } } +fn parse_str_to_signed(value: &str, witdh: u32) -> Result { + if value.starts_with("0x") { + FieldElement::from_hex(value).ok_or_else(|| InputParserError::ParseHexStr(value.to_owned())) + } else { + BigInt::from_str_radix(value, 10) + .map_err(|err_msg| InputParserError::ParseStr(err_msg.to_string())) + .and_then(|bigint| { + let modulus: BigInt = FieldElement::modulus().into(); + let bigint = if bigint.sign() == num_bigint::Sign::Minus { + BigInt::from(2).pow(witdh) + bigint + } else { + bigint + }; + if bigint.is_zero() || (bigint.sign() == num_bigint::Sign::Plus && bigint < modulus) + { + Ok(field_from_big_int(bigint)) + } else { + Err(InputParserError::ParseStr(format!( + "Input exceeds field modulus. Values must fall within [0, {})", + FieldElement::modulus(), + ))) + } + }) + } +} + fn field_from_big_uint(bigint: BigUint) -> FieldElement { FieldElement::from_be_bytes_reduce(&bigint.to_bytes_be()) } +fn field_from_big_int(bigint: BigInt) -> FieldElement { + match bigint.sign() { + num_bigint::Sign::Minus => { + unreachable!( + "Unsupported negative value; it should only be called with a positive value" + ) + } + num_bigint::Sign::NoSign => FieldElement::zero(), + num_bigint::Sign::Plus => FieldElement::from_be_bytes_reduce(&bigint.to_bytes_be().1), + } +} + #[cfg(test)] mod test { use acvm::FieldElement; diff --git a/tooling/noirc_abi/src/input_parser/toml.rs b/tooling/noirc_abi/src/input_parser/toml.rs index 3f7ec30e355..dc47cbcda37 100644 --- a/tooling/noirc_abi/src/input_parser/toml.rs +++ b/tooling/noirc_abi/src/input_parser/toml.rs @@ -1,4 +1,4 @@ -use super::{parse_str_to_field, InputValue}; +use super::{parse_str_to_field, parse_str_to_signed, InputValue}; use crate::{errors::InputParserError, Abi, AbiType, MAIN_RETURN_NAME}; use acvm::FieldElement; use iter_extended::{try_btree_map, try_vecmap}; @@ -118,9 +118,13 @@ impl InputValue { (TomlTypes::String(string), AbiType::String { .. }) => InputValue::String(string), ( TomlTypes::String(string), - AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean, + AbiType::Field + | AbiType::Integer { sign: crate::Sign::Unsigned, .. } + | AbiType::Boolean, ) => InputValue::Field(parse_str_to_field(&string)?), - + (TomlTypes::String(string), AbiType::Integer { sign: crate::Sign::Signed, width }) => { + InputValue::Field(parse_str_to_signed(&string, *width)?) + } ( TomlTypes::Integer(integer), AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean,