Skip to content
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

super trait bounds can result in unconstrained regions #136547

Open
lcnr opened this issue Feb 4, 2025 · 2 comments
Open

super trait bounds can result in unconstrained regions #136547

lcnr opened this issue Feb 4, 2025 · 2 comments
Assignees
Labels
A-associated-items Area: Associated items (types, constants & functions) C-enhancement Category: An issue proposing an enhancement or a PR with one. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@lcnr
Copy link
Contributor

lcnr commented Feb 4, 2025

trait Super {
    type SAssoc;
}
trait Trait<'a>: Super<SAssoc = <Self as Trait<'a>>::TAssoc> {
    type TAssoc;
}
fn hr_where_bound<T: for<'a> Trait<'a>>() {}

T: for<'a> Trait<'a>> currently elaborates to T: for<'a> Super<SAssoc = <T as Trait<'a>>::TAssoc>. THe projection clause uses 'a in the term without constraining it in the alias. Manually writing such a where-clause results in an error: https://rust.godbolt.org/z/d8G6GvMbe

error[E0582]: binding for associated type `SAssoc` references lifetime `'a`, which does not appear in the trait input types
  --> <source>:10:22
   |
10 |     T: for<'a> Super<SAssoc = <T as Trait<'a>>::TAssoc>,
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

One can still implement and use Trait as long as TAssoc does not mention 'a. People use such super trait bounds in the wild, as discovered by a crater run in #136303.

Supporting unconstrained regions is difficult as it causes normalization of <T as SUper>::SAssoc to use a different unconstrained region variable each time. This can prevent us from proving type outlive bounds, as they rely on region equality when applying assumptions:

fn test<'a, T: 'a>() {}
fn unconstrained_lt1<'arg, T: for<'a> Trait<'a>>(x: &'arg <T as Super>::SAssoc) {
    test::<'arg, <T as Super>::SAssoc>();
    //~^ ERROR `<T as Trait<'_>>::TAssoc` does not live long enough
}


// Has an implied `<T as Super>::SAssoc: 'arg` bound.
struct Struct<'arg, T: Super>(&'arg <T as Super>::SAssoc);
fn unconstrained_lt2<'arg, T: for<'a> Trait<'a>>(x: &'arg <T as Super>::SAssoc) {
    let _ = Struct::<T>(x);
    //~^ ERROR `<T as Trait<'_>>::TAssoc` does not live long enough
}

I've discussed this issue with @compiler-errors and believe that we need to keep supporting such code as it's 'reasonable' and forbidding it results in significant crater breakage.

Our current idea is to encode "One can still implement and use Trait as long as TAssoc does not mention 'a" in the type system by adding support for bivariant region parameters on associated types. We would then change 'a to be bivariant in <T as Trait<'a>>::TAssoc and ignore it when checking outlives bounds. We'd still get unconstrained regions this way, but they should no longer cause issues.

@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Feb 4, 2025
@lcnr lcnr added C-enhancement Category: An issue proposing an enhancement or a PR with one. T-types Relevant to the types team, which will review and decide on the PR/issue. A-associated-items Area: Associated items (types, constants & functions) and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Feb 4, 2025
@lcnr lcnr pinned this issue Feb 4, 2025
@lcnr lcnr unpinned this issue Feb 4, 2025
@aliemjay
Copy link
Member

aliemjay commented Feb 5, 2025

I’ve encountered this issue before with implied bounds and was fairly certain that I had opened a ticket for it, but I couldn’t find one when checked.

Our current idea is to encode "One can still implement and use Trait as long as TAssoc does not mention 'a" in the type system by adding support for bivariant region parameters on associated types. We would then change 'a to be bivariant in <T as Trait<'a>>::TAssoc and ignore it when checking outlives bounds.

The issue is independent of TAssoc:

trait Super { type SAssoc; }
trait Trait<'a>: Super<SAssoc = &'a ()> {}
fn hr_where_bound<T: for<'a> Trait<'a>>() {}

How the proposed solution deals with the code above?

@lcnr
Copy link
Contributor Author

lcnr commented Feb 5, 2025

How the proposed solution deals with the code above?

Good point. There are two ways to implement trait here

impl<'a> Super for &'a () {
    type SAssoc = &'a ();
}
impl<'a> Trait<'a> for &'a () {}

impl Super for () {
    type SAssoc = &'static ();
}
impl Trait<'static> for () {}

With both of these impls T: for<'a> Trait<'a> is unsatisfiable. We can probably not do anything here and just accept the broken type outlives constraints. I don't think such unconstrained regions are unsound, they are just difficult to support correctly. The reason this is a problem right now is that it makes it significantly more difficult to compute implied bounds outside of mir borrowck, as we rely on the normalized signature being used by the implied bounds computation being the same as the normalized signature used in borrowck.

We could check that elaboration does not result in unconstrained regions outside of aliases? Checking that elaboration does not result in unconstrained regions being mentioned in the term at all also breaks the following code as the foo where-clauses elaborates to for<'a> T: FnOnce<I::Item<'a>> -> <Self as FnOnceDup<I::Item<'a>>>::Output:

trait LendingIterator {
    type Item<'a>;
}

trait FnOnceDup<Arg>: FnOnce(Arg) -> <Self as FnOnceDup<Arg>>::Output {
    type Output;
}

fn foo<T>()
where
    for<'a> T: FnOnceDup<I::Item<'a>>,

That doesn't seem too helpful and kinda hacky however 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-items Area: Associated items (types, constants & functions) C-enhancement Category: An issue proposing an enhancement or a PR with one. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants