diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 4fb747b74c6..20527f8bea0 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -4,6 +4,11 @@ use std::{ }; use acvm::{AcirField, FieldElement}; +use builtin_helpers::{ + check_argument_count, check_one_argument, check_three_arguments, check_two_arguments, + get_function_def, get_quoted, get_slice, get_trait_constraint, get_trait_def, get_type, + get_u32, hir_pattern_to_tokens, +}; use chumsky::Parser; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; @@ -13,7 +18,6 @@ use crate::{ ast::IntegerBitSize, hir::comptime::{errors::IResult, value::add_token_spans, InterpreterError, Value}, macros_api::{NodeInterner, Signedness}, - node_interner::TraitId, parser, token::Token, QuotedType, Shared, Type, @@ -21,6 +25,8 @@ use crate::{ use super::Interpreter; +pub(crate) mod builtin_helpers; + impl<'local, 'context> Interpreter<'local, 'context> { pub(super) fn call_builtin( &mut self, @@ -34,11 +40,16 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), "is_unconstrained" => Ok(Value::Bool(true)), + "function_def_parameters" => function_def_parameters(interner, arguments, location), + "function_def_return_type" => function_def_return_type(interner, arguments, location), "modulus_be_bits" => modulus_be_bits(interner, arguments, location), "modulus_be_bytes" => modulus_be_bytes(interner, arguments, location), "modulus_le_bits" => modulus_le_bits(interner, arguments, location), "modulus_le_bytes" => modulus_le_bytes(interner, arguments, location), "modulus_num_bits" => modulus_num_bits(interner, arguments, location), + "quoted_as_trait_constraint" => quoted_as_trait_constraint(self, arguments, location), + "quoted_as_type" => quoted_as_type(self, arguments, location), + "quoted_eq" => quoted_eq(arguments, location), "slice_insert" => slice_insert(interner, arguments, location), "slice_pop_back" => slice_pop_back(interner, arguments, location), "slice_pop_front" => slice_pop_front(interner, arguments, location), @@ -55,8 +66,6 @@ impl<'local, 'context> Interpreter<'local, 'context> { } "trait_def_eq" => trait_def_eq(interner, arguments, location), "trait_def_hash" => trait_def_hash(interner, arguments, location), - "quoted_as_trait_constraint" => quoted_as_trait_constraint(self, arguments, location), - "quoted_as_type" => quoted_as_type(self, arguments, location), "type_as_array" => type_as_array(arguments, return_type, location), "type_as_constant" => type_as_constant(arguments, return_type, location), "type_as_integer" => type_as_integer(arguments, return_type, location), @@ -76,154 +85,10 @@ impl<'local, 'context> Interpreter<'local, 'context> { } } -pub(super) fn check_argument_count( - expected: usize, - arguments: &[(Value, Location)], - location: Location, -) -> IResult<()> { - if arguments.len() == expected { - Ok(()) - } else { - let actual = arguments.len(); - Err(InterpreterError::ArgumentCountMismatch { expected, actual, location }) - } -} - -pub(super) fn check_one_argument( - mut arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { - check_argument_count(1, &arguments, location)?; - - Ok(arguments.pop().unwrap().0) -} - -pub(super) fn check_two_arguments( - mut arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult<(Value, Value)> { - check_argument_count(2, &arguments, location)?; - - let argument2 = arguments.pop().unwrap().0; - let argument1 = arguments.pop().unwrap().0; - - Ok((argument1, argument2)) -} - -pub(super) fn check_three_arguments( - mut arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult<(Value, Value, Value)> { - check_argument_count(3, &arguments, location)?; - - let argument3 = arguments.pop().unwrap().0; - let argument2 = arguments.pop().unwrap().0; - let argument1 = arguments.pop().unwrap().0; - - Ok((argument1, argument2, argument3)) -} - fn failing_constraint(message: impl Into, location: Location) -> IResult { Err(InterpreterError::FailingConstraint { message: Some(message.into()), location }) } -pub(super) fn get_array( - interner: &NodeInterner, - value: Value, - location: Location, -) -> IResult<(im::Vector, Type)> { - match value { - Value::Array(values, typ) => Ok((values, typ)), - value => { - let type_var = Box::new(interner.next_type_variable()); - let expected = Type::Array(type_var.clone(), type_var); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } - } -} - -fn get_slice( - interner: &NodeInterner, - value: Value, - location: Location, -) -> IResult<(im::Vector, Type)> { - match value { - Value::Slice(values, typ) => Ok((values, typ)), - value => { - let type_var = Box::new(interner.next_type_variable()); - let expected = Type::Slice(type_var); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } - } -} - -pub(super) fn get_field(value: Value, location: Location) -> IResult { - match value { - Value::Field(value) => Ok(value), - value => { - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected: Type::FieldElement, actual, location }) - } - } -} - -pub(super) fn get_u32(value: Value, location: Location) -> IResult { - match value { - Value::U32(value) => Ok(value), - value => { - let expected = Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } - } -} - -fn get_trait_constraint(value: Value, location: Location) -> IResult<(TraitId, Vec)> { - match value { - Value::TraitConstraint(trait_id, generics) => Ok((trait_id, generics)), - value => { - let expected = Type::Quoted(QuotedType::TraitConstraint); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } - } -} - -fn get_trait_def(value: Value, location: Location) -> IResult { - match value { - Value::TraitDefinition(id) => Ok(id), - value => { - let expected = Type::Quoted(QuotedType::TraitDefinition); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } - } -} - -fn get_type(value: Value, location: Location) -> IResult { - match value { - Value::Type(typ) => Ok(typ), - value => { - let expected = Type::Quoted(QuotedType::Type); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } - } -} - -fn get_quoted(value: Value, location: Location) -> IResult>> { - match value { - Value::Quoted(tokens) => Ok(tokens), - value => { - let expected = Type::Quoted(QuotedType::Quoted); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } - } -} - fn array_len( interner: &NodeInterner, arguments: Vec<(Value, Location)>, @@ -615,6 +480,9 @@ where fn type_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { let (self_type, other_type) = check_two_arguments(arguments, location)?; + let self_type = get_type(self_type, location)?; + let other_type = get_type(other_type, location)?; + Ok(Value::Bool(self_type == other_type)) } @@ -784,6 +652,47 @@ fn zeroed(return_type: Type) -> IResult { } } +// fn parameters(self) -> [(Quoted, Type)] +fn function_def_parameters( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let func_id = get_function_def(self_argument, location)?; + let func_meta = interner.function_meta(&func_id); + + let parameters = func_meta + .parameters + .iter() + .map(|(hir_pattern, typ, _visibility)| { + let name = Value::Quoted(Rc::new(hir_pattern_to_tokens(interner, hir_pattern))); + let typ = Value::Type(typ.clone()); + Value::Tuple(vec![name, typ]) + }) + .collect(); + + let typ = Type::Slice(Box::new(Type::Tuple(vec![ + Type::Quoted(QuotedType::Quoted), + Type::Quoted(QuotedType::Type), + ]))); + + Ok(Value::Slice(parameters, typ)) +} + +// fn return_type(self) -> Type +fn function_def_return_type( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let func_id = get_function_def(self_argument, location)?; + let func_meta = interner.function_meta(&func_id); + + Ok(Value::Type(func_meta.return_type().follow_bindings())) +} + fn modulus_be_bits( _interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, @@ -848,6 +757,16 @@ fn modulus_num_bits( Ok(Value::U64(bits)) } +// fn quoted_eq(_first: Quoted, _second: Quoted) -> bool +fn quoted_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let (self_value, other_value) = check_two_arguments(arguments, location)?; + + let self_quoted = get_quoted(self_value, location)?; + let other_quoted = get_quoted(other_value, location)?; + + Ok(Value::Bool(self_quoted == other_quoted)) +} + fn trait_def_as_trait_constraint( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, 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 new file mode 100644 index 00000000000..45e4b13a47c --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -0,0 +1,242 @@ +use std::rc::Rc; + +use acvm::FieldElement; +use noirc_errors::Location; + +use crate::{ + ast::{IntegerBitSize, Signedness}, + hir::comptime::{errors::IResult, InterpreterError, Value}, + hir_def::stmt::HirPattern, + macros_api::NodeInterner, + node_interner::{FuncId, TraitId}, + token::Token, + QuotedType, Type, +}; + +pub(crate) fn check_argument_count( + expected: usize, + arguments: &[(Value, Location)], + location: Location, +) -> IResult<()> { + if arguments.len() == expected { + Ok(()) + } else { + let actual = arguments.len(); + Err(InterpreterError::ArgumentCountMismatch { expected, actual, location }) + } +} + +pub(crate) fn check_one_argument( + mut arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + check_argument_count(1, &arguments, location)?; + + Ok(arguments.pop().unwrap().0) +} + +pub(crate) fn check_two_arguments( + mut arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult<(Value, Value)> { + check_argument_count(2, &arguments, location)?; + + let argument2 = arguments.pop().unwrap().0; + let argument1 = arguments.pop().unwrap().0; + + Ok((argument1, argument2)) +} + +pub(crate) fn check_three_arguments( + mut arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult<(Value, Value, Value)> { + check_argument_count(3, &arguments, location)?; + + let argument3 = arguments.pop().unwrap().0; + let argument2 = arguments.pop().unwrap().0; + let argument1 = arguments.pop().unwrap().0; + + Ok((argument1, argument2, argument3)) +} + +pub(crate) fn get_array( + interner: &NodeInterner, + value: Value, + location: Location, +) -> IResult<(im::Vector, Type)> { + match value { + Value::Array(values, typ) => Ok((values, typ)), + value => { + let type_var = Box::new(interner.next_type_variable()); + let expected = Type::Array(type_var.clone(), type_var); + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) + } + } +} + +pub(crate) fn get_slice( + interner: &NodeInterner, + value: Value, + location: Location, +) -> IResult<(im::Vector, Type)> { + match value { + Value::Slice(values, typ) => Ok((values, typ)), + value => { + let type_var = Box::new(interner.next_type_variable()); + let expected = Type::Slice(type_var); + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) + } + } +} + +pub(crate) fn get_field(value: Value, location: Location) -> IResult { + match value { + Value::Field(value) => Ok(value), + value => { + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected: Type::FieldElement, actual, location }) + } + } +} + +pub(crate) fn get_u32(value: Value, location: Location) -> IResult { + match value { + Value::U32(value) => Ok(value), + value => { + let expected = Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo); + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) + } + } +} + +pub(crate) fn get_function_def(value: Value, location: Location) -> IResult { + match value { + Value::FunctionDefinition(id) => Ok(id), + value => { + let expected = Type::Quoted(QuotedType::FunctionDefinition); + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) + } + } +} + +pub(crate) fn get_trait_constraint( + value: Value, + location: Location, +) -> IResult<(TraitId, Vec)> { + match value { + Value::TraitConstraint(trait_id, generics) => Ok((trait_id, generics)), + value => { + let expected = Type::Quoted(QuotedType::TraitConstraint); + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) + } + } +} + +pub(crate) fn get_trait_def(value: Value, location: Location) -> IResult { + match value { + Value::TraitDefinition(id) => Ok(id), + value => { + let expected = Type::Quoted(QuotedType::TraitDefinition); + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) + } + } +} + +pub(crate) fn get_type(value: Value, location: Location) -> IResult { + match value { + Value::Type(typ) => Ok(typ), + value => { + let expected = Type::Quoted(QuotedType::Type); + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) + } + } +} + +pub(crate) fn get_quoted(value: Value, location: Location) -> IResult>> { + match value { + Value::Quoted(tokens) => Ok(tokens), + value => { + let expected = Type::Quoted(QuotedType::Quoted); + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) + } + } +} + +pub(crate) fn hir_pattern_to_tokens( + interner: &NodeInterner, + hir_pattern: &HirPattern, +) -> Vec { + let mut tokens = Vec::new(); + gather_hir_pattern_tokens(interner, hir_pattern, &mut tokens); + tokens +} + +fn gather_hir_pattern_tokens( + interner: &NodeInterner, + hir_pattern: &HirPattern, + tokens: &mut Vec, +) { + match hir_pattern { + HirPattern::Identifier(hir_ident) => { + let name = interner.definition_name(hir_ident.id).to_string(); + tokens.push(Token::Ident(name)); + } + HirPattern::Mutable(pattern, _) => { + tokens.push(Token::Keyword(crate::token::Keyword::Mut)); + gather_hir_pattern_tokens(interner, pattern, tokens); + } + HirPattern::Tuple(patterns, _) => { + tokens.push(Token::LeftParen); + for (index, pattern) in patterns.iter().enumerate() { + if index != 0 { + tokens.push(Token::Comma); + } + gather_hir_pattern_tokens(interner, pattern, tokens); + } + tokens.push(Token::RightParen); + } + HirPattern::Struct(typ, fields, _) => { + let Type::Struct(struct_type, _) = typ.follow_bindings() else { + panic!("Expected type to be a struct"); + }; + + let name = struct_type.borrow().name.to_string(); + tokens.push(Token::Ident(name)); + + tokens.push(Token::LeftBrace); + for (index, (field_name, pattern)) in fields.iter().enumerate() { + if index != 0 { + tokens.push(Token::Comma); + } + + let field_name = &field_name.0.contents; + tokens.push(Token::Ident(field_name.to_string())); + + // If we have a pattern like `Foo { x }`, that's internally represented as `Foo { x: x }` so + // here we check if the field name is the same as the pattern and, if so, omit the `: x` part. + let field_name_is_same_as_pattern = if let HirPattern::Identifier(pattern) = pattern + { + let pattern_name = interner.definition_name(pattern.id); + field_name == pattern_name + } else { + false + }; + + if !field_name_is_same_as_pattern { + tokens.push(Token::Colon); + gather_hir_pattern_tokens(interner, pattern, tokens); + } + } + tokens.push(Token::RightBrace); + } + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index f0dc2dcf487..d6e4553d142 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -4,11 +4,11 @@ use iter_extended::try_vecmap; use noirc_errors::Location; use crate::{ - hir::comptime::{errors::IResult, interpreter::builtin::get_field, InterpreterError, Value}, + hir::comptime::{errors::IResult, InterpreterError, Value}, macros_api::NodeInterner, }; -use super::builtin::{check_two_arguments, get_array, get_u32}; +use super::builtin::builtin_helpers::{check_two_arguments, get_array, get_field, get_u32}; pub(super) fn call_foreign( interner: &mut NodeInterner, diff --git a/noir_stdlib/src/meta/function_def.nr b/noir_stdlib/src/meta/function_def.nr new file mode 100644 index 00000000000..284e8a0c9c0 --- /dev/null +++ b/noir_stdlib/src/meta/function_def.nr @@ -0,0 +1,7 @@ +impl FunctionDefinition { + #[builtin(function_def_parameters)] + fn parameters(self) -> [(Quoted, Type)] {} + + #[builtin(function_def_return_type)] + fn return_type(self) -> Type {} +} diff --git a/noir_stdlib/src/meta/mod.nr b/noir_stdlib/src/meta/mod.nr index 7ed5e3ff44f..615b4e5aa14 100644 --- a/noir_stdlib/src/meta/mod.nr +++ b/noir_stdlib/src/meta/mod.nr @@ -2,6 +2,7 @@ use crate::collections::umap::UHashMap; use crate::hash::BuildHasherDefault; use crate::hash::poseidon2::Poseidon2Hasher; +mod function_def; mod struct_def; mod trait_constraint; mod trait_def; diff --git a/noir_stdlib/src/meta/quoted.nr b/noir_stdlib/src/meta/quoted.nr index 8e96e8828f8..0fd00b8bf82 100644 --- a/noir_stdlib/src/meta/quoted.nr +++ b/noir_stdlib/src/meta/quoted.nr @@ -1,3 +1,5 @@ +use crate::cmp::Eq; + impl Quoted { #[builtin(quoted_as_trait_constraint)] fn as_trait_constraint(self) -> TraitConstraint {} @@ -5,3 +7,13 @@ impl Quoted { #[builtin(quoted_as_type)] fn as_type(self) -> Type {} } + +impl Eq for Quoted { + fn eq(self, other: Quoted) -> bool { + quoted_eq(self, other) + } +} + +#[builtin(quoted_eq)] +fn quoted_eq(_first: Quoted, _second: Quoted) -> bool {} + diff --git a/test_programs/compile_success_empty/comptime_function_definition/Nargo.toml b/test_programs/compile_success_empty/comptime_function_definition/Nargo.toml new file mode 100644 index 00000000000..a5f1e75dcef --- /dev/null +++ b/test_programs/compile_success_empty/comptime_function_definition/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_function_definition" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/comptime_function_definition/src/main.nr b/test_programs/compile_success_empty/comptime_function_definition/src/main.nr new file mode 100644 index 00000000000..b0eeca2bb7f --- /dev/null +++ b/test_programs/compile_success_empty/comptime_function_definition/src/main.nr @@ -0,0 +1,33 @@ +use std::meta::type_of; + +struct Foo { x: Field, field: Field } + +#[function_attr] +fn foo(w: i32, y: Field, Foo { x, field: some_field }: Foo, mut a: bool, (b, c): (i32, i32)) -> i32 { + 1 +} + +comptime fn function_attr(f: FunctionDefinition) { + // Check FunctionDefinition::parameters + let parameters = f.parameters(); + assert_eq(parameters.len(), 5); + + assert_eq(parameters[0].0, quote { w }); + assert_eq(parameters[1].0, quote { y }); + assert_eq(parameters[2].0, quote { Foo { x, field: some_field } }); + assert_eq(parameters[3].0, quote { mut a }); + assert_eq(parameters[4].0, quote { (b, c) }); + + let an_i32: i32 = 0; + + assert_eq(parameters[0].1, type_of(an_i32)); + assert_eq(parameters[1].1, type_of(0)); + assert_eq(parameters[2].1, type_of(Foo { x: 0, field: 1 })); + assert_eq(parameters[3].1, type_of(true)); + assert_eq(parameters[4].1, type_of((an_i32, an_i32))); + + // Check FunctionDefinition::return_type + assert_eq(f.return_type(), type_of(an_i32)); +} + +fn main() {}