Skip to content

Commit ca7e27a

Browse files
authored
Rollup merge of #113182 - compiler-errors:rpit-stricter-captures, r=oli-obk
Error when RPITITs' hidden types capture more lifetimes than their trait definitions This implements a stricter set of captures rules for RPITITs. They now may only capture: 1. Lifetimes from the impl header (both the self type and any trait substs -- we may want to restrict just to the self type's lifetimes, but the PR makes that easy to do, too) 2. Lifetimes mentioned by the `impl Trait` in the trait method's definition. Namely, they may not mention lifetimes from the method (early or late) that are not mentioned in the `impl Trait`. cc #105258 which I think was trying to do this too, though I'm not super familiar with what exactly differs from that or why that one was broken. cc #112194 (doesn't fix this issue per se, because it's still an open question, but I think this is objectively better than the status quo, and gets us closer to resolving that issue.) Technically is a fix for the ICE in #108580, but it turns that issue into an error now. We can decide separately whether or not nested RPITITs should capture lifetimes from their parents. r? ``@oli-obk``
2 parents 9082287 + 8ad5ea7 commit ca7e27a

File tree

5 files changed

+283
-56
lines changed

5 files changed

+283
-56
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+116-32
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_middle::ty::{
1919
self, InternalSubsts, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
2020
};
2121
use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
22-
use rustc_span::Span;
22+
use rustc_span::{Span, DUMMY_SP};
2323
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
2424
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
2525
use rustc_trait_selection::traits::{
@@ -651,11 +651,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
651651
let impl_sig = ocx.normalize(
652652
&norm_cause,
653653
param_env,
654-
infcx.instantiate_binder_with_fresh_vars(
655-
return_span,
656-
infer::HigherRankedType,
657-
tcx.fn_sig(impl_m.def_id).subst_identity(),
658-
),
654+
tcx.liberate_late_bound_regions(impl_m.def_id, tcx.fn_sig(impl_m.def_id).subst_identity()),
659655
);
660656
impl_sig.error_reported()?;
661657
let impl_return_ty = impl_sig.output();
@@ -665,9 +661,10 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
665661
// them with inference variables.
666662
// We will use these inference variables to collect the hidden types of RPITITs.
667663
let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_def_id);
668-
let unnormalized_trait_sig = tcx
669-
.liberate_late_bound_regions(
670-
impl_m.def_id,
664+
let unnormalized_trait_sig = infcx
665+
.instantiate_binder_with_fresh_vars(
666+
return_span,
667+
infer::HigherRankedType,
671668
tcx.fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
672669
)
673670
.fold_with(&mut collector);
@@ -760,15 +757,17 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
760757

761758
let mut collected_tys = FxHashMap::default();
762759
for (def_id, (ty, substs)) in collected_types {
763-
match infcx.fully_resolve(ty) {
764-
Ok(ty) => {
760+
match infcx.fully_resolve((ty, substs)) {
761+
Ok((ty, substs)) => {
765762
// `ty` contains free regions that we created earlier while liberating the
766763
// trait fn signature. However, projection normalization expects `ty` to
767764
// contains `def_id`'s early-bound regions.
768765
let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
769766
debug!(?id_substs, ?substs);
770-
let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> =
771-
std::iter::zip(substs, id_substs).collect();
767+
let map: FxHashMap<_, _> = std::iter::zip(substs, id_substs)
768+
.skip(tcx.generics_of(trait_m.def_id).count())
769+
.filter_map(|(a, b)| Some((a.as_region()?, b.as_region()?)))
770+
.collect();
772771
debug!(?map);
773772

774773
// NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound
@@ -793,25 +792,19 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
793792
// same generics.
794793
let num_trait_substs = trait_to_impl_substs.len();
795794
let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len();
796-
let ty = tcx.fold_regions(ty, |region, _| {
797-
match region.kind() {
798-
// Remap all free regions, which correspond to late-bound regions in the function.
799-
ty::ReFree(_) => {}
800-
// Remap early-bound regions as long as they don't come from the `impl` itself.
801-
ty::ReEarlyBound(ebr) if tcx.parent(ebr.def_id) != impl_m.container_id(tcx) => {}
802-
_ => return region,
803-
}
804-
let Some(ty::ReEarlyBound(e)) = map.get(&region.into()).map(|r| r.expect_region().kind())
805-
else {
806-
return ty::Region::new_error_with_message(tcx, return_span, "expected ReFree to map to ReEarlyBound")
807-
};
808-
ty::Region::new_early_bound(tcx, ty::EarlyBoundRegion {
809-
def_id: e.def_id,
810-
name: e.name,
811-
index: (e.index as usize - num_trait_substs + num_impl_substs) as u32,
812-
})
813-
});
814-
debug!(%ty);
795+
let ty = match ty.try_fold_with(&mut RemapHiddenTyRegions {
796+
tcx,
797+
map,
798+
num_trait_substs,
799+
num_impl_substs,
800+
def_id,
801+
impl_def_id: impl_m.container_id(tcx),
802+
ty,
803+
return_span,
804+
}) {
805+
Ok(ty) => ty,
806+
Err(guar) => tcx.ty_error(guar),
807+
};
815808
collected_tys.insert(def_id, ty::EarlyBinder::bind(ty));
816809
}
817810
Err(err) => {
@@ -895,6 +888,97 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'_, 'tcx> {
895888
}
896889
}
897890

891+
struct RemapHiddenTyRegions<'tcx> {
892+
tcx: TyCtxt<'tcx>,
893+
map: FxHashMap<ty::Region<'tcx>, ty::Region<'tcx>>,
894+
num_trait_substs: usize,
895+
num_impl_substs: usize,
896+
def_id: DefId,
897+
impl_def_id: DefId,
898+
ty: Ty<'tcx>,
899+
return_span: Span,
900+
}
901+
902+
impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
903+
type Error = ErrorGuaranteed;
904+
905+
fn interner(&self) -> TyCtxt<'tcx> {
906+
self.tcx
907+
}
908+
909+
fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
910+
if let ty::Alias(ty::Opaque, ty::AliasTy { substs, def_id, .. }) = *t.kind() {
911+
let mut mapped_substs = Vec::with_capacity(substs.len());
912+
for (arg, v) in std::iter::zip(substs, self.tcx.variances_of(def_id)) {
913+
mapped_substs.push(match (arg.unpack(), v) {
914+
// Skip uncaptured opaque substs
915+
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg,
916+
_ => arg.try_fold_with(self)?,
917+
});
918+
}
919+
Ok(self.tcx.mk_opaque(def_id, self.tcx.mk_substs(&mapped_substs)))
920+
} else {
921+
t.try_super_fold_with(self)
922+
}
923+
}
924+
925+
fn try_fold_region(
926+
&mut self,
927+
region: ty::Region<'tcx>,
928+
) -> Result<ty::Region<'tcx>, Self::Error> {
929+
match region.kind() {
930+
// Remap all free regions, which correspond to late-bound regions in the function.
931+
ty::ReFree(_) => {}
932+
// Remap early-bound regions as long as they don't come from the `impl` itself,
933+
// in which case we don't really need to renumber them.
934+
ty::ReEarlyBound(ebr) if self.tcx.parent(ebr.def_id) != self.impl_def_id => {}
935+
_ => return Ok(region),
936+
}
937+
938+
let e = if let Some(region) = self.map.get(&region) {
939+
if let ty::ReEarlyBound(e) = region.kind() { e } else { bug!() }
940+
} else {
941+
let guar = match region.kind() {
942+
ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. })
943+
| ty::ReFree(ty::FreeRegion {
944+
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
945+
..
946+
}) => {
947+
let return_span = if let ty::Alias(ty::Opaque, opaque_ty) = self.ty.kind() {
948+
self.tcx.def_span(opaque_ty.def_id)
949+
} else {
950+
self.return_span
951+
};
952+
self.tcx
953+
.sess
954+
.struct_span_err(
955+
return_span,
956+
"return type captures more lifetimes than trait definition",
957+
)
958+
.span_label(self.tcx.def_span(def_id), "this lifetime was captured")
959+
.span_note(
960+
self.tcx.def_span(self.def_id),
961+
"hidden type must only reference lifetimes captured by this impl trait",
962+
)
963+
.note(format!("hidden type inferred to be `{}`", self.ty))
964+
.emit()
965+
}
966+
_ => self.tcx.sess.delay_span_bug(DUMMY_SP, "should've been able to remap region"),
967+
};
968+
return Err(guar);
969+
};
970+
971+
Ok(ty::Region::new_early_bound(
972+
self.tcx,
973+
ty::EarlyBoundRegion {
974+
def_id: e.def_id,
975+
name: e.name,
976+
index: (e.index as usize - self.num_trait_substs + self.num_impl_substs) as u32,
977+
},
978+
))
979+
}
980+
}
981+
898982
fn report_trait_method_mismatch<'tcx>(
899983
infcx: &InferCtxt<'tcx>,
900984
mut cause: ObligationCause<'tcx>,

tests/ui/impl-trait/in-trait/method-signature-matches.lt.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ LL | fn early<'late, T>(_: &'late ()) {}
55
| - ^^^^^^^^^
66
| | |
77
| | expected type parameter `T`, found `()`
8-
| | help: change the parameter type to match the trait: `&'early T`
8+
| | help: change the parameter type to match the trait: `&T`
99
| this type parameter
1010
|
1111
note: type in trait
1212
--> $DIR/method-signature-matches.rs:53:28
1313
|
1414
LL | fn early<'early, T>(x: &'early T) -> impl Sized;
1515
| ^^^^^^^^^
16-
= note: expected signature `fn(&'early T)`
17-
found signature `fn(&())`
16+
= note: expected signature `fn(&T)`
17+
found signature `fn(&'late ())`
1818

1919
error: aborting due to previous error
2020

Original file line numberDiff line numberDiff line change
@@ -1,16 +1,61 @@
1-
error: `impl` item signature doesn't match `trait` item signature
2-
--> $DIR/signature-mismatch.rs:17:5
1+
error: return type captures more lifetimes than trait definition
2+
--> $DIR/signature-mismatch.rs:36:47
3+
|
4+
LL | fn async_fn<'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a {
5+
| -- this lifetime was captured ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: hidden type must only reference lifetimes captured by this impl trait
8+
--> $DIR/signature-mismatch.rs:17:40
39
|
410
LL | fn async_fn(&self, buff: &[u8]) -> impl Future<Output = Vec<u8>>;
5-
| ----------------------------------------------------------------- expected `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '3`
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= note: hidden type inferred to be `impl Future<Output = Vec<u8>> + 'a`
13+
14+
error: return type captures more lifetimes than trait definition
15+
--> $DIR/signature-mismatch.rs:41:57
16+
|
17+
LL | fn async_fn_early<'a: 'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a {
18+
| -- this lifetime was captured ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
|
20+
note: hidden type must only reference lifetimes captured by this impl trait
21+
--> $DIR/signature-mismatch.rs:18:57
22+
|
23+
LL | fn async_fn_early<'a: 'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>>;
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
= note: hidden type inferred to be `impl Future<Output = Vec<u8>> + 'a`
26+
27+
error: return type captures more lifetimes than trait definition
28+
--> $DIR/signature-mismatch.rs:49:10
29+
|
30+
LL | fn async_fn_multiple<'a, 'b>(
31+
| -- this lifetime was captured
632
...
7-
LL | fn async_fn<'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a {
8-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '2`
33+
LL | ) -> impl Future<Output = Vec<u8>> + Captures2<'a, 'b> {
34+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
35+
|
36+
note: hidden type must only reference lifetimes captured by this impl trait
37+
--> $DIR/signature-mismatch.rs:20:12
38+
|
39+
LL | -> impl Future<Output = Vec<u8>> + Captures<'a>;
40+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41+
= note: hidden type inferred to be `impl Future<Output = Vec<u8>> + Captures2<'a, 'b>`
42+
43+
error[E0309]: the parameter type `T` may not live long enough
44+
--> $DIR/signature-mismatch.rs:58:10
45+
|
46+
LL | ) -> impl Future<Output = Vec<u8>> {
47+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `impl Future<Output = Vec<u8>>` will meet its required lifetime bounds...
48+
|
49+
note: ...that is required by this bound
50+
--> $DIR/signature-mismatch.rs:25:42
51+
|
52+
LL | ) -> impl Future<Output = Vec<u8>> + 'a;
53+
| ^^
54+
help: consider adding an explicit lifetime bound...
955
|
10-
= note: expected signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '3`
11-
found signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '2`
12-
= help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
13-
= help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output
56+
LL | fn async_fn_reduce_outlive<'a, 'b, T: 'a>(
57+
| ++++
1458

15-
error: aborting due to previous error
59+
error: aborting due to 4 previous errors
1660

61+
For more information about this error, try `rustc --explain E0309`.
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,61 @@
1-
error: `impl` item signature doesn't match `trait` item signature
2-
--> $DIR/signature-mismatch.rs:17:5
1+
error: return type captures more lifetimes than trait definition
2+
--> $DIR/signature-mismatch.rs:36:47
3+
|
4+
LL | fn async_fn<'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a {
5+
| -- this lifetime was captured ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: hidden type must only reference lifetimes captured by this impl trait
8+
--> $DIR/signature-mismatch.rs:17:40
39
|
410
LL | fn async_fn(&self, buff: &[u8]) -> impl Future<Output = Vec<u8>>;
5-
| ----------------------------------------------------------------- expected `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '3`
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= note: hidden type inferred to be `impl Future<Output = Vec<u8>> + 'a`
13+
14+
error: return type captures more lifetimes than trait definition
15+
--> $DIR/signature-mismatch.rs:41:57
16+
|
17+
LL | fn async_fn_early<'a: 'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a {
18+
| -- this lifetime was captured ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
|
20+
note: hidden type must only reference lifetimes captured by this impl trait
21+
--> $DIR/signature-mismatch.rs:18:57
22+
|
23+
LL | fn async_fn_early<'a: 'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>>;
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
= note: hidden type inferred to be `impl Future<Output = Vec<u8>> + 'a`
26+
27+
error: return type captures more lifetimes than trait definition
28+
--> $DIR/signature-mismatch.rs:49:10
29+
|
30+
LL | fn async_fn_multiple<'a, 'b>(
31+
| -- this lifetime was captured
632
...
7-
LL | fn async_fn<'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a {
8-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '2`
33+
LL | ) -> impl Future<Output = Vec<u8>> + Captures2<'a, 'b> {
34+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
35+
|
36+
note: hidden type must only reference lifetimes captured by this impl trait
37+
--> $DIR/signature-mismatch.rs:20:12
38+
|
39+
LL | -> impl Future<Output = Vec<u8>> + Captures<'a>;
40+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41+
= note: hidden type inferred to be `impl Future<Output = Vec<u8>> + Captures2<'a, 'b>`
42+
43+
error[E0309]: the parameter type `T` may not live long enough
44+
--> $DIR/signature-mismatch.rs:58:10
45+
|
46+
LL | ) -> impl Future<Output = Vec<u8>> {
47+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `impl Future<Output = Vec<u8>>` will meet its required lifetime bounds...
48+
|
49+
note: ...that is required by this bound
50+
--> $DIR/signature-mismatch.rs:25:42
51+
|
52+
LL | ) -> impl Future<Output = Vec<u8>> + 'a;
53+
| ^^
54+
help: consider adding an explicit lifetime bound...
955
|
10-
= note: expected signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '3`
11-
found signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '2`
12-
= help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
13-
= help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output
56+
LL | fn async_fn_reduce_outlive<'a, 'b, T: 'a>(
57+
| ++++
1458

15-
error: aborting due to previous error
59+
error: aborting due to 4 previous errors
1660

61+
For more information about this error, try `rustc --explain E0309`.

0 commit comments

Comments
 (0)