diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 74596f54a7f..8aa8e92408f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -65,6 +65,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_as_index" => expr_as_index(arguments, return_type, location), "expr_as_integer" => expr_as_integer(arguments, return_type, location), "expr_as_member_access" => expr_as_member_access(arguments, return_type, location), + "expr_as_method_call" => expr_as_method_call(arguments, return_type, location), "expr_as_repeated_element_array" => { expr_as_repeated_element_array(arguments, return_type, location) } @@ -1056,6 +1057,39 @@ fn expr_as_member_access( }) } +// fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> +fn expr_as_method_call( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExprValue::Expression(ExpressionKind::MethodCall(method_call)) = expr { + let object = Value::expression(method_call.object.kind); + + let name_tokens = + Rc::new(vec![Token::Ident(method_call.method_name.0.contents.clone())]); + let name = Value::Quoted(name_tokens); + + let generics = method_call.generics.unwrap_or_default().into_iter(); + let generics = generics.map(|generic| Value::UnresolvedType(generic.typ)).collect(); + let generics = Value::Slice( + generics, + Type::Slice(Box::new(Type::Quoted(QuotedType::UnresolvedType))), + ); + + let arguments = method_call.arguments.into_iter(); + let arguments = arguments.map(|argument| Value::expression(argument.kind)).collect(); + let arguments = + Value::Slice(arguments, Type::Slice(Box::new(Type::Quoted(QuotedType::Expr)))); + + Some(Value::Tuple(vec![object, name, generics, arguments])) + } else { + None + } + }) +} + // fn as_repeated_element_array(self) -> Option<(Expr, Expr)> fn expr_as_repeated_element_array( arguments: Vec<(Value, Location)>, diff --git a/docs/docs/noir/standard_library/meta/expr.md b/docs/docs/noir/standard_library/meta/expr.md index f6b6c84805a..0a32b2b04fc 100644 --- a/docs/docs/noir/standard_library/meta/expr.md +++ b/docs/docs/noir/standard_library/meta/expr.md @@ -19,13 +19,6 @@ If this expression is an array, this returns a slice of each element in the arra 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 - -If this element is an integer literal, return the integer as a field -as well as whether the integer is negative (true) or not (false). - ### as_binary_op #include_code as_binary_op noir_stdlib/src/meta/expr.nr rust @@ -75,6 +68,13 @@ return the condition, then branch, and else branch. If there is no else branch, If this expression is an index into an array `array[index]`, return the array and the index. +### as_integer + +#include_code as_integer noir_stdlib/src/meta/expr.nr rust + +If this element is an integer literal, return the integer as a field +as well as whether the integer is negative (true) or not (false). + ### as_member_access #include_code as_member_access noir_stdlib/src/meta/expr.nr rust @@ -82,6 +82,13 @@ array and the index. If this expression is a member access `foo.bar`, return the struct/tuple expression and the field. The field will be represented as a quoted value. +### as_method_call + +#include_code as_method_call noir_stdlib/src/meta/expr.nr rust + +If this expression is a method call `foo.bar::(arg1, ..., argN)`, return +the receiver, method name, a slice of each generic argument, and a slice of each argument. + ### as_repeated_element_array #include_code as_repeated_element_array noir_stdlib/src/meta/expr.nr rust diff --git a/noir_stdlib/src/meta/expr.nr b/noir_stdlib/src/meta/expr.nr index 60234b5e87b..ee3980f8f54 100644 --- a/noir_stdlib/src/meta/expr.nr +++ b/noir_stdlib/src/meta/expr.nr @@ -61,6 +61,11 @@ impl Expr { fn as_member_access(self) -> Option<(Expr, Quoted)> {} // docs:end:as_member_access + #[builtin(expr_as_method_call)] + // docs:start:as_method_call + fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} + // docs:end:as_method_call + #[builtin(expr_as_repeated_element_array)] // docs:start:as_repeated_element_array fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} @@ -106,297 +111,3 @@ impl Expr { fn is_continue(self) -> bool {} // docs:end:is_continue } - -mod tests { - use crate::meta::op::UnaryOp; - use crate::meta::op::BinaryOp; - - #[test] - fn test_expr_as_array() { - comptime - { - let expr = quote { [1, 2, 4] }.as_expr().unwrap(); - let elems = expr.as_array().unwrap(); - assert_eq(elems.len(), 3); - assert_eq(elems[0].as_integer().unwrap(), (1, false)); - assert_eq(elems[1].as_integer().unwrap(), (2, false)); - assert_eq(elems[2].as_integer().unwrap(), (4, false)); - } - } - - #[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 - { - let expr = quote { { 1; 4; 23 } }.as_expr().unwrap(); - let exprs = expr.as_block().unwrap(); - assert_eq(exprs.len(), 3); - assert_eq(exprs[0].as_integer().unwrap(), (1, false)); - assert_eq(exprs[1].as_integer().unwrap(), (4, false)); - assert_eq(exprs[2].as_integer().unwrap(), (23, false)); - - assert(exprs[0].has_semicolon()); - assert(exprs[1].has_semicolon()); - assert(!exprs[2].has_semicolon()); - } - } - - #[test] - fn test_expr_as_integer() { - comptime - { - let expr = quote { 1 }.as_expr().unwrap(); - assert_eq((1, false), expr.as_integer().unwrap()); - - let expr = quote { -2 }.as_expr().unwrap(); - assert_eq((2, true), expr.as_integer().unwrap()); - } - } - - #[test] - fn test_expr_as_binary_op() { - comptime - { - assert(get_binary_op(quote { x + y }).is_add()); - assert(get_binary_op(quote { x - y }).is_subtract()); - assert(get_binary_op(quote { x * y }).is_multiply()); - assert(get_binary_op(quote { x / y }).is_divide()); - assert(get_binary_op(quote { x == y }).is_equal()); - assert(get_binary_op(quote { x != y }).is_not_equal()); - assert(get_binary_op(quote { x < y }).is_less_than()); - assert(get_binary_op(quote { x <= y }).is_less_than_or_equal()); - assert(get_binary_op(quote { x > y }).is_greater_than()); - assert(get_binary_op(quote { x >= y }).is_greater_than_or_equal()); - assert(get_binary_op(quote { x & y }).is_and()); - assert(get_binary_op(quote { x | y }).is_or()); - assert(get_binary_op(quote { x ^ y }).is_xor()); - assert(get_binary_op(quote { x >> y }).is_shift_right()); - assert(get_binary_op(quote { x << y }).is_shift_left()); - assert(get_binary_op(quote { x % y }).is_modulo()); - } - } - - #[test] - fn test_expr_as_bool() { - comptime - { - let expr = quote { false }.as_expr().unwrap(); - assert(expr.as_bool().unwrap() == false); - - let expr = quote { true }.as_expr().unwrap(); - assert_eq(expr.as_bool().unwrap(), true); - } - } - - #[test] - fn test_expr_as_cast() { - comptime - { - let expr = quote { 1 as Field }.as_expr().unwrap(); - let (expr, typ) = expr.as_cast().unwrap(); - assert_eq(expr.as_integer().unwrap(), (1, false)); - assert(typ.is_field()); - } - } - - #[test] - fn test_expr_as_comptime() { - comptime - { - let expr = quote { comptime { 1; 4; 23 } }.as_expr().unwrap(); - let exprs = expr.as_comptime().unwrap(); - assert_eq(exprs.len(), 3); - } - } - - #[test] - fn test_expr_as_comptime_as_statement() { - comptime - { - let expr = quote { { comptime { 1; 4; 23 } } }.as_expr().unwrap(); - let exprs = expr.as_block().unwrap(); - assert_eq(exprs.len(), 1); - - let exprs = exprs[0].as_comptime().unwrap(); - assert_eq(exprs.len(), 3); - } - } - - // This test can't only be around the comptime block since that will cause - // `nargo fmt` to remove the comptime keyword. - // docs:start:as_expr_example - #[test] - fn test_expr_as_function_call() { - comptime - { - let expr = quote { foo(42) }.as_expr().unwrap(); - let (_function, args) = expr.as_function_call().unwrap(); - assert_eq(args.len(), 1); - assert_eq(args[0].as_integer().unwrap(), (42, false)); - } - } - // docs:end:as_expr_example - - #[test] - fn test_expr_as_if() { - comptime - { - let expr = quote { if 1 { 2 } }.as_expr().unwrap(); - let (_condition, _consequence, alternative) = expr.as_if().unwrap(); - assert(alternative.is_none()); - - let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); - let (_condition, _consequence, alternative) = expr.as_if().unwrap(); - assert(alternative.is_some()); - } - } - - #[test] - fn test_expr_as_index() { - comptime - { - let expr = quote { foo[bar] }.as_expr().unwrap(); - assert(expr.as_index().is_some()); - } - } - - #[test] - fn test_expr_as_member_access() { - comptime - { - let expr = quote { foo.bar }.as_expr().unwrap(); - let (_, name) = expr.as_member_access().unwrap(); - assert_eq(name, quote { bar }); - } - } - - #[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 - { - let expr = quote { [1; 3] }.as_expr().unwrap(); - let (expr, length) = expr.as_repeated_element_array().unwrap(); - assert_eq(expr.as_integer().unwrap(), (1, false)); - assert_eq(length.as_integer().unwrap(), (3, false)); - } - } - - #[test] - fn test_expr_as_repeated_element_slice() { - comptime - { - let expr = quote { &[1; 3] }.as_expr().unwrap(); - let (expr, length) = expr.as_repeated_element_slice().unwrap(); - assert_eq(expr.as_integer().unwrap(), (1, false)); - assert_eq(length.as_integer().unwrap(), (3, false)); - } - } - - #[test] - fn test_expr_as_slice() { - comptime - { - let expr = quote { &[1, 3, 5] }.as_expr().unwrap(); - let elems = expr.as_slice().unwrap(); - assert_eq(elems.len(), 3); - assert_eq(elems[0].as_integer().unwrap(), (1, false)); - assert_eq(elems[1].as_integer().unwrap(), (3, false)); - assert_eq(elems[2].as_integer().unwrap(), (5, false)); - } - } - - #[test] - fn test_expr_as_tuple() { - comptime - { - let expr = quote { (1, 2) }.as_expr().unwrap(); - let tuple_exprs = expr.as_tuple().unwrap(); - assert_eq(tuple_exprs.len(), 2); - } - } - - #[test] - fn test_expr_as_unary_op() { - comptime - { - assert(get_unary_op(quote { -x }).is_minus()); - assert(get_unary_op(quote { !x }).is_not()); - assert(get_unary_op(quote { &mut x }).is_mutable_reference()); - assert(get_unary_op(quote { *x }).is_dereference()); - } - } - - #[test] - fn test_expr_as_unsafe() { - comptime - { - let expr = quote { unsafe { 1; 4; 23 } }.as_expr().unwrap(); - let exprs = expr.as_unsafe().unwrap(); - assert_eq(exprs.len(), 3); - } - } - - #[test] - fn test_expr_is_break() { - comptime - { - let expr = quote { { break; } }.as_expr().unwrap(); - let exprs = expr.as_block().unwrap(); - assert(exprs[0].is_break()); - } - } - - #[test] - fn test_expr_is_continue() { - comptime - { - let expr = quote { { continue; } }.as_expr().unwrap(); - let exprs = expr.as_block().unwrap(); - assert(exprs[0].is_continue()); - } - } - - #[test] - fn test_automatically_unwraps_parenthesized_expression() { - comptime - { - let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); - assert(expr.as_if().is_some()); - } - } - - comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { - let expr = quoted.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - op - } - - comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { - let expr = quoted.as_expr().unwrap(); - let (_, op, _) = expr.as_binary_op().unwrap(); - op - } -} diff --git a/test_programs/noir_test_success/comptime_expr/Nargo.toml b/test_programs/noir_test_success/comptime_expr/Nargo.toml new file mode 100644 index 00000000000..a40da9c5f28 --- /dev/null +++ b/test_programs/noir_test_success/comptime_expr/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_expr" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/noir_test_success/comptime_expr/src/main.nr b/test_programs/noir_test_success/comptime_expr/src/main.nr new file mode 100644 index 00000000000..329e97dc9d9 --- /dev/null +++ b/test_programs/noir_test_success/comptime_expr/src/main.nr @@ -0,0 +1,313 @@ +mod tests { + use std::meta::op::UnaryOp; + use std::meta::op::BinaryOp; + + #[test] + fn test_expr_as_array() { + comptime + { + let expr = quote { [1, 2, 4] }.as_expr().unwrap(); + let elems = expr.as_array().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (1, false)); + assert_eq(elems[1].as_integer().unwrap(), (2, false)); + assert_eq(elems[2].as_integer().unwrap(), (4, false)); + } + } + + #[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 + { + let expr = quote { { 1; 4; 23 } }.as_expr().unwrap(); + let exprs = expr.as_block().unwrap(); + assert_eq(exprs.len(), 3); + assert_eq(exprs[0].as_integer().unwrap(), (1, false)); + assert_eq(exprs[1].as_integer().unwrap(), (4, false)); + assert_eq(exprs[2].as_integer().unwrap(), (23, false)); + + assert(exprs[0].has_semicolon()); + assert(exprs[1].has_semicolon()); + assert(!exprs[2].has_semicolon()); + } + } + + #[test] + fn test_expr_as_method_call() { + comptime + { + let expr = quote { foo.bar::(3, 4) }.as_expr().unwrap(); + let (_object, name, generics, arguments) = expr.as_method_call().unwrap(); + + assert_eq(name, quote { bar }); + + assert_eq(generics.len(), 1); + assert(generics[0].is_field()); + + assert_eq(arguments.len(), 2); + assert_eq(arguments[0].as_integer().unwrap(), (3, false)); + assert_eq(arguments[1].as_integer().unwrap(), (4, false)); + } + } + + #[test] + fn test_expr_as_integer() { + comptime + { + let expr = quote { 1 }.as_expr().unwrap(); + assert_eq((1, false), expr.as_integer().unwrap()); + + let expr = quote { -2 }.as_expr().unwrap(); + assert_eq((2, true), expr.as_integer().unwrap()); + } + } + + #[test] + fn test_expr_as_binary_op() { + comptime + { + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x < y }).is_less_than()); + assert(get_binary_op(quote { x <= y }).is_less_than_or_equal()); + assert(get_binary_op(quote { x > y }).is_greater_than()); + assert(get_binary_op(quote { x >= y }).is_greater_than_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); + } + } + + #[test] + fn test_expr_as_bool() { + comptime + { + let expr = quote { false }.as_expr().unwrap(); + assert(expr.as_bool().unwrap() == false); + + let expr = quote { true }.as_expr().unwrap(); + assert_eq(expr.as_bool().unwrap(), true); + } + } + + #[test] + fn test_expr_as_cast() { + comptime + { + let expr = quote { 1 as Field }.as_expr().unwrap(); + let (expr, typ) = expr.as_cast().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert(typ.is_field()); + } + } + + #[test] + fn test_expr_as_comptime() { + comptime + { + let expr = quote { comptime { 1; 4; 23 } }.as_expr().unwrap(); + let exprs = expr.as_comptime().unwrap(); + assert_eq(exprs.len(), 3); + } + } + + #[test] + fn test_expr_as_comptime_as_statement() { + comptime + { + let expr = quote { { comptime { 1; 4; 23 } } }.as_expr().unwrap(); + let exprs = expr.as_block().unwrap(); + assert_eq(exprs.len(), 1); + + let exprs = exprs[0].as_comptime().unwrap(); + assert_eq(exprs.len(), 3); + } + } + + // This test can't only be around the comptime block since that will cause + // `nargo fmt` to remove the comptime keyword. + // docs:start:as_expr_example + #[test] + fn test_expr_as_function_call() { + comptime + { + let expr = quote { foo(42) }.as_expr().unwrap(); + let (_function, args) = expr.as_function_call().unwrap(); + assert_eq(args.len(), 1); + assert_eq(args[0].as_integer().unwrap(), (42, false)); + } + } + // docs:end:as_expr_example + + #[test] + fn test_expr_as_if() { + comptime + { + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_some()); + } + } + + #[test] + fn test_expr_as_index() { + comptime + { + let expr = quote { foo[bar] }.as_expr().unwrap(); + assert(expr.as_index().is_some()); + } + } + + #[test] + fn test_expr_as_member_access() { + comptime + { + let expr = quote { foo.bar }.as_expr().unwrap(); + let (_, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); + } + } + + #[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 + { + let expr = quote { [1; 3] }.as_expr().unwrap(); + let (expr, length) = expr.as_repeated_element_array().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert_eq(length.as_integer().unwrap(), (3, false)); + } + } + + #[test] + fn test_expr_as_repeated_element_slice() { + comptime + { + let expr = quote { &[1; 3] }.as_expr().unwrap(); + let (expr, length) = expr.as_repeated_element_slice().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert_eq(length.as_integer().unwrap(), (3, false)); + } + } + + #[test] + fn test_expr_as_slice() { + comptime + { + let expr = quote { &[1, 3, 5] }.as_expr().unwrap(); + let elems = expr.as_slice().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (1, false)); + assert_eq(elems[1].as_integer().unwrap(), (3, false)); + assert_eq(elems[2].as_integer().unwrap(), (5, false)); + } + } + + #[test] + fn test_expr_as_tuple() { + comptime + { + let expr = quote { (1, 2) }.as_expr().unwrap(); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + } + } + + #[test] + fn test_expr_as_unary_op() { + comptime + { + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); + } + } + + #[test] + fn test_expr_as_unsafe() { + comptime + { + let expr = quote { unsafe { 1; 4; 23 } }.as_expr().unwrap(); + let exprs = expr.as_unsafe().unwrap(); + assert_eq(exprs.len(), 3); + } + } + + #[test] + fn test_expr_is_break() { + comptime + { + let expr = quote { { break; } }.as_expr().unwrap(); + let exprs = expr.as_block().unwrap(); + assert(exprs[0].is_break()); + } + } + + #[test] + fn test_expr_is_continue() { + comptime + { + let expr = quote { { continue; } }.as_expr().unwrap(); + let exprs = expr.as_block().unwrap(); + assert(exprs[0].is_continue()); + } + } + + #[test] + fn test_automatically_unwraps_parenthesized_expression() { + comptime + { + let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); + assert(expr.as_if().is_some()); + } + } + + comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op + } + + comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op + } +} + +fn main() {}