Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 73 additions & 30 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -413,19 +413,20 @@ impl<'context> Elaborator<'context> {
unresolved_turbofish: Option<Vec<UnresolvedType>>,
span: Span,
) -> Option<Vec<Type>> {
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)
})
}

Expand All @@ -436,21 +437,33 @@ impl<'context> Elaborator<'context> {
unresolved_turbofish: Option<Vec<UnresolvedType>>,
span: Span,
) -> Vec<Type> {
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;
}
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,
)
}

self.resolve_turbofish_generics(&struct_type.generics, turbofish_generics)
pub(super) fn resolve_trait_turbofish_generics(
&mut self,
trait_name: &str,
trait_generic_kinds: Vec<Kind>,
generics: Vec<Type>,
unresolved_turbofish: Option<Vec<UnresolvedType>>,
span: Span,
) -> Vec<Type> {
self.resolve_item_turbofish_generics(
"trait",
trait_name,
trait_generic_kinds,
generics,
unresolved_turbofish,
span,
)
}

pub(super) fn resolve_alias_turbofish_generics(
Expand All @@ -459,32 +472,52 @@ impl<'context> Elaborator<'context> {
generics: Vec<Type>,
unresolved_turbofish: Option<Vec<UnresolvedType>>,
span: Span,
) -> Vec<Type> {
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<Kind>,
generics: Vec<Type>,
unresolved_turbofish: Option<Vec<UnresolvedType>>,
span: Span,
) -> Vec<Type> {
let Some(turbofish_generics) = unresolved_turbofish else {
return generics;
};

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,
});
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<Kind>,
turbofish_generics: Vec<UnresolvedType>,
) -> Vec<Type> {
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)
})
}

Expand Down Expand Up @@ -581,10 +614,20 @@ 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())
});
let kinds = vecmap(&trait_.generics, |generic| generic.kind());

self.resolve_trait_turbofish_generics(
&trait_.name.to_string(),
kinds,
trait_generics,
Some(generics.generics),
generics.span,
)
}
_ => Vec::new(),
}
Expand All @@ -602,7 +645,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
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub const WILDCARD_TYPE: &str = "_";

pub(super) struct TraitPathResolution {
pub(super) method: TraitMethod,
pub(super) item: Option<PathResolutionItem>,
pub(super) errors: Vec<PathResolutionError>,
}

Expand Down Expand Up @@ -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(),
});
}
Expand All @@ -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,
})
}
Expand Down Expand Up @@ -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(),
});
}
Expand Down
6 changes: 0 additions & 6 deletions compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}`")]
Expand Down Expand Up @@ -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());
Expand Down
57 changes: 57 additions & 0 deletions compiler/noirc_frontend/src/tests/turbofish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -399,4 +411,49 @@ 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");
}

#[test]
fn trait_function_with_turbofish_on_trait_gives_error() {
let src = r#"
trait Foo<T> {
fn foo(_x: T) -> Self;
}

impl<T> Foo<T> for i32 {
fn foo(_x: T) -> Self {
1
}
}

fn main() {
let _: i32 = Foo::<bool>::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");
}