Rollup merge of #105254 - cjgillot:issue-105251, r=oli-obk

Recurse into nested impl-trait when computing variance.

Fixes https://github.com/rust-lang/rust/issues/105251
This commit is contained in:
Matthias Krüger 2022-12-06 13:27:42 +01:00 committed by GitHub
commit ddb98e0aac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 4 deletions

View File

@ -7,7 +7,8 @@ use rustc_arena::DroplessArena;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt, TypeSuperVisitable, TypeVisitable};
use rustc_middle::ty::{self, CrateVariancesMap, SubstsRef, Ty, TyCtxt};
use rustc_middle::ty::{DefIdTree, TypeSuperVisitable, TypeVisitable};
use std::ops::ControlFlow;
/// Defines the `TermsContext` basically houses an arena where we can
@ -75,11 +76,30 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
// type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
// ```
// we may not use `'c` in the hidden type.
struct OpaqueTypeLifetimeCollector {
struct OpaqueTypeLifetimeCollector<'tcx> {
tcx: TyCtxt<'tcx>,
root_def_id: DefId,
variances: Vec<ty::Variance>,
}
impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector {
impl<'tcx> OpaqueTypeLifetimeCollector<'tcx> {
#[instrument(level = "trace", skip(self), ret)]
fn visit_opaque(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> ControlFlow<!> {
if def_id != self.root_def_id && self.tcx.is_descendant_of(def_id, self.root_def_id) {
let child_variances = self.tcx.variances_of(def_id);
for (a, v) in substs.iter().zip(child_variances) {
if *v != ty::Bivariant {
a.visit_with(self)?;
}
}
ControlFlow::CONTINUE
} else {
substs.visit_with(self)
}
}
}
impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector<'tcx> {
#[instrument(level = "trace", skip(self), ret)]
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::RegionKind::ReEarlyBound(ebr) = r.kind() {
@ -87,6 +107,19 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
}
r.super_visit_with(self)
}
#[instrument(level = "trace", skip(self), ret)]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match t.kind() {
ty::Opaque(def_id, substs) => self.visit_opaque(*def_id, substs),
ty::Projection(proj)
if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder =>
{
self.visit_opaque(proj.item_def_id, proj.substs)
}
_ => t.super_visit_with(self),
}
}
}
// 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
}
}
let mut collector = OpaqueTypeLifetimeCollector { variances };
let mut collector =
OpaqueTypeLifetimeCollector { tcx, root_def_id: item_def_id.to_def_id(), variances };
let id_substs = ty::InternalSubsts::identity_for_item(tcx, item_def_id.to_def_id());
for pred in tcx.bound_explicit_item_bounds(item_def_id.to_def_id()).transpose_iter() {
let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);

View File

@ -0,0 +1,8 @@
// edition: 2021
fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
async move { let _s = s; }
//~^ ERROR hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
}
fn main() {}

View File

@ -0,0 +1,20 @@
error[E0700]: hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
--> $DIR/nested-return-type4.rs:4:5
|
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
| -- hidden type `[async block@$DIR/nested-return-type4.rs:4:5: 4:31]` captures the lifetime `'s` as defined here
LL | async move { let _s = s; }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: to declare that `impl Future<Output = impl Sized>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> + 's {
| ++++
help: to declare that `impl Sized` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized + 's> {
| ++++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0700`.