diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index ef4932fa9f0..4fb5730e93b 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -39,6 +39,18 @@ pub enum IntegerBitSize { SixtyFour, } +impl IntegerBitSize { + pub fn bit_size(&self) -> u8 { + match self { + IntegerBitSize::One => 1, + IntegerBitSize::Eight => 8, + IntegerBitSize::Sixteen => 16, + IntegerBitSize::ThirtyTwo => 32, + IntegerBitSize::SixtyFour => 64, + } + } +} + impl IntegerBitSize { pub fn allowed_sizes() -> Vec { vec![Self::One, Self::Eight, Self::ThirtyTwo, Self::SixtyFour] @@ -297,6 +309,15 @@ pub enum Signedness { Signed, } +impl Signedness { + pub fn is_signed(&self) -> bool { + match self { + Signedness::Unsigned => false, + Signedness::Signed => true, + } + } +} + impl UnresolvedTypeExpression { // This large error size is justified because it improves parsing speeds by around 40% in // release mode. See `ParserError` definition for further explanation. diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 888fa17d0af..0bb53432b78 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -339,22 +339,30 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let argument = Value::Pointer(Shared::new(argument), true); self.define_pattern(pattern, typ, argument, location) } - HirPattern::Tuple(pattern_fields, _) => match (argument, typ) { - (Value::Tuple(fields), Type::Tuple(type_fields)) - if fields.len() == pattern_fields.len() => - { - for ((pattern, typ), argument) in - pattern_fields.iter().zip(type_fields).zip(fields) + HirPattern::Tuple(pattern_fields, _) => { + let typ = &typ.follow_bindings(); + + match (argument, typ) { + (Value::Tuple(fields), Type::Tuple(type_fields)) + if fields.len() == pattern_fields.len() => { - self.define_pattern(pattern, typ, argument, location)?; + for ((pattern, typ), argument) in + pattern_fields.iter().zip(type_fields).zip(fields) + { + self.define_pattern(pattern, typ, argument, location)?; + } + Ok(()) + } + (value, _) => { + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { + expected: typ.clone(), + actual, + location, + }) } - Ok(()) - } - (value, _) => { - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected: typ.clone(), actual, location }) } - }, + } HirPattern::Struct(struct_type, pattern_fields, _) => { self.push_scope(); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 76f85740195..c92e9a73b5a 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -57,7 +57,9 @@ impl<'local, 'context> Interpreter<'local, 'context> { "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_integer" => type_as_integer(arguments, return_type, location), "type_eq" => type_eq(arguments, location), + "type_is_field" => type_is_field(arguments, location), "type_of" => type_of(arguments, location), "zeroed" => zeroed(return_type), _ => { @@ -81,6 +83,40 @@ pub(super) fn check_argument_count( } } +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 }) } @@ -160,6 +196,17 @@ fn get_trait_def(value: Value, location: Location) -> IResult { } } +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), @@ -173,12 +220,12 @@ fn get_quoted(value: Value, location: Location) -> IResult>> { fn array_len( interner: &NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - match arguments.pop().unwrap().0 { + match argument { Value::Array(values, _) | Value::Slice(values, _) => Ok(Value::U32(values.len() as u32)), value => { let type_var = Box::new(interner.next_type_variable()); @@ -191,12 +238,11 @@ fn array_len( fn as_slice( interner: &NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let array = check_one_argument(arguments, location)?; - let (array, _) = arguments.pop().unwrap(); match array { Value::Array(values, Type::Array(_, typ)) => Ok(Value::Slice(values, Type::Slice(typ))), value => { @@ -210,13 +256,12 @@ fn as_slice( fn slice_push_back( interner: &NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(2, &arguments, location)?; + let (slice, element) = check_two_arguments(arguments, location)?; - let (element, _) = arguments.pop().unwrap(); - let (mut values, typ) = get_slice(interner, arguments.pop().unwrap().0, location)?; + let (mut values, typ) = get_slice(interner, slice, location)?; values.push_back(element); Ok(Value::Slice(values, typ)) } @@ -224,12 +269,12 @@ fn slice_push_back( /// fn as_type(self) -> Type fn struct_def_as_type( interner: &NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let struct_def = match arguments.pop().unwrap().0 { + let struct_def = match argument { Value::StructDefinition(id) => id, value => { let expected = Type::Quoted(QuotedType::StructDefinition); @@ -252,12 +297,12 @@ fn struct_def_as_type( /// fn generics(self) -> [Quoted] fn struct_def_generics( interner: &NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let struct_def = match arguments.pop().unwrap().0 { + let struct_def = match argument { Value::StructDefinition(id) => id, value => { let expected = Type::Quoted(QuotedType::StructDefinition); @@ -282,12 +327,12 @@ fn struct_def_generics( /// Returns (name, type) pairs of each field of this StructDefinition fn struct_def_fields( interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let struct_def = match arguments.pop().unwrap().0 { + let struct_def = match argument { Value::StructDefinition(id) => id, value => { let expected = Type::Quoted(QuotedType::StructDefinition); @@ -316,13 +361,13 @@ fn struct_def_fields( fn slice_remove( interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(2, &arguments, location)?; + let (slice, index) = check_two_arguments(arguments, location)?; - let index = get_u32(arguments.pop().unwrap().0, location)? as usize; - let (mut values, typ) = get_slice(interner, arguments.pop().unwrap().0, location)?; + let index = get_u32(index, location)? as usize; + let (mut values, typ) = get_slice(interner, slice, location)?; if values.is_empty() { return failing_constraint("slice_remove called on empty slice", location); @@ -342,25 +387,24 @@ fn slice_remove( fn slice_push_front( interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(2, &arguments, location)?; + let (slice, element) = check_two_arguments(arguments, location)?; - let (element, _) = arguments.pop().unwrap(); - let (mut values, typ) = get_slice(interner, arguments.pop().unwrap().0, location)?; + let (mut values, typ) = get_slice(interner, slice, location)?; values.push_front(element); Ok(Value::Slice(values, typ)) } fn slice_pop_front( interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let (mut values, typ) = get_slice(interner, arguments.pop().unwrap().0, location)?; + let (mut values, typ) = get_slice(interner, argument, location)?; match values.pop_front() { Some(element) => Ok(Value::Tuple(vec![element, Value::Slice(values, typ)])), None => failing_constraint("slice_pop_front called on empty slice", location), @@ -369,12 +413,12 @@ fn slice_pop_front( fn slice_pop_back( interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let (mut values, typ) = get_slice(interner, arguments.pop().unwrap().0, location)?; + let (mut values, typ) = get_slice(interner, argument, location)?; match values.pop_back() { Some(element) => Ok(Value::Tuple(vec![Value::Slice(values, typ), element])), None => failing_constraint("slice_pop_back called on empty slice", location), @@ -383,27 +427,26 @@ fn slice_pop_back( fn slice_insert( interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(3, &arguments, location)?; + let (slice, index, element) = check_three_arguments(arguments, location)?; - let (element, _) = arguments.pop().unwrap(); - let index = get_u32(arguments.pop().unwrap().0, location)?; - let (mut values, typ) = get_slice(interner, arguments.pop().unwrap().0, location)?; - values.insert(index as usize, element); + let index = get_u32(index, location)? as usize; + let (mut values, typ) = get_slice(interner, slice, location)?; + values.insert(index, element); Ok(Value::Slice(values, typ)) } // fn as_trait_constraint(quoted: Quoted) -> TraitConstraint fn quoted_as_trait_constraint( interpreter: &mut Interpreter, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let tokens = get_quoted(arguments.pop().unwrap().0, location)?; + let tokens = get_quoted(argument, location)?; let quoted = add_token_spans(tokens.clone(), location.span); let trait_bound = parser::trait_bound().parse(quoted).map_err(|mut errors| { @@ -424,12 +467,12 @@ fn quoted_as_trait_constraint( // fn as_type(quoted: Quoted) -> Type fn quoted_as_type( interpreter: &mut Interpreter, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let tokens = get_quoted(arguments.pop().unwrap().0, location)?; + let tokens = get_quoted(argument, location)?; let quoted = add_token_spans(tokens.clone(), location.span); let typ = parser::parse_type().parse(quoted).map_err(|mut errors| { @@ -444,18 +487,42 @@ fn quoted_as_type( Ok(Value::Type(typ)) } -fn type_eq(mut arguments: Vec<(Value, Location)>, location: Location) -> IResult { - check_argument_count(2, &arguments, location)?; +// fn as_integer(self) -> Option<(bool, u8)> +fn type_as_integer( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + let value = check_one_argument(arguments, location)?; + let typ = get_type(value, location)?; - let value1 = arguments.pop().unwrap().0; - let value2 = arguments.pop().unwrap().0; - Ok(Value::Bool(value1 == value2)) + let option_value = if let Type::Integer(sign, bits) = typ { + Some(Value::Tuple(vec![Value::Bool(sign.is_signed()), Value::U8(bits.bit_size())])) + } else { + None + }; + + option(return_type, option_value) } -fn type_of(mut arguments: Vec<(Value, Location)>, location: Location) -> IResult { - check_argument_count(1, &arguments, location)?; +// fn type_eq(_first: Type, _second: Type) -> bool +fn type_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let (self_type, other_type) = check_two_arguments(arguments, location)?; + + Ok(Value::Bool(self_type == other_type)) +} + +// fn is_field(self) -> bool +fn type_is_field(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let value = check_one_argument(arguments, location)?; + let typ = get_type(value, location)?; + + Ok(Value::Bool(matches!(typ, Type::FieldElement))) +} - let value = arguments.pop().unwrap().0; +// fn type_of(x: T) -> Type +fn type_of(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let value = check_one_argument(arguments, location)?; let typ = value.get_type().into_owned(); Ok(Value::Type(typ)) } @@ -463,12 +530,12 @@ fn type_of(mut arguments: Vec<(Value, Location)>, location: Location) -> IResult // fn constraint_hash(constraint: TraitConstraint) -> Field fn trait_constraint_hash( _interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let bound = get_trait_constraint(arguments.pop().unwrap().0, location)?; + let bound = get_trait_constraint(argument, location)?; let mut hasher = std::collections::hash_map::DefaultHasher::new(); bound.hash(&mut hasher); @@ -480,13 +547,13 @@ fn trait_constraint_hash( // fn constraint_eq(constraint_a: TraitConstraint, constraint_b: TraitConstraint) -> bool fn trait_constraint_eq( _interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(2, &arguments, location)?; + let (value_a, value_b) = check_two_arguments(arguments, location)?; - let constraint_b = get_trait_constraint(arguments.pop().unwrap().0, location)?; - let constraint_a = get_trait_constraint(arguments.pop().unwrap().0, location)?; + let constraint_a = get_trait_constraint(value_a, location)?; + let constraint_b = get_trait_constraint(value_b, location)?; Ok(Value::Bool(constraint_a == constraint_b)) } @@ -668,12 +735,12 @@ fn modulus_num_bits( fn trait_def_as_trait_constraint( interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> Result { - check_argument_count(1, &arguments, location)?; + let argument = check_one_argument(arguments, location)?; - let trait_id = get_trait_def(arguments.pop().unwrap().0, location)?; + let trait_id = get_trait_def(argument, location)?; let the_trait = interner.get_trait(trait_id); let trait_generics = vecmap(&the_trait.generics, |generic| { Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), generic.kind.clone()) @@ -681,3 +748,26 @@ fn trait_def_as_trait_constraint( Ok(Value::TraitConstraint(trait_id, trait_generics)) } + +/// Creates a value that holds an `Option`. +/// `option_type` must be a Type referencing the `Option` type. +pub(crate) fn option(option_type: Type, value: Option) -> IResult { + let Type::Struct(shared_option_type, mut generics) = option_type.clone() else { + panic!("Expected type to be a struct"); + }; + + let shared_option_type = shared_option_type.borrow(); + assert_eq!(shared_option_type.name.0.contents, "Option"); + + let t = generics.pop().expect("Expected Option to have a T generic type"); + + let (is_some, value) = match value { + Some(value) => (Value::Bool(true), value), + None => (Value::Bool(false), zeroed(t)?), + }; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("_is_some".to_string()), is_some); + fields.insert(Rc::new("_value".to_string()), value); + Ok(Value::Struct(fields, option_type)) +} diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index fc8c57ab634..f0dc2dcf487 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -8,7 +8,7 @@ use crate::{ macros_api::NodeInterner, }; -use super::builtin::{check_argument_count, get_array, get_u32}; +use super::builtin::{check_two_arguments, get_array, get_u32}; pub(super) fn call_foreign( interner: &mut NodeInterner, @@ -28,13 +28,13 @@ pub(super) fn call_foreign( // poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] fn poseidon2_permutation( interner: &mut NodeInterner, - mut arguments: Vec<(Value, Location)>, + arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { - check_argument_count(2, &arguments, location)?; + let (input, state_length) = check_two_arguments(arguments, location)?; - let state_length = get_u32(arguments.pop().unwrap().0, location)?; - let (input, typ) = get_array(interner, arguments.pop().unwrap().0, location)?; + let (input, typ) = get_array(interner, input, location)?; + let state_length = get_u32(state_length, location)?; let input = try_vecmap(input, |integer| get_field(integer, location))?; diff --git a/noir_stdlib/src/meta/typ.nr b/noir_stdlib/src/meta/typ.nr index 197c41a482c..a9b77fdd969 100644 --- a/noir_stdlib/src/meta/typ.nr +++ b/noir_stdlib/src/meta/typ.nr @@ -1,4 +1,13 @@ use crate::cmp::Eq; +use crate::option::Option; + +impl Type { + #[builtin(type_as_integer)] + fn as_integer(self) -> Option<(bool, u8)> {} + + #[builtin(type_is_field)] + fn is_field(self) -> bool {} +} impl Eq for Type { fn eq(self, other: Self) -> bool { diff --git a/test_programs/compile_success_empty/comptime_type/src/main.nr b/test_programs/compile_success_empty/comptime_type/src/main.nr index e74cc0a4d14..768155f0fc6 100644 --- a/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -12,5 +12,22 @@ fn main() { let i32_type = type_of(an_i32); assert(field_type_1 == field_type_2); assert(field_type_1 != i32_type); + + // Check Type::is_field + assert(field_type_1.is_field()); + assert(!i32_type.is_field()); + + // Check Type::as_integer + assert(field_type_1.as_integer().is_none()); + + let (signed, bits) = i32_type.as_integer().unwrap(); + assert(signed); + assert_eq(bits, 32); + + let a_u8: u8 = 0; + let u8_type = type_of(a_u8); + let (signed, bits) = u8_type.as_integer().unwrap(); + assert(!signed); + assert_eq(bits, 8); } }