diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 57c1ff054ff..3b55f5051ae 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -264,8 +264,10 @@ fn display_instruction_inner( Instruction::DecrementRc { value } => { writeln!(f, "dec_rc {}", show(*value)) } - Instruction::RangeCheck { value, max_bit_size, .. } => { - writeln!(f, "range_check {} to {} bits", show(*value), *max_bit_size,) + Instruction::RangeCheck { value, max_bit_size, assert_message } => { + let message = + assert_message.as_ref().map(|message| format!(", {message:?}")).unwrap_or_default(); + writeln!(f, "range_check {} to {} bits{}", show(*value), *max_bit_size, message) } Instruction::IfElse { then_condition, then_value, else_condition, else_value } => { let then_condition = show(*then_condition); diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 818d9c1b065..40e53cc4974 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -155,6 +155,7 @@ pub(crate) enum ParsedInstruction { RangeCheck { value: ParsedValue, max_bit_size: u32, + assert_message: Option, }, Store { value: ParsedValue, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 995f5e41386..37f0fa30393 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -322,9 +322,9 @@ impl Translator { let value_id = self.builder.insert_not(value); self.define_variable(target, value_id)?; } - ParsedInstruction::RangeCheck { value, max_bit_size } => { + ParsedInstruction::RangeCheck { value, max_bit_size, assert_message } => { let value = self.translate_value(value)?; - self.builder.insert_range_check(value, max_bit_size, None); + self.builder.insert_range_check(value, max_bit_size, assert_message); } ParsedInstruction::Store { value, address } => { let value = self.translate_value(value)?; diff --git a/compiler/noirc_evaluator/src/ssa/parser/mod.rs b/compiler/noirc_evaluator/src/ssa/parser/mod.rs index 60dc6b4e504..3bd0563e5e6 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/mod.rs @@ -449,7 +449,11 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::Keyword(Keyword::To))?; let max_bit_size = self.eat_int_or_error()?.to_u128() as u32; self.eat_or_error(Token::Keyword(Keyword::Bits))?; - Ok(Some(ParsedInstruction::RangeCheck { value, max_bit_size })) + + let assert_message = + if self.eat(Token::Comma)? { Some(self.eat_str_or_error()?) } else { None }; + + Ok(Some(ParsedInstruction::RangeCheck { value, max_bit_size, assert_message })) } fn parse_store(&mut self) -> ParseResult> { @@ -988,6 +992,10 @@ impl<'a> Parser<'a> { } } + fn eat_str_or_error(&mut self) -> ParseResult { + if let Some(message) = self.eat_str()? { Ok(message) } else { self.expected_string() } + } + fn eat_byte_str(&mut self) -> ParseResult> { if matches!(self.token.token(), Token::ByteStr(..)) { let token = self.bump()?; @@ -1037,6 +1045,13 @@ impl<'a> Parser<'a> { }) } + fn expected_string(&mut self) -> ParseResult { + Err(ParserError::ExpectedString { + found: self.token.token().clone(), + span: self.token.span(), + }) + } + fn expected_string_or_data(&mut self) -> ParseResult { Err(ParserError::ExpectedStringOrData { found: self.token.token().clone(), @@ -1120,6 +1135,8 @@ pub(crate) enum ParserError { ExpectedType { found: Token, span: Span }, #[error("Expected an instruction or terminator, found '{found}'")] ExpectedInstructionOrTerminator { found: Token, span: Span }, + #[error("Expected a string literal, found '{found}'")] + ExpectedString { found: Token, span: Span }, #[error("Expected a string literal or 'data', found '{found}'")] ExpectedStringOrData { found: Token, span: Span }, #[error("Expected a byte string literal, found '{found}'")] @@ -1146,6 +1163,7 @@ impl ParserError { | ParserError::ExpectedInt { span, .. } | ParserError::ExpectedType { span, .. } | ParserError::ExpectedInstructionOrTerminator { span, .. } + | ParserError::ExpectedString { span, .. } | ParserError::ExpectedStringOrData { span, .. } | ParserError::ExpectedByteString { span, .. } | ParserError::ExpectedValue { span, .. } diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index c2d0fe9a022..4ed4dd82c72 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -494,6 +494,18 @@ fn test_range_check() { assert_ssa_roundtrip(src); } +#[test] +fn test_range_check_with_message() { + let src = r#" + acir(inline) fn main f0 { + b0(v0: Field): + range_check v0 to 8 bits, "overflow error\n\t" + return + } + "#; + assert_ssa_roundtrip(src); +} + #[test] fn test_allocate() { let src = "