diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index dcbdf89391e..d7d330f891a 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -48,7 +48,7 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn elaborate_statement(&mut self, statement: Statement) -> (StmtId, Type) { + pub(crate) fn elaborate_statement(&mut self, statement: Statement) -> (StmtId, Type) { let span = statement.span; let (hir_statement, typ) = self.elaborate_statement_value(statement); let id = self.interner.push_stmt(hir_statement); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 5ffc58004be..0dafe408c7f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -8,9 +8,9 @@ use builtin_helpers::{ block_expression_to_value, check_argument_count, check_function_not_yet_resolved, check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_field, get_function_def, get_module, get_quoted, get_slice, get_struct, get_trait_constraint, - get_trait_def, get_trait_impl, get_tuple, get_type, get_u32, get_unresolved_type, - hir_pattern_to_tokens, mutate_func_meta_type, parse, replace_func_meta_parameters, - replace_func_meta_return_type, + get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr, get_u32, + get_unresolved_type, hir_pattern_to_tokens, mutate_func_meta_type, parse, + replace_func_meta_parameters, replace_func_meta_return_type, }; use chumsky::{prelude::choice, Parser}; use im::Vector; @@ -25,7 +25,11 @@ use crate::{ FunctionReturnType, IntegerBitSize, LValue, Literal, Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, }, - hir::comptime::{errors::IResult, value::ExprValue, InterpreterError, Value}, + hir::comptime::{ + errors::IResult, + value::{ExprValue, TypedExpr}, + InterpreterError, Value, + }, hir_def::function::FunctionBody, macros_api::{HirExpression, HirLiteral, ModuleDefId, NodeInterner, Signedness}, node_interner::{DefinitionKind, TraitImplKind}, @@ -87,6 +91,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_has_semicolon" => expr_has_semicolon(interner, arguments, location), "expr_is_break" => expr_is_break(interner, arguments, location), "expr_is_continue" => expr_is_continue(interner, arguments, location), + "expr_resolve" => expr_resolve(self, arguments, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_body" => function_def_body(interner, arguments, location), "function_def_name" => function_def_name(interner, arguments, location), @@ -145,6 +150,9 @@ impl<'local, 'context> Interpreter<'local, 'context> { "type_is_bool" => type_is_bool(arguments, location), "type_is_field" => type_is_field(arguments, location), "type_of" => type_of(arguments, location), + "typed_expr_as_function_definition" => { + typed_expr_as_function_definition(interner, arguments, return_type, location) + } "unresolved_type_is_field" => unresolved_type_is_field(interner, arguments, location), "zeroed" => zeroed(return_type), _ => { @@ -763,6 +771,23 @@ fn trait_impl_trait_generic_args( Ok(Value::Slice(trait_generics, slice_type)) } +fn typed_expr_as_function_definition( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let typed_expr = get_typed_expr(self_argument)?; + let option_value = if let TypedExpr::ExprId(expr_id) = typed_expr { + let func_id = interner.lookup_function_from_expr(&expr_id); + func_id.map(Value::FunctionDefinition) + } else { + None + }; + option(return_type, option_value) +} + // fn is_field(self) -> bool fn unresolved_type_is_field( interner: &NodeInterner, @@ -1380,7 +1405,48 @@ where F: FnOnce(ExprValue) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let mut expr_value = get_expr(interner, self_argument)?; + let expr_value = get_expr(interner, self_argument)?; + let expr_value = unwrap_expr_value(interner, expr_value); + + let option_value = f(expr_value); + option(return_type, option_value) +} + +// fn resolve(self) -> TypedExpr +fn expr_resolve( + interpreter: &mut Interpreter, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let self_argument_location = self_argument.1; + let expr_value = get_expr(interpreter.elaborator.interner, self_argument)?; + let expr_value = unwrap_expr_value(interpreter.elaborator.interner, expr_value); + + let value = + interpreter.elaborate_item(interpreter.current_function, |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)) + } + }); + + Ok(value) +} + +fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> ExprValue { loop { match expr_value { ExprValue::Expression(ExpressionKind::Parenthesized(expression)) => { @@ -1402,9 +1468,7 @@ where _ => break, } } - - let option_value = f(expr_value); - option(return_type, option_value) + expr_value } // fn body(self) -> Expr 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 2e06240e995..dd9ea51961e 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 @@ -11,7 +11,7 @@ use crate::{ hir::{ comptime::{ errors::IResult, - value::{add_token_spans, ExprValue}, + value::{add_token_spans, ExprValue, TypedExpr}, Interpreter, InterpreterError, Value, }, def_map::ModuleId, @@ -227,6 +227,13 @@ pub(crate) fn get_type((value, location): (Value, Location)) -> IResult { } } +pub(crate) fn get_typed_expr((value, location): (Value, Location)) -> IResult { + match value { + Value::TypedExpr(typed_expr) => Ok(typed_expr), + value => type_mismatch(value, Type::Quoted(QuotedType::TypedExpr), location), + } +} + pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult>> { match value { Value::Quoted(tokens) => Ok(tokens), diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 5b4875c8c41..b96c4852931 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, TraitId, TraitImplId}, + node_interner::{ExprId, FuncId, StmtId, TraitId, TraitImplId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, QuotedType, Shared, Type, TypeBindings, @@ -69,6 +69,7 @@ pub enum Value { Type(Type), Zeroed(Type), Expr(ExprValue), + TypedExpr(TypedExpr), UnresolvedType(UnresolvedTypeData), } @@ -79,6 +80,12 @@ pub enum ExprValue { LValue(LValue), } +#[derive(Debug, Clone, PartialEq, Eq, Display)] +pub enum TypedExpr { + ExprId(ExprId), + StmtId(StmtId), +} + impl Value { pub(crate) fn expression(expr: ExpressionKind) -> Self { Value::Expr(ExprValue::Expression(expr)) @@ -137,6 +144,7 @@ impl Value { Value::Type(_) => Type::Quoted(QuotedType::Type), Value::Zeroed(typ) => return Cow::Borrowed(typ), Value::Expr(_) => Type::Quoted(QuotedType::Expr), + Value::TypedExpr(_) => Type::Quoted(QuotedType::TypedExpr), Value::UnresolvedType(_) => Type::Quoted(QuotedType::UnresolvedType), }) } @@ -264,7 +272,8 @@ impl Value { statements: vec![Statement { kind: statement, span: location.span }], }) } - Value::Expr(ExprValue::LValue(_)) + Value::Expr(ExprValue::LValue(lvalue)) => lvalue.as_expression().kind, + Value::TypedExpr(..) | Value::Pointer(..) | Value::StructDefinition(_) | Value::TraitConstraint(..) @@ -389,7 +398,9 @@ impl Value { HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) } Value::Quoted(tokens) => HirExpression::Unquote(add_token_spans(tokens, location.span)), - Value::Expr(..) + Value::TypedExpr(TypedExpr::ExprId(expr_id)) => interner.expression(&expr_id), + Value::TypedExpr(TypedExpr::StmtId(..)) + | Value::Expr(..) | Value::Pointer(..) | Value::StructDefinition(_) | Value::TraitConstraint(..) @@ -621,6 +632,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::Expr(ExprValue::LValue(lvalue)) => { write!(f, "{}", remove_interned_in_lvalue(self.interner, lvalue.clone())) } + Value::TypedExpr(_) => write!(f, "(typed expr)"), Value::UnresolvedType(typ) => { if let UnresolvedTypeData::Interned(id) = typ { let typ = self.interner.get_unresolved_type_data(*id); diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index c59c86b9616..638003d3fcd 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -153,6 +153,7 @@ pub enum QuotedType { Quoted, TopLevelItem, Type, + TypedExpr, StructDefinition, TraitConstraint, TraitDefinition, @@ -741,6 +742,7 @@ impl std::fmt::Display for QuotedType { QuotedType::Quoted => write!(f, "Quoted"), QuotedType::TopLevelItem => write!(f, "TopLevelItem"), QuotedType::Type => write!(f, "Type"), + QuotedType::TypedExpr => write!(f, "TypedExpr"), QuotedType::StructDefinition => write!(f, "StructDefinition"), QuotedType::TraitDefinition => write!(f, "TraitDefinition"), QuotedType::TraitConstraint => write!(f, "TraitConstraint"), diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index b3b6d25480f..c9bd465b6a6 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -962,6 +962,7 @@ pub enum Keyword { TraitDefinition, TraitImpl, Type, + TypedExpr, TypeType, Unchecked, Unconstrained, @@ -1017,6 +1018,7 @@ impl fmt::Display for Keyword { Keyword::TraitDefinition => write!(f, "TraitDefinition"), Keyword::TraitImpl => write!(f, "TraitImpl"), Keyword::Type => write!(f, "type"), + Keyword::TypedExpr => write!(f, "TypedExpr"), Keyword::TypeType => write!(f, "Type"), Keyword::Unchecked => write!(f, "unchecked"), Keyword::Unconstrained => write!(f, "unconstrained"), @@ -1075,6 +1077,7 @@ impl Keyword { "TraitImpl" => Keyword::TraitImpl, "type" => Keyword::Type, "Type" => Keyword::TypeType, + "TypedExpr" => Keyword::TypedExpr, "StructDefinition" => Keyword::StructDefinition, "unchecked" => Keyword::Unchecked, "unconstrained" => Keyword::Unconstrained, diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index f83303151eb..9dabb8b83b6 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -88,6 +88,7 @@ pub(super) fn comptime_type() -> impl NoirParser { type_of_quoted_types(), top_level_item_type(), quoted_type(), + typed_expr_type(), )) } @@ -159,6 +160,12 @@ fn quoted_type() -> impl NoirParser { .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Quoted).with_span(span)) } +/// This is the type of a typed/resolved expression. +fn typed_expr_type() -> impl NoirParser { + keyword(Keyword::TypedExpr) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::TypedExpr).with_span(span)) +} + /// This is the type of an already resolved type. /// The only way this can appear in the token input is if an already resolved `Type` object /// was spliced into a macro's token stream via the `$` operator. diff --git a/docs/docs/noir/concepts/comptime.md b/docs/docs/noir/concepts/comptime.md index 2b5c29538b9..ed55a541fbd 100644 --- a/docs/docs/noir/concepts/comptime.md +++ b/docs/docs/noir/concepts/comptime.md @@ -232,6 +232,7 @@ The following is an incomplete list of some `comptime` types along with some use - `fn fields(self) -> [(Quoted, Type)]` - Return the name and type of each field - `TraitConstraint`: A trait constraint such as `From` +- `TypedExpr`: A type-checked expression. - `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet There are many more functions available by exploring the `std::meta` module and its submodules. diff --git a/docs/docs/noir/standard_library/meta/expr.md b/docs/docs/noir/standard_library/meta/expr.md index 7d3d1c2f453..8f708b2359e 100644 --- a/docs/docs/noir/standard_library/meta/expr.md +++ b/docs/docs/noir/standard_library/meta/expr.md @@ -184,4 +184,10 @@ for expressions that are integers, doubling them, would return `(&[2], &[4, 6])` #include_code quoted noir_stdlib/src/meta/expr.nr rust -Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. \ No newline at end of file +Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. + +### resolve + +#include_code resolve noir_stdlib/src/meta/expr.nr rust + +Resolves and type-checks this expression and returns the result as a `TypedExpr`. If any names used by this expression are not in scope or if there are any type errors, this will give compiler errors as if the expression was written directly into the current `comptime` function. \ No newline at end of file diff --git a/docs/docs/noir/standard_library/meta/typed_expr.md b/docs/docs/noir/standard_library/meta/typed_expr.md new file mode 100644 index 00000000000..eacfd9c1230 --- /dev/null +++ b/docs/docs/noir/standard_library/meta/typed_expr.md @@ -0,0 +1,13 @@ +--- +title: TypedExpr +--- + +`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. + +## Methods + +### as_function_definition + +#include_code as_function_definition noir_stdlib/src/meta/typed_expr.nr rust + +If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. \ No newline at end of file diff --git a/noir_stdlib/src/meta/expr.nr b/noir_stdlib/src/meta/expr.nr index 838c96570b5..5c677f39a9e 100644 --- a/noir_stdlib/src/meta/expr.nr +++ b/noir_stdlib/src/meta/expr.nr @@ -151,6 +151,11 @@ impl Expr { // docs:end:quoted quote { $self } } + + #[builtin(expr_resolve)] + // docs:start:resolve + fn resolve(self) -> TypedExpr {} + // docs:end:resolve } fn modify_array(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { diff --git a/noir_stdlib/src/meta/mod.nr b/noir_stdlib/src/meta/mod.nr index be1b12540c9..24398054467 100644 --- a/noir_stdlib/src/meta/mod.nr +++ b/noir_stdlib/src/meta/mod.nr @@ -7,6 +7,7 @@ mod trait_constraint; mod trait_def; mod trait_impl; mod typ; +mod typed_expr; mod quoted; mod unresolved_type; diff --git a/noir_stdlib/src/meta/typed_expr.nr b/noir_stdlib/src/meta/typed_expr.nr new file mode 100644 index 00000000000..8daede97438 --- /dev/null +++ b/noir_stdlib/src/meta/typed_expr.nr @@ -0,0 +1,8 @@ +use crate::option::Option; + +impl TypedExpr { + #[builtin(typed_expr_as_function_definition)] + // docs:start:as_function_definition + fn as_function_definition(self) -> Option {} + // docs:end:as_function_definition +} 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 e7e156ad1f1..7248d51ca9a 100644 --- a/test_programs/noir_test_success/comptime_expr/src/main.nr +++ b/test_programs/noir_test_success/comptime_expr/src/main.nr @@ -577,6 +577,17 @@ mod tests { } } + #[test] + fn test_resolve_to_function_definition() { + comptime + { + let expr = quote { times_two }.as_expr().unwrap(); + let func = expr.resolve().as_function_definition().unwrap(); + assert_eq(func.name(), quote { times_two }); + assert_eq(func.parameters().len(), 1); + } + } + comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { let expr = quoted.as_expr().unwrap(); let (op, _) = expr.as_unary_op().unwrap(); diff --git a/tooling/lsp/src/requests/completion/builtins.rs b/tooling/lsp/src/requests/completion/builtins.rs index 430e04aedfd..800ab76c623 100644 --- a/tooling/lsp/src/requests/completion/builtins.rs +++ b/tooling/lsp/src/requests/completion/builtins.rs @@ -95,6 +95,7 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { Keyword::TraitConstraint => Some("TraitConstraint"), Keyword::TraitDefinition => Some("TraitDefinition"), Keyword::TraitImpl => Some("TraitImpl"), + Keyword::TypedExpr => Some("TypedExpr"), Keyword::TypeType => Some("Type"), Keyword::UnresolvedType => Some("UnresolvedType"), @@ -200,6 +201,7 @@ pub(super) fn keyword_builtin_function(keyword: &Keyword) -> Option