diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index dcfdfe72e37..74596f54a7f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -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}; @@ -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), @@ -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}"); @@ -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 { + 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 fn zeroed(return_type: Type) -> IResult { match return_type { @@ -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 { + 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)>, @@ -1010,16 +1040,19 @@ fn expr_as_member_access( return_type: Type, location: Location, ) -> IResult { - 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, }) } 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 2bc5e2c44ef..a409731a5e4 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 @@ -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, @@ -207,6 +207,15 @@ pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult IResult { + match value { + Value::UnresolvedType(typ) => Ok(typ), + value => type_mismatch(value, Type::Quoted(QuotedType::UnresolvedType), location), + } +} + fn type_mismatch(value: Value, expected: Type, location: Location) -> IResult { let actual = value.get_type().into_owned(); Err(InterpreterError::TypeMismatch { expected, actual, location }) diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index be73a8fb82d..18f482585ea 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -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::{ @@ -73,6 +73,7 @@ pub enum Value { pub enum ExprValue { Expression(ExpressionKind), Statement(StatementKind), + LValue(LValue), } impl Value { @@ -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 { Cow::Owned(match self { Value::Unit => Type::Unit, @@ -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(_) @@ -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), } } diff --git a/docs/docs/noir/concepts/comptime.md b/docs/docs/noir/concepts/comptime.md index 13bfbb13522..24ec7bd93d1 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` +- `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. diff --git a/docs/docs/noir/standard_library/meta/expr.md b/docs/docs/noir/standard_library/meta/expr.md index bcc9756c06c..f6b6c84805a 100644 --- a/docs/docs/noir/standard_library/meta/expr.md +++ b/docs/docs/noir/standard_library/meta/expr.md @@ -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 diff --git a/docs/docs/noir/standard_library/meta/unresolved_type.md b/docs/docs/noir/standard_library/meta/unresolved_type.md new file mode 100644 index 00000000000..9c61f91dee2 --- /dev/null +++ b/docs/docs/noir/standard_library/meta/unresolved_type.md @@ -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. diff --git a/noir_stdlib/src/meta/expr.nr b/noir_stdlib/src/meta/expr.nr index 718ba9607d4..60234b5e87b 100644 --- a/noir_stdlib/src/meta/expr.nr +++ b/noir_stdlib/src/meta/expr.nr @@ -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)> {} @@ -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 @@ -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()); } } @@ -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 diff --git a/noir_stdlib/src/meta/mod.nr b/noir_stdlib/src/meta/mod.nr index 1ace2d028a4..be1b12540c9 100644 --- a/noir_stdlib/src/meta/mod.nr +++ b/noir_stdlib/src/meta/mod.nr @@ -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` @@ -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 } diff --git a/noir_stdlib/src/meta/unresolved_type.nr b/noir_stdlib/src/meta/unresolved_type.nr new file mode 100644 index 00000000000..2589174ed64 --- /dev/null +++ b/noir_stdlib/src/meta/unresolved_type.nr @@ -0,0 +1,6 @@ +impl UnresolvedType { + #[builtin(unresolved_type_is_field)] + // docs:start:is_field + fn is_field(self) -> bool {} + // docs:end:is_field +}