]> git.lizzy.rs Git - rust.git/commitdiff
Constrain all regions in the concrete type for an opaque type
authorMatthew Jasper <mjjasper1@gmail.com>
Wed, 1 May 2019 20:15:01 +0000 (21:15 +0100)
committerMatthew Jasper <mjjasper1@gmail.com>
Thu, 2 May 2019 17:56:56 +0000 (18:56 +0100)
src/librustc/infer/opaque_types/mod.rs
src/test/ui/impl-trait/can-return-unconstrained-closure.rs [new file with mode: 0644]
src/test/ui/impl-trait/issue-55608-captures-empty-region.rs
src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr [deleted file]
src/test/ui/impl-trait/issue-57464-unexpected-regions.rs [new file with mode: 0644]
src/test/ui/issues/issue-49556.rs

index b1d009146473fadf88066623b05ad755a02e766a..072de3dd013160ff647d131e904d6f3f1784ca6d 100644 (file)
@@ -1,13 +1,14 @@
+use rustc_data_structures::fx::FxHashMap;
+use syntax_pos::Span;
+
 use crate::hir::def_id::DefId;
 use crate::hir;
 use crate::hir::Node;
 use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin};
 use crate::infer::outlives::free_region_map::FreeRegionRelations;
-use rustc_data_structures::fx::FxHashMap;
 use crate::traits::{self, PredicateObligation};
 use crate::ty::{self, Ty, TyCtxt, GenericParamDefKind};
-use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder};
-use crate::ty::outlives::Component;
+use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor};
 use crate::ty::subst::{Kind, InternalSubsts, SubstsRef, UnpackedKind};
 use crate::util::nodemap::DefIdMap;
 
@@ -373,58 +374,11 @@ pub fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
         let least_region = least_region.unwrap_or(self.tcx.lifetimes.re_static);
         debug!("constrain_opaque_types: least_region={:?}", least_region);
 
-        // Require that the type `concrete_ty` outlives
-        // `least_region`, modulo any type parameters that appear
-        // in the type, which we ignore. This is because impl
-        // trait values are assumed to capture all the in-scope
-        // type parameters. This little loop here just invokes
-        // `outlives` repeatedly, draining all the nested
-        // obligations that result.
-        let mut types = vec![concrete_ty];
-        let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r);
-        while let Some(ty) = types.pop() {
-            let mut components = smallvec![];
-            self.tcx.push_outlives_components(ty, &mut components);
-            while let Some(component) = components.pop() {
-                match component {
-                    Component::Region(r) => {
-                        bound_region(r);
-                    }
-
-                    Component::Param(_) => {
-                        // ignore type parameters like `T`, they are captured
-                        // implicitly by the `impl Trait`
-                    }
-
-                    Component::UnresolvedInferenceVariable(_) => {
-                        // we should get an error that more type
-                        // annotations are needed in this case
-                        self.tcx
-                            .sess
-                            .delay_span_bug(span, "unresolved inf var in opaque");
-                    }
-
-                    Component::Projection(ty::ProjectionTy {
-                        substs,
-                        item_def_id: _,
-                    }) => {
-                        for k in substs {
-                            match k.unpack() {
-                                UnpackedKind::Lifetime(lt) => bound_region(lt),
-                                UnpackedKind::Type(ty) => types.push(ty),
-                                UnpackedKind::Const(_) => {
-                                    // Const parameters don't impose constraints.
-                                }
-                            }
-                        }
-                    }
-
-                    Component::EscapingProjection(more_components) => {
-                        components.extend(more_components);
-                    }
-                }
-            }
-        }
+        concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor {
+            infcx: self,
+            least_region,
+            span,
+        });
     }
 
     /// Given the fully resolved, instantiated type for an opaque
@@ -502,6 +456,80 @@ pub fn infer_opaque_definition_from_instantiation(
     }
 }
 
+// Visitor that requires that (almost) all regions in the type visited outlive
+// `least_region`. We cannot use `push_outlives_components` because regions in
+// closure signatures are not included in their outlives components. We need to
+// ensure all regions outlive the given bound so that we don't end up with,
+// say, `ReScope` appearing in a return type and causing ICEs when other
+// functions end up with region constraints involving regions from other
+// functions.
+//
+// We also cannot use `for_each_free_region` because for closures it includes
+// the regions parameters from the enclosing item.
+//
+// We ignore any type parameters because impl trait values are assumed to
+// capture all the in-scope type parameters.
+struct OpaqueTypeOutlivesVisitor<'a, 'gcx, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    least_region: ty::Region<'tcx>,
+    span: Span,
+}
+
+impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, '_, 'tcx>
+{
+    fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> bool {
+        t.skip_binder().visit_with(self);
+        false // keep visiting
+    }
+
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
+        match *r {
+            // ignore bound regions, keep visiting
+            ty::ReLateBound(_, _) => false,
+            _ => {
+                self.infcx.sub_regions(infer::CallReturn(self.span), self.least_region, r);
+                false
+            }
+        }
+    }
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
+        // We're only interested in types involving regions
+        if !ty.flags.intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
+            return false; // keep visiting
+        }
+
+        match ty.sty {
+            ty::Closure(def_id, ref substs) => {
+                // Skip lifetime parameters of the enclosing item(s)
+
+                for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
+                    upvar_ty.visit_with(self);
+                }
+
+                substs.closure_sig_ty(def_id, self.infcx.tcx).visit_with(self);
+            }
+
+            ty::Generator(def_id, ref substs, _) => {
+                // Skip lifetime parameters of the enclosing item(s)
+                // Also skip the witness type, because that has no free regions.
+
+                for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
+                    upvar_ty.visit_with(self);
+                }
+
+                substs.return_ty(def_id, self.infcx.tcx).visit_with(self);
+                substs.yield_ty(def_id, self.infcx.tcx).visit_with(self);
+            }
+            _ => {
+                ty.super_visit_with(self);
+            }
+        }
+
+        false
+    }
+}
+
 struct ReverseMapper<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
     tcx: TyCtxt<'cx, 'gcx, 'tcx>,
 
@@ -563,8 +591,7 @@ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
             // ignore `'static`, as that can appear anywhere
             ty::ReStatic |
 
-            // ignore `ReScope`, as that can appear anywhere
-            // See `src/test/run-pass/issue-49556.rs` for example.
+            // ignore `ReScope`, which may appear in impl Trait in bindings.
             ty::ReScope(..) => return r,
 
             _ => { }
diff --git a/src/test/ui/impl-trait/can-return-unconstrained-closure.rs b/src/test/ui/impl-trait/can-return-unconstrained-closure.rs
new file mode 100644 (file)
index 0000000..a982b17
--- /dev/null
@@ -0,0 +1,19 @@
+// Test that we are special casing "outlives" for opaque types.
+//
+// The return type of a closure is not required to outlive the closure. As such
+// the following code would not compile if we used a standard outlives check
+// when checking the return type, because the return type of the closure would
+// be `&ReEmpty i32`, and we don't allow `ReEmpty` to occur in the concrete
+// type used for an opaque type.
+//
+// However, opaque types are special cased to include check all regions in the
+// concrete type against the bound, which forces the return type to be
+// `&'static i32` here.
+
+// compile-pass
+
+fn make_identity() -> impl Sized {
+    |x: &'static i32| x
+}
+
+fn main() {}
index 7ebc348996f5e7bfa40a8c6145b9e457ffffd63c..50646edd61a8585f6de31b7dd87dc062839e21e1 100644 (file)
@@ -1,9 +1,9 @@
 // This used to ICE because it creates an `impl Trait` that captures a
 // hidden empty region.
 
-#![feature(conservative_impl_trait)]
+// compile-pass
 
-fn server() -> impl FilterBase2 { //~ ERROR [E0700]
+fn server() -> impl FilterBase2 {
     segment2(|| { loop { } }).map2(|| "")
 }
 
diff --git a/src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr b/src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr
deleted file mode 100644 (file)
index 6311a7f..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
-  --> $DIR/issue-55608-captures-empty-region.rs:6:16
-   |
-LL | fn server() -> impl FilterBase2 {
-   |                ^^^^^^^^^^^^^^^^
-   |
-   = note: hidden type `Map2<[closure@$DIR/issue-55608-captures-empty-region.rs:7:36: 7:41]>` captures an empty lifetime
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0700`.
diff --git a/src/test/ui/impl-trait/issue-57464-unexpected-regions.rs b/src/test/ui/impl-trait/issue-57464-unexpected-regions.rs
new file mode 100644 (file)
index 0000000..29e271c
--- /dev/null
@@ -0,0 +1,22 @@
+// Regression test for issue 57464.
+//
+// Closure are (surprisingly) allowed to outlive their signature. As such it
+// was possible to end up with `ReScope`s appearing in the concrete type of an
+// opaque type. As all regions are now required to outlive the bound in an
+// opaque type we avoid the issue here.
+
+// compile-pass
+
+struct A<F>(F);
+
+unsafe impl <'a, 'b, F: Fn(&'a i32) -> &'b i32> Send for A<F> {}
+
+fn wrapped_closure() -> impl Sized {
+    let f = |x| x;
+    f(&0);
+    A(f)
+}
+
+fn main() {
+    let x: Box<dyn Send> = Box::new(wrapped_closure());
+}
index b8fcc645a59d3ed0f81e3c3df50a1621f7496757..46d9e749aae2398b2f681942265d457b34fcc508 100644 (file)
@@ -2,10 +2,10 @@
 fn iter<'a>(data: &'a [usize]) -> impl Iterator<Item = usize> + 'a {
     data.iter()
         .map(
-            |x| x // fn(&'a usize) -> &'(ReScope) usize
+            |x| x // fn(&'a usize) -> &'a usize
         )
         .map(
-            |x| *x // fn(&'(ReScope) usize) -> usize
+            |x| *x // fn(&'a usize) -> usize
         )
 }