Skip to content

Commit b969b83

Browse files
committed
Auto merge of rust-lang#113704 - compiler-errors:rpitit-assumed-wf-inherit, r=spastorino
Make RPITITs inherit the `assumed_wf_types` of their parent method ... and then move the RPITIT well-formedness check to just use the regular logic of wfchecking an associated type. --- We need to inherit the `assumed_wf_types` of the RPITIT's parent function in order for the given code to be considered well-formed: ```rust trait Foo { fn bar<'a, T>(_: &'a T) -> impl Iterator<Output = &'a T>; } ``` Since for `&'a T` to be WF, we need `T: 'a`. In order for this to work for late-bound lifetimes, we need to do some additional mapping of any late-bound lifetimes captured by these assumed wf types. This is because within the body of the function (and thus in the `assumed_wf_types`), they're represented as `ReFree` variants of the original late-bound lifetimes declared in the function's generics, but in the RPITIT's GAT, they're represented as "reified" `ReEarlyBound` vars (duplicated during opaque type lowering). Luckily, the mapping between these two is already [stored in the opaque](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.OpaqueTy.html#structfield.lifetime_mapping). Fixes rust-lang#113796
2 parents 32303b2 + 744e770 commit b969b83

File tree

13 files changed

+151
-117
lines changed

13 files changed

+151
-117
lines changed

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+11-88
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,17 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
284284
};
285285
check_object_unsafe_self_trait_by_name(tcx, trait_item);
286286
check_associated_item(tcx, def_id, span, method_sig);
287+
288+
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
289+
for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) {
290+
check_associated_item(
291+
tcx,
292+
assoc_ty_def_id.expect_local(),
293+
tcx.def_span(assoc_ty_def_id),
294+
None,
295+
);
296+
}
297+
}
287298
}
288299

289300
/// Require that the user writes where clauses on GATs for the implicit
@@ -1466,13 +1477,6 @@ fn check_fn_or_method<'tcx>(
14661477

14671478
check_where_clauses(wfcx, span, def_id);
14681479

1469-
check_return_position_impl_trait_in_trait_bounds(
1470-
wfcx,
1471-
def_id,
1472-
sig.output(),
1473-
hir_decl.output.span(),
1474-
);
1475-
14761480
if sig.abi == Abi::RustCall {
14771481
let span = tcx.def_span(def_id);
14781482
let has_implicit_self = hir_decl.implicit_self != hir::ImplicitSelfKind::None;
@@ -1507,87 +1511,6 @@ fn check_fn_or_method<'tcx>(
15071511
}
15081512
}
15091513

1510-
/// Basically `check_associated_type_bounds`, but separated for now and should be
1511-
/// deduplicated when RPITITs get lowered into real associated items.
1512-
#[tracing::instrument(level = "trace", skip(wfcx))]
1513-
fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
1514-
wfcx: &WfCheckingCtxt<'_, 'tcx>,
1515-
fn_def_id: LocalDefId,
1516-
fn_output: Ty<'tcx>,
1517-
span: Span,
1518-
) {
1519-
let tcx = wfcx.tcx();
1520-
let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) else {
1521-
return;
1522-
};
1523-
if assoc_item.container != ty::AssocItemContainer::TraitContainer {
1524-
return;
1525-
}
1526-
fn_output.visit_with(&mut ImplTraitInTraitFinder {
1527-
wfcx,
1528-
fn_def_id,
1529-
depth: ty::INNERMOST,
1530-
seen: FxHashSet::default(),
1531-
});
1532-
}
1533-
1534-
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): Even with the new lowering
1535-
// strategy, we can't just call `check_associated_item` on the new RPITITs,
1536-
// because tests like `tests/ui/async-await/in-trait/implied-bounds.rs` will fail.
1537-
// That's because we need to check that the bounds of the RPITIT hold using
1538-
// the special args that we create during opaque type lowering, otherwise we're
1539-
// getting a bunch of early bound and free regions mixed up... Haven't looked too
1540-
// deep into this, though.
1541-
struct ImplTraitInTraitFinder<'a, 'tcx> {
1542-
wfcx: &'a WfCheckingCtxt<'a, 'tcx>,
1543-
fn_def_id: LocalDefId,
1544-
depth: ty::DebruijnIndex,
1545-
seen: FxHashSet<DefId>,
1546-
}
1547-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
1548-
type BreakTy = !;
1549-
1550-
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<!> {
1551-
let tcx = self.wfcx.tcx();
1552-
if let ty::Alias(ty::Opaque, unshifted_opaque_ty) = *ty.kind()
1553-
&& self.seen.insert(unshifted_opaque_ty.def_id)
1554-
&& let Some(opaque_def_id) = unshifted_opaque_ty.def_id.as_local()
1555-
&& let origin = tcx.opaque_type_origin(opaque_def_id)
1556-
&& let hir::OpaqueTyOrigin::FnReturn(source) | hir::OpaqueTyOrigin::AsyncFn(source) = origin
1557-
&& source == self.fn_def_id
1558-
{
1559-
let opaque_ty = tcx.fold_regions(unshifted_opaque_ty, |re, _depth| {
1560-
match re.kind() {
1561-
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReError(_) | ty::ReStatic => re,
1562-
r => bug!("unexpected region: {r:?}"),
1563-
}
1564-
});
1565-
for (bound, bound_span) in tcx
1566-
.explicit_item_bounds(opaque_ty.def_id)
1567-
.iter_instantiated_copied(tcx, opaque_ty.args)
1568-
{
1569-
let bound = self.wfcx.normalize(bound_span, None, bound);
1570-
self.wfcx.register_obligations(traits::wf::predicate_obligations(
1571-
self.wfcx.infcx,
1572-
self.wfcx.param_env,
1573-
self.wfcx.body_def_id,
1574-
bound.as_predicate(),
1575-
bound_span,
1576-
));
1577-
// Set the debruijn index back to innermost here, since we already eagerly
1578-
// shifted the args that we use to generate these bounds. This is unfortunately
1579-
// subtly different behavior than the `ImplTraitInTraitFinder` we use in `param_env`,
1580-
// but that function doesn't actually need to normalize the bound it's visiting
1581-
// (whereas we have to do so here)...
1582-
let old_depth = std::mem::replace(&mut self.depth, ty::INNERMOST);
1583-
bound.visit_with(self);
1584-
self.depth = old_depth;
1585-
}
1586-
}
1587-
ty.super_visit_with(self)
1588-
}
1589-
}
1590-
15911514
const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \
15921515
`self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
15931516
of the previous types except `Self`)";

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ provide! { tcx, def_id, other, cdata,
246246
debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy);
247247
cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index)
248248
}
249+
assumed_wf_types_for_rpitit => { table }
249250
collect_return_position_impl_trait_in_trait_tys => {
250251
Ok(cdata
251252
.root

compiler/rustc_metadata/src/rmeta/encoder.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
15601560
}
15611561
if let Some(rpitit_info) = item.opt_rpitit_info {
15621562
record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info);
1563+
if matches!(rpitit_info, ty::ImplTraitInTraitData::Trait { .. }) {
1564+
record_array!(
1565+
self.tables.assumed_wf_types_for_rpitit[def_id]
1566+
<- self.tcx.assumed_wf_types_for_rpitit(def_id)
1567+
);
1568+
}
15631569
}
15641570
}
15651571

compiler/rustc_metadata/src/rmeta/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ define_tables! {
457457
trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, ty::EarlyBinder<Ty<'static>>>>>,
458458
doc_link_resolutions: Table<DefIndex, LazyValue<DocLinkResMap>>,
459459
doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>,
460+
assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>,
460461
}
461462

462463
#[derive(TyEncodable, TyDecodable)]

compiler/rustc_middle/src/query/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,13 @@ rustc_queries! {
885885
desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) }
886886
}
887887

888+
/// We need to store the assumed_wf_types for an RPITIT so that impls of foreign
889+
/// traits with return-position impl trait in traits can inherit the right wf types.
890+
query assumed_wf_types_for_rpitit(key: DefId) -> &'tcx [(Ty<'tcx>, Span)] {
891+
desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) }
892+
separate_provide_extern
893+
}
894+
888895
/// Computes the signature of the function.
889896
query fn_sig(key: DefId) -> ty::EarlyBinder<ty::PolyFnSig<'tcx>> {
890897
desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) }

compiler/rustc_ty_utils/src/implied_bounds.rs

+85-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1+
use rustc_data_structures::fx::FxHashMap;
12
use rustc_hir as hir;
23
use rustc_hir::def::DefKind;
34
use rustc_hir::def_id::LocalDefId;
5+
use rustc_middle::middle::resolve_bound_vars as rbv;
46
use rustc_middle::query::Providers;
57
use rustc_middle::ty::{self, Ty, TyCtxt};
68
use rustc_span::Span;
79
use std::iter;
810

911
pub fn provide(providers: &mut Providers) {
10-
*providers = Providers { assumed_wf_types, ..*providers };
12+
*providers = Providers {
13+
assumed_wf_types,
14+
assumed_wf_types_for_rpitit: |tcx, def_id| {
15+
assert!(tcx.is_impl_trait_in_trait(def_id.to_def_id()));
16+
tcx.assumed_wf_types(def_id)
17+
},
18+
..*providers
19+
};
1120
}
1221

1322
fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] {
@@ -42,6 +51,81 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
4251
let mut impl_spans = impl_spans(tcx, def_id);
4352
tcx.arena.alloc_from_iter(tys.into_iter().map(|ty| (ty, impl_spans.next().unwrap())))
4453
}
54+
DefKind::AssocTy if let Some(data) = tcx.opt_rpitit_info(def_id.to_def_id()) => match data {
55+
ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => {
56+
let hir::OpaqueTy { lifetime_mapping, .. } =
57+
*tcx.hir().expect_item(opaque_def_id.expect_local()).expect_opaque_ty();
58+
// We need to remap all of the late-bound lifetimes in theassumed wf types
59+
// of the fn (which are represented as ReFree) to the early-bound lifetimes
60+
// of the RPITIT (which are represented by ReEarlyBound owned by the opaque).
61+
// Luckily, this is very easy to do because we already have that mapping
62+
// stored in the HIR of this RPITIT.
63+
//
64+
// Side-note: We don't really need to do this remapping for early-bound
65+
// lifetimes because they're already "linked" by the bidirectional outlives
66+
// predicates we insert in the `explicit_predicates_of` query for RPITITs.
67+
let mut mapping = FxHashMap::default();
68+
let generics = tcx.generics_of(def_id);
69+
for &(lifetime, new_early_bound_def_id) in
70+
lifetime_mapping.expect("expected lifetime mapping for RPITIT")
71+
{
72+
if let Some(rbv::ResolvedArg::LateBound(_, _, def_id)) =
73+
tcx.named_bound_var(lifetime.hir_id)
74+
{
75+
let name = tcx.hir().name(lifetime.hir_id);
76+
let index = generics
77+
.param_def_id_to_index(tcx, new_early_bound_def_id.to_def_id())
78+
.unwrap();
79+
mapping.insert(
80+
ty::Region::new_free(
81+
tcx,
82+
fn_def_id,
83+
ty::BoundRegionKind::BrNamed(def_id, name),
84+
),
85+
ty::Region::new_early_bound(
86+
tcx,
87+
ty::EarlyBoundRegion {
88+
def_id: new_early_bound_def_id.to_def_id(),
89+
index,
90+
name,
91+
},
92+
),
93+
);
94+
}
95+
}
96+
// FIXME: This could use a real folder, I guess.
97+
let remapped_wf_tys = tcx.fold_regions(
98+
tcx.assumed_wf_types(fn_def_id.expect_local()).to_vec(),
99+
|region, _| {
100+
// If `region` is a `ReFree` that is captured by the
101+
// opaque, remap it to its corresponding the early-
102+
// bound region.
103+
if let Some(remapped_region) = mapping.get(&region) {
104+
*remapped_region
105+
} else {
106+
region
107+
}
108+
},
109+
);
110+
tcx.arena.alloc_from_iter(remapped_wf_tys)
111+
}
112+
// Assumed wf types for RPITITs in an impl just inherit (and instantiate)
113+
// the assumed wf types of the trait's RPITIT GAT.
114+
ty::ImplTraitInTraitData::Impl { .. } => {
115+
let impl_def_id = tcx.local_parent(def_id);
116+
let rpitit_def_id = tcx.associated_item(def_id).trait_item_def_id.unwrap();
117+
let args = ty::GenericArgs::identity_for_item(tcx, def_id).rebase_onto(
118+
tcx,
119+
impl_def_id.to_def_id(),
120+
tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity().args,
121+
);
122+
tcx.arena.alloc_from_iter(
123+
ty::EarlyBinder::bind(tcx.assumed_wf_types_for_rpitit(rpitit_def_id))
124+
.iter_instantiated_copied(tcx, args)
125+
.chain(tcx.assumed_wf_types(impl_def_id).into_iter().copied()),
126+
)
127+
}
128+
},
45129
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
46130
DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) {
47131
DefKind::TyAlias => ty::List::empty(),

compiler/rustc_ty_utils/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#![feature(assert_matches)]
99
#![feature(iterator_try_collect)]
1010
#![feature(let_chains)]
11+
#![feature(if_let_guard)]
1112
#![feature(never_type)]
1213
#![feature(box_patterns)]
1314
#![recursion_limit = "256"]

tests/ui/async-await/in-trait/async-generics-and-bounds.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ error[E0311]: the parameter type `U` may not live long enough
44
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
55
| ^^^^^^^
66
|
7-
note: the parameter type `U` must be valid for the anonymous lifetime defined here...
7+
note: the parameter type `U` must be valid for the anonymous lifetime as defined here...
88
--> $DIR/async-generics-and-bounds.rs:12:18
99
|
1010
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
11-
| ^^^^^
11+
| ^
1212
note: ...so that the reference type `&(T, U)` does not outlive the data it points at
1313
--> $DIR/async-generics-and-bounds.rs:12:28
1414
|
@@ -21,11 +21,11 @@ error[E0311]: the parameter type `T` may not live long enough
2121
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
2222
| ^^^^^^^
2323
|
24-
note: the parameter type `T` must be valid for the anonymous lifetime defined here...
24+
note: the parameter type `T` must be valid for the anonymous lifetime as defined here...
2525
--> $DIR/async-generics-and-bounds.rs:12:18
2626
|
2727
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
28-
| ^^^^^
28+
| ^
2929
note: ...so that the reference type `&(T, U)` does not outlive the data it points at
3030
--> $DIR/async-generics-and-bounds.rs:12:28
3131
|

tests/ui/async-await/in-trait/async-generics.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ error[E0311]: the parameter type `U` may not live long enough
44
LL | async fn foo(&self) -> &(T, U);
55
| ^^^^^^^
66
|
7-
note: the parameter type `U` must be valid for the anonymous lifetime defined here...
7+
note: the parameter type `U` must be valid for the anonymous lifetime as defined here...
88
--> $DIR/async-generics.rs:9:18
99
|
1010
LL | async fn foo(&self) -> &(T, U);
11-
| ^^^^^
11+
| ^
1212
note: ...so that the reference type `&(T, U)` does not outlive the data it points at
1313
--> $DIR/async-generics.rs:9:28
1414
|
@@ -21,11 +21,11 @@ error[E0311]: the parameter type `T` may not live long enough
2121
LL | async fn foo(&self) -> &(T, U);
2222
| ^^^^^^^
2323
|
24-
note: the parameter type `T` must be valid for the anonymous lifetime defined here...
24+
note: the parameter type `T` must be valid for the anonymous lifetime as defined here...
2525
--> $DIR/async-generics.rs:9:18
2626
|
2727
LL | async fn foo(&self) -> &(T, U);
28-
| ^^^^^
28+
| ^
2929
note: ...so that the reference type `&(T, U)` does not outlive the data it points at
3030
--> $DIR/async-generics.rs:9:28
3131
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// check-pass
2+
// edition: 2021
3+
// issue: 113796
4+
5+
#![feature(async_fn_in_trait)]
6+
7+
trait AsyncLendingIterator {
8+
type Item<'a>
9+
where
10+
Self: 'a;
11+
12+
async fn next(&mut self) -> Option<Self::Item<'_>>;
13+
}
14+
15+
struct Lend<I>(I);
16+
impl<I> AsyncLendingIterator for Lend<I> {
17+
type Item<'a> = &'a I
18+
where
19+
Self: 'a;
20+
21+
// Checking that the synthetic `<Self as AsyncLendingIterator>::next()` GAT
22+
// is well-formed requires being able to assume the WF types of `next`.
23+
24+
async fn next(&mut self) -> Option<Self::Item<'_>> {
25+
todo!()
26+
}
27+
}
28+
29+
fn main() {}

tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs

-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ impl<'a, I: 'a + Iterable> Iterable for &'a I {
1717
//~^ ERROR impl has stricter requirements than trait
1818

1919
fn iter(&self) -> impl 'a + Iterator<Item = I::Item<'a>> {
20-
//~^ ERROR the type `&'a I` does not fulfill the required lifetime
2120
(*self).iter()
2221
}
2322
}

tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr

+2-15
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,6 @@ help: copy the `where` clause predicates from the trait
1212
LL | where Self: 'b;
1313
| ~~~~~~~~~~~~~~
1414

15-
error[E0477]: the type `&'a I` does not fulfill the required lifetime
16-
--> $DIR/bad-item-bound-within-rpitit.rs:19:23
17-
|
18-
LL | fn iter(&self) -> impl 'a + Iterator<Item = I::Item<'a>> {
19-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20-
|
21-
note: type must outlive the anonymous lifetime as defined here
22-
--> $DIR/bad-item-bound-within-rpitit.rs:10:28
23-
|
24-
LL | fn iter(&self) -> impl '_ + Iterator<Item = Self::Item<'_>>;
25-
| ^^
26-
27-
error: aborting due to 2 previous errors
15+
error: aborting due to previous error
2816

29-
Some errors have detailed explanations: E0276, E0477.
30-
For more information about an error, try `rustc --explain E0276`.
17+
For more information about this error, try `rustc --explain E0276`.

0 commit comments

Comments
 (0)