diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index c92e9a73b5a..0ed9729bad4 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -57,8 +57,13 @@ 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_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), + "type_as_slice" => type_as_slice(arguments, return_type, location), + "type_as_tuple" => type_as_tuple(arguments, return_type, location), "type_eq" => type_eq(arguments, location), + "type_is_bool" => type_is_bool(arguments, location), "type_is_field" => type_is_field(arguments, location), "type_of" => type_of(arguments, location), "zeroed" => zeroed(return_type), @@ -487,20 +492,101 @@ fn quoted_as_type( Ok(Value::Type(typ)) } +// fn as_array(self) -> Option<(Type, Type)> +fn type_as_array( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + type_as(arguments, return_type, location, |typ| { + if let Type::Array(length, array_type) = typ { + Some(Value::Tuple(vec![Value::Type(*array_type), Value::Type(*length)])) + } else { + None + } + }) +} + +// fn as_constant(self) -> Option +fn type_as_constant( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + type_as(arguments, return_type, location, |typ| { + if let Type::Constant(n) = typ { + Some(Value::U32(n)) + } else { + None + } + }) +} + // fn as_integer(self) -> Option<(bool, u8)> fn type_as_integer( arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { + type_as(arguments, return_type, location, |typ| { + if let Type::Integer(sign, bits) = typ { + Some(Value::Tuple(vec![Value::Bool(sign.is_signed()), Value::U8(bits.bit_size())])) + } else { + None + } + }) +} + +// fn as_slice(self) -> Option +fn type_as_slice( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + type_as(arguments, return_type, location, |typ| { + if let Type::Slice(slice_type) = typ { + Some(Value::Type(*slice_type)) + } else { + None + } + }) +} + +// fn as_tuple(self) -> Option<[Type]> +fn type_as_tuple( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + type_as(arguments, return_type.clone(), location, |typ| { + if let Type::Tuple(types) = typ { + let t = extract_option_generic_type(return_type); + + let Type::Slice(slice_type) = t else { + panic!("Expected T to be a slice"); + }; + + Some(Value::Slice(types.into_iter().map(Value::Type).collect(), *slice_type)) + } else { + None + } + }) +} + +// Helper function for implementing the `type_as_...` functions. +fn type_as( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, + f: F, +) -> IResult +where + F: FnOnce(Type) -> Option, +{ let value = check_one_argument(arguments, location)?; let typ = get_type(value, location)?; - 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 - }; + let option_value = f(typ); option(return_type, option_value) } @@ -512,6 +598,14 @@ fn type_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult bool +fn type_is_bool(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::Bool))) +} + // fn is_field(self) -> bool fn type_is_field(arguments: Vec<(Value, Location)>, location: Location) -> IResult { let value = check_one_argument(arguments, location)?; @@ -752,14 +846,7 @@ fn trait_def_as_trait_constraint( /// 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 t = extract_option_generic_type(option_type.clone()); let (is_some, value) = match value { Some(value) => (Value::Bool(true), value), @@ -771,3 +858,15 @@ pub(crate) fn option(option_type: Type, value: Option) -> IResult fields.insert(Rc::new("_value".to_string()), value); Ok(Value::Struct(fields, option_type)) } + +/// Given a type, assert that it's an Option and return the Type for T +pub(crate) fn extract_option_generic_type(typ: Type) -> Type { + let Type::Struct(struct_type, mut generics) = typ else { + panic!("Expected type to be a struct"); + }; + + let struct_type = struct_type.borrow(); + assert_eq!(struct_type.name.0.contents, "Option"); + + generics.pop().expect("Expected Option to have a T generic type") +} diff --git a/noir_stdlib/src/meta/typ.nr b/noir_stdlib/src/meta/typ.nr index a9b77fdd969..2a043b373bc 100644 --- a/noir_stdlib/src/meta/typ.nr +++ b/noir_stdlib/src/meta/typ.nr @@ -2,9 +2,24 @@ use crate::cmp::Eq; use crate::option::Option; impl Type { + #[builtin(type_as_array)] + fn as_array(self) -> Option<(Type, Type)> {} + + #[builtin(type_as_constant)] + fn as_constant(self) -> Option {} + #[builtin(type_as_integer)] fn as_integer(self) -> Option<(bool, u8)> {} + #[builtin(type_as_slice)] + fn as_slice(self) -> Option {} + + #[builtin(type_as_tuple)] + fn as_tuple(self) -> Option<[Type]> {} + + #[builtin(type_is_bool)] + fn is_bool(self) -> bool {} + #[builtin(type_is_field)] fn is_field(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 768155f0fc6..b2b724db6fd 100644 --- a/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -29,5 +29,42 @@ fn main() { let (signed, bits) = u8_type.as_integer().unwrap(); assert(!signed); assert_eq(bits, 8); + + // Check Type::as_tuple + assert(u8_type.as_tuple().is_none()); + + let tuple = (an_i32, a_u8); + let tuple_type = type_of(tuple); + let tuple_types = tuple_type.as_tuple().unwrap(); + assert_eq(tuple_types.len(), 2); + assert_eq(tuple_types[0], i32_type); + assert_eq(tuple_types[1], u8_type); + + // Check Type::as_slice + assert(u8_type.as_slice().is_none()); + + let slice = &[1]; + let slice_type = type_of(slice); + let slice_type_element_type = slice_type.as_slice().unwrap(); + assert_eq(slice_type_element_type, field_type_1); + + // Check Type::as_array + assert(u8_type.as_array().is_none()); + + let array = [1, 2, 3]; + let array_type = type_of(array); + let (array_type_element_type , array_length) = array_type.as_array().unwrap(); + assert_eq(array_type_element_type, field_type_1); + + // Check Type::as_constant + assert(u8_type.as_constant().is_none()); + assert_eq(array_length.as_constant().unwrap(), 3); + + // Check Type::is_bool + assert(!u8_type.is_bool()); + + let yes = true; + let bool_type = type_of(yes); + assert(bool_type.is_bool()); } }