Skip to content

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

Open
@fmease

Description

@fmease
Member

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(); }

Activity

added
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
C-bugCategory: This is a bug.
T-typesRelevant to the types team, which will review and decide on the PR/issue.
on Jun 4, 2025
added
needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.
on Jun 4, 2025
removed
needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.
on Jun 4, 2025
fmease

fmease commented on Jun 4, 2025

@fmease
MemberAuthor
fmease

fmease commented on Jun 4, 2025

@fmease
MemberAuthor

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.

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[/+] on Jun 4, 2025
BoxyUwU

BoxyUwU commented on Jun 5, 2025

@BoxyUwU
Member

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

fmease

fmease commented on Jun 5, 2025

@fmease
MemberAuthor

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.F-inherent_associated_types`#![feature(inherent_associated_types)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    Status

    To Do

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @fmease@BoxyUwU@rustbot

        Issue actions

          If inherent associated type selection fails, we should still consider trait associated type candidates as a fallback · Issue #142006 · rust-lang/rust