Description
see https://rust-lang.zulipchat.com/#narrow/stream/315482-t-compiler.2Fetc.2Fopaque-types/topic/non-defining.20uses.20due.20to.20equal.20lifetimes for more details. This did not get a "formal approval" yet but summarizes the previous discussion on zulip.
We currently don't check that the provided lifetimes are unique for RPIT and async function return values in fn check_opaque_type_parameter_valid
. We do check them for TAIT. This currently results in somewhat inconsistent behavior:
#![feature(type_alias_impl_trait)]
trait Trait<'a, 'b> {}
impl Trait<'_, '_> for () {}
type Ok<'a, 'b> = impl Trait<'a, 'b>;
fn ok<'a: 'b, 'b: 'a>() -> Ok<'a, 'b> {
()
}
type Fail1<'a, 'b> = impl Trait<'a, 'b>;
fn fail1<'a: 'b, 'b: 'a>() -> Fail1<'a, 'b> {
(|| {})() //~ ERROR non-defining opaque type use in defining scope
}
type Fail2<'a, 'b> = impl Trait<'a, 'b>;
fn fail2<'a, 'b>(x: &'a &'b &'a ()) -> Fail2<'a, 'b> {
let _: &'a &'b &'a () = x; //~ ERROR non-defining opaque type use in defining scope
}
The issue here is that we don't have unique lifetimes to instantiate the TAITs here, as the lifetimes are required to be equal.
The reason fn ok
works is because nll evaluates lifetime equality based on the constraint graph alone, ignoring the the environment bounds. This is a bug, not a feature.
In #112842 I check type and const arguments for defined RPITs and async returns to be unique params but ignore parameters. This is the case as otherwise the following example breaks
struct Type<'a>(&'a ());
impl<'a> Type<'a> {
// `'b == 'a`
fn do_stuff<'b: 'a>(&'b self) -> impl Trait<'a, 'b> {}
}
Because of the implied bound from the self type and the explicit 'b: 'a
bound 'a
and 'b
are equal. This causes us to use the same region param multiple times while defining the RPIT. What we should instead do here is the following:
- only allow duplicate lifetime params in the defining use iff the params of the opaque they instantiate are required to be equal by bounds of the opaque
- this includes the implied WF bounds of their containing item, i.e. RPITs have the same bounds as the function defining them
This handling should allow us to check the region params for RPIT and async function return types, fixing buggy (but probably not unsound) behavior. It also allowsg the following code with TAIT:
#![feature(type_alias_impl_trait)]
trait Trait<'a, 'b> {}
impl Trait<'_, '_> for () {}
type Tait<'a: 'b, 'b: 'a> = impl Trait<'a, 'b>;
fn foo<'a>() -> Tait<'a, 'a> {}
Landing this change will require an FCP.
cc @rust-lang/initiative-impl-trait @aliemjay
Metadata
Metadata
Assignees
Labels
Type
Projects
Status