From d8dfb90005245eb14245d180c16fe44f38d828f6 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 31 Oct 2024 10:19:11 -0300 Subject: [PATCH 1/4] Check error in tests --- .../noirc_frontend/src/tests/turbofish.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/compiler/noirc_frontend/src/tests/turbofish.rs b/compiler/noirc_frontend/src/tests/turbofish.rs index 3bde3e17c80..b656f066542 100644 --- a/compiler/noirc_frontend/src/tests/turbofish.rs +++ b/compiler/noirc_frontend/src/tests/turbofish.rs @@ -375,6 +375,18 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "bool"); + assert_eq!(expr_typ, "Field"); } #[test] @@ -399,4 +411,16 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "i32"); + assert_eq!(expr_typ, "bool"); } From db406be6af2b8e56b5a7ebd9dd8dd78ee4047985 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 31 Oct 2024 10:29:17 -0300 Subject: [PATCH 2/4] Check turbofish in trait function call --- .../noirc_frontend/src/elaborator/patterns.rs | 44 ++++++++++++++++--- .../noirc_frontend/src/elaborator/types.rs | 4 ++ .../src/hir/type_check/errors.rs | 6 --- .../noirc_frontend/src/tests/turbofish.rs | 33 ++++++++++++++ 4 files changed, 76 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index a953c54dc4b..758df0bba1b 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -453,6 +453,31 @@ impl<'context> Elaborator<'context> { self.resolve_turbofish_generics(&struct_type.generics, turbofish_generics) } + pub(super) fn resolve_trait_turbofish_generics( + &mut self, + trait_name: &str, + trait_generics: &[ResolvedGeneric], + generics: Vec, + unresolved_turbofish: Option>, + span: Span, + ) -> Vec { + let Some(turbofish_generics) = unresolved_turbofish else { + return generics; + }; + + if turbofish_generics.len() != generics.len() { + self.push_err(TypeCheckError::GenericCountMismatch { + item: format!("trait {}", trait_name), + expected: generics.len(), + found: turbofish_generics.len(), + span, + }); + return generics; + } + + self.resolve_turbofish_generics(trait_generics, turbofish_generics) + } + pub(super) fn resolve_alias_turbofish_generics( &mut self, type_alias: &TypeAlias, @@ -581,10 +606,19 @@ impl<'context> Elaborator<'context> { generics } - PathResolutionItem::TraitFunction(_trait_id, Some(generics), _func_id) => { - // TODO: https://github.com/noir-lang/noir/issues/6310 - self.push_err(TypeCheckError::UnsupportedTurbofishUsage { span: generics.span }); - Vec::new() + PathResolutionItem::TraitFunction(trait_id, Some(generics), _func_id) => { + let trait_ = self.interner.get_trait(trait_id); + let trait_generics = vecmap(&trait_.generics, |generic| { + self.interner.next_type_variable_with_kind(generic.kind()) + }); + + self.resolve_trait_turbofish_generics( + &trait_.name.to_string(), + &trait_.generics.clone(), + trait_generics, + Some(generics.generics), + generics.span, + ) } _ => Vec::new(), } @@ -602,7 +636,7 @@ impl<'context> Elaborator<'context> { id: self.interner.trait_method_id(trait_path_resolution.method.method_id), impl_kind: ImplKind::TraitMethod(trait_path_resolution.method), }, - None, + trait_path_resolution.item, ) } else { // If the Path is being used as an Expression, then it is referring to a global from a separate module diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 2879204d3ee..c8a16a6cd9b 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -48,6 +48,7 @@ pub const WILDCARD_TYPE: &str = "_"; pub(super) struct TraitPathResolution { pub(super) method: TraitMethod, + pub(super) item: Option, pub(super) errors: Vec, } @@ -553,6 +554,7 @@ impl<'context> Elaborator<'context> { let constraint = the_trait.as_constraint(path.span); return Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: true }, + item: None, errors: Vec::new(), }); } @@ -573,6 +575,7 @@ impl<'context> Elaborator<'context> { let constraint = the_trait.as_constraint(path.span); Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: false }, + item: Some(path_resolution.item), errors: path_resolution.errors, }) } @@ -601,6 +604,7 @@ impl<'context> Elaborator<'context> { if let Some(method) = the_trait.find_method(path.last_name()) { return Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: true }, + item: None, errors: Vec::new(), }); } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 3b4ab148ef7..cb69053b72a 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -174,8 +174,6 @@ pub enum TypeCheckError { StringIndexAssign { span: Span }, #[error("Macro calls may only return `Quoted` values")] MacroReturningNonExpr { typ: Type, span: Span }, - #[error("turbofish (`::<_>`) usage at this position isn't supported yet")] - UnsupportedTurbofishUsage { span: Span }, #[error("`{name}` has already been specified")] DuplicateNamedTypeArg { name: Ident, prev_span: Span }, #[error("`{item}` has no associated type named `{name}`")] @@ -443,10 +441,6 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { error.add_secondary("Hint: remove the `!` from the end of the function name.".to_string(), *span); error }, - TypeCheckError::UnsupportedTurbofishUsage { span } => { - let msg = "turbofish (`::<_>`) usage at this position isn't supported yet"; - Diagnostic::simple_error(msg.to_string(), "".to_string(), *span) - }, TypeCheckError::DuplicateNamedTypeArg { name, prev_span } => { let msg = format!("`{name}` has already been specified"); let mut error = Diagnostic::simple_error(msg.to_string(), "".to_string(), name.span()); diff --git a/compiler/noirc_frontend/src/tests/turbofish.rs b/compiler/noirc_frontend/src/tests/turbofish.rs index b656f066542..3e34ea9521b 100644 --- a/compiler/noirc_frontend/src/tests/turbofish.rs +++ b/compiler/noirc_frontend/src/tests/turbofish.rs @@ -424,3 +424,36 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er assert_eq!(expected_typ, "i32"); assert_eq!(expr_typ, "bool"); } + +#[test] +fn trait_function_with_turbofish_on_trait_gives_error() { + let src = r#" + trait Foo { + fn foo(_x: T) -> Self; + } + + impl Foo for i32 { + fn foo(_x: T) -> Self { + 1 + } + } + + fn main() { + let _: i32 = Foo::::foo(1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "bool"); + assert_eq!(expr_typ, "Field"); +} From 667340814e324870af8b930c25bf8ccb33ed7cd5 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 31 Oct 2024 10:41:14 -0300 Subject: [PATCH 3/4] Small refactor --- .../noirc_frontend/src/elaborator/patterns.rs | 95 ++++++++++--------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 758df0bba1b..1ea2a38939e 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -17,7 +17,7 @@ use crate::{ stmt::HirPattern, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - Kind, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeBindings, + Kind, Shared, StructType, Type, TypeAlias, TypeBindings, }; use super::{Elaborator, ResolverMeta}; @@ -413,19 +413,20 @@ impl<'context> Elaborator<'context> { unresolved_turbofish: Option>, span: Span, ) -> Option> { - let direct_generics = self.interner.function_meta(func_id).direct_generics.clone(); + let direct_generic_kinds = + vecmap(&self.interner.function_meta(func_id).direct_generics, |generic| generic.kind()); unresolved_turbofish.map(|unresolved_turbofish| { - if unresolved_turbofish.len() != direct_generics.len() { + if unresolved_turbofish.len() != direct_generic_kinds.len() { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { - expected_count: direct_generics.len(), + expected_count: direct_generic_kinds.len(), actual_count: unresolved_turbofish.len(), span, }; self.push_err(type_check_err); } - self.resolve_turbofish_generics(&direct_generics, unresolved_turbofish) + self.resolve_turbofish_generics(direct_generic_kinds, unresolved_turbofish) }) } @@ -436,46 +437,33 @@ impl<'context> Elaborator<'context> { unresolved_turbofish: Option>, span: Span, ) -> Vec { - let Some(turbofish_generics) = unresolved_turbofish else { - return generics; - }; - - if turbofish_generics.len() != generics.len() { - self.push_err(TypeCheckError::GenericCountMismatch { - item: format!("struct {}", struct_type.name), - expected: generics.len(), - found: turbofish_generics.len(), - span, - }); - return generics; - } - - self.resolve_turbofish_generics(&struct_type.generics, turbofish_generics) + let kinds = vecmap(&struct_type.generics, |generic| generic.kind()); + self.resolve_item_turbofish_generics( + "struct", + &struct_type.name.0.contents, + kinds, + generics, + unresolved_turbofish, + span, + ) } pub(super) fn resolve_trait_turbofish_generics( &mut self, trait_name: &str, - trait_generics: &[ResolvedGeneric], + trait_generic_kinds: Vec, generics: Vec, unresolved_turbofish: Option>, span: Span, ) -> Vec { - let Some(turbofish_generics) = unresolved_turbofish else { - return generics; - }; - - if turbofish_generics.len() != generics.len() { - self.push_err(TypeCheckError::GenericCountMismatch { - item: format!("trait {}", trait_name), - expected: generics.len(), - found: turbofish_generics.len(), - span, - }); - return generics; - } - - self.resolve_turbofish_generics(trait_generics, turbofish_generics) + self.resolve_item_turbofish_generics( + "trait", + trait_name, + trait_generic_kinds, + generics, + unresolved_turbofish, + span, + ) } pub(super) fn resolve_alias_turbofish_generics( @@ -484,6 +472,26 @@ impl<'context> Elaborator<'context> { generics: Vec, unresolved_turbofish: Option>, span: Span, + ) -> Vec { + let kinds = vecmap(&type_alias.generics, |generic| generic.kind()); + self.resolve_item_turbofish_generics( + "alias", + &type_alias.name.0.contents, + kinds, + generics, + unresolved_turbofish, + span, + ) + } + + pub(super) fn resolve_item_turbofish_generics( + &mut self, + item_kind: &'static str, + item_name: &str, + item_generic_kinds: Vec, + generics: Vec, + unresolved_turbofish: Option>, + span: Span, ) -> Vec { let Some(turbofish_generics) = unresolved_turbofish else { return generics; @@ -491,7 +499,7 @@ impl<'context> Elaborator<'context> { if turbofish_generics.len() != generics.len() { self.push_err(TypeCheckError::GenericCountMismatch { - item: format!("alias {}", type_alias.name), + item: format!("{item_kind} {item_name}"), expected: generics.len(), found: turbofish_generics.len(), span, @@ -499,17 +507,17 @@ impl<'context> Elaborator<'context> { return generics; } - self.resolve_turbofish_generics(&type_alias.generics, turbofish_generics) + self.resolve_turbofish_generics(item_generic_kinds, turbofish_generics) } pub(super) fn resolve_turbofish_generics( &mut self, - generics: &[ResolvedGeneric], + kinds: Vec, turbofish_generics: Vec, ) -> Vec { - let generics_with_types = generics.iter().zip(turbofish_generics); - vecmap(generics_with_types, |(generic, unresolved_type)| { - self.resolve_type_inner(unresolved_type, &generic.kind()) + let kinds_with_types = kinds.into_iter().zip(turbofish_generics); + vecmap(kinds_with_types, |(kind, unresolved_type)| { + self.resolve_type_inner(unresolved_type, &kind) }) } @@ -611,10 +619,11 @@ impl<'context> Elaborator<'context> { let trait_generics = vecmap(&trait_.generics, |generic| { self.interner.next_type_variable_with_kind(generic.kind()) }); + let kinds = vecmap(&trait_.generics, |generic| generic.kind()); self.resolve_trait_turbofish_generics( &trait_.name.to_string(), - &trait_.generics.clone(), + kinds, trait_generics, Some(generics.generics), generics.span, From 9cb0c77fbb38f6886e2f880a3cc430ae604ea8be Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 31 Oct 2024 15:58:12 -0300 Subject: [PATCH 4/4] Compute trait generics from kinds --- compiler/noirc_frontend/src/elaborator/patterns.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 1ea2a38939e..9e60adcbc6f 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -616,10 +616,9 @@ impl<'context> Elaborator<'context> { } PathResolutionItem::TraitFunction(trait_id, Some(generics), _func_id) => { let trait_ = self.interner.get_trait(trait_id); - let trait_generics = vecmap(&trait_.generics, |generic| { - self.interner.next_type_variable_with_kind(generic.kind()) - }); let kinds = vecmap(&trait_.generics, |generic| generic.kind()); + let trait_generics = + vecmap(&kinds, |kind| self.interner.next_type_variable_with_kind(kind.clone())); self.resolve_trait_turbofish_generics( &trait_.name.to_string(),