Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 39 additions & 6 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +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_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, hir_pattern_to_tokens, mutate_func_meta_type,
parse, parse_tokens, replace_func_meta_parameters, replace_func_meta_return_type,
get_trait_impl, get_tuple, get_type, get_u32, get_unresolved_type, hir_pattern_to_tokens,
mutate_func_meta_type, parse, parse_tokens, replace_func_meta_parameters,
replace_func_meta_return_type,
};
use im::Vector;
use iter_extended::{try_vecmap, vecmap};
Expand Down Expand Up @@ -53,6 +54,7 @@ impl<'local, 'context> Interpreter<'local, 'context> {
"array_len" => array_len(interner, arguments, location),
"as_slice" => as_slice(interner, arguments, location),
"expr_as_array" => expr_as_array(arguments, return_type, location),
"expr_as_assign" => expr_as_assign(arguments, return_type, location),
"expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location),
"expr_as_block" => expr_as_block(arguments, return_type, location),
"expr_as_bool" => expr_as_bool(arguments, return_type, location),
Expand Down Expand Up @@ -132,6 +134,7 @@ 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),
"unresolved_type_is_field" => unresolved_type_is_field(arguments, location),
"zeroed" => zeroed(return_type),
_ => {
let item = format!("Comptime evaluation for builtin function {name}");
Expand Down Expand Up @@ -705,6 +708,16 @@ fn trait_impl_trait_generic_args(
Ok(Value::Slice(trait_generics, slice_type))
}

// fn is_field(self) -> bool
fn unresolved_type_is_field(
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
let self_argument = check_one_argument(arguments, location)?;
let typ = get_unresolved_type(self_argument)?;
Ok(Value::Bool(matches!(typ, UnresolvedTypeData::FieldElement)))
}

// fn zeroed<T>() -> T
fn zeroed(return_type: Type) -> IResult<Value> {
match return_type {
Expand Down Expand Up @@ -806,6 +819,23 @@ fn expr_as_array(
})
}

// fn as_assign(self) -> Option<(Expr, Expr)>
fn expr_as_assign(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExprValue::Statement(StatementKind::Assign(assign)) = expr {
let lhs = Value::lvalue(assign.lvalue);
let rhs = Value::expression(assign.expression.kind);
Some(Value::Tuple(vec![lhs, rhs]))
} else {
None
}
})
}

// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)>
fn expr_as_binary_op(
arguments: Vec<(Value, Location)>,
Expand Down Expand Up @@ -1010,16 +1040,19 @@ fn expr_as_member_access(
return_type: Type,
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExprValue::Expression(ExpressionKind::MemberAccess(member_access)) = expr {
expr_as(arguments, return_type, location, |expr| match expr {
ExprValue::Expression(ExpressionKind::MemberAccess(member_access)) => {
let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]);
Some(Value::Tuple(vec![
Value::expression(member_access.lhs.kind),
Value::Quoted(tokens),
]))
} else {
None
}
ExprValue::LValue(crate::ast::LValue::MemberAccess { object, field_name, span: _ }) => {
let tokens = Rc::new(vec![Token::Ident(field_name.0.contents.clone())]);
Some(Value::Tuple(vec![Value::lvalue(*object), Value::Quoted(tokens)]))
}
_ => None,
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use acvm::FieldElement;
use noirc_errors::Location;

use crate::{
ast::{BlockExpression, IntegerBitSize, Signedness},
ast::{BlockExpression, IntegerBitSize, Signedness, UnresolvedTypeData},
hir::{
comptime::{
errors::IResult,
Expand Down Expand Up @@ -207,6 +207,15 @@ pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult<Rc<Vec
}
}

pub(crate) fn get_unresolved_type(
(value, location): (Value, Location),
) -> IResult<UnresolvedTypeData> {
match value {
Value::UnresolvedType(typ) => Ok(typ),
value => type_mismatch(value, Type::Quoted(QuotedType::UnresolvedType), location),
}
}

fn type_mismatch<T>(value: Value, expected: Type, location: Location) -> IResult<T> {
let actual = value.get_type().into_owned();
Err(InterpreterError::TypeMismatch { expected, actual, location })
Expand Down
13 changes: 10 additions & 3 deletions compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use strum_macros::Display;

use crate::{
ast::{
ArrayLiteral, BlockExpression, ConstructorExpression, Ident, IntegerBitSize, Signedness,
Statement, StatementKind, UnresolvedTypeData,
ArrayLiteral, BlockExpression, ConstructorExpression, Ident, IntegerBitSize, LValue,
Signedness, Statement, StatementKind, UnresolvedTypeData,
},
hir::{def_map::ModuleId, type_check::generics::TraitGenerics},
hir_def::{
Expand Down Expand Up @@ -73,6 +73,7 @@ pub enum Value {
pub enum ExprValue {
Expression(ExpressionKind),
Statement(StatementKind),
LValue(LValue),
}

impl Value {
Expand All @@ -84,6 +85,10 @@ impl Value {
Value::Expr(ExprValue::Statement(statement))
}

pub(crate) fn lvalue(lvaue: LValue) -> Self {
Value::Expr(ExprValue::LValue(lvaue))
}

pub(crate) fn get_type(&self) -> Cow<Type> {
Cow::Owned(match self {
Value::Unit => Type::Unit,
Expand Down Expand Up @@ -256,7 +261,8 @@ impl Value {
statements: vec![Statement { kind: statement, span: location.span }],
})
}
Value::Pointer(..)
Value::Expr(ExprValue::LValue(_))
| Value::Pointer(..)
| Value::StructDefinition(_)
| Value::TraitConstraint(..)
| Value::TraitDefinition(_)
Expand Down Expand Up @@ -593,6 +599,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> {
Value::Type(typ) => write!(f, "{}", typ),
Value::Expr(ExprValue::Expression(expr)) => write!(f, "{}", expr),
Value::Expr(ExprValue::Statement(statement)) => write!(f, "{}", statement),
Value::Expr(ExprValue::LValue(lvalue)) => write!(f, "{}", lvalue),
Value::UnresolvedType(typ) => write!(f, "{}", typ),
}
}
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>`
- `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.
Using these methods is the key to writing powerful metaprogramming libraries.
Expand Down
7 changes: 7 additions & 0 deletions docs/docs/noir/standard_library/meta/expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ title: Expr

If this expression is an array, this returns a slice of each element in the array.

### as_assign

#include_code as_assign noir_stdlib/src/meta/expr.nr rust

If this expression is an assignment, this returns a tuple with the left hand side
and right hand side in order.

### as_integer

#include_code as_integer noir_stdlib/src/meta/expr.nr rust
Expand Down
13 changes: 13 additions & 0 deletions docs/docs/noir/standard_library/meta/unresolved_type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
title: UnresolvedType
---

`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types.

## Methods

### is_field

#include_code is_field noir_stdlib/src/meta/unresolved_type.nr rust

Returns true if this type refers to the Field type.
31 changes: 30 additions & 1 deletion noir_stdlib/src/meta/expr.nr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ impl Expr {
fn as_array(self) -> Option<[Expr]> {}
// docs:end:as_array

#[builtin(expr_as_assign)]
// docs:start:as_assign
fn as_assign(self) -> Option<(Expr, Expr)> {}
// docs:end:as_assign

#[builtin(expr_as_integer)]
// docs:start:as_integer
fn as_integer(self) -> Option<(Field, bool)> {}
Expand Down Expand Up @@ -119,6 +124,17 @@ mod tests {
}
}

#[test]
fn test_expr_as_assign() {
comptime
{
let expr = quote { { a = 1; } }.as_expr().unwrap();
let exprs = expr.as_block().unwrap();
let (_lhs, rhs) = exprs[0].as_assign().unwrap();
assert_eq(rhs.as_integer().unwrap(), (1, false));
}
}

#[test]
fn test_expr_as_block() {
comptime
Expand Down Expand Up @@ -188,8 +204,9 @@ mod tests {
comptime
{
let expr = quote { 1 as Field }.as_expr().unwrap();
let (expr, _typ) = expr.as_cast().unwrap();
let (expr, typ) = expr.as_cast().unwrap();
assert_eq(expr.as_integer().unwrap(), (1, false));
assert(typ.is_field());
}
}

Expand Down Expand Up @@ -264,6 +281,18 @@ mod tests {
}
}

#[test]
fn test_expr_as_member_access_with_an_lvalue() {
comptime
{
let expr = quote { { foo.bar = 1; } }.as_expr().unwrap();
let exprs = expr.as_block().unwrap();
let (lhs, _rhs) = exprs[0].as_assign().unwrap();
let (_, name) = lhs.as_member_access().unwrap();
assert_eq(name, quote { bar });
}
}

#[test]
fn test_expr_as_repeated_element_array() {
comptime
Expand Down
3 changes: 2 additions & 1 deletion noir_stdlib/src/meta/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod trait_def;
mod trait_impl;
mod typ;
mod quoted;
mod unresolved_type;

/// Calling unquote as a macro (via `unquote!(arg)`) will unquote
/// its argument. Since this is the effect `!` already does, `unquote`
Expand Down Expand Up @@ -163,7 +164,7 @@ mod tests {
}
// docs:end:annotation-arguments-example

// docs:end:annotation-varargs-example
// docs:start:annotation-varargs-example
#[assert_three_args(1, 2, 3)]
struct MyOtherStruct { my_other_field: u32 }

Expand Down
6 changes: 6 additions & 0 deletions noir_stdlib/src/meta/unresolved_type.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
impl UnresolvedType {
#[builtin(unresolved_type_is_field)]
// docs:start:is_field
fn is_field(self) -> bool {}
// docs:end:is_field
}