---
tags: traits, async, rustc
---
# RPITIT Ahoy (Return position impl trait in traits discussion)
## 2022-11-29
```rust
trait Foo {
type Bar: Trait = impl Trait;
fn foo() -> Self::Bar {
}
}
```
## 2022-11-18
```rust
trait Foo {
fn foo() -> impl Trait;
}
// desugars to
trait Foo {
type Cake: Trait;
fn foo() -> Self::Cake;
}
```
```rust
trait Foo {
fn foo() -> impl Trait {}
}
// desugars to
trait Foo {
type Cake: Trait = impl Trait;
^^^^ same def_id ^^^^^^^^^^ as that
fn foo() -> Self::Cake {}
}
```
## Older stuff
Example:
```rust
impl<'a> ... {
fn bar<'b>(
t: impl for<'c> SomeTrait<'c>
) -> impl Debug + for<'d> SomeTrait<'d> + 'b
where
...
}
```
Version 1 (Niko's ideal):
* we have a visitor that extracts the "free" lifetimes from some piece of AST
* when applied to the bounds `Debug + for<'d> SomeTrait<'d> + 'b`, it would just yield `'b`
* when we are translating the bounds from AST into HIR
* Niko's ideal
* name resolution links from a reference to a lifetime like `'b` over to the definition (in the AST itself)
* when we "lower" binders
* e.g., we start lowering the `fn bar<'b>` method
* we would create the HIR definition
* and have a mapping like
* ast node -> hir definition
* so that we can translate the name resolution result into the HIR
* each time we "lower" a binder, we get a fresh HIR definition
* so in that case:
* when we lower `Debug + for<'d> SomeTrait<'d> + 'b`
* we encounter `for<'d> SomeTrait<'d>`, we create a `'d1` in the HIR and map `'d -> 'd1`.
* How it actually works today:
* we create most def-ids during name resolution
* so they have parents and things
* things are a bit more complex
* an alternative might be
* visitor that visits some ast A that returns a tuple of
* lifetimes that appear free
* lifetimes that are bound within A
* when applied to `Debug + for<'d> SomeTrait<'d> + 'b`, it would yield:
* free lifetimes: `'b`
* bound lifetimes: `'d`
* when we are lowering a RPIT (or RPITIT), we could create a remapping from
* 'b -> 'b1 (as today)
* 'd -> 'd1 (this is the new thing)
* these seem a bit different -- we haven't seen the binder for `'d` yet -- but that's... ok
* there is add'l info that we need to create the generics for the new opaque type
* we need to know which type parameters are in scope
* this includes any APIT parameters
* we need to know the where clauses that are in scope
* this includes the bounds on APIT parameters
to come back to our example...
```rust
impl<'a> ... {
fn bar<'b>(
t: impl for<'c> SomeTrait<'c>
) -> impl Debug + for<'d> SomeTrait<'d> + 'b
where
...
}
```
...for the APIT `impl for<'c> SomeTrait<'c>`, which is roughly equivalent to this...
```rust
impl<'a> ... {
fn bar<'b, T>(
t: T
) -> impl Debug + for<'d> SomeTrait<'d> + 'b
where
T: for<'c> SomeTrait<'c>
}
```
* The "lifetime visitor", when applied to the RPIT bounds, has the following role today:
* it identifies the lifetimes which should be "captured" by the opaque type (i.e., added as parameters to the opaque type)
* If we extend it to return the `'d` lifetime:
* it has the added job of identifying lifetime parameters that will be remapped within the bounds
The APIT example is interesting because the bounds get lowered *twice*. For an RPIT, the bounds only get lowered once.
* The lifetime visitor could also be used for other things
* could apply to the APIT bounds to find out...
* which lifetimes (like `'c`) will need to be mapped to a fresh def-id the second time
```rust
fn filter_fold<T, Acc>(
mut predicate: impl for<'c> FnMut(&'c T) -> bool,
mut fold: impl FnMut(Acc, T) -> Acc,
) -> impl FnMut(Acc, T) -> Acc {
move |acc, item| if predicate(&item) { fold(acc, item) } else { acc }
}
fn foo<T: for<'c> FnMut(&'c u32)>() -> impl Sized {}
//fn foo<T: for<'a> Fn(&'a u8)>() -> impl Sized
```
### niko's proposal
* when we lower a polytraitref
* check if we have remappings installed
* this is the sign that we are lowering the same AST twice
* in the future, this shouldn't be necessary, because we should ALWAYS be creating new def-ids
* if so, create new def-ids for each lifetime introduced by the binder
* these new def-ids will have a different parent than the original def-ids, we could even assert this if we wanted
* introduce a new remapping (layer) from the old def-ids to the new ones
* continue with lowering and it will "just work"
## 2022-06-28
Idea for this:
```rust
fn foo<'a, T>() -> impl Iterator<Item = T::Item>
where
T: Foo<'a>
{}
```
without full existentials, what can we do?
```rust
type foo<T>: Iterator<Item = T::Item>
where
T: Foo<'erased>; // <-- idea: use a special marker lifetime for "something existential", present in the HIR
fn foo<'a, T>() -> foo<T>
where
T: Foo<'a>
{}
trait Foo<'a>: Bar { }
trait Bar { type Item; }
```
* There is currently a query that tries to resolve `T::Item`--
* it does a "special walk" through the predicates
* in this special query to resolve `T::Item`, we allow `'erased` to appear BUT
* if the final result is includes `'erased`, e.g., `<T as Foo<'erased>>::Item`, we issue an error
* if the final result might not include `'erased`, e.g., `<T as Bar>::Item`, that's ok
* We also need the hack that:
* when we ask for the predicates of `foo` (the TAIT), we either return an empty list or we ignore the `'erased` entries or something
* and then we don't try to prove that the final inferred value is well-formed given those where-clauses (because the list is incomplete)
* these parts are blocked on proper existentials
## 2022-06-27
```rust
fn foo<'a, 'b, 'c>() -> impl Sized + 'a + 'c
where
'c: 'b,
'b: 'a,
{}
// * we do not capture 'b
// * but if we ignore both where clauses, we lose the fact that 'c: 'a
```
```rust
trait Foo<'a>: Bar
trait Bar
fn foo<'a, T>() -> impl Sized
where
T: Foo<'a>
{}
// * we do not capture 'a
// * but if we ignore this where-clause, we lose track of the fact that `T: Bar`
```
solutions:
* elaborate the bounds first :-1:
* `where T: Foo<'a>`, elaboration would expand to `where T: Foo<'a>, T: Bar`
* if we filter after that, it works
* three problems:
* we don't elaborate transition lifetime relationships like the first example
* we elaborate after HIR is constructed (too late)
* I think there are more complex scenarios like `for<'a> ..` that would be harder to solve w/ elaboration
* alternative, that a-mir-formality would support :+1: but can't do it yet
* instead of removing `T: Foo<'a>` altogether
* replace with `where exists<'a> T: Foo<'a>`
* you could do the same with the first example
* two problems:
* rustc doesn't support this kind of thing
* and neither does a-mir-formality (yet)
* we do need this kind of support for other use cases
* ignore the bounds :scream:
* when we create this TAIT we flagit as "desugared from a impl Trait"
* and we ignore checking the bounds when we check if the inferred type is a good fit for the TAIT
* this is horrible, but it's actually what we do today, so no *more* horrible (pretty sure)
### connection to WF check
```rust
trait Foo<'a>: Bar
trait Bar
fn foo<'a, T>() -> impl Sized
where
T: Foo<'a>
{}
// becomes
type Foo<MY_T>: Sized
where
MY_T: Foo<'static>;
fn foo<'a, T>() -> Foo<T>
where
T: Foo<'a>
{
None::<T> // returning an `Option<T>`
}
```
Two relevant checks:
* First, `fn foo<'a, T>() -> Foo<T>` has to show that
* `Foo<T>` is well-formed
* which implies `T: Foo<'static>` must hold (that's the where clause on `Foo`)
* which is not true
* Second, after we infer a hidden type `X`
* in this case, `X = Option<MY_T>`
* there is a check in the compiler that `Option<MY_T>: Sized`
* assuming that the where-clauses hold
* in this case, the where-clauses are `MY_T: Foo<'static>`
* these where clause are false, not great
### connection to WF check
```rust
trait Foo<'a> {
}
trait Bar {
type Item;
}
fn foo<'a, T>() -> impl Iterator<Item = T::Item>
where
T: Foo<'a>
{}
```
* How will we solve this problem with `T::Item` -- given that where-clauses have to come from the owner type?
### RPITIT current design
```rust
trait Foo {
fn foo(&self) -> impl Debug;
// ~~~~~~~~~~ how do users name this type?
}
fn do_something<F: Foo>()
where
/* F::foo() returns a Send type */
F::foo(..): Send,
F: Foo<foo(..): Send>, // shorthand
```
### async
```rust=
async fn foo(x: &u32, b: &u64) {}
// desugars to
fn foo<'a, 'b>(x: &'a u32, b: &'b u64) -> impl Future<Output = ()> + 'a + 'b { async move { } }
```
## 2022-04-01
```rust
fn foo<T: Clone>(t: T) -> impl Clone {
t
}
```
desugars to this in HIR:
```rust
fn foo<T: Clone>(t: T) -> Bar<T> {
type Bar<U: Clone> = impl Clone;
t
}
```
in the AST we have...
* [`ast::Fn`][](node_id=N0)
* Generics [`ast::Generics`][]
* [`ast::GenericParam`][](ident=T, node_id=N1)
* ReturnType [`ast::Ty`][](node_id=N2)
* [`TyKind::ImplTrait`][](node_id=N3)
* [`ast::GenericBounds`][]
in the name resolver we have created...
* a DefId D1 for the function
* a DefId D2 for T
* a DefId D3 for the Impl Trait
and we have a map `resolver.node_id_to_def_id` that contains *at least* the def-id for each *declaration*
* N0 -> D1
* N1 -> D2
* N3 -> D3 <-- Niko has mixed feelings about this
we want to generate...
* [`hir::ItemKind::Fn`][]: Item for the function
* this has DefId D1
* [`hir::Generics`][]
* [`hir::GenericParam`][](hir_id = H0, name = T)
* this has DefId D2
* [`hir::GenericBounds`][]
* ... `T: Clone` ... with hir_id H2
* return type is a `hir::Ty` with...
* [`hir::OpaqueDef`](D3, `[T]`)
* [`hir::ItemKind::OpaqueTy`][]
* this has DefId D3 *which is a child of D1*
* [`hir::Generics`][]
* [`hir::GenericParam`][](hir_id = H1, name = U)
* this has (fresh) DefId D4
* [`hir::GenericBounds`][]
* ... `U: Clone` ... with hir_id H3
implementation notes
* each def-id we synthesize during hir lowering has a DUMMY_NODE_ID
* because there is no node-id in the AST that maps to it
* we sometimes lower ast nodes *twice* in order to produce duplicates in the HIR
* but with different hir-id-owners, hence the mapping from node-id to hir-id is distinct and fresh hir-ids are assigned
* also we use the remapping to remap DefId D2 (`T`) to D4 (`U`)
also some kind of def-id maps
[`hir::GenericBounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/type.GenericBounds.html
[`hir::ItemKind::OpaqueTy`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.OpaqueTy.html
[`hir::GenericParam`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.GenericParam.html
[`hir::Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Generics.html
[`hir::ItemKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/enum.ItemKind.html#variant.Fn
[`TyKind::ImplTrait`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.TyKind.html#variant.ImplTrait
[`ast::GenericBounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/type.GenericBounds.html
[`ast::GenericParam`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.GenericParam.html
[`ast::Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.Generics.html
[`ast::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.Fn.html
[`ast::Ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.Ty.html
## 2022-02-23
```rust
fn foo<'a, 'b, T>() -> impl Trait + 'b {
...
}
/// HIR representation
// old is really annoying
fn foo<'a, 'b, T>() -> Anon<'static, 'static, 'b, T> {
#[magically_have_access_to_owner_generics]
type Anon<'b> = impl Trait;
}
// new version
fn foo<'a, 'b, T>() -> Anon<'b, T> {
type Anon<'b, T> = impl Trait;
}
/// Type system representation:
// function signature
fn foo<'a, 'b, T>() -> ty::Opaque(some_def_id, subst: ['static, 'static, 'b, T])
// new case:
fn foo<'a, 'b, T>() -> ty::Opaque(some_def_id, subst: ['b, T])
```
/// RPIT marker
is OpaqueTypeOrigin::FnReturn
## 2022-01-19
Problem:
There is a map in AST lowering
```
{AstNodeId -> HirId}
HirId = (OwnerDefId, RelativeHirId)
```
Idea #1:
```
{OwnerDefId -> {AstNodeId -> RelativeHirId}}
```
Idea #2:
* Conceptually a stack of `{AstNodeId -> RelativeHirId}` maps
* Push when you enter a new owner hir-id
* Pop when you exit
* [There is already a helper that swaps fields out when entering a new owner hir-id](https://github.com/rust-lang/rust/blob/2f004d2d401682e553af3984ebd9a3976885e752/compiler/rustc_ast_lowering/src/lib.rs#L443)
we have to validate that Idea #2 works, maybe there is some reason we keep the cached entries for older HIR nodes.
Step 1
* Add an assertion when looking into the map that the current owner def-id is equal to the owner def-id of the thing you find in the map
*
## 2021-12-21
```rust
// User writes this:
type Foo = (impl Trait, impl Trait);
// Internally, we have:
type Opaque1: Trait; // ItemKind::OpaqueTy
type Opaque2: Trait; // ItemKind::OpaqueTy
type Foo = (Opaque1, Opaque2); // ItemKind::TyAlias
// Ty = TyKind::Tuple([
// TyKind::OpaqueDef(Opaque1),
// TyKind::OpaqueDef(Opaque2)
// ])
// User writes this:
type Foo = impl Trait;
// Internally, we have:
type Opaque1: Trait; // ItemKind::OpaqueTy
type Foo = Opaque1; // ItemKind::TyAlias(<Ty>, _)
// Ty = TyKind::OpaqueDef(Opaque1)
```
`TyKind::OpaqueDef` is a specialized variant of `TyKind::Path` that refers to the (builtin, anonymous) opaque types.
```rust
// User writes this:
type Foo<'a, T> = (impl Trait, impl Trait);
// Internally, we have:
type Opaque1<'a, T>: Trait; // ItemKind::OpaqueTy
type Opaque2<'a, T>: Trait; // ItemKind::OpaqueTy
type Foo<'a, T> = (Opaque1<'a, T>, Opaque2<'a, T>); // ItemKind::TyAlias
// Ty = TyKind::Tuple([
// TyKind::OpaqueDef(Opaque1, &['a, T]),
// TyKind::OpaqueDef(Opaque2, &['a, T])
// ])
```
## ---
```rust=
fn foo<'a, 'b, T, U>(a: &'a i32, b: &'b i32) -> impl MyTrait<T> + 'a
where
'a: 'b,
{
a
}
```
```rust=
OpaqueDef<'a, T, U>
```
## 2021-12-16
```rust
fn foo<'a, 'b, T, U>(a: &'a i32, b: &'b i32) -> impl MyTrait<T> + 'a
where
'a: 'b,
{
a
}
```
`OpaqueDef<T, U, 'a>`
```rust
// not real rust syntax but prob it should be:
type Foo<'a, T, U>: MyTrait<T> + 'a /* = hidden type */;
// ^^^^^^^^ captures
fn foo<'a, 'b, T, U>(a: &'a i32, b: &'b i32) -> Foo<'a, T, U>
where
'a: 'b,
{
a
}
```
```rust
struct Hidden<T: Debug>(Option<T>);
// SUGARY
fn foo<'a, 'b, T, U>(a: &'a i32, b: &'b i32) -> impl MyTrait<T> + 'a
where
T: Debug,
U: Debug,
{
Hidden(None::<(T, U)>)
//Hidden Hidden<(T, U)>
}
// DESUGARED
// not real rust syntax but prob it should be:
type Foo<'a, T, U>: MyTrait<T> + 'a /* = Hidden<(T, U)> */
// ^^^^^^^^ captures
where
T: Debug,
U: Debug;
// ^^^^^^^^ bounds
fn foo<'a, 'b, T, U>(a: &'a i32, b: &'b i32) -> Foo<'a, T, U>
where
'a: 'b,
T: Debug,
{
Hidden(None::<T>)
}
```
---
```rust
fn foo<'a, 'b>(a: &'a i32, b: &'b i32) -> impl Sized + 'a + 'b
where
'a: 'b,
{
a
}
fn bar<'c, 'd>(c: &'c i32, d: &'d i32) -> impl Sized + 'c + 'd
where
'c: 'd,
{
foo(c, d)
}
fn main() {}
```
---
```rust
// Hidden type: Option<T>, which references T
fn foo<T: Clone, U, 'a, 'b>(t: T) -> impl Clone + 'a + 'b
where 'a: 'b { // OpaqueDef<T, U, 'a>
Some(t) // Option<T>
}
```
## 2021-12-07
### Why is the rule that we capture only types and not lifetimes (unless explicitly in the bounds)
```rust
fn outer2(x: &Vec<u32>) -> impl Debug { x.iter().sum() }
fn main() {
let mut something = vec![22, 44];
let z = outer2(&something);
something.push(66); // don't want an error here
println!("{:?}", z);
}
```
tl;dr:
* We try to make "capturing" in the return type explicit
* It happens a lot that most of the input lifetimes are not being used
```rust
fn outer2<T>(x: T) -> impl Debug {
// note that T is not used here
22
}
fn main() {
let mut something = vec![22, 44];
let z = outer2::<&Vec<u32>>(&something); // T = &Vec<u32>, T gets captured
something.push(66); // ERROR
println!("{:?}", z);
}
```
Try to capture the most common cases. This is the "explicit form" for when that doesn't work for you.
```rust
type Outer2 = impl Debug;
fn outer2<T>(x: T) -> Outer2 {
// note that T is not used here
22
}
fn main() {
let mut something = vec![22, 44];
let z = outer2::<&Vec<u32>>(&something); // T = &Vec<u32>, T not captured
something.push(66); // no error
println!("{:?}", z);
}
```
### Solution
[How to manage this](https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/rpit.20refactor)
1. Parsing into AST
2. Name resolution on the AST (produces some kind of map from AST paths to definitions)
3. AST lowering into HIR
4. reads the name resolution results and produces a `Res` in the HIR that maps to a `DefId`
```rust
fn foo<T>() -> impl Debug
where
T: Ord
{ }
// want to produce
type Foo<T1> = impl Debug
where
T1: Ord;
fn foo<T>() -> Foo<T>
where
T: Ord
{ }
```
For example:
* There will be some AST nodes that represent `where T: Ord`
* the AST node for `T` will have a "name resolution result" that maps to `T` on `foo`
* Conceptually, you can model this as:
* `AstNameResolution: Map<AstNode, DefId>` 'more or less'
```rust
fn lower_ast_to_hir(&mut self, path: &ast::Path) -> hir::Path {
let res = match self.ast_name_resolution[path.id] {
SomeVariant(def_id) => Res::DefId(def_id),
...
};
hir::Path { res }
}
```
What we want to do, in terms of that example:
* Produce the same HIR we did before (`where T: Ord`) for use within `foo` function
* Produce the HIR for the `Foo` TAIT, which is *almost* the same but any reference to `T` now is mapped to `T1`
* `where T1: Ord`
#### Option 1.
Lower the AST twice, and have a mapping at that time.
```rust
let f = /* AST for some function that returns impl trait */;
let f_wc_hir = lower_ast_to_hir(f.where_clauses, |x| x); // <-- old code, nothing changed
let tait = /* new def id */;
let tait_generics = copy_of(f.generics);
let map = (f.generics -> tait.generics);
let tait_wc_hir = lower_ast_to_hir(f.where_clauses, |x| map[x]); // <-- old code, nothing changed
```
```rust
fn lower_ast_to_hir(&mut self, path: &ast::Path, map_fn: impl Fn(Res) -> Res) -> hir::Path {
let res = match self.ast_name_resolution[path.id] {
SomeVariant(def_id) => Res::DefId(def_id),
...
};
let res = map_fn(res);
hir::Path { res }
}
```
#### Option 2.
Lower the AST to HIR, then clone the HIR and apply a mapping as we do so.
```rust
let f = /* AST for some function that returns impl trait */;
let f_wc_hir = lower_ast_to_hir(f.where_clauses); // <-- old code, nothing changed
let tait = /* new def id */;
let tait_generics = copy_of(f.generics);
let map = (f.generics -> tait.generics);
let tait_wc_hir = clone_hir(f_wc_hir, |x| map[x]);
```
## 2021-12-02
```rust
fn foo() -> impl Send + 'a {}
```
```rust
type Foo<'a> = impl Send;
fn foo() -> Foo<'a>;
```
```rust
fn foo() -> impl Send + 'a + 'b {}
```
```rust
type Foo<'a, 'b> = impl Send;
// fn foo() -> Foo<'a, 'b>;
```
If you are trying to show this...
```rust
Foo<P0...Pn>: 'x
```
where `Foo` is an opaque type and `P0...Pn` are its generic parameters. You can do that in two ways:
1. If the `bounds(Foo<P0...Pn>)` include some `'b` where `'b: 'a`
2. If `Pi: 'x` for all `i`
Applying to this specific example:
```
Foo<'a, 'b>: 'x
```
In both cases:
* If we know that `'a: 'x` and `'b: 'x` that's good enough
In the first case:
Bounds(Foo<'a, 'b>) = [Send, 'a, 'b]
* If 'a: 'x, that's good enough
* If 'b: 'x, that's good enough
### Case we will have to solve eventually
```rust
trait Foo<'a> {
type Bar;
}
fn foo<'a, T> -> impl PartialEq<T::Bar>
where
T: Foo<'a>
```
problem:
* User wrote `T::Bar` but that's "shorthand" for `<T as Foo<'a>>::Bar`
* so actually `'a` is used here
* we don't (currently, at least) discover this until after AST lowering is complete
* in typeck, specifically
* in the astconv/collect code
* oh wait we don't handle this today
* [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=90252a49fb42d2005a75c7c1c68a04dc)
* well that's simpler:
* create a def-id for each parameter that is explicitly referenced (or just all of them, if that's easier)
* create the opaque type as a sibling with
* generics that are explicitly referenced
* where clauses that only contain names that were explicitly referenced
* "remapped" to the corresponding def-ids
* copy of bounds
* "remapped" to the corresponding def-ids
```rust
fn foo<T>() -> (impl Debug, impl Debug) { }
// both impl Debugs need a copy of the `T`
type Foo1<T>: Debug; // <-- "opaque type"
type Foo2<T>: Debug;
```
```rust
fn foo() -> impl Debug {
()
}
fn is_send<T: Send>(t: T) {
}
fn bar() {
// This SHOULD compile with `-> impl Debug` but should not
// compile with `type Foo = impl Debug` and `-> Foo`
let x = foo();
is_send(x);
}
```
```rust
// should compile
mod m {
pub type X = impl Debug;
pub fn foo() -> X {
()
}
}
fn bar() {
fn is_send<T: Send>(t: T) {
}
let x = m::foo();
is_send(x);
}
```
```rust
// should not compile
//
// when you're inside the scope, you are supposed to either
//
// * rely only on the declared bounds (`Debug`)
//
// or
//
// * fully constrain the type for `X`
pub type X = impl Debug;
pub fn foo() -> X {
()
}
// bar does not say what X should be,
// but it does require X to be Send,
// which is not declared
fn bar() {
fn is_send<T: Send>(t: T) {
}
let x = m::foo();
is_send(x);
}
```
interesting example with an opaque type explicitly inside a function:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=849ac936bdf979f0593f77561183127d
final notes
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=7d09605f8ab8b9e2a656a24f92093b74
## 2021-11-30
```rust
trait Foo {}
impl<'x> Foo for &'x mut i32 {}
trait Bar {
fn bar<'a>(x: &'a mut i32) -> impl Foo;
// desugars to
type bar: Foo;
fn bar<'a>(x: &'a mut i32) -> <Self as Bar>::bar;
}
fn muh<'b, T: Bar>(y: &'b mut i32) {
let a = T::bar(y);
let _b = T::bar(y); // ERROR
let _c = a;
}
fn main() {}
```
```rust
trait Bar<'a> {
fn bar<'b>(x: &'a mut i32) -> impl Foo;
// desugars to
type bar: Foo;
fn bar<'b>(x: &'b mut i32) -> <Self as Bar<'a>>::bar;
// ^^^^
}
```
```rust
trait Bar<'a> {
fn bar<'b>(x: &'a mut i32) -> impl Foo + 'b;
// desugars to
type bar<'b>: Foo + 'b;
fn bar<'b>(x: &'b mut i32) -> <Self as Bar<'a>>::bar<'b>;
// ^^^^ ^^^^
}
impl Foo for Bar<'a> {
type bar<'b> = impl Foo + 'b;
fn bar<'b>(x: &'b mut i32) -> <Self as Bar<'a>>::bar<'b> {
...
}
}
let x: <SomeType as Bar<'a>>::bar<'b> = ..;
let x: OpaqueTyp<'a, 'static, 'b> = ..;
```
## precisely how many lifetime arguments *do* we need for the GAT?
* oli: 4
* https://github.com/rust-lang/rust/issues/60670
* can we get away with less?
```rust
trait Foo {
async fn do_the_thing(&self, data: &MyHashMap<&u32, &u64>);
// Naive:
type do_the_thing<'a, 'b, 'c, 'd>: Future<Output = ()>;
fn do_the_thing<'a, 'b, 'c, 'd>(
&'a self,
data: &'b MyHashMap<&'c u32, &'d u64>
) -> Self::do_the_thing<'a, 'b, 'c, 'd>;
type do_the_thing<'e>: Future<Output = ()>;
fn do_the_thing<'a, 'b, 'c, 'd, 'e>(
&'a self,
data: &'b MyHashMap<&'c u32, &'d u64>
) -> Self::do_the_thing<'e>
where
('a + 'b + 'c + 'd): 'e;
}
impl Foo for ... {
fn do_the_thing<'a>(
&'a self,
_data: &MyHashMap<&u32, &u64>
) -> impl Future<Output = ()> + 'a {
async move { self.bar(); }
}
// desugar to
type do_the_thing<'e>: Future<Output = ()>;
fn do_the_thing<'a, 'e>(
&'a self,
_data: &MyHashMap<&u32, &u64>
) -> Self::do_the_thing<'e>
where 'a: 'e;
}
```
[This has been observed before](https://github.com/rust-lang/rust/issues/60670)...
## What Santiago did thus far
* Creating associated type as a sibling
* In the `generics_of` query...
* making the whole system believe that "this type which is a sibling" is actually a child
* so that you inherit the generics and friends
*
## What happens if we make it a sibling?
* Collect lifetimes, generic parameters, predicates
* also implied bounds
* Change the definition of "defining scope" (maybe)
```rust
```
## What Niko expected
```rust
trait Foo {
fn bar<P0..Pn>(A0..An) -> impl SomeThing
where WC...;
// P0..Pn could be types or lifetimes (including all elided lifetimes)
}
// Step 0
//
// only works for cases with no generics
trait Foo {
type bar: SomeThing
where WC...;
fn bar<P0..Pn>(A0..An) -> <Self as Foo>::bar<P0..Pn>
where WC...;
// P0..Pn could be types or lifetimes (including all elided lifetimes)
}
// Step 1
trait Foo {
type bar<Q0..Qn>: SomeThing
where [Q0...Qn / P0...Pn] WC...;
fn bar<P0..Pn>(A0..An) -> <Self as Foo>::bar<P0..Pn>
where WC...;
// P0..Pn could be types or lifetimes (including all elided lifetimes)
}
```
```rust
fn bar<P0..Pn>(A0..An) -> impl Trait
where WC....
{
}
type __bar__<Q0..Qn> = impl SomeThing
where [Q0...Qn / P0...Pn] WC...;
fn bar<P0..Pn>(A0..An) -> __bar__<P0...Pn>
where WC....
{
}
```
* how to tell which generics "appear in" the bounds?
* same problem as we encounter for const generics!
## Older
```rust
trait Foo {
fn foo<'a, T>() -> impl Send;
}
```
would lower into ...
```rust
trait Foo {
type foo<...>: Send;
fn foo<'a, T>() -> Self::foo<...>;
}
```
Should we make the associated type a sibling of the function or a child?.
```rust
trait Foo {
type foo::rpit<'a> = impl Trait<'a, T>;
fn foo<'a, T>() -> foo::rpit<'a>;
}
```
The following example using a generic param should work but unless we carry over `I` it ICEs.
```rust
// check-pass
trait Parser {
type Input;
}
trait Parseable {
fn parser<I>() -> impl Parser<Input = I>;
}
fn main() {}
```
```
[nix-shell:~/src/oss/rust2]$ RUST_BACKTRACE=1 ./build/x86_64-unknown-linux-gnu/stage1/bin/rustc --edition 2018 src/test/ui/impl-trait/rpitit/generic_bounds.rs
error: internal compiler error: compiler/rustc_middle/src/ty/subst.rs:534:17: type parameter `O/#2` (O/2) out of range when substituting, substs=[Self]
thread 'rustc' panicked at 'Box<dyn Any>', /home/spastorino/src/oss/rust2/compiler/rustc_errors/src/lib.rs:1115:9
stack backtrace:
0: std::panicking::begin_panic::<rustc_errors::ExplicitBug>
at ./library/std/src/panicking.rs:525:12
1: std::panic::panic_any::<rustc_errors::ExplicitBug>
at ./library/std/src/panic.rs:57:5
2: <rustc_errors::HandlerInner>::span_bug::<rustc_span::span_encoding::Span>
at ./compiler/rustc_errors/src/lib.rs:1115:9
```
The following example using a lifetime param should work but it fails ...
```rust
trait Parser {
type Input;
}
trait Parseable {
fn parser<'a>() -> impl Parser<Input = &'a str> + 'a;
}
fn main() {}
```
```
error[E0107]: missing generics for associated type `Parseable::{opaque#0}`
|
note: associated type defined here, with 1 lifetime parameter: `'a`
help: add missing lifetime argument
|
1 | <'a>// check-pass
| ++++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0107`.
```
## How to implement incrementally
`compiler\rustc_typeck\src\collect.rs:1374`
```rust
fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
use rustc_hir::*;
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
let node = tcx.hir().get(hir_id);
let parent_def_id = match node {
Node::TraitItem(_) => {
// Check for associate type from impl trait,
// get appropriate associated function as the parent
}
// ...
```
Could probably store the DefId here to start with:
`compiler\rustc_hir\src\hir.rs:2016`
```rust
pub enum TraitItemKind<'hir> {
// ...
Type(GenericBounds<'hir>, Option<&'hir Ty<'hir>>, /* new */ Option<DefId>),
```
DefId should be handled in the same way as `fn_def_id` in `lower_opaque_impl_trait` in `compiler\rustc_ast_lowering\src\lib.rs`.
Specifically, add a new parameter to `lower_impl_trait_in_trait` that's passed to the `TraitItem`, the method `DefId` also needs adding to `ImplTraitContext::ReturnPositionInTrait`.
# Impls
```rust
trait Foo {}
impl<'x> Foo for &'x i32 {}
trait Bar {
fn bar<'a>(x: &'a i32) -> impl Foo;
}
impl Bar for () {
fn bar<'a>(x: &'a i32) -> impl Foo {
x // should get rejected, no `'a` in the `impl Foo`
}
}
```
```rust
trait Foo {}
impl<'x> Foo for &'x mut i32 {}
trait Bar {
type ty#bar<'xx, 'yy>; // 'xx is always 'static, but there is no 'yy, because `impl Foo` has no lifetimes mentioned
fn bar<'a>(x: &'a mut i32) -> impl Foo;
}
fn muh<'b, T: Bar>(y: &'b mut i32) {
let a: T::ty#bar::<'b> = T::bar::<'b>(y);
let b: T::ty#bar::<'b> = T::bar::<'b>(y); // ERROR
let c = a;
}
```
regular impl trait:
```rust
fn bar<'a>(x: &'a mut i32) -> impl Foo {
()
}
// same, but with TAIT
type Bar<'a> = impl Foo;
fn bar2<'a>(x: &'a mut i32) -> Bar<'static> {
()
}
fn foo<'a>(x: &'a mut i32) -> impl Foo + 'a {
x
}
// same, but with TAIT
type Bar2<'a, 'aa> = impl Foo + 'aa;
fn foo2<'a>(x: &'a mut i32) -> Bar2<'static, 'a> {
x
}
```