diff --git a/compiler/noirc_frontend/src/lexer/errors.rs b/compiler/noirc_frontend/src/lexer/errors.rs index 2452e034c1c..387ced05258 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 d2bc3d0f519..0afcb02caac 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,28 @@ 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 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 bigint_result { + Ok(bigint) => { + if bigint > self.max_integer { + return Err(LexerErrorKind::IntegerLiteralTooLarge { + span: Span::inclusive(start, end), + limit: self.max_integer.to_string(), + }); + } + let big_uint = bigint.magnitude(); + FieldElement::from_be_bytes_reduce(&big_uint.to_bytes_be()) + } + Err(_) => { return Err(LexerErrorKind::InvalidIntegerLiteral { span: Span::inclusive(start, end), found: integer_str, }) } - Some(integer) => integer, }; let integer_token = Token::Int(integer); @@ -899,6 +918,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 00000000000..08439d2f02e --- /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 00000000000..a8a2c383fe6 --- /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 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 fa6be84c26e..616ac7ef6ee 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,