From 7325619e448a4873baaf6993ccc30b904f4ab7f2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 6 Sep 2024 14:24:49 -0300 Subject: [PATCH 1/4] feat: `Expr::as_let` --- aztec_macros/src/utils/parse_utils.rs | 1 + compiler/noirc_frontend/src/ast/statement.rs | 9 +- compiler/noirc_frontend/src/ast/visitor.rs | 9 +- compiler/noirc_frontend/src/debug/mod.rs | 2 + .../noirc_frontend/src/elaborator/patterns.rs | 11 ++ .../src/hir/comptime/interpreter/builtin.rs | 49 +++++- .../interpreter/builtin/builtin_helpers.rs | 7 +- .../noirc_frontend/src/hir/comptime/value.rs | 156 ++++++++++++++++-- compiler/noirc_frontend/src/lexer/token.rs | 16 +- compiler/noirc_frontend/src/node_interner.rs | 16 ++ compiler/noirc_frontend/src/parser/parser.rs | 10 +- docs/docs/noir/standard_library/meta/expr.md | 9 +- noir_stdlib/src/meta/expr.nr | 26 +++ .../comptime_expr/src/main.nr | 75 ++++++--- tooling/lsp/src/requests/completion.rs | 3 +- 15 files changed, 352 insertions(+), 47 deletions(-) diff --git a/aztec_macros/src/utils/parse_utils.rs b/aztec_macros/src/utils/parse_utils.rs index e7b3e347a96..20a7f1459c9 100644 --- a/aztec_macros/src/utils/parse_utils.rs +++ b/aztec_macros/src/utils/parse_utils.rs @@ -401,6 +401,7 @@ fn empty_pattern(pattern: &mut Pattern) { empty_pattern(pattern); } } + Pattern::Interned(_, _) => (), } } diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 30db8ad63fd..858617aea44 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -13,7 +13,7 @@ use super::{ use crate::elaborator::types::SELF_TYPE_NAME; use crate::lexer::token::SpannedToken; use crate::macros_api::{SecondaryAttribute, UnresolvedTypeData}; -use crate::node_interner::{InternedExpressionKind, InternedStatementKind}; +use crate::node_interner::{InternedExpressionKind, InternedPattern, InternedStatementKind}; use crate::parser::{ParserError, ParserErrorReason}; use crate::token::Token; @@ -565,6 +565,7 @@ pub enum Pattern { Mutable(Box, Span, /*is_synthesized*/ bool), Tuple(Vec, Span), Struct(Path, Vec<(Ident, Pattern)>, Span), + Interned(InternedPattern, Span), } impl Pattern { @@ -577,7 +578,8 @@ impl Pattern { Pattern::Identifier(ident) => ident.span(), Pattern::Mutable(_, span, _) | Pattern::Tuple(_, span) - | Pattern::Struct(_, _, span) => *span, + | Pattern::Struct(_, _, span) + | Pattern::Interned(_, span) => *span, } } pub fn name_ident(&self) -> &Ident { @@ -905,6 +907,9 @@ impl Display for Pattern { let fields = vecmap(fields, |(name, pattern)| format!("{name}: {pattern}")); write!(f, "{} {{ {} }}", typename, fields.join(", ")) } + Pattern::Interned(_, _) => { + write!(f, "?Interned") + } } } } diff --git a/compiler/noirc_frontend/src/ast/visitor.rs b/compiler/noirc_frontend/src/ast/visitor.rs index 0aeeed39dd0..d965c4e7b06 100644 --- a/compiler/noirc_frontend/src/ast/visitor.rs +++ b/compiler/noirc_frontend/src/ast/visitor.rs @@ -12,8 +12,8 @@ use crate::{ UseTreeKind, }, node_interner::{ - ExprId, InternedExpressionKind, InternedStatementKind, InternedUnresolvedTypeData, - QuotedTypeId, + ExprId, InternedExpressionKind, InternedPattern, InternedStatementKind, + InternedUnresolvedTypeData, QuotedTypeId, }, parser::{Item, ItemKind, ParsedSubModule}, token::{SecondaryAttribute, Tokens}, @@ -433,6 +433,8 @@ pub trait Visitor { true } + fn visit_interned_pattern(&mut self, _: &InternedPattern, _: Span) {} + fn visit_secondary_attribute(&mut self, _: &SecondaryAttribute, _: Span) {} } @@ -1290,6 +1292,9 @@ impl Pattern { } } } + Pattern::Interned(id, span) => { + visitor.visit_interned_pattern(id, *span); + } } } } diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 951c5220707..ed9265536f9 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -675,6 +675,7 @@ fn pattern_vars(pattern: &ast::Pattern) -> Vec<(ast::Ident, bool)> { stack.extend(pids.iter().map(|(_, pattern)| (pattern, is_mut))); vars.extend(pids.iter().map(|(id, _)| (id.clone(), false))); } + ast::Pattern::Interned(_, _) => (), } } vars @@ -701,6 +702,7 @@ fn pattern_to_string(pattern: &ast::Pattern) -> String { .join(", "), ) } + ast::Pattern::Interned(_, _) => "?Interned".to_string(), } } diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 06c153d4c10..f738657fd23 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -143,6 +143,17 @@ impl<'context> Elaborator<'context> { mutable, new_definitions, ), + Pattern::Interned(id, _) => { + let pattern = self.interner.get_pattern(id).clone(); + self.elaborate_pattern_mut( + pattern, + expected_type, + definition, + mutable, + new_definitions, + global_id, + ) + } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 7321f09dd75..249c610b220 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -22,8 +22,8 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, FunctionKind, - FunctionReturnType, IntegerBitSize, LValue, Literal, Statement, StatementKind, UnaryOp, - UnresolvedType, UnresolvedTypeData, Visibility, + FunctionReturnType, IntegerBitSize, LValue, Literal, Pattern, Statement, StatementKind, + UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, }, hir::{ comptime::{ @@ -77,6 +77,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_as_if" => expr_as_if(interner, arguments, return_type, location), "expr_as_index" => expr_as_index(interner, arguments, return_type, location), "expr_as_integer" => expr_as_integer(interner, arguments, return_type, location), + "expr_as_let" => expr_as_let(interner, arguments, return_type, location), "expr_as_member_access" => { expr_as_member_access(interner, arguments, return_type, location) } @@ -1491,6 +1492,41 @@ fn expr_as_integer( }) } +// fn as_let(self) -> Option<(Expr, Option, Expr)> +fn expr_as_let( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(interner, arguments, return_type.clone(), location, |expr| match expr { + ExprValue::Statement(StatementKind::Let(let_statement)) => { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 3); + tuple_types.pop().unwrap(); + let option_type = tuple_types.pop().unwrap(); + + let typ = if let_statement.r#type.typ == UnresolvedTypeData::Unspecified { + None + } else { + Some(Value::UnresolvedType(let_statement.r#type.typ)) + }; + + let typ = option(option_type, typ).ok()?; + + Some(Value::Tuple(vec![ + Value::pattern(let_statement.pattern), + typ, + Value::expression(let_statement.expression.kind), + ])) + } + _ => None, + }) +} + // fn as_member_access(self) -> Option<(Expr, Quoted)> fn expr_as_member_access( interner: &NodeInterner, @@ -1784,6 +1820,9 @@ fn expr_resolve( let (expr_id, _) = elaborator.elaborate_expression(expr); Value::TypedExpr(TypedExpr::ExprId(expr_id)) } + ExprValue::Pattern(_) => { + todo!("Can't resolve a pattern"); + } }); Ok(value) @@ -1808,6 +1847,9 @@ fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> Expr ExprValue::LValue(LValue::Interned(id, span)) => { expr_value = ExprValue::LValue(interner.get_lvalue(id, span).clone()); } + ExprValue::Pattern(Pattern::Interned(id, _)) => { + expr_value = ExprValue::Pattern(interner.get_pattern(id).clone()); + } _ => break, } } @@ -2008,6 +2050,9 @@ fn function_def_set_body( }), ExprValue::Statement(statement_kind) => statement_kind, ExprValue::LValue(lvalue) => StatementKind::Expression(lvalue.as_expression()), + ExprValue::Pattern(_) => { + todo!("Can't set function body to a pattern"); + } }; let statement = Statement { kind: statement_kind, span: body_location.span }; diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index f90d50807b8..6e72866bec0 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -5,8 +5,8 @@ use noirc_errors::Location; use crate::{ ast::{ - BlockExpression, ExpressionKind, IntegerBitSize, LValue, Signedness, StatementKind, - UnresolvedTypeData, + BlockExpression, ExpressionKind, IntegerBitSize, LValue, Pattern, Signedness, + StatementKind, UnresolvedTypeData, }, elaborator::Elaborator, hir::{ @@ -191,6 +191,9 @@ pub(crate) fn get_expr( ExprValue::LValue(LValue::Interned(id, _)) => { Ok(ExprValue::LValue(interner.get_lvalue(id, location.span).clone())) } + ExprValue::Pattern(Pattern::Interned(id, _)) => { + Ok(ExprValue::Pattern(interner.get_pattern(id).clone())) + } _ => Ok(expr), }, value => type_mismatch(value, Type::Quoted(QuotedType::Expr), location), diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 04c557552bd..f6450175955 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt::Display, rc::Rc}; +use std::{borrow::Cow, fmt::Display, rc::Rc, vec}; use acvm::{AcirField, FieldElement}; use chumsky::Parser; @@ -9,11 +9,11 @@ use strum_macros::Display; use crate::{ ast::{ - ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, - ConstrainStatement, ConstructorExpression, ForLoopStatement, ForRange, Ident, IfExpression, - IndexExpression, InfixExpression, IntegerBitSize, LValue, Lambda, LetStatement, - MemberAccessExpression, MethodCallExpression, PrefixExpression, Signedness, Statement, - StatementKind, UnresolvedTypeData, + ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, + CastExpression, ConstrainStatement, ConstructorExpression, ForLoopStatement, ForRange, + GenericTypeArgs, Ident, IfExpression, IndexExpression, InfixExpression, IntegerBitSize, + LValue, Lambda, LetStatement, MemberAccessExpression, MethodCallExpression, Pattern, + PrefixExpression, Signedness, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, }, hir::{def_map::ModuleId, type_check::generics::TraitGenerics}, hir_def::{ @@ -78,6 +78,7 @@ pub enum ExprValue { Expression(ExpressionKind), Statement(StatementKind), LValue(LValue), + Pattern(Pattern), } #[derive(Debug, Clone, PartialEq, Eq, Display)] @@ -99,6 +100,10 @@ impl Value { Value::Expr(ExprValue::LValue(lvaue)) } + pub(crate) fn pattern(pattern: Pattern) -> Self { + Value::Expr(ExprValue::Pattern(pattern)) + } + pub(crate) fn get_type(&self) -> Cow { Cow::Owned(match self { Value::Unit => Type::Unit, @@ -273,7 +278,8 @@ impl Value { }) } Value::Expr(ExprValue::LValue(lvalue)) => lvalue.as_expression().kind, - Value::TypedExpr(..) + Value::Expr(ExprValue::Pattern(_)) + | Value::TypedExpr(..) | Value::Pointer(..) | Value::StructDefinition(_) | Value::TraitConstraint(..) @@ -443,6 +449,9 @@ impl Value { Value::Expr(ExprValue::LValue(lvalue)) => { Token::InternedLValue(interner.push_lvalue(lvalue)) } + Value::Expr(ExprValue::Pattern(pattern)) => { + Token::InternedPattern(interner.push_pattern(pattern)) + } Value::UnresolvedType(typ) => { Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ)) } @@ -671,6 +680,9 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::Expr(ExprValue::LValue(lvalue)) => { write!(f, "{}", remove_interned_in_lvalue(self.interner, lvalue.clone())) } + Value::Expr(ExprValue::Pattern(pattern)) => { + write!(f, "{}", remove_interned_in_pattern(self.interner, pattern.clone())) + } Value::TypedExpr(TypedExpr::ExprId(id)) => { let hir_expr = self.interner.expression(id); let expr = hir_expr.to_display_ast(self.interner, Span::default()); @@ -682,12 +694,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { write!(f, "{}", stmt.kind) } Value::UnresolvedType(typ) => { - if let UnresolvedTypeData::Interned(id) = typ { - let typ = self.interner.get_unresolved_type_data(*id); - write!(f, "{}", typ) - } else { - write!(f, "{}", typ) - } + write!(f, "{}", remove_interned_in_unresolved_type_data(self.interner, typ.clone())) } } } @@ -729,6 +736,10 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { let value = Value::UnresolvedType(UnresolvedTypeData::Interned(*id)); value.display(self.interner).fmt(f) } + Token::InternedPattern(id) => { + let value = Value::pattern(Pattern::Interned(*id, Span::default())); + value.display(self.interner).fmt(f) + } Token::UnquoteMarker(id) => { let value = Value::TypedExpr(TypedExpr::ExprId(*id)); value.display(self.interner).fmt(f) @@ -901,7 +912,9 @@ fn remove_interned_in_statement_kind( ) -> StatementKind { match statement { StatementKind::Let(let_statement) => StatementKind::Let(LetStatement { + pattern: remove_interned_in_pattern(interner, let_statement.pattern), expression: remove_interned_in_expression(interner, let_statement.expression), + r#type: remove_interned_in_unresolved_type(interner, let_statement.r#type), ..let_statement }), StatementKind::Constrain(constrain) => StatementKind::Constrain(ConstrainStatement( @@ -966,3 +979,120 @@ fn remove_interned_in_lvalue(interner: &NodeInterner, lvalue: LValue) -> LValue } } } + +fn remove_interned_in_unresolved_type( + interner: &NodeInterner, + typ: UnresolvedType, +) -> UnresolvedType { + UnresolvedType { + typ: remove_interned_in_unresolved_type_data(interner, typ.typ), + span: typ.span, + } +} + +fn remove_interned_in_unresolved_type_data( + interner: &NodeInterner, + typ: UnresolvedTypeData, +) -> UnresolvedTypeData { + match typ { + UnresolvedTypeData::Array(expr, typ) => UnresolvedTypeData::Array( + expr, + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Slice(typ) => { + UnresolvedTypeData::Slice(Box::new(remove_interned_in_unresolved_type(interner, *typ))) + } + UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( + expr, + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Parenthesized(typ) => UnresolvedTypeData::Parenthesized(Box::new( + remove_interned_in_unresolved_type(interner, *typ), + )), + UnresolvedTypeData::Named(path, generic_type_args, is_synthesized) => { + UnresolvedTypeData::Named( + path, + remove_interned_in_generic_type_args(interner, generic_type_args), + is_synthesized, + ) + } + UnresolvedTypeData::TraitAsType(path, generic_type_args) => { + UnresolvedTypeData::TraitAsType( + path, + remove_interned_in_generic_type_args(interner, generic_type_args), + ) + } + UnresolvedTypeData::MutableReference(typ) => UnresolvedTypeData::MutableReference( + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Tuple(types) => UnresolvedTypeData::Tuple(vecmap(types, |typ| { + remove_interned_in_unresolved_type(interner, typ) + })), + UnresolvedTypeData::Function(arg_types, ret_type, env_type, unconstrained) => { + UnresolvedTypeData::Function( + vecmap(arg_types, |typ| remove_interned_in_unresolved_type(interner, typ)), + Box::new(remove_interned_in_unresolved_type(interner, *ret_type)), + Box::new(remove_interned_in_unresolved_type(interner, *env_type)), + unconstrained, + ) + } + UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(Box::new(AsTraitPath { + typ: remove_interned_in_unresolved_type(interner, as_trait_path.typ), + trait_generics: remove_interned_in_generic_type_args( + interner, + as_trait_path.trait_generics, + ), + ..*as_trait_path + })) + } + UnresolvedTypeData::Interned(id) => interner.get_unresolved_type_data(id).clone(), + UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Error => typ, + } +} + +fn remove_interned_in_generic_type_args( + interner: &NodeInterner, + args: GenericTypeArgs, +) -> GenericTypeArgs { + GenericTypeArgs { + ordered_args: vecmap(args.ordered_args, |typ| { + remove_interned_in_unresolved_type(interner, typ) + }), + named_args: vecmap(args.named_args, |(name, typ)| { + (name, remove_interned_in_unresolved_type(interner, typ)) + }), + } +} + +// Returns a new Pattern where all Interned Patterns have been turned into Pattern. +fn remove_interned_in_pattern(interner: &NodeInterner, pattern: Pattern) -> Pattern { + match pattern { + Pattern::Identifier(_) => pattern, + Pattern::Mutable(pattern, span, is_synthesized) => Pattern::Mutable( + Box::new(remove_interned_in_pattern(interner, *pattern)), + span, + is_synthesized, + ), + Pattern::Tuple(patterns, span) => Pattern::Tuple( + vecmap(patterns, |pattern| remove_interned_in_pattern(interner, pattern)), + span, + ), + Pattern::Struct(path, patterns, span) => { + let patterns = vecmap(patterns, |(name, pattern)| { + (name, remove_interned_in_pattern(interner, pattern)) + }); + Pattern::Struct(path, patterns, span) + } + Pattern::Interned(id, _) => interner.get_pattern(id).clone(), + } +} diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 7b805b5fd8d..285eb8f7899 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -5,8 +5,8 @@ use std::{fmt, iter::Map, vec::IntoIter}; use crate::{ lexer::errors::LexerErrorKind, node_interner::{ - ExprId, InternedExpressionKind, InternedStatementKind, InternedUnresolvedTypeData, - QuotedTypeId, + ExprId, InternedExpressionKind, InternedPattern, InternedStatementKind, + InternedUnresolvedTypeData, QuotedTypeId, }, }; @@ -36,6 +36,7 @@ pub enum BorrowedToken<'input> { InternedStatement(InternedStatementKind), InternedLValue(InternedExpressionKind), InternedUnresolvedTypeData(InternedUnresolvedTypeData), + InternedPattern(InternedPattern), /// < Less, /// <= @@ -151,6 +152,8 @@ pub enum Token { InternedLValue(InternedExpressionKind), /// A reference to an interned `UnresolvedTypeData`. InternedUnresolvedTypeData(InternedUnresolvedTypeData), + /// A reference to an interned `Patter`. + InternedPattern(InternedPattern), /// < Less, /// <= @@ -255,6 +258,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::InternedStatement(id) => BorrowedToken::InternedStatement(*id), Token::InternedLValue(id) => BorrowedToken::InternedLValue(*id), Token::InternedUnresolvedTypeData(id) => BorrowedToken::InternedUnresolvedTypeData(*id), + Token::InternedPattern(id) => BorrowedToken::InternedPattern(*id), Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, @@ -378,7 +382,10 @@ impl fmt::Display for Token { } // Quoted types and exprs only have an ID so there is nothing to display Token::QuotedType(_) => write!(f, "(type)"), - Token::InternedExpr(_) | Token::InternedStatement(_) | Token::InternedLValue(_) => { + Token::InternedExpr(_) + | Token::InternedStatement(_) + | Token::InternedLValue(_) + | Token::InternedPattern(_) => { write!(f, "(expr)") } Token::InternedUnresolvedTypeData(_) => write!(f, "(type)"), @@ -439,6 +446,7 @@ pub enum TokenKind { InternedStatement, InternedLValue, InternedUnresolvedTypeData, + InternedPattern, UnquoteMarker, } @@ -457,6 +465,7 @@ impl fmt::Display for TokenKind { TokenKind::InternedStatement => write!(f, "interned statement"), TokenKind::InternedLValue => write!(f, "interned lvalue"), TokenKind::InternedUnresolvedTypeData => write!(f, "interned unresolved type"), + TokenKind::InternedPattern => write!(f, "interned pattern"), TokenKind::UnquoteMarker => write!(f, "macro result"), } } @@ -481,6 +490,7 @@ impl Token { Token::InternedStatement(_) => TokenKind::InternedStatement, Token::InternedLValue(_) => TokenKind::InternedLValue, Token::InternedUnresolvedTypeData(_) => TokenKind::InternedUnresolvedTypeData, + Token::InternedPattern(_) => TokenKind::InternedPattern, tok => TokenKind::Token(tok.clone()), } } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index aa51779d24b..34eec96a31f 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -16,6 +16,7 @@ use rustc_hash::FxHashMap as HashMap; use crate::ast::ExpressionKind; use crate::ast::Ident; use crate::ast::LValue; +use crate::ast::Pattern; use crate::ast::StatementKind; use crate::ast::UnresolvedTypeData; use crate::graph::CrateId; @@ -222,6 +223,9 @@ pub struct NodeInterner { // Interned `UnresolvedTypeData`s during comptime code. interned_unresolved_type_datas: noirc_arena::Arena, + // Interned `Pattern`s during comptime code. + interned_patterns: noirc_arena::Arena, + /// Determins whether to run in LSP mode. In LSP mode references are tracked. pub(crate) lsp_mode: bool, @@ -607,6 +611,9 @@ pub struct InternedStatementKind(noirc_arena::Index); #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct InternedUnresolvedTypeData(noirc_arena::Index); +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InternedPattern(noirc_arena::Index); + impl Default for NodeInterner { fn default() -> Self { NodeInterner { @@ -647,6 +654,7 @@ impl Default for NodeInterner { interned_expression_kinds: Default::default(), interned_statement_kinds: Default::default(), interned_unresolved_type_datas: Default::default(), + interned_patterns: Default::default(), lsp_mode: false, location_indices: LocationIndices::default(), reference_graph: petgraph::graph::DiGraph::new(), @@ -2097,6 +2105,14 @@ impl NodeInterner { LValue::from_expression_kind(self.get_expression_kind(id).clone(), span) } + pub fn push_pattern(&mut self, pattern: Pattern) -> InternedPattern { + InternedPattern(self.interned_patterns.insert(pattern)) + } + + pub fn get_pattern(&self, id: InternedPattern) -> &Pattern { + &self.interned_patterns[id.0] + } + pub fn push_unresolved_type_data( &mut self, typ: UnresolvedTypeData, diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 1aee697aa88..4bd78239e01 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -637,7 +637,15 @@ pub fn pattern() -> impl NoirParser { .delimited_by(just(Token::LeftParen), just(Token::RightParen)) .map_with_span(Pattern::Tuple); - choice((mut_pattern, tuple_pattern, struct_pattern, ident_pattern)) + let interned = + token_kind(TokenKind::InternedPattern).map_with_span(|token, span| match token { + Token::InternedPattern(id) => Pattern::Interned(id, span), + _ => unreachable!( + "token_kind(InternedPattern) guarantees we parse an interned pattern" + ), + }); + + choice((mut_pattern, tuple_pattern, struct_pattern, ident_pattern, interned)) }) .labelled(ParsingRuleLabel::Pattern) } diff --git a/docs/docs/noir/standard_library/meta/expr.md b/docs/docs/noir/standard_library/meta/expr.md index 3a3c61b41f5..58a6252c523 100644 --- a/docs/docs/noir/standard_library/meta/expr.md +++ b/docs/docs/noir/standard_library/meta/expr.md @@ -85,9 +85,16 @@ array and the index. #include_code as_integer noir_stdlib/src/meta/expr.nr rust -If this element is an integer literal, return the integer as a field +If this expressoin is an integer literal, return the integer as a field as well as whether the integer is negative (true) or not (false). +### as_let + +#include_code as_let noir_stdlib/src/meta/expr.nr rust + +If this expression is a let statement, returns the let pattern as an `Expr`, +the optional type annotation, and the assigned expression. + ### as_member_access #include_code as_member_access noir_stdlib/src/meta/expr.nr rust diff --git a/noir_stdlib/src/meta/expr.nr b/noir_stdlib/src/meta/expr.nr index 43638ad791b..96a83f954c9 100644 --- a/noir_stdlib/src/meta/expr.nr +++ b/noir_stdlib/src/meta/expr.nr @@ -66,6 +66,11 @@ impl Expr { fn as_index(self) -> Option<(Expr, Expr)> {} // docs:end:as_index + #[builtin(expr_as_let)] + // docs:start:as_let + fn as_let(self) -> Option<(Expr, Option, Expr)> {} + // docs:end:as_let + #[builtin(expr_as_member_access)] // docs:start:as_member_access fn as_member_access(self) -> Option<(Expr, Quoted)> {} @@ -134,6 +139,7 @@ impl Expr { let result = result.or_else(|| modify_comptime(self, f)); let result = result.or_else(|| modify_if(self, f)); let result = result.or_else(|| modify_index(self, f)); + let result = result.or_else(|| modify_let(self, f)); let result = result.or_else(|| modify_function_call(self, f)); let result = result.or_else(|| modify_member_access(self, f)); let result = result.or_else(|| modify_method_call(self, f)); @@ -280,6 +286,17 @@ fn modify_index(expr: Expr, f: fn[Env](Expr) -> Option) -> Option(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_let().map( + |expr: (Expr, Option, Expr)| { + let (pattern, typ, expr) = expr; + let pattern = pattern.modify(f); + let expr = expr.modify(f); + new_let(pattern, typ, expr) + } + ) +} + fn modify_member_access(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { expr.as_member_access().map( |expr: (Expr, Quoted)| { @@ -423,6 +440,15 @@ fn new_index(object: Expr, index: Expr) -> Expr { quote { $object[$index] }.as_expr().unwrap() } +fn new_let(pattern: Expr, typ: Option, expr: Expr) -> Expr { + if typ.is_some() { + let typ = typ.unwrap(); + quote { let $pattern : $typ = $expr; }.as_expr().unwrap() + } else { + quote { let $pattern = $expr; }.as_expr().unwrap() + } +} + fn new_member_access(object: Expr, name: Quoted) -> Expr { quote { $object.$name }.as_expr().unwrap() } diff --git a/test_programs/noir_test_success/comptime_expr/src/main.nr b/test_programs/noir_test_success/comptime_expr/src/main.nr index c082c1dde33..c1f70e7acee 100644 --- a/test_programs/noir_test_success/comptime_expr/src/main.nr +++ b/test_programs/noir_test_success/comptime_expr/src/main.nr @@ -16,7 +16,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_array() { + fn test_expr_modify_for_array() { comptime { let expr = quote { [1, 2, 4] }.as_expr().unwrap(); @@ -46,7 +46,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_assert() { + fn test_expr_modify_for_assert() { comptime { let expr = quote { assert(1) }.as_expr().unwrap(); @@ -82,7 +82,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_assert_eq() { + fn test_expr_modify_for_assert_eq() { comptime { let expr = quote { assert_eq(1, 2) }.as_expr().unwrap(); @@ -113,7 +113,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_assign() { + fn test_expr_modify_for_assign() { comptime { let expr = quote { { a = 1; } }.as_expr().unwrap(); @@ -142,7 +142,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_block() { + fn test_expr_modify_for_block() { comptime { let expr = quote { { 1; 4; 23 } }.as_expr().unwrap(); @@ -178,7 +178,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_method_call() { + fn test_expr_modify_for_method_call() { comptime { let expr = quote { foo.bar(3, 4) }.as_expr().unwrap(); @@ -209,7 +209,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_integer() { + fn test_expr_modify_for_integer() { comptime { let expr = quote { 1 }.as_expr().unwrap(); @@ -243,7 +243,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_binary_op() { + fn test_expr_modify_for_binary_op() { comptime { let expr = quote { 3 + 4 }.as_expr().unwrap(); @@ -280,7 +280,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_cast() { + fn test_expr_modify_for_cast() { comptime { let expr = quote { 1 as Field }.as_expr().unwrap(); @@ -302,7 +302,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_comptime() { + fn test_expr_modify_for_comptime() { comptime { let expr = quote { comptime { 1; 4; 23 } }.as_expr().unwrap(); @@ -342,7 +342,7 @@ mod tests { // docs:end:as_expr_example #[test] - fn test_expr_mutate_for_function_call() { + fn test_expr_modify_for_function_call() { comptime { let expr = quote { foo(42) }.as_expr().unwrap(); @@ -368,7 +368,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_if() { + fn test_expr_modify_for_if() { comptime { let expr = quote { if 1 { 2 } }.as_expr().unwrap(); @@ -400,7 +400,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_index() { + fn test_expr_modify_for_index() { comptime { let expr = quote { 1[2] }.as_expr().unwrap(); @@ -422,7 +422,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_member_access() { + fn test_expr_modify_for_member_access() { comptime { let expr = quote { 1.bar }.as_expr().unwrap(); @@ -457,7 +457,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_repeated_element_array() { + fn test_expr_modify_for_repeated_element_array() { comptime { let expr = quote { [1; 3] }.as_expr().unwrap(); @@ -480,7 +480,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_repeated_element_slice() { + fn test_expr_modify_for_repeated_element_slice() { comptime { let expr = quote { &[1; 3] }.as_expr().unwrap(); @@ -505,7 +505,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_slice() { + fn test_expr_modify_for_slice() { comptime { let expr = quote { &[1, 3, 5] }.as_expr().unwrap(); @@ -529,7 +529,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_tuple() { + fn test_expr_modify_for_tuple() { comptime { let expr = quote { (1, 2) }.as_expr().unwrap(); @@ -553,7 +553,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_unary_op() { + fn test_expr_modify_for_unary_op() { comptime { let expr = quote { -(1) }.as_expr().unwrap(); @@ -575,7 +575,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_unsafe() { + fn test_expr_modify_for_unsafe() { comptime { let expr = quote { unsafe { 1; 4; 23 } }.as_expr().unwrap(); @@ -606,6 +606,41 @@ mod tests { } } + #[test] + fn test_expr_as_let() { + comptime + { + let expr = quote { let x: Field = 1; }.as_expr().unwrap(); + let (_pattern, typ, expr) = expr.as_let().unwrap(); + assert(typ.unwrap().is_field()); + assert_eq(expr.as_integer().unwrap(), (1, false)); + } + } + + #[test] + fn test_expr_modify_for_let() { + comptime + { + let expr = quote { let x : Field = 1; }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (_pattern, typ, expr) = expr.as_let().unwrap(); + assert(typ.unwrap().is_field()); + assert_eq(expr.as_integer().unwrap(), (2, false)); + } + } + + #[test] + fn test_expr_modify_for_let_without_type() { + comptime + { + let expr = quote { let x = 1; }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (_pattern, typ, expr) = expr.as_let().unwrap(); + assert(typ.is_none()); + assert_eq(expr.as_integer().unwrap(), (2, false)); + } + } + #[test] fn test_automatically_unwraps_parenthesized_expression() { comptime diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index 51790ec107b..f4c233c0ce5 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -506,6 +506,7 @@ impl<'a> NodeFinder<'a> { self.collect_local_variables(pattern); } } + Pattern::Interned(..) => (), } } @@ -799,7 +800,7 @@ impl<'a> NodeFinder<'a> { } } Pattern::Mutable(pattern, ..) => self.try_set_self_type(pattern), - Pattern::Tuple(..) | Pattern::Struct(..) => (), + Pattern::Tuple(..) | Pattern::Struct(..) | Pattern::Interned(..) => (), } } From d94b5327c1ff8d24c914d6958bf30761769700d2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 6 Sep 2024 15:57:51 -0300 Subject: [PATCH 2/4] Fix typo --- docs/docs/noir/standard_library/meta/expr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/noir/standard_library/meta/expr.md b/docs/docs/noir/standard_library/meta/expr.md index 58a6252c523..7ee33027354 100644 --- a/docs/docs/noir/standard_library/meta/expr.md +++ b/docs/docs/noir/standard_library/meta/expr.md @@ -85,7 +85,7 @@ array and the index. #include_code as_integer noir_stdlib/src/meta/expr.nr rust -If this expressoin is an integer literal, return the integer as a field +If this expression is an integer literal, return the integer as a field as well as whether the integer is negative (true) or not (false). ### as_let From bae65a457be07f87d83dd660656a3236fe721dba Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 9 Sep 2024 10:28:19 -0300 Subject: [PATCH 3/4] Try to convert pattern to expression --- compiler/noirc_frontend/src/ast/statement.rs | 39 +++++++++++- .../noirc_frontend/src/hir/comptime/errors.rs | 20 ++++++- .../src/hir/comptime/interpreter/builtin.rs | 59 +++++++++++-------- 3 files changed, 89 insertions(+), 29 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 858617aea44..52c39a49e8a 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -7,12 +7,12 @@ use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; use super::{ - BlockExpression, Expression, ExpressionKind, GenericTypeArgs, IndexExpression, ItemVisibility, - MemberAccessExpression, MethodCallExpression, UnresolvedType, + BlockExpression, ConstructorExpression, Expression, ExpressionKind, GenericTypeArgs, + IndexExpression, ItemVisibility, MemberAccessExpression, MethodCallExpression, UnresolvedType, }; use crate::elaborator::types::SELF_TYPE_NAME; use crate::lexer::token::SpannedToken; -use crate::macros_api::{SecondaryAttribute, UnresolvedTypeData}; +use crate::macros_api::{NodeInterner, SecondaryAttribute, UnresolvedTypeData}; use crate::node_interner::{InternedExpressionKind, InternedPattern, InternedStatementKind}; use crate::parser::{ParserError, ParserErrorReason}; use crate::token::Token; @@ -597,6 +597,39 @@ impl Pattern { other => panic!("Pattern::into_ident called on {other} pattern with no identifier"), } } + + pub(crate) fn try_as_expression(&self, interner: &NodeInterner) -> Option { + match self { + Pattern::Identifier(ident) => Some(Expression { + kind: ExpressionKind::Variable(Path::from_ident(ident.clone())), + span: ident.span(), + }), + Pattern::Mutable(_, _, _) => None, + Pattern::Tuple(patterns, span) => { + let mut expressions = Vec::new(); + for pattern in patterns { + expressions.push(pattern.try_as_expression(interner)?); + } + Some(Expression { kind: ExpressionKind::Tuple(expressions), span: *span }) + } + Pattern::Struct(path, patterns, span) => { + let mut fields = Vec::new(); + for (field, pattern) in patterns { + let expression = pattern.try_as_expression(interner)?; + fields.push((field.clone(), expression)); + } + Some(Expression { + kind: ExpressionKind::Constructor(Box::new(ConstructorExpression { + type_name: path.clone(), + fields, + struct_type: None, + })), + span: *span, + }) + } + Pattern::Interned(id, _) => interner.get_pattern(*id).try_as_expression(interner), + } + } } impl Recoverable for Pattern { diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index 70589973745..9c4761f3156 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -220,6 +220,14 @@ pub enum InterpreterError { duplicate_location: Location, existing_location: Location, }, + CannotResolveExpression { + location: Location, + expression: String, + }, + CannotSetFunctionBody { + location: Location, + expression: String, + }, // These cases are not errors, they are just used to prevent us from running more code // until the loop can be resumed properly. These cases will never be displayed to users. @@ -291,7 +299,9 @@ impl InterpreterError { | InterpreterError::InvalidAttribute { location, .. } | InterpreterError::GenericNameShouldBeAnIdent { location, .. } | InterpreterError::DuplicateGeneric { duplicate_location: location, .. } - | InterpreterError::TypeAnnotationsNeededForMethodCall { location } => *location, + | InterpreterError::TypeAnnotationsNeededForMethodCall { location } + | InterpreterError::CannotResolveExpression { location, .. } + | InterpreterError::CannotSetFunctionBody { location, .. } => *location, InterpreterError::FailedToParseMacro { error, file, .. } => { Location::new(error.span(), *file) @@ -626,6 +636,14 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { ); error } + InterpreterError::CannotResolveExpression { location, expression } => { + let msg = format!("Cannot resolve expression `{expression}`"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::CannotSetFunctionBody { location, expression } => { + let msg = format!("`{expression}` is not a valid function body"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 5af4eb4a297..72560fde2f0 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -1807,30 +1807,32 @@ fn expr_resolve( interpreter.current_function }; - let value = - interpreter.elaborate_in_function(function_to_resolve_in, |elaborator| match expr_value { - ExprValue::Expression(expression_kind) => { - let expr = Expression { kind: expression_kind, span: self_argument_location.span }; - let (expr_id, _) = elaborator.elaborate_expression(expr); - Value::TypedExpr(TypedExpr::ExprId(expr_id)) - } - ExprValue::Statement(statement_kind) => { - let statement = - Statement { kind: statement_kind, span: self_argument_location.span }; - let (stmt_id, _) = elaborator.elaborate_statement(statement); - Value::TypedExpr(TypedExpr::StmtId(stmt_id)) - } - ExprValue::LValue(lvalue) => { - let expr = lvalue.as_expression(); - let (expr_id, _) = elaborator.elaborate_expression(expr); - Value::TypedExpr(TypedExpr::ExprId(expr_id)) - } - ExprValue::Pattern(_) => { - todo!("Can't resolve a pattern"); + interpreter.elaborate_in_function(function_to_resolve_in, |elaborator| match expr_value { + ExprValue::Expression(expression_kind) => { + let expr = Expression { kind: expression_kind, span: self_argument_location.span }; + let (expr_id, _) = elaborator.elaborate_expression(expr); + Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) + } + ExprValue::Statement(statement_kind) => { + let statement = Statement { kind: statement_kind, span: self_argument_location.span }; + let (stmt_id, _) = elaborator.elaborate_statement(statement); + Ok(Value::TypedExpr(TypedExpr::StmtId(stmt_id))) + } + ExprValue::LValue(lvalue) => { + let expr = lvalue.as_expression(); + let (expr_id, _) = elaborator.elaborate_expression(expr); + Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) + } + ExprValue::Pattern(pattern) => { + if let Some(expression) = pattern.try_as_expression(elaborator.interner) { + Ok(Value::expression(expression.kind)) + } else { + let expression = Value::pattern(pattern).display(elaborator.interner).to_string(); + let location = self_argument_location; + Err(InterpreterError::CannotResolveExpression { location, expression }) } - }); - - Ok(value) + } + }) } fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> ExprValue { @@ -2055,8 +2057,15 @@ fn function_def_set_body( }), ExprValue::Statement(statement_kind) => statement_kind, ExprValue::LValue(lvalue) => StatementKind::Expression(lvalue.as_expression()), - ExprValue::Pattern(_) => { - todo!("Can't set function body to a pattern"); + ExprValue::Pattern(pattern) => { + if let Some(expression) = pattern.try_as_expression(interpreter.elaborator.interner) { + StatementKind::Expression(expression) + } else { + let expression = + Value::pattern(pattern).display(interpreter.elaborator.interner).to_string(); + let location = body_location; + return Err(InterpreterError::CannotSetFunctionBody { location, expression }); + } } }; From 256843c31a89d4b55d66a4bab884673c45124e33 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 9 Sep 2024 11:19:42 -0300 Subject: [PATCH 4/4] Resolve expression resulting from pattern --- .../noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index f4bb1bda058..899d62ecb61 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -1831,7 +1831,8 @@ fn expr_resolve( } ExprValue::Pattern(pattern) => { if let Some(expression) = pattern.try_as_expression(elaborator.interner) { - Ok(Value::expression(expression.kind)) + let (expr_id, _) = elaborator.elaborate_expression(expression); + Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) } else { let expression = Value::pattern(pattern).display(elaborator.interner).to_string(); let location = self_argument_location;