diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 330a6efaac3..4ed72cc0263 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -17,8 +17,8 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, UnresolvedType, - UnresolvedTypeData, Visibility, + ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, UnaryOp, + UnresolvedType, UnresolvedTypeData, Visibility, }, hir::comptime::{errors::IResult, value::add_token_spans, InterpreterError, Value}, hir_def::function::FunctionBody, @@ -51,6 +51,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), "expr_as_if" => expr_as_if(arguments, return_type, location), "expr_as_index" => expr_as_index(arguments, return_type, location), + "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_name" => function_def_name(interner, arguments, location), @@ -837,6 +838,43 @@ fn expr_as_index( }) } +// fn as_unary_op(self) -> Option<(UnaryOp, Expr)> +fn expr_as_unary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Prefix(prefix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 2); + + tuple_types.pop().unwrap(); + let unary_op_type = tuple_types.pop().unwrap(); + + // These values should match the values used in noir_stdlib/src/meta/op.nr + let unary_op_value = match prefix_expr.operator { + UnaryOp::Minus => 0_u128, + UnaryOp::Not => 1_u128, + UnaryOp::MutableReference => 2_u128, + UnaryOp::Dereference { .. } => 3_u128, + }; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into())); + + let unary_op = Value::Struct(fields, unary_op_type); + let rhs = Value::Expr(prefix_expr.rhs.kind); + Some(Value::Tuple(vec![unary_op, rhs])) + } else { + None + } + }) +} + // fn as_tuple(self) -> Option<[Expr]> fn expr_as_tuple( arguments: Vec<(Value, Location)>, diff --git a/noir_stdlib/src/meta/expr.nr b/noir_stdlib/src/meta/expr.nr index df375fa97d3..3230ce42457 100644 --- a/noir_stdlib/src/meta/expr.nr +++ b/noir_stdlib/src/meta/expr.nr @@ -1,4 +1,5 @@ use crate::option::Option; +use crate::meta::op::UnaryOp; impl Expr { #[builtin(expr_as_bool)] @@ -15,4 +16,7 @@ impl Expr { #[builtin(expr_as_tuple)] fn as_tuple(self) -> Option<[Expr]> {} + + #[builtin(expr_as_unary_op)] + fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} } diff --git a/noir_stdlib/src/meta/mod.nr b/noir_stdlib/src/meta/mod.nr index e081d4ecd4b..beaee8e44ea 100644 --- a/noir_stdlib/src/meta/mod.nr +++ b/noir_stdlib/src/meta/mod.nr @@ -5,6 +5,7 @@ use crate::hash::poseidon2::Poseidon2Hasher; mod expr; mod function_def; mod module; +mod op; mod struct_def; mod trait_constraint; mod trait_def; diff --git a/noir_stdlib/src/meta/op.nr b/noir_stdlib/src/meta/op.nr new file mode 100644 index 00000000000..1fbd4884841 --- /dev/null +++ b/noir_stdlib/src/meta/op.nr @@ -0,0 +1,21 @@ +struct UnaryOp { + op: Field +} + +impl UnaryOp { + fn is_minus(self) -> bool { + self.op == 0 + } + + fn is_not(self) -> bool { + self.op == 1 + } + + fn is_mutable_reference(self) -> bool { + self.op == 2 + } + + fn is_dereference(self) -> bool { + self.op == 3 + } +} diff --git a/test_programs/compile_success_empty/comptime_exp/src/main.nr b/test_programs/compile_success_empty/comptime_exp/src/main.nr index 339b8ecdce6..eb0d27e2a38 100644 --- a/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ b/test_programs/compile_success_empty/comptime_exp/src/main.nr @@ -34,5 +34,22 @@ fn main() { let expr = quote { true }.as_expr().unwrap(); assert_eq(expr.as_bool().unwrap(), true); + + // Check Expr::as_unary_op + let expr = quote { -x }.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + assert(op.is_minus()); + + let expr = quote { !x }.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + assert(op.is_not()); + + let expr = quote { &mut x }.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + assert(op.is_mutable_reference()); + + let expr = quote { *x }.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + assert(op.is_dereference()); } }