From 6a805e5ea8155c1146a3f15dcfe612c438f1ce59 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 1 Jul 2024 09:59:44 -0300 Subject: [PATCH 1/3] feat: error on too large integer value --- compiler/noirc_frontend/src/lexer/errors.rs | 8 +++ compiler/noirc_frontend/src/lexer/lexer.rs | 49 +++++++++++++++++-- .../integer_too_large/Nargo.toml | 5 ++ .../integer_too_large/src/main.nr | 4 ++ 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 test_programs/compile_failure/integer_too_large/Nargo.toml create mode 100644 test_programs/compile_failure/integer_too_large/src/main.nr diff --git a/compiler/noirc_frontend/src/lexer/errors.rs b/compiler/noirc_frontend/src/lexer/errors.rs index 2452e034c1..387ced0525 100644 --- a/compiler/noirc_frontend/src/lexer/errors.rs +++ b/compiler/noirc_frontend/src/lexer/errors.rs @@ -15,6 +15,8 @@ pub enum LexerErrorKind { NotADoubleChar { span: Span, found: Token }, #[error("Invalid integer literal, {:?} is not a integer", found)] InvalidIntegerLiteral { span: Span, found: String }, + #[error("Integer literal is too large")] + IntegerLiteralTooLarge { span: Span, limit: String }, #[error("{:?} is not a valid attribute", found)] MalformedFuncAttribute { span: Span, found: String }, #[error("Logical and used instead of bitwise and")] @@ -46,6 +48,7 @@ impl LexerErrorKind { LexerErrorKind::UnexpectedCharacter { span, .. } => *span, LexerErrorKind::NotADoubleChar { span, .. } => *span, LexerErrorKind::InvalidIntegerLiteral { span, .. } => *span, + LexerErrorKind::IntegerLiteralTooLarge { span, .. } => *span, LexerErrorKind::MalformedFuncAttribute { span, .. } => *span, LexerErrorKind::LogicalAnd { span } => *span, LexerErrorKind::UnterminatedBlockComment { span } => *span, @@ -83,6 +86,11 @@ impl LexerErrorKind { format!(" {found} is not an integer"), *span, ), + LexerErrorKind::IntegerLiteralTooLarge { span, limit } => ( + "Integer literal is too large".to_string(), + format!("value exceeds limit of {limit}"), + *span, + ), LexerErrorKind::MalformedFuncAttribute { span, found } => ( "Malformed function attribute".to_string(), format!(" {found} is not a valid attribute"), diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index d2bc3d0f51..fe39179279 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -6,9 +6,11 @@ use super::{ token_to_borrowed_token, BorrowedToken, IntType, Keyword, SpannedToken, Token, Tokens, }, }; -use acvm::FieldElement; +use acvm::{AcirField, FieldElement}; use noirc_errors::{Position, Span}; -use std::str::CharIndices; +use num_bigint::BigInt; +use num_traits::{Num, One}; +use std::str::{CharIndices, FromStr}; /// The job of the lexer is to transform an iterator of characters (`char_iter`) /// into an iterator of `SpannedToken`. Each `Token` corresponds roughly to 1 word or operator. @@ -19,6 +21,7 @@ pub struct Lexer<'a> { done: bool, skip_comments: bool, skip_whitespaces: bool, + max_integer: BigInt, } pub type SpannedTokenResult = Result; @@ -61,6 +64,8 @@ impl<'a> Lexer<'a> { done: false, skip_comments: true, skip_whitespaces: true, + max_integer: BigInt::from_biguint(num_bigint::Sign::Plus, FieldElement::modulus()) + - BigInt::one(), } } @@ -376,14 +381,35 @@ impl<'a> Lexer<'a> { // Underscores needs to be stripped out before the literal can be converted to a `FieldElement. let integer_str = integer_str.replace('_', ""); - let integer = match FieldElement::try_from_str(&integer_str) { - None => { + let big_integer_result = match integer_str.strip_prefix("0x") { + Some(integer_str) => BigInt::from_str_radix(integer_str, 16), + None => BigInt::from_str(&integer_str), + }; + + let integer = match big_integer_result { + Ok(big_integer) => { + if big_integer > self.max_integer { + return Err(LexerErrorKind::IntegerLiteralTooLarge { + span: Span::inclusive(start, end), + limit: self.max_integer.to_string(), + }); + } + match FieldElement::try_from_str(&integer_str) { + Some(field_element) => field_element, + None => { + return Err(LexerErrorKind::InvalidIntegerLiteral { + span: Span::inclusive(start, end), + found: integer_str, + }) + } + } + } + Err(_) => { return Err(LexerErrorKind::InvalidIntegerLiteral { span: Span::inclusive(start, end), found: integer_str, }) } - Some(integer) => integer, }; let integer_token = Token::Int(integer); @@ -899,6 +925,19 @@ mod tests { } } + #[test] + fn test_int_too_large() { + let modulus = FieldElement::modulus(); + let input = modulus.to_string(); + + let mut lexer = Lexer::new(&input); + let token = lexer.next_token(); + assert!( + matches!(token, Err(LexerErrorKind::IntegerLiteralTooLarge { .. })), + "expected {input} to throw error" + ); + } + #[test] fn test_arithmetic_sugar() { let input = "+= -= *= /= %="; diff --git a/test_programs/compile_failure/integer_too_large/Nargo.toml b/test_programs/compile_failure/integer_too_large/Nargo.toml new file mode 100644 index 0000000000..08439d2f02 --- /dev/null +++ b/test_programs/compile_failure/integer_too_large/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "integer_too_large" +type = "bin" +authors = [""] +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/integer_too_large/src/main.nr b/test_programs/compile_failure/integer_too_large/src/main.nr new file mode 100644 index 0000000000..a8a2c383fe --- /dev/null +++ b/test_programs/compile_failure/integer_too_large/src/main.nr @@ -0,0 +1,4 @@ +fn main(x: Field) { + let too_large: Field = 233149999999999999999999999999999999999999999999999999999999923314999999999999999999999999999999999999999999999999999999999923314999999999999999999999999999999999999999999999999999999999; + assert(x == too_large); +} \ No newline at end of file From d920cc1bec556738eae76a84c5888e61d55c75d4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 1 Jul 2024 10:01:50 -0300 Subject: [PATCH 2/3] Fix test program that now errors --- test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr b/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr index fa6be84c26..616ac7ef6e 100644 --- a/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr +++ b/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr @@ -9,10 +9,11 @@ use std::ec::swcurve::curvegroup::Point as SWG; use std::ec::montcurve::affine::Point as MGaffine; use std::ec::montcurve::curvegroup::Point as MG; +use std::compat; fn main() { // This test only makes sense if Field is the right prime field. - if 21888242871839275222246405745257275088548364400416034343698204186575808495617 == 0 { + if compat::is_bn254() { // Define Baby Jubjub (ERC-2494) parameters in affine representation let bjj_affine = AffineCurve::new( 168700, From 9786267218048b06f86e326057c47e1fc94d2750 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 1 Jul 2024 12:54:08 -0300 Subject: [PATCH 3/3] Use FieldElement::from_be_bytes_reduce --- compiler/noirc_frontend/src/lexer/lexer.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index fe39179279..0afcb02caa 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -381,28 +381,21 @@ impl<'a> Lexer<'a> { // Underscores needs to be stripped out before the literal can be converted to a `FieldElement. let integer_str = integer_str.replace('_', ""); - let big_integer_result = match integer_str.strip_prefix("0x") { + let bigint_result = match integer_str.strip_prefix("0x") { Some(integer_str) => BigInt::from_str_radix(integer_str, 16), None => BigInt::from_str(&integer_str), }; - let integer = match big_integer_result { - Ok(big_integer) => { - if big_integer > self.max_integer { + let integer = match bigint_result { + Ok(bigint) => { + if bigint > self.max_integer { return Err(LexerErrorKind::IntegerLiteralTooLarge { span: Span::inclusive(start, end), limit: self.max_integer.to_string(), }); } - match FieldElement::try_from_str(&integer_str) { - Some(field_element) => field_element, - None => { - return Err(LexerErrorKind::InvalidIntegerLiteral { - span: Span::inclusive(start, end), - found: integer_str, - }) - } - } + let big_uint = bigint.magnitude(); + FieldElement::from_be_bytes_reduce(&big_uint.to_bytes_be()) } Err(_) => { return Err(LexerErrorKind::InvalidIntegerLiteral {