Skip to content

Commit

Permalink
fix: parse a bit more SSA stuff (#6599)
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored Nov 22, 2024
1 parent 8e046af commit 0a6207d
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 13 deletions.
4 changes: 2 additions & 2 deletions compiler/noirc_evaluator/src/ssa/ir/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,13 +272,13 @@ fn display_constrain_error(
) -> Result {
match error {
ConstrainError::StaticString(assert_message_string) => {
writeln!(f, " '{assert_message_string:?}'")
writeln!(f, ", {assert_message_string:?}")
}
ConstrainError::Dynamic(_, is_string, values) => {
if let Some(constant_string) =
try_to_extract_string_from_error_payload(*is_string, values, &function.dfg)
{
writeln!(f, " '{}'", constant_string)
writeln!(f, ", {constant_string:?}")
} else {
writeln!(f, ", data {}", value_list(function, values))
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/noirc_evaluator/src/ssa/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub(crate) enum ParsedInstruction {
Constrain {
lhs: ParsedValue,
rhs: ParsedValue,
assert_message: Option<AssertMessage>,
},
DecrementRc {
value: ParsedValue,
Expand Down Expand Up @@ -129,6 +130,12 @@ pub(crate) enum ParsedInstruction {
},
}

#[derive(Debug)]
pub(crate) enum AssertMessage {
Static(String),
Dynamic(Vec<ParsedValue>),
}

#[derive(Debug)]
pub(crate) enum ParsedTerminator {
Jmp { destination: Identifier, arguments: Vec<ParsedValue> },
Expand Down
41 changes: 34 additions & 7 deletions compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
use std::collections::HashMap;

use acvm::acir::circuit::ErrorSelector;

use crate::ssa::{
function_builder::FunctionBuilder,
ir::{basic_block::BasicBlockId, function::FunctionId, value::ValueId},
ir::{
basic_block::BasicBlockId, function::FunctionId, instruction::ConstrainError,
value::ValueId,
},
};

use super::{
Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedSsa, ParsedTerminator,
ParsedValue, RuntimeType, Ssa, SsaError,
ast::AssertMessage, Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedSsa,
ParsedTerminator, ParsedValue, RuntimeType, Ssa, SsaError,
};

impl ParsedSsa {
Expand All @@ -31,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<FunctionId, HashMap<String, ValueId>>,

error_selector_counter: u64,
}

impl Translator {
Expand Down Expand Up @@ -64,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)
Expand Down Expand Up @@ -198,10 +210,25 @@ impl Translator {
let value_id = self.builder.insert_cast(lhs, typ);
self.define_variable(target, value_id)?;
}
ParsedInstruction::Constrain { lhs, rhs } => {
ParsedInstruction::Constrain { lhs, rhs, assert_message } => {
let lhs = self.translate_value(lhs)?;
let rhs = self.translate_value(rhs)?;
self.builder.insert_constrain(lhs, rhs, None);
let assert_message = match assert_message {
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);
}
ParsedInstruction::DecrementRc { value } => {
let value = self.translate_value(value)?;
Expand Down
46 changes: 45 additions & 1 deletion compiler/noirc_evaluator/src/ssa/parser/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl<'a> Lexer<'a> {
Some('&') => self.single_char_token(Token::Ampersand),
Some('-') if self.peek_char() == Some('>') => self.double_char_token(Token::Arrow),
Some('-') => self.single_char_token(Token::Dash),
Some('"') => self.eat_string_literal(),
Some(ch) if ch.is_ascii_alphanumeric() || ch == '_' => self.eat_alpha_numeric(ch),
Some(char) => Err(LexerError::UnexpectedCharacter {
char,
Expand Down Expand Up @@ -177,6 +178,41 @@ impl<'a> Lexer<'a> {
Ok(integer_token.into_span(start, end))
}

fn eat_string_literal(&mut self) -> SpannedTokenResult {
let start = self.position;
let mut string = String::new();

while let Some(next) = self.next_char() {
let char = match next {
'"' => break,
'\\' => match self.next_char() {
Some('r') => '\r',
Some('n') => '\n',
Some('t') => '\t',
Some('0') => '\0',
Some('"') => '"',
Some('\\') => '\\',
Some(escaped) => {
let span = Span::inclusive(start, self.position);
return Err(LexerError::InvalidEscape { escaped, span });
}
None => {
let span = Span::inclusive(start, self.position);
return Err(LexerError::UnterminatedStringLiteral { span });
}
},
other => other,
};

string.push(char);
}

let str_literal_token = Token::Str(string);

let end = self.position;
Ok(str_literal_token.into_span(start, end))
}

fn eat_while<F: Fn(char) -> bool>(
&mut self,
initial_char: Option<char>,
Expand Down Expand Up @@ -247,14 +283,22 @@ pub(crate) enum LexerError {
InvalidIntegerLiteral { span: Span, found: String },
#[error("Integer literal too large")]
IntegerLiteralTooLarge { span: Span, limit: String },
#[error("Unterminated string literal")]
UnterminatedStringLiteral { span: Span },
#[error(
"'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."
)]
InvalidEscape { escaped: char, span: Span },
}

impl LexerError {
pub(crate) fn span(&self) -> Span {
match self {
LexerError::UnexpectedCharacter { span, .. }
| LexerError::InvalidIntegerLiteral { span, .. }
| LexerError::IntegerLiteralTooLarge { span, .. } => *span,
| LexerError::IntegerLiteralTooLarge { span, .. }
| LexerError::UnterminatedStringLiteral { span }
| LexerError::InvalidEscape { span, .. } => *span,
}
}
}
45 changes: 42 additions & 3 deletions compiler/noirc_evaluator/src/ssa/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use super::{

use acvm::{AcirField, FieldElement};
use ast::{
Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedParameter, ParsedSsa,
ParsedValue,
AssertMessage, Identifier, ParsedBlock, ParsedFunction, ParsedInstruction, ParsedParameter,
ParsedSsa, ParsedValue,
};
use lexer::{Lexer, LexerError};
use noirc_errors::Span;
Expand Down Expand Up @@ -313,7 +313,20 @@ impl<'a> Parser<'a> {
let lhs = self.parse_value_or_error()?;
self.eat_or_error(Token::Equal)?;
let rhs = self.parse_value_or_error()?;
Ok(Some(ParsedInstruction::Constrain { lhs, rhs }))

let assert_message = if self.eat(Token::Comma)? {
if let Some(str) = self.eat_str()? {
Some(AssertMessage::Static(str))
} else if self.eat_keyword(Keyword::Data)? {
Some(AssertMessage::Dynamic(self.parse_comma_separated_values()?))
} else {
return self.expected_string_or_data();
}
} else {
None
};

Ok(Some(ParsedInstruction::Constrain { lhs, rhs, assert_message }))
}

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

if self.eat_keyword(Keyword::Function)? {
return Ok(Type::Function);
}

self.expected_type()
}

Expand Down Expand Up @@ -767,6 +784,18 @@ impl<'a> Parser<'a> {
}
}

fn eat_str(&mut self) -> ParseResult<Option<String>> {
if matches!(self.token.token(), Token::Str(..)) {
let token = self.bump()?;
match token.into_token() {
Token::Str(string) => Ok(Some(string)),
_ => unreachable!(),
}
} else {
Ok(None)
}
}

fn eat(&mut self, token: Token) -> ParseResult<bool> {
if self.token.token() == &token {
self.bump()?;
Expand Down Expand Up @@ -812,6 +841,13 @@ impl<'a> Parser<'a> {
})
}

fn expected_string_or_data<T>(&mut self) -> ParseResult<T> {
Err(ParserError::ExpectedStringOrData {
found: self.token.token().clone(),
span: self.token.to_span(),
})
}

fn expected_identifier<T>(&mut self) -> ParseResult<T> {
Err(ParserError::ExpectedIdentifier {
found: self.token.token().clone(),
Expand Down Expand Up @@ -873,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")]
Expand All @@ -889,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
Expand Down
37 changes: 37 additions & 0 deletions compiler/noirc_evaluator/src/ssa/parser/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,31 @@ fn test_constrain() {
assert_ssa_roundtrip(src);
}

#[test]
fn test_constrain_with_static_message() {
let src = r#"
acir(inline) fn main f0 {
b0(v0: Field):
constrain v0 == Field 1, "Oh no!"
return
}
"#;
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 = "
Expand Down Expand Up @@ -441,3 +466,15 @@ fn test_negative() {
";
assert_ssa_roundtrip(src);
}

#[test]
fn test_function_type() {
let src = "
acir(inline) fn main f0 {
b0():
v0 = allocate -> &mut function
return
}
";
assert_ssa_roundtrip(src);
}
8 changes: 8 additions & 0 deletions compiler/noirc_evaluator/src/ssa/parser/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ impl SpannedToken {
pub(crate) enum Token {
Ident(String),
Int(FieldElement),
Str(String),
Keyword(Keyword),
IntType(IntType),
/// =
Expand Down Expand Up @@ -77,6 +78,7 @@ impl Display for Token {
match self {
Token::Ident(ident) => write!(f, "{}", ident),
Token::Int(int) => write!(f, "{}", int),
Token::Str(string) => write!(f, "{string:?}"),
Token::Keyword(keyword) => write!(f, "{}", keyword),
Token::IntType(int_type) => write!(f, "{}", int_type),
Token::Assign => write!(f, "="),
Expand Down Expand Up @@ -120,6 +122,7 @@ pub(crate) enum Keyword {
Call,
Cast,
Constrain,
Data,
DecRc,
Div,
Inline,
Expand All @@ -130,6 +133,7 @@ pub(crate) enum Keyword {
Field,
Fold,
Fn,
Function,
IncRc,
Index,
Jmp,
Expand Down Expand Up @@ -175,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,
Expand All @@ -185,6 +190,7 @@ impl Keyword {
"Field" => Keyword::Field,
"fold" => Keyword::Fold,
"fn" => Keyword::Fn,
"function" => Keyword::Function,
"inc_rc" => Keyword::IncRc,
"index" => Keyword::Index,
"jmp" => Keyword::Jmp,
Expand Down Expand Up @@ -234,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"),
Expand All @@ -242,6 +249,7 @@ impl Display for Keyword {
Keyword::Field => write!(f, "Field"),
Keyword::Fold => write!(f, "fold"),
Keyword::Fn => write!(f, "fn"),
Keyword::Function => write!(f, "function"),
Keyword::IncRc => write!(f, "inc_rc"),
Keyword::Index => write!(f, "index"),
Keyword::Inline => write!(f, "inline"),
Expand Down

0 comments on commit 0a6207d

Please sign in to comment.