about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-12-06 13:27:42 +0100
committerGitHub <noreply@github.com>2022-12-06 13:27:42 +0100
commitddb98e0aace469b3bc361cb273ca44e32d1c3aa8 (patch)
treee64f074020027cbb058e07b9e611b6a0fb442812
parent7d8e32919448d30d5d2281b29bf119f917bf583b (diff)
parent44948d1fdc97a4fd24594be77df673a2c3b40544 (diff)
downloadrust-ddb98e0aace469b3bc361cb273ca44e32d1c3aa8.tar.gz
rust-ddb98e0aace469b3bc361cb273ca44e32d1c3aa8.zip
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
-rw-r--r--compiler/rustc_hir_analysis/src/variance/mod.rs42
-rw-r--r--src/test/ui/impl-trait/nested-return-type4.rs8
-rw-r--r--src/test/ui/impl-trait/nested-return-type4.stderr20
3 files changed, 66 insertions, 4 deletions
diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 9db05eedbde..8b2719c2f8a 100644
--- a/compiler/rustc_hir_analysis/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
@@ -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);
diff --git a/src/test/ui/impl-trait/nested-return-type4.rs b/src/test/ui/impl-trait/nested-return-type4.rs
new file mode 100644
index 00000000000..cec70bb1a0d
--- /dev/null
+++ b/src/test/ui/impl-trait/nested-return-type4.rs
@@ -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() {}
diff --git a/src/test/ui/impl-trait/nested-return-type4.stderr b/src/test/ui/impl-trait/nested-return-type4.stderr
new file mode 100644
index 00000000000..e761a60e79c
--- /dev/null
+++ b/src/test/ui/impl-trait/nested-return-type4.stderr
@@ -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`.