From 0b999d304da9e186cfdac96ff953921baf26a86c Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 13 Sep 2024 14:36:35 -0500 Subject: [PATCH 1/3] Parse a statement as an expression --- aztec_macros/src/utils/parse_utils.rs | 1 + compiler/noirc_frontend/src/ast/expression.rs | 7 ++++- compiler/noirc_frontend/src/ast/visitor.rs | 1 + .../src/elaborator/expressions.rs | 30 +++++++++++++++++-- .../noirc_frontend/src/hir/comptime/value.rs | 18 ++++++++++- .../src/hir/resolution/errors.rs | 9 ++++++ compiler/noirc_frontend/src/parser/parser.rs | 15 ++++------ .../src/parser/parser/primitives.rs | 22 +++++++++++++- .../comptime_parse_statement_as_expression/' | 4 +++ .../Nargo.toml | 7 +++++ .../src/main.nr | 27 +++++++++++++++++ tooling/lsp/src/requests/inlay_hint.rs | 1 + tooling/nargo_fmt/src/rewrite/expr.rs | 5 ++++ 13 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 test_programs/compile_success_empty/comptime_parse_statement_as_expression/' create mode 100644 test_programs/compile_success_empty/comptime_parse_statement_as_expression/Nargo.toml create mode 100644 test_programs/compile_success_empty/comptime_parse_statement_as_expression/src/main.nr diff --git a/aztec_macros/src/utils/parse_utils.rs b/aztec_macros/src/utils/parse_utils.rs index 046ced105bf..61410bbb5b2 100644 --- a/aztec_macros/src/utils/parse_utils.rs +++ b/aztec_macros/src/utils/parse_utils.rs @@ -299,6 +299,7 @@ fn empty_expression(expression: &mut Expression) { ExpressionKind::Quote(..) | ExpressionKind::Resolved(_) | ExpressionKind::Interned(_) + | ExpressionKind::InternedStatement(_) | ExpressionKind::Error => (), } } diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 821750c8cb0..cce8a50601e 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -7,7 +7,7 @@ use crate::ast::{ }; use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; -use crate::node_interner::{ExprId, InternedExpressionKind, QuotedTypeId}; +use crate::node_interner::{ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId}; use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; @@ -48,6 +48,10 @@ pub enum ExpressionKind { // The actual ExpressionKind can be retrieved with a NodeInterner. Interned(InternedExpressionKind), + /// Interned statements are allowed to be parsed as expressions in case they resolve + /// to an StatementKind::Expression or StatementKind::Semi. + InternedStatement(InternedStatementKind), + Error, } @@ -617,6 +621,7 @@ impl Display for ExpressionKind { write!(f, "quote {{ {} }}", tokens.join(" ")) } AsTraitPath(path) => write!(f, "{path}"), + InternedStatement(_) => write!(f, "?InternedStatement"), } } } diff --git a/compiler/noirc_frontend/src/ast/visitor.rs b/compiler/noirc_frontend/src/ast/visitor.rs index 8f207a55e9a..9b2354850cd 100644 --- a/compiler/noirc_frontend/src/ast/visitor.rs +++ b/compiler/noirc_frontend/src/ast/visitor.rs @@ -841,6 +841,7 @@ impl Expression { ExpressionKind::Quote(tokens) => visitor.visit_quote(tokens), ExpressionKind::Resolved(expr_id) => visitor.visit_resolved_expression(*expr_id), ExpressionKind::Interned(id) => visitor.visit_interned_expression(*id), + ExpressionKind::InternedStatement(id) => visitor.visit_interned_statement(*id), ExpressionKind::Error => visitor.visit_error_expression(), } } diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 0ab9a252f09..7a5bcb89d6c 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -25,9 +25,9 @@ use crate::{ macros_api::{ BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, HirLiteral, HirStatement, Ident, IndexExpression, Literal, MemberAccessExpression, - MethodCallExpression, PrefixExpression, + MethodCallExpression, PrefixExpression, StatementKind, }, - node_interner::{DefinitionKind, ExprId, FuncId, TraitMethodId}, + node_interner::{DefinitionKind, ExprId, FuncId, InternedStatementKind, TraitMethodId}, token::Tokens, QuotedType, Shared, StructType, Type, }; @@ -67,6 +67,9 @@ impl<'context> Elaborator<'context> { let expr = Expression::new(expr_kind.clone(), expr.span); return self.elaborate_expression(expr); } + ExpressionKind::InternedStatement(id) => { + return self.elaborate_interned_statement_as_expr(id, expr.span); + } ExpressionKind::Error => (HirExpression::Error, Type::Error), ExpressionKind::Unquote(_) => { self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); @@ -80,6 +83,29 @@ impl<'context> Elaborator<'context> { (id, typ) } + fn elaborate_interned_statement_as_expr( + &mut self, + id: InternedStatementKind, + span: Span, + ) -> (ExprId, Type) { + match self.interner.get_statement_kind(id) { + StatementKind::Expression(expr) | StatementKind::Semi(expr) => { + self.elaborate_expression(expr.clone()) + } + StatementKind::Interned(id) => self.elaborate_interned_statement_as_expr(*id, span), + StatementKind::Error => { + let expr = Expression::new(ExpressionKind::Error, span); + self.elaborate_expression(expr) + } + other => { + let statement = other.to_string(); + self.push_err(ResolverError::InvalidInternedStatementInExpr { statement, span }); + let expr = Expression::new(ExpressionKind::Error, span); + self.elaborate_expression(expr) + } + } + } + pub(super) fn elaborate_block(&mut self, block: BlockExpression) -> (HirExpression, Type) { let (block, typ) = self.elaborate_block_expression(block); (HirExpression::Block(block), typ) diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 966138d9dea..4c1e2c7cf2f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -24,7 +24,7 @@ use crate::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, StructId, }, - node_interner::{ExprId, FuncId, StmtId, TraitId, TraitImplId}, + node_interner::{ExprId, FuncId, InternedStatementKind, StmtId, TraitId, TraitImplId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, QuotedType, Shared, Type, TypeBindings, @@ -454,6 +454,9 @@ impl Value { Value::Expr(ExprValue::Expression(expr)) => { Token::InternedExpr(interner.push_expression_kind(expr)) } + Value::Expr(ExprValue::Statement(StatementKind::Expression(expr))) => { + Token::InternedExpr(interner.push_expression_kind(expr.kind)) + } Value::Expr(ExprValue::Statement(statement)) => { Token::InternedStatement(interner.push_statement_kind(statement)) } @@ -872,9 +875,22 @@ fn remove_interned_in_expression_kind( remove_interned_in_expression_kind(interner, expr) } ExpressionKind::Error => expr, + ExpressionKind::InternedStatement(id) => remove_interned_in_statement_expr(interner, id), } } +fn remove_interned_in_statement_expr( + interner: &NodeInterner, + id: InternedStatementKind, +) -> ExpressionKind { + let expr = match interner.get_statement_kind(id).clone() { + StatementKind::Expression(expr) | StatementKind::Semi(expr) => expr.kind, + StatementKind::Interned(id) => remove_interned_in_statement_expr(interner, id), + _ => ExpressionKind::Error, + }; + remove_interned_in_expression_kind(interner, expr) +} + fn remove_interned_in_literal(interner: &NodeInterner, literal: Literal) -> Literal { match literal { Literal::Array(array_literal) => { diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 92c71c0ebf8..680d04f0842 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -130,6 +130,8 @@ pub enum ResolverError { ComptimeTypeInRuntimeCode { typ: String, span: Span }, #[error("Comptime variable `{name}` cannot be mutated in a non-comptime context")] MutatingComptimeInNonComptimeContext { name: String, span: Span }, + #[error("Failed to parse `{statement}` as an expression")] + InvalidInternedStatementInExpr { statement: String, span: Span }, } impl ResolverError { @@ -531,6 +533,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, + ResolverError::InvalidInternedStatementInExpr { statement, span } => { + Diagnostic::simple_error( + format!("Failed to parse `{statement}` as an expression"), + format!("The statement was used from a macro here"), + *span, + ) + }, } } } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 0c4dbfda99e..972ad0e8c90 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -24,7 +24,10 @@ //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. use self::path::as_trait_path; -use self::primitives::{keyword, macro_quote_marker, mutable_reference, variable}; +use self::primitives::{ + interned_statement, interned_statement_expr, keyword, macro_quote_marker, mutable_reference, + variable, +}; use self::types::{generic_type_args, maybe_comp_time}; use attributes::{attributes, inner_attribute, validate_secondary_attributes}; use doc_comments::{inner_doc_comments, outer_doc_comments}; @@ -512,15 +515,6 @@ where keyword(Keyword::Comptime).ignore_then(comptime_statement).map(StatementKind::Comptime) } -pub(super) fn interned_statement() -> impl NoirParser { - token_kind(TokenKind::InternedStatement).map(|token| match token { - Token::InternedStatement(id) => StatementKind::Interned(id), - _ => { - unreachable!("token_kind(InternedStatement) guarantees we parse an interned statement") - } - }) -} - /// Comptime in an expression position only accepts entire blocks fn comptime_expr<'a, S>(statement: S) -> impl NoirParser + 'a where @@ -1158,6 +1152,7 @@ where as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), macro_quote_marker(), interned_expr(), + interned_statement_expr(), )) .map_with_span(Expression::new) .or(parenthesized(expr_parser.clone()).map_with_span(|sub_expr, span| { diff --git a/compiler/noirc_frontend/src/parser/parser/primitives.rs b/compiler/noirc_frontend/src/parser/parser/primitives.rs index c1516e2c927..7fcca89f70c 100644 --- a/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ b/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -1,7 +1,7 @@ use chumsky::prelude::*; use crate::ast::{ExpressionKind, GenericTypeArgs, Ident, PathSegment, UnaryOp}; -use crate::macros_api::UnresolvedType; +use crate::macros_api::{StatementKind, UnresolvedType}; use crate::parser::ParserErrorReason; use crate::{ parser::{labels::ParsingRuleLabel, ExprParser, NoirParser, ParserError}, @@ -126,6 +126,26 @@ pub(super) fn interned_expr() -> impl NoirParser { }) } +pub(super) fn interned_statement() -> impl NoirParser { + token_kind(TokenKind::InternedStatement).map(|token| match token { + Token::InternedStatement(id) => StatementKind::Interned(id), + _ => { + unreachable!("token_kind(InternedStatement) guarantees we parse an interned statement") + } + }) +} + +// This rule is so that we can re-parse StatementKind::Expression and Semi in +// an expression position (ignoring the semicolon) if needed. +pub(super) fn interned_statement_expr() -> impl NoirParser { + token_kind(TokenKind::InternedStatement).map(|token| match token { + Token::InternedStatement(id) => ExpressionKind::InternedStatement(id), + _ => { + unreachable!("token_kind(InternedStatement) guarantees we parse an interned statement") + } + }) +} + #[cfg(test)] mod test { use crate::parser::parser::{ diff --git a/test_programs/compile_success_empty/comptime_parse_statement_as_expression/' b/test_programs/compile_success_empty/comptime_parse_statement_as_expression/' new file mode 100644 index 00000000000..ee0fec02eb7 --- /dev/null +++ b/test_programs/compile_success_empty/comptime_parse_statement_as_expression/' @@ -0,0 +1,4 @@ +struct ArrData { + a: [Field; N], + b: [Field; N + N - 1], +} diff --git a/test_programs/compile_success_empty/comptime_parse_statement_as_expression/Nargo.toml b/test_programs/compile_success_empty/comptime_parse_statement_as_expression/Nargo.toml new file mode 100644 index 00000000000..65fa5b1ed06 --- /dev/null +++ b/test_programs/compile_success_empty/comptime_parse_statement_as_expression/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_parse_statement_as_expression" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/comptime_parse_statement_as_expression/src/main.nr b/test_programs/compile_success_empty/comptime_parse_statement_as_expression/src/main.nr new file mode 100644 index 00000000000..4fa47dbc2f7 --- /dev/null +++ b/test_programs/compile_success_empty/comptime_parse_statement_as_expression/src/main.nr @@ -0,0 +1,27 @@ +fn main() { + comptime + { + let block = quote[{ + 1; + 2; + 3 + }]; + let statements = block.as_expr().unwrap().as_block().unwrap(); + let last = statements.pop_back().1; + + // `3` should fit in an expression position even though it is + // originally a StatementKind::Expression + let regression1 = quote[{ + let _ = $last; + }]; + assert(regression1.as_expr().is_some()); + + // `1;` should fit in an expression position even though it is + // originally a StatementKind::Semi + let first = statements.pop_front().0; + let regression2 = quote[{ + let _ = $first; + }]; + assert(regression2.as_expr().is_some()); + } +} diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index 43e0227fa26..012e2cbef13 100644 --- a/tooling/lsp/src/requests/inlay_hint.rs +++ b/tooling/lsp/src/requests/inlay_hint.rs @@ -542,6 +542,7 @@ fn get_expression_name(expression: &Expression) -> Option { | ExpressionKind::Comptime(..) | ExpressionKind::Resolved(..) | ExpressionKind::Interned(..) + | ExpressionKind::InternedStatement(..) | ExpressionKind::Literal(..) | ExpressionKind::Unsafe(..) | ExpressionKind::Error => None, diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 2d967e94833..81a7a219e06 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -178,6 +178,11 @@ pub(crate) fn rewrite( ExpressionKind::Interned(_) => { unreachable!("ExpressionKind::Interned should only emitted by the comptime interpreter") } + ExpressionKind::InternedStatement(_) => { + unreachable!( + "ExpressionKind::InternedStatement should only emitted by the comptime interpreter" + ) + } ExpressionKind::Unquote(expr) => { if matches!(&expr.kind, ExpressionKind::Variable(..)) { format!("${expr}") From 3d41609039d9cbb1e18d8198ef4cdb5962081ae2 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 13 Sep 2024 14:42:37 -0500 Subject: [PATCH 2/3] Clippy --- compiler/noirc_frontend/src/hir/resolution/errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 680d04f0842..08f57ae562a 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -536,7 +536,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { ResolverError::InvalidInternedStatementInExpr { statement, span } => { Diagnostic::simple_error( format!("Failed to parse `{statement}` as an expression"), - format!("The statement was used from a macro here"), + "The statement was used from a macro here".to_string(), *span, ) }, From c261fed1dde6384dc44c7eed59023a612a99d763 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 13 Sep 2024 14:48:27 -0500 Subject: [PATCH 3/3] Remove extra file --- .../comptime_parse_statement_as_expression/' | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 test_programs/compile_success_empty/comptime_parse_statement_as_expression/' diff --git a/test_programs/compile_success_empty/comptime_parse_statement_as_expression/' b/test_programs/compile_success_empty/comptime_parse_statement_as_expression/' deleted file mode 100644 index ee0fec02eb7..00000000000 --- a/test_programs/compile_success_empty/comptime_parse_statement_as_expression/' +++ /dev/null @@ -1,4 +0,0 @@ -struct ArrData { - a: [Field; N], - b: [Field; N + N - 1], -}