Skip to content

Commit

Permalink
feat: add Type methods: as_tuple, as_slice, as_array, `as_con…
Browse files Browse the repository at this point in the history
…stant`, `is_bool` (#5678)

# Description

## Problem

Part of #5668

## Summary

I'm leaving `as_struct` for a next PR.

## Additional Context

None.

## Documentation

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
asterite authored Aug 2, 2024
1 parent 19e58a9 commit 604fa0d
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 13 deletions.
125 changes: 112 additions & 13 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -485,20 +490,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<Value> {
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<u32>
fn type_as_constant(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
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<Value> {
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<Type>
fn type_as_slice(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
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<Value> {
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<F>(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
f: F,
) -> IResult<Value>
where
F: FnOnce(Type) -> Option<Value>,
{
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)
}
Expand All @@ -510,6 +596,14 @@ fn type_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult<Val
Ok(Value::Bool(self_type == other_type))
}

// fn is_bool(self) -> bool
fn type_is_bool(arguments: Vec<(Value, Location)>, location: Location) -> IResult<Value> {
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<Value> {
let value = check_one_argument(arguments, location)?;
Expand Down Expand Up @@ -751,14 +845,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<Value>) -> IResult<Value> {
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),
Expand All @@ -770,3 +857,15 @@ pub(crate) fn option(option_type: Type, value: Option<Value>) -> IResult<Value>
fields.insert(Rc::new("_value".to_string()), value);
Ok(Value::Struct(fields, option_type))
}

/// Given a type, assert that it's an Option<T> 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")
}
15 changes: 15 additions & 0 deletions noir_stdlib/src/meta/typ.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32> {}

#[builtin(type_as_integer)]
fn as_integer(self) -> Option<(bool, u8)> {}

#[builtin(type_as_slice)]
fn as_slice(self) -> Option<Type> {}

#[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 {}
}
Expand Down
37 changes: 37 additions & 0 deletions test_programs/compile_success_empty/comptime_type/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

0 comments on commit 604fa0d

Please sign in to comment.