Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
22 changes: 16 additions & 6 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.filter(|item| item.is_type() || item.is_const())
// Traits with RPITITs are simply not dyn compatible (for now).
.filter(|item| !item.is_impl_trait_in_trait())
.map(|item| (item.def_id, trait_ref)),
.map(|item| (item, trait_ref)),
);
}
ty::ClauseKind::Projection(pred) => {
Expand Down Expand Up @@ -314,14 +314,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let mut missing_assoc_items = FxIndexSet::default();
let projection_bounds: Vec<_> = ordered_associated_items
.into_iter()
.filter_map(|key @ (def_id, _)| {
if let Some(&assoc) = projection_bounds.get(&key) {
.filter_map(|(item, trait_ref)| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please also update the large comment above. I'm referring to the part where it says The user has to constrain all associated types & consts via bindings unless the corresponding associated item has a where Self: Sized clause.. Could you update it to say sth. along the lines of […] unless […] has a […] clause or it has a default value which we would then pick.

Thanks!

let def_id = item.def_id;
let key = (def_id, trait_ref);
if let Some(&(assoc, _)) = projection_bounds.get(&key) {
return Some(assoc);
}
if !tcx.generics_require_sized_self(def_id) {
let has_default = item.defaultness(tcx).has_value();
if !has_default && !tcx.generics_require_sized_self(def_id) {
missing_assoc_items.insert(key);
}
None
if has_default {
Some(trait_ref.map_bound(|trait_ref| ty::ProjectionPredicate {
projection_term: ty::AliasTerm::new_from_args(tcx, def_id, trait_ref.args),
term: tcx.type_of(def_id).instantiate(tcx, trait_ref.args).into(),
Copy link
Member

@fmease fmease Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure if your current approach can handle the following code that was suggested in similar form in the RFC:

#![feature(associated_type_defaults)]

trait Trait {
    type T = Self::U;
    type U;
}

fn main() {
    let _: dyn Trait<U = ()>; // `Trait::T` should default to `()`
}

I have a hunch that this might not work right now but I might be mistaken. I think this will get rejected with the value of associated type T must be specified similar to:

trait Trait: SuperTrait<T = Self::U> { type U; }
trait SuperTrait { type T; }

fn main() {
    let _: dyn Trait<U = ()>;
    //~^ ERROR the value of the associated type `T` in `SuperTrait` must be specified
}

In any case, encapsulating this as a UI test would be nice I think.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, I couldn't think of this case. Thanks!

Yeah, this case is a bit irritating, as the defaulted T = Self::U would contain a dummy self ty and that would result in either err or ICE.
I guess gathering the user written bounds as a param env and then normalizing those default alias terms with it might hopefully fix it.
I'll try it along with your other feedback comments. Thanks!

Copy link
Member

@fmease fmease Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, well, I'm actually questioning the feasibility of this idea. That's because you're not allowed to normalize in ItemCtxts ("places where we don't perform inference" / item signatures / non-bodies). That's because it has nowhere to store the resulting obligations. It might not even be correct in FnCtxts ("places where we perform inference" / bodies) since it can only normalize ignoring regions IINM which might be exploitable, not sure yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking ofTyCtxt::normalize_erasing_regions and that might be incorrect as you said 🤔 I'll re-think about alternative implementations

}))
} else {
None
}
})
.collect();

Expand Down Expand Up @@ -394,7 +404,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
});

let existential_projections = projection_bounds.into_iter().map(|(bound, _)| {
let existential_projections = projection_bounds.into_iter().map(|bound| {
bound.map_bound(|mut b| {
assert_eq!(b.projection_term.self_ty(), dummy_self);

Expand Down
14 changes: 14 additions & 0 deletions tests/ui/associated-types/defaults-on-trait-object-cycle.rs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please also add a corresponding test for type-level associated constants (feature min_generic_const_args (mGCA))? Please put it into tests/ui/const-generics/associated-const-bindings/ and name it something like dyn-compat-assoc-const-defaults.rs following the established naming convention.

It should look something like:

// Type associated consts needn't be specified in the trait object type
// if they have a default in which case we use said default.
//@ check-pass

#![feature(min_generic_const_args, associated_type_defaults)]
#![expect(incomplete_features)]

trait Trait {
    type const N: usize = 1;
}

fn main() {
    // `Trait::N` defaults to `1`.
    let _: dyn Trait;
    let _: [(); <dyn Trait as Trait>::N] = [(); 1];
}

(The the phrasing of the description is admittedly rather awkward, feel free to improve it)

(I've intentionally proactively added associated_type_defaults since that will become necessary for such type-level assoc consts once PR #152385 is merged.)

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![feature(associated_type_defaults)]

trait Foo {
// This causes cycle, as being effectively `Box<dyn Foo<Assoc = Box<dyn Foo<..>>>>`.
type Assoc = Box<dyn Foo>;
//~^ ERROR: cycle detected when computing type of `Foo::Assoc`
}

trait Bar {
// This does not.
type Assoc = Box<dyn Bar<Assoc = ()>>;
}

fn main() {}
17 changes: 17 additions & 0 deletions tests/ui/associated-types/defaults-on-trait-object-cycle.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0391]: cycle detected when computing type of `Foo::Assoc`
--> $DIR/defaults-on-trait-object-cycle.rs:5:5
|
LL | type Assoc = Box<dyn Foo>;
| ^^^^^^^^^^
|
= note: ...which immediately requires computing type of `Foo::Assoc` again
note: cycle used when checking that `Foo` is well-formed
--> $DIR/defaults-on-trait-object-cycle.rs:3:1
|
LL | trait Foo {
| ^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0391`.
33 changes: 33 additions & 0 deletions tests/ui/associated-types/defaults-on-trait-object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#![feature(associated_type_defaults)]

trait Foo {
type Assoc: Default = ();

fn foo(&self) -> Self::Assoc {
Default::default()
}
}

// The assoc type constraint can be omitted for the assoc type with a default.
type FooObj = Box<dyn Foo>;

trait Bar {
type Assoc1 = i32;
type Assoc2;
}

// We can't omit assoc type constraints for an assoc type without a default value.
type BarObj1 = Box<dyn Bar>;
//~^ ERROR: the value of the associated type `Assoc2` in `Bar` must be specified

type BarObj2 = Box<dyn Bar<Assoc2 = ()>>;

type BarObj3 = Box<dyn Bar<Assoc1 = u32, Assoc2 = i32>>;

fn check_projs(foo1: &dyn Foo, foo2: &dyn Foo<Assoc = bool>) {
let _: () = foo1.foo();
let _: () = foo2.foo();
//~^ ERROR: mismatched types
}

fn main() {}
26 changes: 26 additions & 0 deletions tests/ui/associated-types/defaults-on-trait-object.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error[E0191]: the value of the associated type `Assoc2` in `Bar` must be specified
--> $DIR/defaults-on-trait-object.rs:20:24
|
LL | type Assoc2;
| ----------- `Assoc2` defined here
...
LL | type BarObj1 = Box<dyn Bar>;
| ^^^
|
help: specify the associated type
|
LL | type BarObj1 = Box<dyn Bar<Assoc2 = /* Type */>>;
| +++++++++++++++++++++

error[E0308]: mismatched types
--> $DIR/defaults-on-trait-object.rs:29:17
|
LL | let _: () = foo2.foo();
| -- ^^^^^^^^^^ expected `()`, found `bool`
| |
| expected due to this

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0191, E0308.
For more information about an error, try `rustc --explain E0191`.
3 changes: 2 additions & 1 deletion tests/ui/associated-types/issue-23595-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ trait Hierarchy {
type Value;
type ChildKey;
type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy>;
//~^ ERROR: the value of the associated types
//~^ ERROR: cycle detected when computing type of `Hierarchy::Children`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cycle here is unfortunate but I think this might be inevitable 🤔

//~| ERROR: the value of the associated types

fn data(&self) -> Option<(Self::Value, Self::Children)>;
}
Expand Down
27 changes: 21 additions & 6 deletions tests/ui/associated-types/issue-23595-1.stderr
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
error[E0191]: the value of the associated types `Value`, `ChildKey` and `Children` in `Hierarchy` must be specified
error[E0391]: cycle detected when computing type of `Hierarchy::Children`
--> $DIR/issue-23595-1.rs:8:5
|
LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy>;
| ^^^^^^^^^^^^^
|
= note: ...which immediately requires computing type of `Hierarchy::Children` again
note: cycle used when checking that `Hierarchy` is well-formed
--> $DIR/issue-23595-1.rs:5:1
|
LL | trait Hierarchy {
| ^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

error[E0191]: the value of the associated types `Value` and `ChildKey` in `Hierarchy` must be specified
--> $DIR/issue-23595-1.rs:8:60
|
LL | type Value;
| ---------- `Value` defined here
LL | type ChildKey;
| ------------- `ChildKey` defined here
LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy>;
| ------------- `Children` defined here ^^^^^^^^^
| ^^^^^^^^^
|
help: specify the associated types
|
LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy<Value = /* Type */, ChildKey = /* Type */, Children = /* Type */>>;
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy<Value = /* Type */, ChildKey = /* Type */>>;
| +++++++++++++++++++++++++++++++++++++++++++

error: aborting due to 1 previous error
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0191`.
Some errors have detailed explanations: E0191, E0391.
For more information about an error, try `rustc --explain E0191`.
Loading