Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: parse a bit more SSA stuff #6599

Merged
merged 3 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -22,7 +22,7 @@
chars: source.char_indices(),
position: 0,
done: false,
max_integer: BigInt::from_biguint(num_bigint::Sign::Plus, FieldElement::modulus())

Check warning on line 25 in compiler/noirc_evaluator/src/ssa/parser/lexer.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (biguint)
- BigInt::one(),
}
}
Expand Down Expand Up @@ -61,6 +61,7 @@
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 @@
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 @@
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
Loading