Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
8 changes: 7 additions & 1 deletion compiler/noirc_errors/src/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl<T> Located<T> {
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct Location {
pub span: Span,
pub file: FileId,
Expand Down Expand Up @@ -103,3 +103,9 @@ impl PartialOrd for Location {
Some(self.cmp(other))
}
}

impl std::fmt::Debug for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}/{}:{}", self.file.as_usize(), self.span.start(), self.span.end())
}
}
24 changes: 22 additions & 2 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2650,6 +2650,9 @@ impl Elaborator<'_> {
(expr_location, empty_function)
}

/// Insert the ordered generics and associated types from the trait bound.
/// If the constraint is `assumed`, it also inserts the binding to the `Self`
/// type to whatever type the constraint is defined on.
pub fn bind_generics_from_trait_constraint(
&self,
constraint: &TraitConstraint,
Expand All @@ -2669,6 +2672,7 @@ impl Elaborator<'_> {
}
}

/// Insert the ordered generics and associated types from the trait bound.
pub fn bind_generics_from_trait_bound(
&self,
trait_bound: &ResolvedTraitBound,
Expand Down Expand Up @@ -2755,24 +2759,35 @@ impl Elaborator<'_> {
}
}

/// Binds the ordered [ResolvedGeneric]s of a trait to the ordered generics in a [ResolvedTraitBound].
///
/// Panics if the number of types do not match the ordered generics in the trait.
pub(super) fn bind_ordered_generics(
params: &[ResolvedGeneric],
args: &[Type],
bindings: &mut TypeBindings,
) {
assert_eq!(params.len(), args.len());
assert_eq!(params.len(), args.len(), "unexpected number of ordered generics");

for (param, arg) in params.iter().zip(args) {
bind_generic(param, arg, bindings);
}
}

/// Binds the associated [ResolvedGeneric]s of a trait to the named generics in a [ResolvedTraitBound].
///
/// Panics if the number of types exceeds the named generics in the trait.
/// Any named parameter that does not appear in the arguments is bound to [Type::Error].
fn bind_named_generics(
mut params: Vec<ResolvedGeneric>,
args: &[NamedType],
bindings: &mut TypeBindings,
) {
assert!(args.len() <= params.len());
assert!(args.len() <= params.len(), "more named generics than associated types");

if params.is_empty() {
return;
}

for arg in args {
let i = params
Expand All @@ -2781,6 +2796,7 @@ fn bind_named_generics(
.unwrap_or_else(|| unreachable!("Expected to find associated type named {}", arg.name));

let param = params.swap_remove(i);

bind_generic(&param, &arg.typ, bindings);
}

Expand All @@ -2789,6 +2805,10 @@ fn bind_named_generics(
}
}

/// Binds the type variable in a [ResolvedGeneric], e.g. a generic parameter of a trait,
/// to a [Type], which itself can be an unbound type variable.
///
/// If the type varia itself appears in the type, then it does nothing.
fn bind_generic(param: &ResolvedGeneric, arg: &Type, bindings: &mut TypeBindings) {
// Avoid binding t = t
if !arg.occurs(param.type_var.id()) {
Expand Down
18 changes: 15 additions & 3 deletions compiler/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,9 @@ impl std::fmt::Display for Type {
Type::Unit => write!(f, "()"),
Type::Error => write!(f, "error"),
Type::NamedGeneric(NamedGeneric { type_var, name, .. }) => match &*type_var.borrow() {
TypeBinding::Bound(type_var) => type_var.fmt(f),
TypeBinding::Unbound(_, _) if name.is_empty() => write!(f, "_"),
TypeBinding::Unbound(_, _) => write!(f, "{name}"),
TypeBinding::Bound(type_var) if !type_var.is_unbound_type_var() => type_var.fmt(f),
_ if name.is_empty() => write!(f, "_"),
_ => write!(f, "{name}"),
},
Type::CheckedCast { to, .. } => write!(f, "{to}"),
Type::Constant(x, _kind) => write!(f, "{x}"),
Expand Down Expand Up @@ -1895,6 +1895,18 @@ impl Type {
}
}

/// Check if this is a [Type::TypeVariable] with a [TypeBinding::Unbound] binding.
fn is_unbound_type_var(&self) -> bool {
let Type::TypeVariable(type_var) = self else {
return false;
};
let binding = type_var.borrow();
match &*binding {
TypeBinding::Unbound(_, _) => true,
TypeBinding::Bound(typ) => typ.is_unbound_type_var(),
}
}

pub(crate) fn contains_reference(&self) -> bool {
match self {
Type::Unit
Expand Down
48 changes: 48 additions & 0 deletions compiler/noirc_frontend/src/tests/traits/trait_associated_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,54 @@ fn associated_type_mismatch_across_modules() {
check_errors(src);
}

#[test]
fn associated_type_mismatch_with_inheritance() {
let src = r#"
pub trait Foo {
type Bar;
fn foo(x: Self::Bar) -> Self::Bar;
}

pub trait Qux: Foo {
type Baz;
fn qux(x: Self::Baz) -> Self::Baz {
^^^^^^^^^ expected type Self::Baz, found type Self::Bar
~~~~~~~~~ expected Self::Baz because of return type
<Self as Foo>::foo(x)
^ Expected type Self::Bar, found type Self::Baz
~~~~~~~~~~~~~~~~~~~~~ Self::Bar returned here
}
}

fn main() {}
"#;
check_errors(src);
}

#[test]
fn associated_type_and_constant_composite() {
let src = r#"
pub trait Foo {
type Bar;
let Baz: u32;
fn foo(x: [Self::Bar; Self::Baz]) -> u32;
}

impl Foo for () {
type Bar = ();
let Baz: u32 = 0;
fn foo(_x: [Self::Bar; Self::Baz]) -> u32 {
0
}
}

fn main() {
let _ = <() as Foo>::foo([(); 0]);
}
"#;
assert_no_errors(src);
}

#[test]
fn associated_type_behind_self_as_trait() {
let src = r#"
Expand Down
17 changes: 17 additions & 0 deletions compiler/noirc_frontend/src/tests/traits/trait_inheritance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,20 @@ fn trait_impl_with_child_constraint() {
"#;
assert_no_errors(src);
}

#[test]
fn trait_inheritance_with_ambiguous_associated_type() {
let src = r#"
pub trait Foo {
type Bar;
fn foo(x: Self::Bar) -> Self::Bar;
}

pub trait Qux: Foo {
type Bar;
fn qux(x: Self::Bar) -> Self::Bar;
}
"#;
// TODO: Add errors for ambiguous type. But it should work as `<Self as Qux>::Bar`
check_errors(src);
}
Loading