diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index ef7b9f2be55..12bc6fe2c62 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -23,7 +23,7 @@ use crate::{ hir::comptime::{errors::IResult, value::add_token_spans, InterpreterError, Value}, hir_def::function::FunctionBody, macros_api::{ModuleDefId, NodeInterner, Signedness}, - node_interner::DefinitionKind, + node_interner::{DefinitionKind, TraitImplKind}, parser::{self}, token::{SpannedToken, Token}, QuotedType, Shared, Type, @@ -93,6 +93,9 @@ impl<'local, 'context> Interpreter<'local, 'context> { "type_as_struct" => type_as_struct(arguments, return_type, location), "type_as_tuple" => type_as_tuple(arguments, return_type, location), "type_eq" => type_eq(arguments, location), + "type_get_trait_impl" => { + type_get_trait_impl(interner, arguments, return_type, location) + } "type_implements" => type_implements(interner, arguments, location), "type_is_bool" => type_is_bool(arguments, location), "type_is_field" => type_is_field(arguments, location), @@ -507,6 +510,26 @@ fn type_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult Option +fn type_get_trait_impl( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + let (typ, constraint) = check_two_arguments(arguments, location)?; + + let typ = get_type(typ)?; + let (trait_id, generics) = get_trait_constraint(constraint)?; + + let option_value = match interner.try_lookup_trait_implementation(&typ, trait_id, &generics) { + Ok((TraitImplKind::Normal(trait_impl_id), _)) => Some(Value::TraitImpl(trait_impl_id)), + _ => None, + }; + + option(return_type, option_value) +} + // fn implements(self, constraint: TraitConstraint) -> bool fn type_implements( interner: &NodeInterner, diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index d5408309e55..d65c2bb7dcc 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -9,12 +9,15 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ArrayLiteral, ConstructorExpression, Ident, IntegerBitSize, Signedness}, hir::def_map::ModuleId, - hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, + hir_def::{ + expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, + traits::TraitConstraint, + }, macros_api::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, StructId, }, - node_interner::{ExprId, FuncId, TraitId}, + node_interner::{ExprId, FuncId, TraitId, TraitImplId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, QuotedType, Shared, Type, TypeBindings, @@ -53,6 +56,7 @@ pub enum Value { StructDefinition(StructId), TraitConstraint(TraitId, /* trait generics */ Vec), TraitDefinition(TraitId), + TraitImpl(TraitImplId), FunctionDefinition(FuncId), ModuleDefinition(ModuleId), Type(Type), @@ -100,6 +104,7 @@ impl Value { } Value::TraitConstraint { .. } => Type::Quoted(QuotedType::TraitConstraint), Value::TraitDefinition(_) => Type::Quoted(QuotedType::TraitDefinition), + Value::TraitImpl(_) => Type::Quoted(QuotedType::TraitImpl), Value::FunctionDefinition(_) => Type::Quoted(QuotedType::FunctionDefinition), Value::ModuleDefinition(_) => Type::Quoted(QuotedType::Module), Value::Type(_) => Type::Quoted(QuotedType::Type), @@ -230,6 +235,7 @@ impl Value { | Value::StructDefinition(_) | Value::TraitConstraint(..) | Value::TraitDefinition(_) + | Value::TraitImpl(_) | Value::FunctionDefinition(_) | Value::Zeroed(_) | Value::Type(_) @@ -353,6 +359,7 @@ impl Value { | Value::StructDefinition(_) | Value::TraitConstraint(..) | Value::TraitDefinition(_) + | Value::TraitImpl(_) | Value::FunctionDefinition(_) | Value::Zeroed(_) | Value::Type(_) @@ -516,18 +523,40 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { write!(f, "{}", def.name) } Value::TraitConstraint(trait_id, generics) => { - let trait_ = self.interner.get_trait(*trait_id); - let generic_string = vecmap(generics, ToString::to_string).join(", "); - if generics.is_empty() { - write!(f, "{}", trait_.name) - } else { - write!(f, "{}<{generic_string}>", trait_.name) - } + write!(f, "{}", display_trait_id_and_generics(self.interner, trait_id, generics)) } Value::TraitDefinition(trait_id) => { let trait_ = self.interner.get_trait(*trait_id); write!(f, "{}", trait_.name) } + Value::TraitImpl(trait_impl_id) => { + let trait_impl = self.interner.get_trait_implementation(*trait_impl_id); + let trait_impl = trait_impl.borrow(); + + let generic_string = + vecmap(&trait_impl.trait_generics, ToString::to_string).join(", "); + let generic_string = if generic_string.is_empty() { + generic_string + } else { + format!("<{}>", generic_string) + }; + + let where_clause = vecmap(&trait_impl.where_clause, |trait_constraint| { + display_trait_constraint(self.interner, trait_constraint) + }); + let where_clause = where_clause.join(", "); + let where_clause = if where_clause.is_empty() { + where_clause + } else { + format!(" where {}", where_clause) + }; + + write!( + f, + "impl {}{} for {}{}", + trait_impl.ident, generic_string, trait_impl.typ, where_clause + ) + } Value::FunctionDefinition(function_id) => { write!(f, "{}", self.interner.function_name(function_id)) } @@ -538,3 +567,26 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { } } } + +fn display_trait_id_and_generics( + interner: &NodeInterner, + trait_id: &TraitId, + generics: &Vec, +) -> String { + let trait_ = interner.get_trait(*trait_id); + let generic_string = vecmap(generics, ToString::to_string).join(", "); + if generics.is_empty() { + format!("{}", trait_.name) + } else { + format!("{}<{generic_string}>", trait_.name) + } +} + +fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitConstraint) -> String { + let trait_constraint_string = display_trait_id_and_generics( + interner, + &trait_constraint.trait_id, + &trait_constraint.trait_generics, + ); + format!("{}: {}", trait_constraint.typ, trait_constraint_string) +} diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 177d23c74dd..979263a26f6 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -146,6 +146,7 @@ pub enum QuotedType { StructDefinition, TraitConstraint, TraitDefinition, + TraitImpl, FunctionDefinition, Module, } @@ -727,6 +728,7 @@ impl std::fmt::Display for QuotedType { QuotedType::StructDefinition => write!(f, "StructDefinition"), QuotedType::TraitDefinition => write!(f, "TraitDefinition"), QuotedType::TraitConstraint => write!(f, "TraitConstraint"), + QuotedType::TraitImpl => write!(f, "TraitImpl"), QuotedType::FunctionDefinition => write!(f, "FunctionDefinition"), QuotedType::Module => write!(f, "Module"), } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 4222d2b585f..c4145c62791 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -925,6 +925,7 @@ pub enum Keyword { Trait, TraitConstraint, TraitDefinition, + TraitImpl, Type, TypeType, Unchecked, @@ -977,6 +978,7 @@ impl fmt::Display for Keyword { Keyword::Trait => write!(f, "trait"), Keyword::TraitConstraint => write!(f, "TraitConstraint"), Keyword::TraitDefinition => write!(f, "TraitDefinition"), + Keyword::TraitImpl => write!(f, "TraitImpl"), Keyword::Type => write!(f, "type"), Keyword::TypeType => write!(f, "Type"), Keyword::Unchecked => write!(f, "unchecked"), @@ -1031,6 +1033,7 @@ impl Keyword { "trait" => Keyword::Trait, "TraitConstraint" => Keyword::TraitConstraint, "TraitDefinition" => Keyword::TraitDefinition, + "TraitImpl" => Keyword::TraitImpl, "type" => Keyword::Type, "Type" => Keyword::TypeType, "StructDefinition" => Keyword::StructDefinition, diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 7c551ca96d1..7644c451dff 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -31,6 +31,7 @@ pub(super) fn parse_type_inner<'a>( struct_definition_type(), trait_constraint_type(), trait_definition_type(), + trait_impl_type(), function_definition_type(), module_type(), top_level_item_type(), @@ -107,6 +108,11 @@ pub(super) fn trait_definition_type() -> impl NoirParser { }) } +pub(super) fn trait_impl_type() -> impl NoirParser { + keyword(Keyword::TraitImpl) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::TraitImpl).with_span(span)) +} + pub(super) fn function_definition_type() -> impl NoirParser { keyword(Keyword::FunctionDefinition).map_with_span(|_, span| { UnresolvedTypeData::Quoted(QuotedType::FunctionDefinition).with_span(span) diff --git a/noir_stdlib/src/meta/typ.nr b/noir_stdlib/src/meta/typ.nr index ad669e93c0a..67ad2a96739 100644 --- a/noir_stdlib/src/meta/typ.nr +++ b/noir_stdlib/src/meta/typ.nr @@ -20,6 +20,9 @@ impl Type { #[builtin(type_as_tuple)] fn as_tuple(self) -> Option<[Type]> {} + #[builtin(type_get_trait_impl)] + fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} + #[builtin(type_implements)] fn implements(self, constraint: TraitConstraint) -> 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 170292b0e37..f0b53a392ed 100644 --- a/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -108,6 +108,8 @@ fn main() { let some_trait_field = quote { SomeTrait }.as_trait_constraint(); assert(!struct_implements_some_trait.implements(some_trait_field)); assert(!struct_does_not_implement_some_trait.implements(some_trait_field)); + + let _trait_impl = struct_implements_some_trait.get_trait_impl(some_trait_i32).unwrap(); } } @@ -117,5 +119,7 @@ fn function_with_where(_x: T) where T: SomeTrait { let t = quote { T }.as_type(); let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); assert(t.implements(some_trait_i32)); + + assert(t.get_trait_impl(some_trait_i32).is_none()); } } diff --git a/tooling/lsp/src/requests/completion/builtins.rs b/tooling/lsp/src/requests/completion/builtins.rs index 070be109f13..00f7717046c 100644 --- a/tooling/lsp/src/requests/completion/builtins.rs +++ b/tooling/lsp/src/requests/completion/builtins.rs @@ -14,6 +14,7 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { Keyword::StructDefinition => Some("StructDefinition"), Keyword::TraitConstraint => Some("TraitConstraint"), Keyword::TraitDefinition => Some("TraitDefinition"), + Keyword::TraitImpl => Some("TraitImpl"), Keyword::TypeType => Some("Type"), Keyword::As @@ -116,6 +117,7 @@ pub(super) fn keyword_builtin_function(keyword: &Keyword) -> Option