diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 6ef4aee7531..1303ca83e1f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -18,9 +18,13 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ ArrayLiteral, ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, - UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, + StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, + }, + hir::comptime::{ + errors::IResult, + value::{add_token_spans, ExprValue}, + InterpreterError, Value, }, - hir::comptime::{errors::IResult, value::add_token_spans, InterpreterError, Value}, hir_def::function::FunctionBody, macros_api::{ModuleDefId, NodeInterner, Signedness}, node_interner::{DefinitionKind, TraitImplKind}, @@ -49,6 +53,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "as_slice" => as_slice(interner, arguments, location), "expr_as_array" => expr_as_array(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), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), "expr_as_if" => expr_as_if(arguments, return_type, location), @@ -64,6 +69,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_as_slice" => expr_as_slice(arguments, return_type, location), "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), + "expr_has_semicolon" => expr_has_semicolon(arguments, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), @@ -346,7 +352,7 @@ fn quoted_as_expr( let argument = check_one_argument(arguments, location)?; let expr = parse(argument, parser::expression(), "an expression").ok(); - let value = expr.map(|expr| Value::Expr(expr.kind)); + let value = expr.map(|expr| Value::expression(expr.kind)); option(return_type, value) } @@ -773,8 +779,11 @@ fn expr_as_array( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr { - let exprs = exprs.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + if let ExprValue::Expression(ExpressionKind::Literal(Literal::Array( + ArrayLiteral::Standard(exprs), + ))) = expr + { + let exprs = exprs.into_iter().map(|expr| Value::expression(expr.kind)).collect(); let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); Some(Value::Slice(exprs, typ)) } else { @@ -790,7 +799,7 @@ fn expr_as_binary_op( location: Location, ) -> IResult { expr_as(arguments, return_type.clone(), location, |expr| { - if let ExpressionKind::Infix(infix_expr) = expr { + if let ExprValue::Expression(ExpressionKind::Infix(infix_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"); @@ -807,8 +816,8 @@ fn expr_as_binary_op( fields.insert(Rc::new("op".to_string()), Value::Field(binary_op_value.into())); let unary_op = Value::Struct(fields, binary_op_type); - let lhs = Value::Expr(infix_expr.lhs.kind); - let rhs = Value::Expr(infix_expr.rhs.kind); + let lhs = Value::expression(infix_expr.lhs.kind); + let rhs = Value::expression(infix_expr.rhs.kind); Some(Value::Tuple(vec![lhs, unary_op, rhs])) } else { None @@ -816,6 +825,25 @@ fn expr_as_binary_op( }) } +// fn as_block(self) -> Option<[Expr]> +fn expr_as_block( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExprValue::Expression(ExpressionKind::Block(block_expr)) = expr { + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + let statements = block_expr.statements.into_iter(); + let statements = statements.map(|statement| Value::statement(statement.kind)).collect(); + + Some(Value::Slice(statements, typ)) + } else { + None + } + }) +} + // fn as_bool(self) -> Option fn expr_as_bool( arguments: Vec<(Value, Location)>, @@ -823,7 +851,7 @@ fn expr_as_bool( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::Literal(Literal::Bool(bool)) = expr { + if let ExprValue::Expression(ExpressionKind::Literal(Literal::Bool(bool))) = expr { Some(Value::Bool(bool)) } else { None @@ -838,10 +866,10 @@ fn expr_as_function_call( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::Call(call_expression) = expr { - let function = Value::Expr(call_expression.func.kind); + if let ExprValue::Expression(ExpressionKind::Call(call_expression)) = expr { + let function = Value::expression(call_expression.func.kind); let arguments = call_expression.arguments.into_iter(); - let arguments = arguments.map(|argument| Value::Expr(argument.kind)).collect(); + 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![function, arguments])) @@ -858,7 +886,7 @@ fn expr_as_if( location: Location, ) -> IResult { expr_as(arguments, return_type.clone(), location, |expr| { - if let ExpressionKind::If(if_expr) = expr { + if let ExprValue::Expression(ExpressionKind::If(if_expr)) = expr { // Get the type of `Option` let option_type = extract_option_generic_type(return_type.clone()); let Type::Tuple(option_types) = option_type else { @@ -867,12 +895,14 @@ fn expr_as_if( assert_eq!(option_types.len(), 3); let alternative_option_type = option_types[2].clone(); - let alternative = - option(alternative_option_type, if_expr.alternative.map(|e| Value::Expr(e.kind))); + let alternative = option( + alternative_option_type, + if_expr.alternative.map(|e| Value::expression(e.kind)), + ); Some(Value::Tuple(vec![ - Value::Expr(if_expr.condition.kind), - Value::Expr(if_expr.consequence.kind), + Value::expression(if_expr.condition.kind), + Value::expression(if_expr.consequence.kind), alternative.ok()?, ])) } else { @@ -888,10 +918,10 @@ fn expr_as_index( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::Index(index_expr) = expr { + if let ExprValue::Expression(ExpressionKind::Index(index_expr)) = expr { Some(Value::Tuple(vec![ - Value::Expr(index_expr.collection.kind), - Value::Expr(index_expr.index.kind), + Value::expression(index_expr.collection.kind), + Value::expression(index_expr.index.kind), ])) } else { None @@ -906,7 +936,8 @@ fn expr_as_integer( location: Location, ) -> IResult { expr_as(arguments, return_type.clone(), location, |expr| { - if let ExpressionKind::Literal(Literal::Integer(field, sign)) = expr { + if let ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field, sign))) = expr + { Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) } else { None @@ -921,9 +952,12 @@ fn expr_as_member_access( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::MemberAccess(member_access) = expr { + if let ExprValue::Expression(ExpressionKind::MemberAccess(member_access)) = expr { let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]); - Some(Value::Tuple(vec![Value::Expr(member_access.lhs.kind), Value::Quoted(tokens)])) + Some(Value::Tuple(vec![ + Value::expression(member_access.lhs.kind), + Value::Quoted(tokens), + ])) } else { None } @@ -937,12 +971,14 @@ fn expr_as_repeated_element_array( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { - repeated_element, - length, - })) = expr + if let ExprValue::Expression(ExpressionKind::Literal(Literal::Array( + ArrayLiteral::Repeated { repeated_element, length }, + ))) = expr { - Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)])) + Some(Value::Tuple(vec![ + Value::expression(repeated_element.kind), + Value::expression(length.kind), + ])) } else { None } @@ -956,12 +992,14 @@ fn expr_as_repeated_element_slice( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Repeated { - repeated_element, - length, - })) = expr + if let ExprValue::Expression(ExpressionKind::Literal(Literal::Slice( + ArrayLiteral::Repeated { repeated_element, length }, + ))) = expr { - Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)])) + Some(Value::Tuple(vec![ + Value::expression(repeated_element.kind), + Value::expression(length.kind), + ])) } else { None } @@ -975,8 +1013,11 @@ fn expr_as_slice( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr { - let exprs = exprs.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + if let ExprValue::Expression(ExpressionKind::Literal(Literal::Slice( + ArrayLiteral::Standard(exprs), + ))) = expr + { + let exprs = exprs.into_iter().map(|expr| Value::expression(expr.kind)).collect(); let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); Some(Value::Slice(exprs, typ)) } else { @@ -992,8 +1033,9 @@ fn expr_as_tuple( location: Location, ) -> IResult { expr_as(arguments, return_type, location, |expr| { - if let ExpressionKind::Tuple(expressions) = expr { - let expressions = expressions.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + if let ExprValue::Expression(ExpressionKind::Tuple(expressions)) = expr { + let expressions = + expressions.into_iter().map(|expr| Value::expression(expr.kind)).collect(); let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); Some(Value::Slice(expressions, typ)) } else { @@ -1009,7 +1051,7 @@ fn expr_as_unary_op( location: Location, ) -> IResult { expr_as(arguments, return_type.clone(), location, |expr| { - if let ExpressionKind::Prefix(prefix_expr) = expr { + if let ExprValue::Expression(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"); @@ -1031,7 +1073,7 @@ fn expr_as_unary_op( 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); + let rhs = Value::expression(prefix_expr.rhs.kind); Some(Value::Tuple(vec![unary_op, rhs])) } else { None @@ -1039,6 +1081,13 @@ fn expr_as_unary_op( }) } +// fn as_has_semicolon(self) -> bool +fn expr_has_semicolon(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let expr_value = get_expr(self_argument)?; + Ok(Value::Bool(matches!(expr_value, ExprValue::Statement(StatementKind::Semi(..))))) +} + // Helper function for implementing the `expr_as_...` functions. fn expr_as( arguments: Vec<(Value, Location)>, @@ -1047,15 +1096,24 @@ fn expr_as( f: F, ) -> IResult where - F: FnOnce(ExpressionKind) -> Option, + F: FnOnce(ExprValue) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let mut expression_kind = get_expr(self_argument)?; - while let ExpressionKind::Parenthesized(expression) = expression_kind { - expression_kind = expression.kind; + let mut expr_value = get_expr(self_argument)?; + loop { + match expr_value { + ExprValue::Expression(ExpressionKind::Parenthesized(expression)) => { + expr_value = ExprValue::Expression(expression.kind); + } + ExprValue::Statement(StatementKind::Expression(expression)) + | ExprValue::Statement(StatementKind::Semi(expression)) => { + expr_value = ExprValue::Expression(expression.kind); + } + _ => break, + } } - let option_value = f(expression_kind); + let option_value = f(expr_value); option(return_type, option_value) } 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 8d2f55b9c20..81abc4e76fa 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,9 +4,13 @@ use acvm::FieldElement; use noirc_errors::Location; use crate::{ - ast::{ExpressionKind, IntegerBitSize, Signedness}, + ast::{IntegerBitSize, Signedness}, hir::{ - comptime::{errors::IResult, value::add_token_spans, Interpreter, InterpreterError, Value}, + comptime::{ + errors::IResult, + value::{add_token_spans, ExprValue}, + Interpreter, InterpreterError, Value, + }, def_map::ModuleId, }, hir_def::{ @@ -137,7 +141,7 @@ pub(crate) fn get_u32((value, location): (Value, Location)) -> IResult { } } -pub(crate) fn get_expr((value, location): (Value, Location)) -> IResult { +pub(crate) fn get_expr((value, location): (Value, Location)) -> IResult { match value { Value::Expr(expr) => Ok(expr), value => type_mismatch(value, Type::Quoted(QuotedType::Expr), location), diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index d65c2bb7dcc..21957f3eb29 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -5,9 +5,13 @@ use chumsky::Parser; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::{Location, Span}; +use strum_macros::Display; use crate::{ - ast::{ArrayLiteral, ConstructorExpression, Ident, IntegerBitSize, Signedness}, + ast::{ + ArrayLiteral, BlockExpression, ConstructorExpression, Ident, IntegerBitSize, Signedness, + Statement, StatementKind, + }, hir::def_map::ModuleId, hir_def::{ expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, @@ -61,10 +65,24 @@ pub enum Value { ModuleDefinition(ModuleId), Type(Type), Zeroed(Type), - Expr(ExpressionKind), + Expr(ExprValue), +} + +#[derive(Debug, Clone, PartialEq, Eq, Display)] +pub enum ExprValue { + Expression(ExpressionKind), + Statement(StatementKind), } impl Value { + pub(crate) fn expression(expr: ExpressionKind) -> Self { + Value::Expr(ExprValue::Expression(expr)) + } + + pub(crate) fn statement(statement: StatementKind) -> Self { + Value::Expr(ExprValue::Statement(statement)) + } + pub(crate) fn get_type(&self) -> Cow { Cow::Owned(match self { Value::Unit => Type::Unit, @@ -230,7 +248,12 @@ impl Value { } }; } - Value::Expr(expr) => expr, + Value::Expr(ExprValue::Expression(expr)) => expr, + Value::Expr(ExprValue::Statement(statement)) => { + ExpressionKind::Block(BlockExpression { + statements: vec![Statement { kind: statement, span: location.span }], + }) + } Value::Pointer(..) | Value::StructDefinition(_) | Value::TraitConstraint(..) @@ -563,7 +586,8 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::ModuleDefinition(_) => write!(f, "(module)"), Value::Zeroed(typ) => write!(f, "(zeroed {typ})"), Value::Type(typ) => write!(f, "{}", typ), - Value::Expr(expr) => write!(f, "{}", expr), + Value::Expr(ExprValue::Expression(expr)) => write!(f, "{}", expr), + Value::Expr(ExprValue::Statement(statement)) => write!(f, "{}", statement), } } } diff --git a/noir_stdlib/src/meta/expr.nr b/noir_stdlib/src/meta/expr.nr index 94889b7c3dd..64678b58eae 100644 --- a/noir_stdlib/src/meta/expr.nr +++ b/noir_stdlib/src/meta/expr.nr @@ -12,6 +12,9 @@ impl Expr { #[builtin(expr_as_binary_op)] fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + #[builtin(expr_as_block)] + fn as_block(self) -> Option<[Expr]> {} + #[builtin(expr_as_bool)] fn as_bool(self) -> Option {} @@ -41,6 +44,9 @@ impl Expr { #[builtin(expr_as_unary_op)] fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} + + #[builtin(expr_has_semicolon)] + fn has_semicolon(self) -> bool {} } mod tests { @@ -60,6 +66,23 @@ mod tests { } } + #[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