Skip to content

Commit

Permalink
feat: add Expr::resolve and TypedExpr::as_function_definition (#5859
Browse files Browse the repository at this point in the history
)

# Description

## Problem

Part of #5668

## Summary



## Additional Context



## Documentation

Check one:
- [ ] No documentation needed.
- [x] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.

---------

Co-authored-by: jfecher <[email protected]>
  • Loading branch information
asterite and jfecher authored Aug 29, 2024
1 parent 663e00c commit bceee55
Show file tree
Hide file tree
Showing 15 changed files with 156 additions and 14 deletions.
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/elaborator/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
80 changes: 72 additions & 8 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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},
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
_ => {
Expand Down Expand Up @@ -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<Value> {
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,
Expand Down Expand Up @@ -1380,7 +1405,48 @@ where
F: FnOnce(ExprValue) -> Option<Value>,
{
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<Value> {
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)) => {
Expand All @@ -1402,9 +1468,7 @@ where
_ => break,
}
}

let option_value = f(expr_value);
option(return_type, option_value)
expr_value
}

// fn body(self) -> Expr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -227,6 +227,13 @@ pub(crate) fn get_type((value, location): (Value, Location)) -> IResult<Type> {
}
}

pub(crate) fn get_typed_expr((value, location): (Value, Location)) -> IResult<TypedExpr> {
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<Rc<Vec<Token>>> {
match value {
Value::Quoted(tokens) => Ok(tokens),
Expand Down
18 changes: 15 additions & 3 deletions compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -69,6 +69,7 @@ pub enum Value {
Type(Type),
Zeroed(Type),
Expr(ExprValue),
TypedExpr(TypedExpr),
UnresolvedType(UnresolvedTypeData),
}

Expand All @@ -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))
Expand Down Expand Up @@ -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),
})
}
Expand Down Expand Up @@ -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(..)
Expand Down Expand Up @@ -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(..)
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ pub enum QuotedType {
Quoted,
TopLevelItem,
Type,
TypedExpr,
StructDefinition,
TraitConstraint,
TraitDefinition,
Expand Down Expand Up @@ -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"),
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,7 @@ pub enum Keyword {
TraitDefinition,
TraitImpl,
Type,
TypedExpr,
TypeType,
Unchecked,
Unconstrained,
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions compiler/noirc_frontend/src/parser/parser/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub(super) fn comptime_type() -> impl NoirParser<UnresolvedType> {
type_of_quoted_types(),
top_level_item_type(),
quoted_type(),
typed_expr_type(),
))
}

Expand Down Expand Up @@ -159,6 +160,12 @@ fn quoted_type() -> impl NoirParser<UnresolvedType> {
.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<UnresolvedType> {
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.
Expand Down
1 change: 1 addition & 0 deletions docs/docs/noir/concepts/comptime.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Field>`
- `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.
Expand Down
8 changes: 7 additions & 1 deletion docs/docs/noir/standard_library/meta/expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 }`.
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.
13 changes: 13 additions & 0 deletions docs/docs/noir/standard_library/meta/typed_expr.md
Original file line number Diff line number Diff line change
@@ -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()`.
5 changes: 5 additions & 0 deletions noir_stdlib/src/meta/expr.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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<Env>(expr: Expr, f: fn[Env](Expr) -> Option<Expr>) -> Option<Expr> {
Expand Down
1 change: 1 addition & 0 deletions noir_stdlib/src/meta/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod trait_constraint;
mod trait_def;
mod trait_impl;
mod typ;
mod typed_expr;
mod quoted;
mod unresolved_type;

Expand Down
8 changes: 8 additions & 0 deletions noir_stdlib/src/meta/typed_expr.nr
Original file line number Diff line number Diff line change
@@ -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<FunctionDefinition> {}
// docs:end:as_function_definition
}
11 changes: 11 additions & 0 deletions test_programs/noir_test_success/comptime_expr/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions tooling/lsp/src/requests/completion/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,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"),

Expand Down Expand Up @@ -207,6 +208,7 @@ pub(super) fn keyword_builtin_function(keyword: &Keyword) -> Option<BuiltInFunct
| Keyword::TraitDefinition
| Keyword::TraitImpl
| Keyword::Type
| Keyword::TypedExpr
| Keyword::TypeType
| Keyword::Unchecked
| Keyword::Unconstrained
Expand Down

0 comments on commit bceee55

Please sign in to comment.