From 4c9c2c1148409846fc5549842d378c7df53a5342 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 5 Sep 2024 14:59:35 -0500 Subject: [PATCH 1/5] Add typ::fresh_type_variable --- .../src/hir/comptime/interpreter/builtin.rs | 14 +++++--- docs/docs/noir/standard_library/meta/typ.md | 24 +++++++++++++ noir_stdlib/src/meta/typ.nr | 5 +++ .../comptime_type/src/main.nr | 35 +++++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index a1bb99dbb46..9a1ddf489ce 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -97,6 +97,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_resolve" => expr_resolve(self, arguments, location), "is_unconstrained" => Ok(Value::Bool(true)), "fmtstr_quoted_contents" => fmtstr_quoted_contents(interner, arguments, location), + "fresh_type_variable" => fresh_type_variable(interner), "function_def_add_attribute" => function_def_add_attribute(self, arguments, location), "function_def_body" => function_def_body(interner, arguments, location), "function_def_has_named_attribute" => { @@ -720,7 +721,7 @@ where F: FnOnce(Type) -> Option, { let value = check_one_argument(arguments, location)?; - let typ = get_type(value)?; + let typ = get_type(value)?.follow_bindings(); let option_value = f(typ); @@ -749,13 +750,13 @@ fn type_get_trait_impl( let typ = get_type(typ)?; let (trait_id, generics) = get_trait_constraint(constraint)?; - let option_value = match interner.try_lookup_trait_implementation( + let option_value = match interner.lookup_trait_implementation( &typ, trait_id, &generics.ordered, &generics.named, ) { - Ok((TraitImplKind::Normal(trait_impl_id), _)) => Some(Value::TraitImpl(trait_impl_id)), + Ok(TraitImplKind::Normal(trait_impl_id)) => Some(Value::TraitImpl(trait_impl_id)), _ => None, }; @@ -774,7 +775,7 @@ fn type_implements( let (trait_id, generics) = get_trait_constraint(constraint)?; let implements = interner - .try_lookup_trait_implementation(&typ, trait_id, &generics.ordered, &generics.named) + .lookup_trait_implementation(&typ, trait_id, &generics.ordered, &generics.named) .is_ok(); Ok(Value::Bool(implements)) } @@ -1670,6 +1671,11 @@ fn fmtstr_quoted_contents( Ok(Value::Quoted(Rc::new(tokens))) } +// fn fresh_type_variable() -> Type +fn fresh_type_variable(interner: &NodeInterner) -> IResult { + Ok(Value::Type(interner.next_type_variable())) +} + // fn add_attribute(self, attribute: str) fn function_def_add_attribute( interpreter: &mut Interpreter, diff --git a/docs/docs/noir/standard_library/meta/typ.md b/docs/docs/noir/standard_library/meta/typ.md index 0b6f8d5f77d..e9eadf53bde 100644 --- a/docs/docs/noir/standard_library/meta/typ.md +++ b/docs/docs/noir/standard_library/meta/typ.md @@ -5,6 +5,30 @@ title: Type `std::meta::typ` contains methods on the built-in `Type` type used for representing a type in the source program. +## Functions + +#include_code fresh_type_variable noir_stdlib/src/meta/typ.nr rust + +Creates and returns an unbound type variable. This is a special kind of type internal +to type checking which will type check with any other type. When it is type checked +against another type it will also be set to that type. For example, if `a` is a type +variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set +`a` equal to `u8`. + +Unbound type variables will often be rendered as `_` while printing them. Bound type +variables will appear as the type they are bound to. + +This can be used in conjunction with functions which internally perform type checks +such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. + +Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always +fail. + +Example: + +#include_code serialize-setup noir_stdlib/src/meta/typ.nr rust +#include_code fresh-type-variable-example noir_stdlib/src/meta/typ.nr rust + ## Methods ### as_array diff --git a/noir_stdlib/src/meta/typ.nr b/noir_stdlib/src/meta/typ.nr index 12dc91a4925..71bd6fd7f1c 100644 --- a/noir_stdlib/src/meta/typ.nr +++ b/noir_stdlib/src/meta/typ.nr @@ -1,6 +1,11 @@ use crate::cmp::Eq; use crate::option::Option; +#[builtin(fresh_type_variable)] +// docs:start:fresh_type_variable +pub fn fresh_type_variable() -> Type {} +// docs:end:fresh_type_variable + impl Type { #[builtin(type_as_array)] // docs:start:as_array 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 0b15c5605b3..c9307570c87 100644 --- a/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -19,6 +19,18 @@ struct StructDoesNotImplementSomeTrait { } +// docs:start:serialize-setup +trait Serialize {} + +impl Serialize<1> for Field {} + +impl Serialize for [T; N] + where T: Serialize {} + +impl Serialize for (T, U) + where T: Serialize, U: Serialize {} +// docs:end:serialize-setup + fn main() { comptime { @@ -115,6 +127,29 @@ fn main() { let str_type = quote { str<10> }.as_type(); let constant = str_type.as_str().unwrap(); assert_eq(constant.as_constant().unwrap(), 10); + + // Check std::meta::typ::fresh_type_variable + // docs:start:fresh-type-variable-example + let typevar1 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); + let field_type = quote { Field }.as_type(); + + // Search for a trait impl (binding typevar1 to 1 when the impl is found): + assert(field_type.implements(constraint)); + + // typevar1 should be bound to the "1" generic now: + assert_eq(typevar1.as_constant().unwrap(), 1); + + // If we want to do the same with a different type, we need to + // create a new type variable now that `typevar1` is bound + let typevar2 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); + let array_type = quote { [(Field, Field); 5] }.as_type(); + assert(array_type.implements(constraint)); + + // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 + assert_eq(typevar2.as_constant().unwrap(), 10); + // docs:end:fresh-type-variable-example } } From 0d5b814d0ef6629ef52c90d619a6453e09b9fccc Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 5 Sep 2024 15:37:14 -0500 Subject: [PATCH 2/5] Fix as_constant --- .../noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 5 ++++- compiler/noirc_frontend/src/hir/comptime/value.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 9a1ddf489ce..1bf500fbfc3 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -615,7 +615,10 @@ fn type_as_constant( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::Constant(n) = typ { + // Prefer to use `evaluate_to_u32` over matching on `Type::Constant` + // since arithmetic generics may be `Type::InfixExpr`s which evaluate to + // constants but are not actually the `Type::Constant` variant. + if let Some(n) = typ.evaluate_to_u32() { Some(Value::U32(n)) } else { None diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 7d6e4475c7b..9387e6b1733 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -655,7 +655,7 @@ 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::Type(typ) => write!(f, "{:?}", typ), Value::Expr(ExprValue::Expression(expr)) => { write!(f, "{}", remove_interned_in_expression_kind(self.interner, expr.clone())) } From 61f66171370eaf6f5cd8ab2a94ab946080f50a0f Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 5 Sep 2024 15:37:46 -0500 Subject: [PATCH 3/5] Clippy --- .../noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 1bf500fbfc3..9f4f24dd167 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -618,11 +618,7 @@ fn type_as_constant( // Prefer to use `evaluate_to_u32` over matching on `Type::Constant` // since arithmetic generics may be `Type::InfixExpr`s which evaluate to // constants but are not actually the `Type::Constant` variant. - if let Some(n) = typ.evaluate_to_u32() { - Some(Value::U32(n)) - } else { - None - } + typ.evaluate_to_u32().map(Value::U32) }) } From 07b511db17d747bcc5e49c4389899c31b9dc72b0 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:19:13 +0100 Subject: [PATCH 4/5] Update docs/docs/noir/standard_library/meta/typ.md --- docs/docs/noir/standard_library/meta/typ.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/noir/standard_library/meta/typ.md b/docs/docs/noir/standard_library/meta/typ.md index e9eadf53bde..0a9fc33c466 100644 --- a/docs/docs/noir/standard_library/meta/typ.md +++ b/docs/docs/noir/standard_library/meta/typ.md @@ -26,8 +26,8 @@ fail. Example: -#include_code serialize-setup noir_stdlib/src/meta/typ.nr rust -#include_code fresh-type-variable-example noir_stdlib/src/meta/typ.nr rust +#include_code serialize-setup test_program/compile_success_empty/comptime_type/src/main.nr rust +#include_code fresh-type-variable-example test_program/compile_success_empty/comptime_type/src/main.nr rust ## Methods From 4e6f175885d75f0b5a3e4aab0581d13ff1e94f8b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:33:56 +0100 Subject: [PATCH 5/5] Update docs/docs/noir/standard_library/meta/typ.md --- docs/docs/noir/standard_library/meta/typ.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/noir/standard_library/meta/typ.md b/docs/docs/noir/standard_library/meta/typ.md index 0a9fc33c466..1334092a9fa 100644 --- a/docs/docs/noir/standard_library/meta/typ.md +++ b/docs/docs/noir/standard_library/meta/typ.md @@ -26,8 +26,8 @@ fail. Example: -#include_code serialize-setup test_program/compile_success_empty/comptime_type/src/main.nr rust -#include_code fresh-type-variable-example test_program/compile_success_empty/comptime_type/src/main.nr rust +#include_code serialize-setup test_programs/compile_success_empty/comptime_type/src/main.nr rust +#include_code fresh-type-variable-example test_programs/compile_success_empty/comptime_type/src/main.nr rust ## Methods