Skip to content

Add supertrait item shadowing for type-level path resolution#152225

Open
Amanieu wants to merge 4 commits into
rust-lang:mainfrom
Amanieu:type-supertrait-shadowing
Open

Add supertrait item shadowing for type-level path resolution#152225
Amanieu wants to merge 4 commits into
rust-lang:mainfrom
Amanieu:type-supertrait-shadowing

Conversation

@Amanieu

@Amanieu Amanieu commented Feb 6, 2026

Copy link
Copy Markdown
Member

This makes type-level name resolution (used primarily for associated types) also prefer subtrait items to supertrait items in case of ambiguity.

This addresses the concern from #148605 about the inconsistency with name resolution in expressions and method calls.

@rustbot

rustbot commented Feb 6, 2026

Copy link
Copy Markdown
Collaborator

HIR ty lowering was modified

cc @fmease

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 6, 2026
@rustbot

rustbot commented Feb 6, 2026

Copy link
Copy Markdown
Collaborator

r? @madsmtm

rustbot has assigned @madsmtm.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • People who recently interacted with files modified in this PR: compiler
  • compiler expanded to 20 candidates
  • Random selection from 12 candidates

println!("{}", size_of::<T::T>());
}

fn generic2<T: C<T = i16>>() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What about T: B<T = i16> and T: A<T = i8> - do those resolve to B::T and A::T respectively?

Also, please use a different name for the generic and the associated type, it's a bit confusing as-is.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've cleaned up the example and added tests showing how B<T = i16> and A<T = i8> are resolved.

@jackh726

jackh726 commented Feb 9, 2026

Copy link
Copy Markdown
Member

r? types

@rustbot rustbot added the T-types Relevant to the types team, which will review and decide on the PR/issue. label Feb 9, 2026
@traviscross traviscross added the I-lang-radar Items that are on lang's radar and will need eventual work or consideration. label Feb 10, 2026
@madsmtm

madsmtm commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

r? types

Wait, I'm not sure if that should've unassigned me or not? In any case, if you also need a compiler reviewer, I'm the wrong one for the job, so I'm gonna unassign myself.

@madsmtm madsmtm removed their assignment Feb 26, 2026
@Amanieu Amanieu force-pushed the type-supertrait-shadowing branch from 1cde4c7 to 5a9ef14 Compare March 28, 2026 12:33
@rustbot

This comment has been minimized.

@jackh726 jackh726 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In general, I think this needs some more tests. I've already commented about adding the testing attr, but ideally we should go through the existing tests/ui/methods/supertrait-shadowing tests (which in general are pretty thorough) and write the equivalent test for associated types and consts. These cover cases like multiple ancestors, unsatisfied trait bounds, scoping, and (in a PR I'm about to make) stability.

I also would like to see test that show that <U as A>::T works just fine, even if you have U: B.

View changes since this review

use std::mem::size_of;

trait A {
type T;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please use a different name for the associated type to name lexically clash with the param.

println!("{}", size_of::<U::T>());
}

fn generic3<U: B<T = i16>>() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is missing here is a test of U: B<T = i8> and U: B, just to show the former is not callable (because of the blanket impl), and the latter still uses <U as B>::T. (Similar for C below)

trait C: B {}
impl<T> C for T {}

fn main() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We have rustc_dump_predicates, which we could use to verify the predicates are getting lowered as we expect. It would be good to use that in this (or another) test.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So, this test probably doesn't make sense here, right? This is not about method resolution.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Finally getting back around to this! Some questions about your preferences for the test organization.

Would you prefer moving all supertrait-shadowing tests into their own directory (tests/ui/supertrait-shadowing) or would you prefer adding a separate directory which just contains tests for type-level shadowing?

In the former case, would you like type-level shadowing tests to be in separate files or in the same file as the corresponding method-level test?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No strong preference. I think a separate directory is good. Putting them in the same file also seems reasonable.

/// supertrait of another candidate trait.
///
/// This implements RFC #3624.
fn collapse_candidates_to_subtrait_pick(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think docs here and for collapse_candidates_to_subtrait_pick in method::probe should link to each other to help ensure they are kept up to date.

It's kind of unfortunate we can't have some shared code here.

continue;
}

// This pick is not a supertrait of the `child_pick`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have a commit with some cleanup that I'm about to push - but child_pick doesn't exist. Should be child_trait. Here and below.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 11, 2026
@joshtriplett

Copy link
Copy Markdown
Member

What's the current state of this PR? AIUI, this is the remaining blocker for stabilizing supertrait item shadowing.

@jackh726

Copy link
Copy Markdown
Member

I think this is waiting on @Amanieu to address the review.

@rust-bors

This comment has been minimized.

@Amanieu

Amanieu commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

I've addressed the review feedback. I couldn't adapt the following tests because the corresponding functionality doesn't exist in type-level name resolution, and always results in an ambiguity error:

  • trivially-false-subtrait.rs
  • unstable.rs
  • false-subtrait-after-inference.rs

@Amanieu Amanieu force-pushed the type-supertrait-shadowing branch from 5a9ef14 to 9407a97 Compare June 4, 2026 22:32
@rustbot

rustbot commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@jackh726

jackh726 commented Jun 8, 2026

Copy link
Copy Markdown
Member

I've addressed the review feedback. I couldn't adapt the following tests because the corresponding functionality doesn't exist in type-level name resolution, and always results in an ambiguity error:

* `trivially-false-subtrait.rs`
* `unstable.rs`
* `false-subtrait-after-inference.rs`

@Amanieu hmm for trivially-false-subtrait and false-subtrait-after-inference, these seem important, right? This basically means that associated type resolution doesn't take into account trait bounds but method resolution does...which seems like an important property to match

Generally, it's also interesting to me (and I must have missed this when I was going through) that false-subtrait-after-inference is ambiguous because of an inference variable...certainly for me it's obvious, but for the average user it feels weird. Especially because the error says i32: Foo doesn't hold, which a natural reaction should be "so don't pick it!" (which we don't if it's not an inference var)

Given the above, I'm wondering if may we want to limit that resolution even for the method case right now. Thoughts?

@Amanieu

Amanieu commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

For trivially-false-subtrait there's no way to directly express the thing we're testing:

//@ run-pass

// Make sure we don't prefer a subtrait that we would've otherwise eliminated
// in `consider_probe` during method probing.

use core::mem::size_of;

#![allow(dead_code)]

struct W<T>(T);

trait Upstream {
    fn hello(&self) -> &'static str {
        "upstream"
    }
    type Assoc = i8;
}
impl<T> Upstream for T {}

trait Downstream: Upstream {
    fn hello(&self) -> &'static str {
        "downstream"
    }
    type Assoc = i16;
}
impl<T> Downstream for W<T> where T: Foo {}

trait Foo {}

fn main() {
    let x = W(1i32);
    assert_eq!(x.hello(), "upstream");

    // This doesn't work: accessing type-level associated items requires 
    // specifying the trait name explicitly with <W<i32> as Upstream> or
    // <W<i32> as Downstream>. But then that overrides supertrait shadowing
    // anyways...
    assert_eq!(size_of::<W<i32>::Assoc>(), 1);
    
    // This works but always resolves to the supertrait because that's what's
    // in the bounds of check1.
    check1::<W<i32>>();
    // This fails because W<i32> doesn't implement Downstream
    check2::<W<i32>>();
}

fn check1<T: Upstream>() {
    assert_eq!(size_of::<T::Assoc>(), 1);
}

fn check2<T: Downstream>() {
    assert_eq!(size_of::<T::Assoc>(), 2);
}

Similar issue with false-subtrait-after-inference.

@Amanieu

Amanieu commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

Generally, it's also interesting to me (and I must have missed this when I was going through) that false-subtrait-after-inference is ambiguous because of an inference variable...certainly for me it's obvious, but for the average user it feels weird. Especially because the error says i32: Foo doesn't hold, which a natural reaction should be "so don't pick it!" (which we don't if it's not an inference var)

Given the above, I'm wondering if may we want to limit that resolution even for the method case right now. Thoughts?

That seems difficult to do in the current code structure, which I'm not super familiar with. Additionally, this is really an edge case: you would need to be intentionally shadowing a supertrait item while also having different trait bounds on the subtrait.

@jackh726 jackh726 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is fine - but we need test that shows that the feature needs to be enabled for this to work. (Though I can trivially see that this is true.)

After that, r=me

View changes since this review

@Amanieu

Amanieu commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

This is fine - but we need test that shows that the feature needs to be enabled for this to work. (Though I can trivially see that this is true.)

Which feature are you talking about specifically? I'm not sure which of my comments you're replying to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

I-lang-radar Items that are on lang's radar and will need eventual work or consideration. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants