Skip to content

If inherent associated type selection fails, we should still consider trait associated type candidates as a fallback #142006

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
fmease opened this issue Jun 4, 2025 · 4 comments
Labels
C-bug Category: This is a bug. F-inherent_associated_types `#![feature(inherent_associated_types)]` 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.

Comments

@fmease
Copy link
Member

fmease commented Jun 4, 2025

For context, applicable inherent associated item candidates shadow trait associated item ones (where applicable means the self type of the candidate impl unifies and its predicates hold). IAT selection does respect that which is good as it follows the behavior of associated functions and constants (in bodies).

However, contrary to inherent associated functions and constants (in bodies), we currently bail out early with a hard error if we fail to select an applicable IAT! Moreover assuming PR #140247 gets merged in its current form, this will extend to candidates in general. Meaning, we'll error early if there are no candidates whatsoever! (See my separate comment for an example).

Ideally, we'd only fail overall if we also failed to find a suitable trait associated type candidate (by means of resolve_type_relative_path). Rephrased, inapplicable IAT candidates should not shadow trait assoc ty candidates.

Examples

Inherent Associated Types

These should compile but currently don't for the reason mentioned above.

#![feature(inherent_associated_types)]

struct Type;
trait Trait { type AssocTy; fn scope(); }

impl Type // *inapplicable* impl candidate (predicate `<String as Copy>` is unsatisfied)
where
    for<'_delay> String: Copy,
{
   type AssocTy = i32; // the IAT
}

impl Trait for Type {
    type AssocTy = ();

    fn scope() {
        let (): Self::AssocTy; //~ ERROR the associated type `AssocTy` exists for `Type`, but its trait bounds were not satisfied
//              ^^^^^^^^^^^^^ Ideally, this would resolve to the *trait* associated type
//                            `<Self as Trait>::AssocTy` (via resolve_type_relative_path/SelfTyAlias)
//                            given the fact that the IAT candidate is *inapplicable*!
    }
}

fn main() { <Type as Trait>::scope(); }
#![feature(inherent_associated_types)]

struct Type<T>(T);
trait Trait { type AssocTy; fn scope(); }

impl Type<u128> { // *inapplicable* impl candidate (`u128` doesn't unify with `()`)
   type AssocTy = i32; // the IAT
}

impl Trait for Type<()> {
    type AssocTy = ();

    fn scope() {
        let (): Self::AssocTy; //~ ERROR associated type `AssocTy` not found for `Type<()>` in the current scope
//              ^^^^^^^^^^^^^ Ideally, this would resolve to the *trait* associated type
//                            `<Self as Trait>::AssocTy` (via resolve_type_relative_path/SelfTyAlias)
//                            given the fact that the IAT candidate is *inapplicable*!
    }
}

fn main() { <Type<()> as Trait>::scope(); }

Inherent Associated Functions & Constants

When resolving associated functions and constants (in bodies), inapplicable inherent candidates do not shadow trait candidates, so the following cases pass compilation as expected and exit with 0 when executed.

Example | IAC&IAF inapplicable (unsatisfied predicate)
//@ run-pass

struct Type;
trait Trait {
    const C: i32; fn f() -> i32;
    fn scope();
}

impl Type // *inapplicable* impl candidate
where
    for<'_delay> String: Copy,
{
   const C: i32 = 0; fn f() -> i32 { 0 }
}

impl Trait for Type {
    const C: i32 = 1; fn f() -> i32 { 1 }
    fn scope() {
        assert_eq!((Self::C, Self::f()), (1, 1)); // OK!
    }
}

fn main() { <Type as Trait>::scope(); }
Example | IAC&IAF inapplicable (ununifiable self ty)
//@ run-pass

struct Type<T>(T);
trait Trait { const C: i32; fn f() -> i32; fn scope(); }

impl Type<u128> { // *inapplicable* impl candidate
    const C: i32 = 0; fn f() -> i32 { 0 }
}

impl Trait for Type<()> {
    const C: i32 = 1; fn f() -> i32 { 1 }

    fn scope() {
         assert_eq!((Self::C, Self::f()), (1, 1)); // OK!
    }
}

fn main() { <Type<()> as Trait>::scope(); }
@fmease fmease added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. C-bug Category: This is a bug. F-inherent_associated_types `#![feature(inherent_associated_types)]` T-types Relevant to the types team, which will review and decide on the PR/issue. labels Jun 4, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jun 4, 2025
@fmease fmease removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jun 4, 2025
@fmease
Copy link
Member Author

fmease commented Jun 4, 2025

cc @BoxyUwU

@fmease
Copy link
Member Author

fmease commented Jun 4, 2025

Moreover assuming PR #140247 gets merged in its current form, this will extend to candidates is general. Meaning, we'll error early if there are no candidates whatsoever!

Due to removal of if candidates.is_empty() { return Ok(None); } in the latest revision of said PR, if I grok things correctly (I haven't checked out / built the branch in its current form), this will regress the following example:

#![feature(inherent_associated_types)]

struct Type;
trait Trait { type AssocTy; fn scope(); }

impl Trait for Type {
    type AssocTy = ();

    fn scope() {
        let (): Self::AssocTy; // this will presumably fail to resolve after PR #140247.
    }
}

fn main() { <Type as Trait>::scope(); }

Notice the lack of any actual inherent associated type!
Meaning: This would be an unreasonable breaking change if — hypothetically — IATs were to be stabilized right after that.

@fmease fmease changed the title If inherent associate type selection fails, we should still consider trait associate type candidates as a fallback If inherent associated type selection fails, we should still consider trait associated type candidates as a fallback Jun 4, 2025
@BoxyUwU
Copy link
Member

BoxyUwU commented Jun 5, 2025

Huh interesting, im surprised we resolve this on stable lol. I thought we only resolves stuff on type parameters.

@fmease
Copy link
Member Author

fmease commented Jun 5, 2025

Yeah when we chatted about if candidates.is_empty() { return Ok(None); } / shadowing and the kinds of self tys inherent and trait assoc tys have in common, I completely forgot about the fact that we support SelfTyAlias for trait assoc tys which can obviously alias a whole class of types ^^' My bad

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. F-inherent_associated_types `#![feature(inherent_associated_types)]` 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
Development

No branches or pull requests

3 participants