Skip to content

Commit

Permalink
Auto merge of #112682 - spastorino:new-rpitit-21, r=compiler-errors
Browse files Browse the repository at this point in the history
Add bidirectional where clauses on RPITIT synthesized GATs

Given the following:

```rust
struct MyStruct<'a, T>(&'a T);

trait MyTrait<'a, T> {
    fn my_fn<'b, 'c, 'd, V>(item: &'c String) -> impl Sized + 'a + 'b + 'c where V: 'b, V: 'd;
}

impl<'a, T> MyTrait<'a, T> for MyStruct<'a, T> {
    fn my_fn<'b, 'c, 'd, V>(_: &'c String) -> impl Sized + 'a + 'b + 'c
    where
        V: 'b,
        V: 'd,
    {
        unimplemented!();
    }
}
```

We need the desugaring to be:

```rust
trait MyTrait<'a, T> {
    type MyFn<'bf, 'df, Vf, 'a2, 'b2, 'c2>: Sized + 'a2 + 'b2 + 'c2 where Vf: 'b2, 'a2: 'a, 'a: 'a2, 'b2: 'bf, 'bf: 'b2;

    fn my_fn<'b, 'c, 'd, V>(item: &'c String) -> MyStruct<'a>::MyFn<'b, 'd, V, 'a, 'b, 'c> where V: 'b, V: 'd {
        type opaque<'a3, 'b3, 'c3>;
    };
}

impl<'a, T> MyIter<'a, T> for MyStruct<'a, T> {
    type MyFn<'bf, 'df, Vf, 'a2, 'b2, 'c2> = impl Sized + 'a2 + 'b2 + 'c2 where Vf: b2, 'a2: 'a, 'a: 'a2, 'b2: 'bf, 'bf: 'b2;

    fn my_fn<'b, 'c, 'd, V>(_: &'c String) -> MyStruct<'a>::MyFn<'a, 'b, 'c, V> where V: 'b, V: 'd {
        type opaque<'a3, 'b3, 'c3>;
        unimplemented!();
    }
}
```

This PR adds the where clauses for the `MyFn` generated GATs.

This is a draft with a very ugly solution so we can make comments over concrete code.

r? `@compiler-errors`
  • Loading branch information
bors committed Jun 29, 2023
2 parents b23a5ad + 6f4a51e commit 3307274
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 104 deletions.
133 changes: 83 additions & 50 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1539,9 +1539,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
);
debug!(?opaque_ty_def_id);

// Contains the new lifetime definitions created for the TAIT (if any).
let mut collected_lifetimes = Vec::new();

// If this came from a TAIT (as opposed to a function that returns an RPIT), we only want
// to capture the lifetimes that appear in the bounds. So visit the bounds to find out
// exactly which ones those are.
Expand All @@ -1558,20 +1555,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
debug!(?lifetimes_to_remap);

self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
let mut new_remapping = FxHashMap::default();
let mut new_remapping = FxHashMap::default();

// If this opaque type is only capturing a subset of the lifetimes (those that appear
// in bounds), then create the new lifetime parameters required and create a mapping
// from the old `'a` (on the function) to the new `'a` (on the opaque type).
collected_lifetimes = lctx.create_lifetime_defs(
opaque_ty_def_id,
&lifetimes_to_remap,
&mut new_remapping,
);
debug!(?collected_lifetimes);
debug!(?new_remapping);
// Contains the new lifetime definitions created for the TAIT (if any).
// If this opaque type is only capturing a subset of the lifetimes (those that appear in
// bounds), then create the new lifetime parameters required and create a mapping from the
// old `'a` (on the function) to the new `'a` (on the opaque type).
let collected_lifetimes =
self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping);
debug!(?collected_lifetimes);
debug!(?new_remapping);

// This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type
// TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`.
let collected_lifetime_mapping: Vec<_> = collected_lifetimes
.iter()
.map(|(node_id, lifetime)| {
let id = self.next_node_id();
let lifetime = self.new_named_lifetime(lifetime.id, id, lifetime.ident);
let def_id = self.local_def_id(*node_id);
(lifetime, def_id)
})
.collect();
debug!(?collected_lifetime_mapping);

self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
// Install the remapping from old to new (if any):
lctx.with_remapping(new_remapping, |lctx| {
// This creates HIR lifetime definitions as `hir::GenericParam`, in the given
Expand Down Expand Up @@ -1610,6 +1618,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let hir_bounds = lctx.lower_param_bounds(bounds, itctx);
debug!(?hir_bounds);

let lifetime_mapping = if in_trait {
self.arena.alloc_from_iter(
collected_lifetime_mapping
.iter()
.map(|(lifetime, def_id)| (**lifetime, *def_id)),
)
} else {
&mut []
};

let opaque_ty_item = hir::OpaqueTy {
generics: self.arena.alloc(hir::Generics {
params: lifetime_defs,
Expand All @@ -1620,6 +1638,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}),
bounds: hir_bounds,
origin,
lifetime_mapping,
in_trait,
};
debug!(?opaque_ty_item);
Expand All @@ -1628,20 +1647,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
})
});

// This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type
// TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`.
let lifetimes =
self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(|(_, lifetime)| {
let id = self.next_node_id();
let l = self.new_named_lifetime(lifetime.id, id, lifetime.ident);
hir::GenericArg::Lifetime(l)
}));
debug!(?lifetimes);

// `impl Trait` now just becomes `Foo<'a, 'b, ..>`.
hir::TyKind::OpaqueDef(
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
lifetimes,
self.arena.alloc_from_iter(
collected_lifetime_mapping
.iter()
.map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
),
in_trait,
)
}
Expand All @@ -1655,7 +1668,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: Span,
opaque_ty_span: Span,
) -> hir::OwnerNode<'hir> {
let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(opaque_ty_item);
let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(self.arena.alloc(opaque_ty_item));
// Generate an `type Foo = impl Trait;` declaration.
trace!("registering opaque type with id {:#?}", opaque_ty_id);
let opaque_ty_item = hir::Item {
Expand Down Expand Up @@ -1983,7 +1996,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let lifetime = Lifetime { id: outer_node_id, ident };
collected_lifetimes.push((inner_node_id, lifetime, Some(inner_res)));
}

debug!(?collected_lifetimes);

// We only want to capture the lifetimes that appear in the bounds. So visit the bounds to
Expand All @@ -1993,22 +2005,36 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let lifetimes_to_remap = lifetime_collector::lifetimes_in_ret_ty(&self.resolver, output);
debug!(?lifetimes_to_remap);

self.with_hir_id_owner(opaque_ty_node_id, |this| {
// If this opaque type is only capturing a subset of the lifetimes (those that appear
// in bounds), then create the new lifetime parameters required and create a mapping
// from the old `'a` (on the function) to the new `'a` (on the opaque type).
collected_lifetimes.extend(
this.create_lifetime_defs(
opaque_ty_def_id,
&lifetimes_to_remap,
&mut new_remapping,
)
// If this opaque type is only capturing a subset of the lifetimes (those that appear in
// bounds), then create the new lifetime parameters required and create a mapping from the
// old `'a` (on the function) to the new `'a` (on the opaque type).
collected_lifetimes.extend(
self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping)
.into_iter()
.map(|(new_node_id, lifetime)| (new_node_id, lifetime, None)),
);
debug!(?collected_lifetimes);
debug!(?new_remapping);
);
debug!(?collected_lifetimes);
debug!(?new_remapping);

// This creates pairs of HIR lifetimes and def_ids. In the given example `type
// TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing the
// new lifetime of the RPIT 'x and the def_id of the lifetime 'x corresponding to
// `TestReturn`.
let collected_lifetime_mapping: Vec<_> = collected_lifetimes
.iter()
.map(|(node_id, lifetime, res)| {
let id = self.next_node_id();
let res = res.unwrap_or(
self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error),
);
let lifetime = self.new_named_lifetime_with_res(id, lifetime.ident, res);
let def_id = self.local_def_id(*node_id);
(lifetime, def_id)
})
.collect();
debug!(?collected_lifetime_mapping);

self.with_hir_id_owner(opaque_ty_node_id, |this| {
// Install the remapping from old to new (if any):
this.with_remapping(new_remapping, |this| {
// We have to be careful to get elision right here. The
Expand Down Expand Up @@ -2063,6 +2089,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
));
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);

let lifetime_mapping = if in_trait {
self.arena.alloc_from_iter(
collected_lifetime_mapping
.iter()
.map(|(lifetime, def_id)| (**lifetime, *def_id)),
)
} else {
&mut []
};

let opaque_ty_item = hir::OpaqueTy {
generics: this.arena.alloc(hir::Generics {
params: generic_params,
Expand All @@ -2073,6 +2109,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}),
bounds: arena_vec![this; future_bound],
origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
lifetime_mapping,
in_trait,
};

Expand All @@ -2096,15 +2133,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
//
// For the "output" lifetime parameters, we just want to
// generate `'_`.
let generic_args = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(
|(_, lifetime, res)| {
let id = self.next_node_id();
let res = res.unwrap_or(
self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error),
);
hir::GenericArg::Lifetime(self.new_named_lifetime_with_res(id, lifetime.ident, res))
},
));
let generic_args = self.arena.alloc_from_iter(
collected_lifetime_mapping
.iter()
.map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
);

// Create the `Foo<...>` reference itself. Note that the `type
// Foo = impl Trait` is, internally, created as a child of the
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2664,6 +2664,10 @@ pub struct OpaqueTy<'hir> {
pub generics: &'hir Generics<'hir>,
pub bounds: GenericBounds<'hir>,
pub origin: OpaqueTyOrigin,
// Opaques have duplicated lifetimes, this mapping connects the original lifetime with the copy
// so we can later generate bidirectional outlives predicates to enforce that these lifetimes
// stay in sync.
pub lifetime_mapping: &'hir [(Lifetime, LocalDefId)],
pub in_trait: bool,
}

Expand Down Expand Up @@ -3315,7 +3319,7 @@ pub enum ItemKind<'hir> {
/// A type alias, e.g., `type Foo = Bar<u8>`.
TyAlias(&'hir Ty<'hir>, &'hir Generics<'hir>),
/// An opaque `impl Trait` type alias, e.g., `type Foo = impl Bar;`.
OpaqueTy(OpaqueTy<'hir>),
OpaqueTy(&'hir OpaqueTy<'hir>),
/// An enum definition, e.g., `enum Foo<A, B> {C<A>, D<B>}`.
Enum(EnumDef<'hir>, &'hir Generics<'hir>),
/// A struct definition, e.g., `struct Foo<A> {x: A}`.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
visitor.visit_ty(ty);
visitor.visit_generics(generics)
}
ItemKind::OpaqueTy(OpaqueTy { ref generics, bounds, .. }) => {
ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => {
visitor.visit_id(item.hir_id());
walk_generics(visitor, generics);
walk_list!(visitor, visit_param_bound, bounds);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2809,7 +2809,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let opaque_ty = tcx.hir().item(item_id);

match opaque_ty.kind {
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin, .. }) => {
let local_def_id = item_id.owner_id.def_id;
// If this is an RPITIT and we are using the new RPITIT lowering scheme, we
// generate the def_id of an associated type for the trait and return as
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
}
}

if let ItemKind::OpaqueTy(hir::OpaqueTy {
if let ItemKind::OpaqueTy(&hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
in_trait,
..
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
Some(tcx.typeck_root_def_id(def_id.to_def_id()))
}
Node::Item(item) => match item.kind {
ItemKind::OpaqueTy(hir::OpaqueTy {
ItemKind::OpaqueTy(&hir::OpaqueTy {
origin:
hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
in_trait,
Expand Down
Loading

0 comments on commit 3307274

Please sign in to comment.