diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 0d006a0661..6c7608a2f1 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -132,7 +132,8 @@ pub(crate) enum ParsedInstruction { #[derive(Debug)] pub(crate) enum AssertMessage { - String(String), + Static(String), + Dynamic(Vec), } #[derive(Debug)] diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 26f3de05ec..e78cbbd75a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use acvm::acir::circuit::ErrorSelector; + use crate::ssa::{ function_builder::FunctionBuilder, ir::{ @@ -34,6 +36,8 @@ struct Translator { /// passes already which replaced some of the original IDs. The translator /// will recreate the SSA step by step, which can result in a new ID layout. variables: HashMap>, + + error_selector_counter: u64, } impl Translator { @@ -67,8 +71,13 @@ impl Translator { functions.insert(function.internal_name.clone(), function_id); } - let mut translator = - Self { builder, functions, variables: HashMap::new(), blocks: HashMap::new() }; + let mut translator = Self { + builder, + functions, + variables: HashMap::new(), + blocks: HashMap::new(), + error_selector_counter: 0, + }; translator.translate_function_body(main_function)?; Ok(translator) @@ -205,9 +214,18 @@ impl Translator { let lhs = self.translate_value(lhs)?; let rhs = self.translate_value(rhs)?; let assert_message = match assert_message { - Some(AssertMessage::String(string)) => { + Some(AssertMessage::Static(string)) => { Some(ConstrainError::StaticString(string)) } + Some(AssertMessage::Dynamic(values)) => { + let error_selector = ErrorSelector::new(self.error_selector_counter); + self.error_selector_counter += 1; + + let is_string_type = false; + let values = self.translate_values(values)?; + + Some(ConstrainError::Dynamic(error_selector, is_string_type, values)) + } None => None, }; self.builder.insert_constrain(lhs, rhs, assert_message); diff --git a/compiler/noirc_evaluator/src/ssa/parser/mod.rs b/compiler/noirc_evaluator/src/ssa/parser/mod.rs index 411ffd9645..3d8bd37dea 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/mod.rs @@ -316,9 +316,11 @@ impl<'a> Parser<'a> { let assert_message = if self.eat(Token::Comma)? { if let Some(str) = self.eat_str()? { - Some(AssertMessage::String(str)) + Some(AssertMessage::Static(str)) + } else if self.eat_keyword(Keyword::Data)? { + Some(AssertMessage::Dynamic(self.parse_comma_separated_values()?)) } else { - todo!("Dynamic assert message") + return self.expected_string_or_data(); } } else { None @@ -839,6 +841,13 @@ impl<'a> Parser<'a> { }) } + fn expected_string_or_data(&mut self) -> ParseResult { + Err(ParserError::ExpectedStringOrData { + found: self.token.token().clone(), + span: self.token.to_span(), + }) + } + fn expected_identifier(&mut self) -> ParseResult { Err(ParserError::ExpectedIdentifier { found: self.token.token().clone(), @@ -900,6 +909,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 or 'data', found '{found}'")] + ExpectedStringOrData { found: Token, span: Span }, #[error("Expected a value, found '{found}'")] ExpectedValue { found: Token, span: Span }, #[error("Multiple return values only allowed for call")] @@ -916,6 +927,7 @@ impl ParserError { | ParserError::ExpectedInt { span, .. } | ParserError::ExpectedType { span, .. } | ParserError::ExpectedInstructionOrTerminator { span, .. } + | ParserError::ExpectedStringOrData { span, .. } | ParserError::ExpectedValue { span, .. } => *span, ParserError::MultipleReturnValuesOnlyAllowedForCall { second_target, .. } => { second_target.span diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 918e5e3d44..593b66d0c9 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -226,6 +226,19 @@ fn test_constrain_with_static_message() { assert_ssa_roundtrip(src); } +#[test] +fn test_constrain_with_dynamic_message() { + let src = " + acir(inline) fn main f0 { + b0(v0: Field, v1: Field): + v7 = make_array [u8 123, u8 120, u8 125, u8 32, u8 123, u8 121, u8 125] : [u8; 7] + constrain v0 == Field 1, data v7, u32 2, v0, v1 + return + } + "; + assert_ssa_roundtrip(src); +} + #[test] fn test_enable_side_effects() { let src = " diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index a055a08d11..d8dd4ec011 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -122,6 +122,7 @@ pub(crate) enum Keyword { Call, Cast, Constrain, + Data, DecRc, Div, Inline, @@ -178,6 +179,7 @@ impl Keyword { "call" => Keyword::Call, "cast" => Keyword::Cast, "constrain" => Keyword::Constrain, + "data" => Keyword::Data, "dec_rc" => Keyword::DecRc, "div" => Keyword::Div, "else" => Keyword::Else, @@ -238,6 +240,7 @@ impl Display for Keyword { Keyword::Call => write!(f, "call"), Keyword::Cast => write!(f, "cast"), Keyword::Constrain => write!(f, "constrain"), + Keyword::Data => write!(f, "data"), Keyword::DecRc => write!(f, "dec_rc"), Keyword::Div => write!(f, "div"), Keyword::Else => write!(f, "else"),