Skip to content

Commit 0a6207d

Browse files
authored
fix: parse a bit more SSA stuff (#6599)
1 parent 8e046af commit 0a6207d

File tree

7 files changed

+175
-13
lines changed

7 files changed

+175
-13
lines changed

compiler/noirc_evaluator/src/ssa/ir/printer.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -272,13 +272,13 @@ fn display_constrain_error(
272272
) -> Result {
273273
match error {
274274
ConstrainError::StaticString(assert_message_string) => {
275-
writeln!(f, " '{assert_message_string:?}'")
275+
writeln!(f, ", {assert_message_string:?}")
276276
}
277277
ConstrainError::Dynamic(_, is_string, values) => {
278278
if let Some(constant_string) =
279279
try_to_extract_string_from_error_payload(*is_string, values, &function.dfg)
280280
{
281-
writeln!(f, " '{}'", constant_string)
281+
writeln!(f, ", {constant_string:?}")
282282
} else {
283283
writeln!(f, ", data {}", value_list(function, values))
284284
}

compiler/noirc_evaluator/src/ssa/parser/ast.rs

+7
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub(crate) enum ParsedInstruction {
8989
Constrain {
9090
lhs: ParsedValue,
9191
rhs: ParsedValue,
92+
assert_message: Option<AssertMessage>,
9293
},
9394
DecrementRc {
9495
value: ParsedValue,
@@ -129,6 +130,12 @@ pub(crate) enum ParsedInstruction {
129130
},
130131
}
131132

133+
#[derive(Debug)]
134+
pub(crate) enum AssertMessage {
135+
Static(String),
136+
Dynamic(Vec<ParsedValue>),
137+
}
138+
132139
#[derive(Debug)]
133140
pub(crate) enum ParsedTerminator {
134141
Jmp { destination: Identifier, arguments: Vec<ParsedValue> },

compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs

+34-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
use std::collections::HashMap;
22

3+
use acvm::acir::circuit::ErrorSelector;
4+
35
use crate::ssa::{
46
function_builder::FunctionBuilder,
5-
ir::{basic_block::BasicBlockId, function::FunctionId, value::ValueId},
7+
ir::{
8+
basic_block::BasicBlockId, function::FunctionId, instruction::ConstrainError,
9+
value::ValueId,
10+
},
611
};
712

813
use super::{
9-
Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedSsa, ParsedTerminator,
10-
ParsedValue, RuntimeType, Ssa, SsaError,
14+
ast::AssertMessage, Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedSsa,
15+
ParsedTerminator, ParsedValue, RuntimeType, Ssa, SsaError,
1116
};
1217

1318
impl ParsedSsa {
@@ -31,6 +36,8 @@ struct Translator {
3136
/// passes already which replaced some of the original IDs. The translator
3237
/// will recreate the SSA step by step, which can result in a new ID layout.
3338
variables: HashMap<FunctionId, HashMap<String, ValueId>>,
39+
40+
error_selector_counter: u64,
3441
}
3542

3643
impl Translator {
@@ -64,8 +71,13 @@ impl Translator {
6471
functions.insert(function.internal_name.clone(), function_id);
6572
}
6673

67-
let mut translator =
68-
Self { builder, functions, variables: HashMap::new(), blocks: HashMap::new() };
74+
let mut translator = Self {
75+
builder,
76+
functions,
77+
variables: HashMap::new(),
78+
blocks: HashMap::new(),
79+
error_selector_counter: 0,
80+
};
6981
translator.translate_function_body(main_function)?;
7082

7183
Ok(translator)
@@ -198,10 +210,25 @@ impl Translator {
198210
let value_id = self.builder.insert_cast(lhs, typ);
199211
self.define_variable(target, value_id)?;
200212
}
201-
ParsedInstruction::Constrain { lhs, rhs } => {
213+
ParsedInstruction::Constrain { lhs, rhs, assert_message } => {
202214
let lhs = self.translate_value(lhs)?;
203215
let rhs = self.translate_value(rhs)?;
204-
self.builder.insert_constrain(lhs, rhs, None);
216+
let assert_message = match assert_message {
217+
Some(AssertMessage::Static(string)) => {
218+
Some(ConstrainError::StaticString(string))
219+
}
220+
Some(AssertMessage::Dynamic(values)) => {
221+
let error_selector = ErrorSelector::new(self.error_selector_counter);
222+
self.error_selector_counter += 1;
223+
224+
let is_string_type = false;
225+
let values = self.translate_values(values)?;
226+
227+
Some(ConstrainError::Dynamic(error_selector, is_string_type, values))
228+
}
229+
None => None,
230+
};
231+
self.builder.insert_constrain(lhs, rhs, assert_message);
205232
}
206233
ParsedInstruction::DecrementRc { value } => {
207234
let value = self.translate_value(value)?;

compiler/noirc_evaluator/src/ssa/parser/lexer.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ impl<'a> Lexer<'a> {
6161
Some('&') => self.single_char_token(Token::Ampersand),
6262
Some('-') if self.peek_char() == Some('>') => self.double_char_token(Token::Arrow),
6363
Some('-') => self.single_char_token(Token::Dash),
64+
Some('"') => self.eat_string_literal(),
6465
Some(ch) if ch.is_ascii_alphanumeric() || ch == '_' => self.eat_alpha_numeric(ch),
6566
Some(char) => Err(LexerError::UnexpectedCharacter {
6667
char,
@@ -177,6 +178,41 @@ impl<'a> Lexer<'a> {
177178
Ok(integer_token.into_span(start, end))
178179
}
179180

181+
fn eat_string_literal(&mut self) -> SpannedTokenResult {
182+
let start = self.position;
183+
let mut string = String::new();
184+
185+
while let Some(next) = self.next_char() {
186+
let char = match next {
187+
'"' => break,
188+
'\\' => match self.next_char() {
189+
Some('r') => '\r',
190+
Some('n') => '\n',
191+
Some('t') => '\t',
192+
Some('0') => '\0',
193+
Some('"') => '"',
194+
Some('\\') => '\\',
195+
Some(escaped) => {
196+
let span = Span::inclusive(start, self.position);
197+
return Err(LexerError::InvalidEscape { escaped, span });
198+
}
199+
None => {
200+
let span = Span::inclusive(start, self.position);
201+
return Err(LexerError::UnterminatedStringLiteral { span });
202+
}
203+
},
204+
other => other,
205+
};
206+
207+
string.push(char);
208+
}
209+
210+
let str_literal_token = Token::Str(string);
211+
212+
let end = self.position;
213+
Ok(str_literal_token.into_span(start, end))
214+
}
215+
180216
fn eat_while<F: Fn(char) -> bool>(
181217
&mut self,
182218
initial_char: Option<char>,
@@ -247,14 +283,22 @@ pub(crate) enum LexerError {
247283
InvalidIntegerLiteral { span: Span, found: String },
248284
#[error("Integer literal too large")]
249285
IntegerLiteralTooLarge { span: Span, limit: String },
286+
#[error("Unterminated string literal")]
287+
UnterminatedStringLiteral { span: Span },
288+
#[error(
289+
"'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."
290+
)]
291+
InvalidEscape { escaped: char, span: Span },
250292
}
251293

252294
impl LexerError {
253295
pub(crate) fn span(&self) -> Span {
254296
match self {
255297
LexerError::UnexpectedCharacter { span, .. }
256298
| LexerError::InvalidIntegerLiteral { span, .. }
257-
| LexerError::IntegerLiteralTooLarge { span, .. } => *span,
299+
| LexerError::IntegerLiteralTooLarge { span, .. }
300+
| LexerError::UnterminatedStringLiteral { span }
301+
| LexerError::InvalidEscape { span, .. } => *span,
258302
}
259303
}
260304
}

compiler/noirc_evaluator/src/ssa/parser/mod.rs

+42-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use super::{
1010

1111
use acvm::{AcirField, FieldElement};
1212
use ast::{
13-
Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedParameter, ParsedSsa,
14-
ParsedValue,
13+
AssertMessage, Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedParameter,
14+
ParsedSsa, ParsedValue,
1515
};
1616
use lexer::{Lexer, LexerError};
1717
use noirc_errors::Span;
@@ -313,7 +313,20 @@ impl<'a> Parser<'a> {
313313
let lhs = self.parse_value_or_error()?;
314314
self.eat_or_error(Token::Equal)?;
315315
let rhs = self.parse_value_or_error()?;
316-
Ok(Some(ParsedInstruction::Constrain { lhs, rhs }))
316+
317+
let assert_message = if self.eat(Token::Comma)? {
318+
if let Some(str) = self.eat_str()? {
319+
Some(AssertMessage::Static(str))
320+
} else if self.eat_keyword(Keyword::Data)? {
321+
Some(AssertMessage::Dynamic(self.parse_comma_separated_values()?))
322+
} else {
323+
return self.expected_string_or_data();
324+
}
325+
} else {
326+
None
327+
};
328+
329+
Ok(Some(ParsedInstruction::Constrain { lhs, rhs, assert_message }))
317330
}
318331

319332
fn parse_decrement_rc(&mut self) -> ParseResult<Option<ParsedInstruction>> {
@@ -654,6 +667,10 @@ impl<'a> Parser<'a> {
654667
return Ok(Type::Reference(Arc::new(typ)));
655668
}
656669

670+
if self.eat_keyword(Keyword::Function)? {
671+
return Ok(Type::Function);
672+
}
673+
657674
self.expected_type()
658675
}
659676

@@ -767,6 +784,18 @@ impl<'a> Parser<'a> {
767784
}
768785
}
769786

787+
fn eat_str(&mut self) -> ParseResult<Option<String>> {
788+
if matches!(self.token.token(), Token::Str(..)) {
789+
let token = self.bump()?;
790+
match token.into_token() {
791+
Token::Str(string) => Ok(Some(string)),
792+
_ => unreachable!(),
793+
}
794+
} else {
795+
Ok(None)
796+
}
797+
}
798+
770799
fn eat(&mut self, token: Token) -> ParseResult<bool> {
771800
if self.token.token() == &token {
772801
self.bump()?;
@@ -812,6 +841,13 @@ impl<'a> Parser<'a> {
812841
})
813842
}
814843

844+
fn expected_string_or_data<T>(&mut self) -> ParseResult<T> {
845+
Err(ParserError::ExpectedStringOrData {
846+
found: self.token.token().clone(),
847+
span: self.token.to_span(),
848+
})
849+
}
850+
815851
fn expected_identifier<T>(&mut self) -> ParseResult<T> {
816852
Err(ParserError::ExpectedIdentifier {
817853
found: self.token.token().clone(),
@@ -873,6 +909,8 @@ pub(crate) enum ParserError {
873909
ExpectedType { found: Token, span: Span },
874910
#[error("Expected an instruction or terminator, found '{found}'")]
875911
ExpectedInstructionOrTerminator { found: Token, span: Span },
912+
#[error("Expected a string literal or 'data', found '{found}'")]
913+
ExpectedStringOrData { found: Token, span: Span },
876914
#[error("Expected a value, found '{found}'")]
877915
ExpectedValue { found: Token, span: Span },
878916
#[error("Multiple return values only allowed for call")]
@@ -889,6 +927,7 @@ impl ParserError {
889927
| ParserError::ExpectedInt { span, .. }
890928
| ParserError::ExpectedType { span, .. }
891929
| ParserError::ExpectedInstructionOrTerminator { span, .. }
930+
| ParserError::ExpectedStringOrData { span, .. }
892931
| ParserError::ExpectedValue { span, .. } => *span,
893932
ParserError::MultipleReturnValuesOnlyAllowedForCall { second_target, .. } => {
894933
second_target.span

compiler/noirc_evaluator/src/ssa/parser/tests.rs

+37
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,31 @@ fn test_constrain() {
214214
assert_ssa_roundtrip(src);
215215
}
216216

217+
#[test]
218+
fn test_constrain_with_static_message() {
219+
let src = r#"
220+
acir(inline) fn main f0 {
221+
b0(v0: Field):
222+
constrain v0 == Field 1, "Oh no!"
223+
return
224+
}
225+
"#;
226+
assert_ssa_roundtrip(src);
227+
}
228+
229+
#[test]
230+
fn test_constrain_with_dynamic_message() {
231+
let src = "
232+
acir(inline) fn main f0 {
233+
b0(v0: Field, v1: Field):
234+
v7 = make_array [u8 123, u8 120, u8 125, u8 32, u8 123, u8 121, u8 125] : [u8; 7]
235+
constrain v0 == Field 1, data v7, u32 2, v0, v1
236+
return
237+
}
238+
";
239+
assert_ssa_roundtrip(src);
240+
}
241+
217242
#[test]
218243
fn test_enable_side_effects() {
219244
let src = "
@@ -441,3 +466,15 @@ fn test_negative() {
441466
";
442467
assert_ssa_roundtrip(src);
443468
}
469+
470+
#[test]
471+
fn test_function_type() {
472+
let src = "
473+
acir(inline) fn main f0 {
474+
b0():
475+
v0 = allocate -> &mut function
476+
return
477+
}
478+
";
479+
assert_ssa_roundtrip(src);
480+
}

compiler/noirc_evaluator/src/ssa/parser/token.rs

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl SpannedToken {
2929
pub(crate) enum Token {
3030
Ident(String),
3131
Int(FieldElement),
32+
Str(String),
3233
Keyword(Keyword),
3334
IntType(IntType),
3435
/// =
@@ -77,6 +78,7 @@ impl Display for Token {
7778
match self {
7879
Token::Ident(ident) => write!(f, "{}", ident),
7980
Token::Int(int) => write!(f, "{}", int),
81+
Token::Str(string) => write!(f, "{string:?}"),
8082
Token::Keyword(keyword) => write!(f, "{}", keyword),
8183
Token::IntType(int_type) => write!(f, "{}", int_type),
8284
Token::Assign => write!(f, "="),
@@ -120,6 +122,7 @@ pub(crate) enum Keyword {
120122
Call,
121123
Cast,
122124
Constrain,
125+
Data,
123126
DecRc,
124127
Div,
125128
Inline,
@@ -130,6 +133,7 @@ pub(crate) enum Keyword {
130133
Field,
131134
Fold,
132135
Fn,
136+
Function,
133137
IncRc,
134138
Index,
135139
Jmp,
@@ -175,6 +179,7 @@ impl Keyword {
175179
"call" => Keyword::Call,
176180
"cast" => Keyword::Cast,
177181
"constrain" => Keyword::Constrain,
182+
"data" => Keyword::Data,
178183
"dec_rc" => Keyword::DecRc,
179184
"div" => Keyword::Div,
180185
"else" => Keyword::Else,
@@ -185,6 +190,7 @@ impl Keyword {
185190
"Field" => Keyword::Field,
186191
"fold" => Keyword::Fold,
187192
"fn" => Keyword::Fn,
193+
"function" => Keyword::Function,
188194
"inc_rc" => Keyword::IncRc,
189195
"index" => Keyword::Index,
190196
"jmp" => Keyword::Jmp,
@@ -234,6 +240,7 @@ impl Display for Keyword {
234240
Keyword::Call => write!(f, "call"),
235241
Keyword::Cast => write!(f, "cast"),
236242
Keyword::Constrain => write!(f, "constrain"),
243+
Keyword::Data => write!(f, "data"),
237244
Keyword::DecRc => write!(f, "dec_rc"),
238245
Keyword::Div => write!(f, "div"),
239246
Keyword::Else => write!(f, "else"),
@@ -242,6 +249,7 @@ impl Display for Keyword {
242249
Keyword::Field => write!(f, "Field"),
243250
Keyword::Fold => write!(f, "fold"),
244251
Keyword::Fn => write!(f, "fn"),
252+
Keyword::Function => write!(f, "function"),
245253
Keyword::IncRc => write!(f, "inc_rc"),
246254
Keyword::Index => write!(f, "index"),
247255
Keyword::Inline => write!(f, "inline"),

0 commit comments

Comments
 (0)