Skip to content

Commit ddb98e0

Browse files
authored
Rollup merge of #105254 - cjgillot:issue-105251, r=oli-obk
Recurse into nested impl-trait when computing variance. Fixes #105251
2 parents 7d8e329 + 44948d1 commit ddb98e0

File tree

3 files changed

+66
-4
lines changed

3 files changed

+66
-4
lines changed

compiler/rustc_hir_analysis/src/variance/mod.rs

+38-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use rustc_arena::DroplessArena;
77
use rustc_hir::def::DefKind;
88
use rustc_hir::def_id::{DefId, LocalDefId};
99
use rustc_middle::ty::query::Providers;
10-
use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt, TypeSuperVisitable, TypeVisitable};
10+
use rustc_middle::ty::{self, CrateVariancesMap, SubstsRef, Ty, TyCtxt};
11+
use rustc_middle::ty::{DefIdTree, TypeSuperVisitable, TypeVisitable};
1112
use std::ops::ControlFlow;
1213

1314
/// Defines the `TermsContext` basically houses an arena where we can
@@ -75,18 +76,50 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
7576
// type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
7677
// ```
7778
// we may not use `'c` in the hidden type.
78-
struct OpaqueTypeLifetimeCollector {
79+
struct OpaqueTypeLifetimeCollector<'tcx> {
80+
tcx: TyCtxt<'tcx>,
81+
root_def_id: DefId,
7982
variances: Vec<ty::Variance>,
8083
}
8184

82-
impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector {
85+
impl<'tcx> OpaqueTypeLifetimeCollector<'tcx> {
86+
#[instrument(level = "trace", skip(self), ret)]
87+
fn visit_opaque(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> ControlFlow<!> {
88+
if def_id != self.root_def_id && self.tcx.is_descendant_of(def_id, self.root_def_id) {
89+
let child_variances = self.tcx.variances_of(def_id);
90+
for (a, v) in substs.iter().zip(child_variances) {
91+
if *v != ty::Bivariant {
92+
a.visit_with(self)?;
93+
}
94+
}
95+
ControlFlow::CONTINUE
96+
} else {
97+
substs.visit_with(self)
98+
}
99+
}
100+
}
101+
102+
impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector<'tcx> {
83103
#[instrument(level = "trace", skip(self), ret)]
84104
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
85105
if let ty::RegionKind::ReEarlyBound(ebr) = r.kind() {
86106
self.variances[ebr.index as usize] = ty::Invariant;
87107
}
88108
r.super_visit_with(self)
89109
}
110+
111+
#[instrument(level = "trace", skip(self), ret)]
112+
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
113+
match t.kind() {
114+
ty::Opaque(def_id, substs) => self.visit_opaque(*def_id, substs),
115+
ty::Projection(proj)
116+
if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder =>
117+
{
118+
self.visit_opaque(proj.item_def_id, proj.substs)
119+
}
120+
_ => t.super_visit_with(self),
121+
}
122+
}
90123
}
91124

92125
// By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt
@@ -111,7 +144,8 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
111144
}
112145
}
113146

114-
let mut collector = OpaqueTypeLifetimeCollector { variances };
147+
let mut collector =
148+
OpaqueTypeLifetimeCollector { tcx, root_def_id: item_def_id.to_def_id(), variances };
115149
let id_substs = ty::InternalSubsts::identity_for_item(tcx, item_def_id.to_def_id());
116150
for pred in tcx.bound_explicit_item_bounds(item_def_id.to_def_id()).transpose_iter() {
117151
let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// edition: 2021
2+
3+
fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
4+
async move { let _s = s; }
5+
//~^ ERROR hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
6+
}
7+
8+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0700]: hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
2+
--> $DIR/nested-return-type4.rs:4:5
3+
|
4+
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
5+
| -- hidden type `[async block@$DIR/nested-return-type4.rs:4:5: 4:31]` captures the lifetime `'s` as defined here
6+
LL | async move { let _s = s; }
7+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
8+
|
9+
help: to declare that `impl Future<Output = impl Sized>` captures `'s`, you can add an explicit `'s` lifetime bound
10+
|
11+
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> + 's {
12+
| ++++
13+
help: to declare that `impl Sized` captures `'s`, you can add an explicit `'s` lifetime bound
14+
|
15+
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized + 's> {
16+
| ++++
17+
18+
error: aborting due to previous error
19+
20+
For more information about this error, try `rustc --explain E0700`.

0 commit comments

Comments
 (0)