about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2019-10-08 10:56:03 -0400
committerNiko Matsakis <niko@alum.mit.edu>2020-02-06 15:57:58 -0500
commitb52414fedebe9774bf8a5339eb5042a2fa3dc9a3 (patch)
tree21ba545a67ef07bbb09af3c1254a72e9e9a62c31
parent03b2fff40ed8c58357934789ab1158eafd942d39 (diff)
downloadrust-b52414fedebe9774bf8a5339eb5042a2fa3dc9a3.tar.gz
rust-b52414fedebe9774bf8a5339eb5042a2fa3dc9a3.zip
integrate the `sub_free_regions` code so we have only one copy of it
-rw-r--r--src/librustc/infer/lexical_region_resolve/mod.rs38
-rw-r--r--src/librustc/infer/opaque_types/mod.rs4
-rw-r--r--src/librustc/middle/free_region.rs60
-rw-r--r--src/librustc/ty/free_region_map.rs89
-rw-r--r--src/librustc_mir/borrow_check/type_check/free_region_relations.rs9
5 files changed, 106 insertions, 94 deletions
diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs
index b0a9e0afa70..cd4d2257de8 100644
--- a/src/librustc/infer/lexical_region_resolve/mod.rs
+++ b/src/librustc/infer/lexical_region_resolve/mod.rs
@@ -420,12 +420,34 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
 
     /// True if `a <= b`, but not defined over inference variables.
     fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool {
+        let tcx = self.tcx();
+        let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2);
+
+        // Check for the case where we know that `'b: 'static` -- in that case,
+        // `a <= b` for all `a`.
+        let b_free_or_static = self.region_rels.free_regions.is_free_or_static(b);
+        if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) {
+            return true;
+        }
+
+        // If both a and b are free, consult the declared
+        // relationships.  Note that this can be more precise than the
+        // `lub` relationship defined below, since sometimes the "lub"
+        // is actually the `postdom_upper_bound` (see
+        // `TransitiveRelation` for more details).
+        let a_free_or_static = self.region_rels.free_regions.is_free_or_static(a);
+        if a_free_or_static && b_free_or_static {
+            return sub_free_regions(a, b);
+        }
+
+        // For other cases, leverage the LUB code to find the LUB and
+        // check if it is equal to b.
         self.lub_concrete_regions(a, b) == b
     }
 
     /// Returns the smallest region `c` such that `a <= c` and `b <= c`.
     fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
-        match (a, b) {
+        let r = match (a, b) {
             (&ty::ReClosureBound(..), _)
             | (_, &ty::ReClosureBound(..))
             | (&ReLateBound(..), _)
@@ -509,7 +531,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                     self.tcx().lifetimes.re_static
                 }
             }
-        }
+        };
+
+        debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);
+
+        r
     }
 
     /// After expansion is complete, go and check upper bounds (i.e.,
@@ -528,7 +554,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                 }
 
                 Constraint::RegSubReg(sub, sup) => {
-                    if self.region_rels.is_subregion_of(sub, sup) {
+                    if self.sub_concrete_regions(sub, sup) {
                         continue;
                     }
 
@@ -557,7 +583,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                     // Do not report these errors immediately:
                     // instead, set the variable value to error and
                     // collect them later.
-                    if !self.region_rels.is_subregion_of(a_region, b_region) {
+                    if !self.sub_concrete_regions(a_region, b_region) {
                         debug!(
                             "collect_errors: region error at {:?}: \
                              cannot verify that {:?}={:?} <= {:?}",
@@ -754,7 +780,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
             };
 
             for upper_bound in &upper_bounds {
-                if !self.region_rels.is_subregion_of(effective_lower_bound, upper_bound.region) {
+                if !self.sub_concrete_regions(effective_lower_bound, upper_bound.region) {
                     let origin = self.var_infos[node_idx].origin;
                     debug!(
                         "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
@@ -884,7 +910,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
             }
 
             VerifyBound::OutlivedBy(r) => {
-                self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r))
+                self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r))
             }
 
             VerifyBound::IsEmpty => {
diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs
index fe3a5d149f6..c86f9f66ec8 100644
--- a/src/librustc/infer/opaque_types/mod.rs
+++ b/src/librustc/infer/opaque_types/mod.rs
@@ -384,9 +384,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             match least_region {
                 None => least_region = Some(subst_arg),
                 Some(lr) => {
-                    if free_region_relations.sub_free_regions(lr, subst_arg) {
+                    if free_region_relations.sub_free_regions(self.tcx, lr, subst_arg) {
                         // keep the current least region
-                    } else if free_region_relations.sub_free_regions(subst_arg, lr) {
+                    } else if free_region_relations.sub_free_regions(self.tcx, subst_arg, lr) {
                         // switch to `subst_arg`
                         least_region = Some(subst_arg);
                     } else {
diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs
index 355f949b870..62ccd946744 100644
--- a/src/librustc/middle/free_region.rs
+++ b/src/librustc/middle/free_region.rs
@@ -4,8 +4,8 @@
 //! and use that to decide when one free region outlives another, and so forth.
 
 use crate::middle::region;
-use crate::ty::free_region_map::{FreeRegionMap, FreeRegionRelations};
-use crate::ty::{self, Region, TyCtxt};
+use crate::ty::free_region_map::FreeRegionMap;
+use crate::ty::{Region, TyCtxt};
 use rustc_hir::def_id::DefId;
 
 /// Combines a `region::ScopeTree` (which governs relationships between
@@ -38,62 +38,6 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> {
         Self { tcx, context, region_scope_tree, free_regions }
     }
 
-    /// Determines whether one region is a subregion of another. This is intended to run *after
-    /// inference* and sadly the logic is somewhat duplicated with the code in infer.rs.
-    pub fn is_subregion_of(
-        &self,
-        sub_region: ty::Region<'tcx>,
-        super_region: ty::Region<'tcx>,
-    ) -> bool {
-        let result = sub_region == super_region || {
-            match (sub_region, super_region) {
-                (ty::ReEmpty, _) | (_, ty::ReStatic) => true,
-
-                (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => {
-                    self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope)
-                }
-
-                (ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => {
-                    let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br);
-                    self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
-                }
-
-                (ty::ReScope(sub_scope), ty::ReFree(fr)) => {
-                    let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
-                    self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
-                }
-
-                (ty::ReEarlyBound(_), ty::ReEarlyBound(_))
-                | (ty::ReFree(_), ty::ReEarlyBound(_))
-                | (ty::ReEarlyBound(_), ty::ReFree(_))
-                | (ty::ReFree(_), ty::ReFree(_)) => {
-                    self.free_regions.sub_free_regions(sub_region, super_region)
-                }
-
-                _ => false,
-            }
-        };
-        let result = result || self.is_static(super_region);
-        debug!(
-            "is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
-            sub_region, super_region, result
-        );
-        result
-    }
-
-    /// Determines whether this free region is required to be `'static`.
-    fn is_static(&self, super_region: ty::Region<'tcx>) -> bool {
-        debug!("is_static(super_region={:?})", super_region);
-        match *super_region {
-            ty::ReStatic => true,
-            ty::ReEarlyBound(_) | ty::ReFree(_) => {
-                let re_static = self.tcx.mk_region(ty::ReStatic);
-                self.free_regions.sub_free_regions(&re_static, &super_region)
-            }
-            _ => false,
-        }
-    }
-
     pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> {
         self.free_regions.lub_free_regions(self.tcx, r_a, r_b)
     }
diff --git a/src/librustc/ty/free_region_map.rs b/src/librustc/ty/free_region_map.rs
index 42f506606e6..4cd6b4cfadb 100644
--- a/src/librustc/ty/free_region_map.rs
+++ b/src/librustc/ty/free_region_map.rs
@@ -23,11 +23,61 @@ impl<'tcx> FreeRegionMap<'tcx> {
     // (with the exception that `'static: 'x` is not notable)
     pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
         debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
-        if is_free_or_static(sub) && is_free(sup) {
+        if self.is_free_or_static(sub) && self.is_free(sup) {
             self.relation.add(sub, sup)
         }
     }
 
+    /// Tests whether `r_a <= r_b`.
+    ///
+    /// Both regions must meet `is_free_or_static`.
+    ///
+    /// Subtle: one tricky case that this code gets correct is as
+    /// follows. If we know that `r_b: 'static`, then this function
+    /// will return true, even though we don't know anything that
+    /// directly relates `r_a` and `r_b`.
+    ///
+    /// Also available through the `FreeRegionRelations` trait below.
+    pub fn sub_free_regions(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        r_a: Region<'tcx>,
+        r_b: Region<'tcx>,
+    ) -> bool {
+        assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b));
+        let re_static = tcx.lifetimes.re_static;
+        if self.check_relation(re_static, r_b) {
+            // `'a <= 'static` is always true, and not stored in the
+            // relation explicitly, so check if `'b` is `'static` (or
+            // equivalent to it)
+            true
+        } else {
+            self.check_relation(r_a, r_b)
+        }
+    }
+
+    /// Check whether `r_a <= r_b` is found in the relation
+    fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
+        r_a == r_b || self.relation.contains(&r_a, &r_b)
+    }
+
+    /// True for free regions other than `'static`.
+    pub fn is_free(&self, r: Region<'_>) -> bool {
+        match *r {
+            ty::ReEarlyBound(_) | ty::ReFree(_) => true,
+            _ => false,
+        }
+    }
+
+    /// True if `r` is a free region or static of the sort that this
+    /// free region map can be used with.
+    pub fn is_free_or_static(&self, r: Region<'_>) -> bool {
+        match *r {
+            ty::ReStatic => true,
+            _ => self.is_free(r),
+        }
+    }
+
     /// Computes the least-upper-bound of two free regions. In some
     /// cases, this is more conservative than necessary, in order to
     /// avoid making arbitrary choices. See
@@ -39,13 +89,13 @@ impl<'tcx> FreeRegionMap<'tcx> {
         r_b: Region<'tcx>,
     ) -> Region<'tcx> {
         debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
-        assert!(is_free(r_a));
-        assert!(is_free(r_b));
+        assert!(self.is_free(r_a));
+        assert!(self.is_free(r_b));
         let result = if r_a == r_b {
             r_a
         } else {
             match self.relation.postdom_upper_bound(&r_a, &r_b) {
-                None => tcx.mk_region(ty::ReStatic),
+                None => tcx.lifetimes.re_static,
                 Some(r) => *r,
             }
         };
@@ -60,31 +110,18 @@ impl<'tcx> FreeRegionMap<'tcx> {
 pub trait FreeRegionRelations<'tcx> {
     /// Tests whether `r_a <= r_b`. Both must be free regions or
     /// `'static`.
-    fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
+    fn sub_free_regions(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        shorter: ty::Region<'tcx>,
+        longer: ty::Region<'tcx>,
+    ) -> bool;
 }
 
 impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
-    fn sub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
-        assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
-        if let ty::ReStatic = r_b {
-            true // `'a <= 'static` is just always true, and not stored in the relation explicitly
-        } else {
-            r_a == r_b || self.relation.contains(&r_a, &r_b)
-        }
-    }
-}
-
-fn is_free(r: Region<'_>) -> bool {
-    match *r {
-        ty::ReEarlyBound(_) | ty::ReFree(_) => true,
-        _ => false,
-    }
-}
-
-fn is_free_or_static(r: Region<'_>) -> bool {
-    match *r {
-        ty::ReStatic => true,
-        _ => is_free(r),
+    fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
+        // invoke the "inherent method"
+        self.sub_free_regions(tcx, r_a, r_b)
     }
 }
 
diff --git a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs
index f0dc94f417c..4caab458025 100644
--- a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs
+++ b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs
@@ -5,7 +5,7 @@ use rustc::mir::ConstraintCategory;
 use rustc::traits::query::outlives_bounds::{self, OutlivesBound};
 use rustc::traits::query::type_op::{self, TypeOp};
 use rustc::ty::free_region_map::FreeRegionRelations;
-use rustc::ty::{self, RegionVid, Ty};
+use rustc::ty::{self, RegionVid, Ty, TyCtxt};
 use rustc_data_structures::transitive_relation::TransitiveRelation;
 use rustc_span::DUMMY_SP;
 use std::rc::Rc;
@@ -359,7 +359,12 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
 /// over the `FreeRegionMap` from lexical regions and
 /// `UniversalRegions` (from NLL)`.
 impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegionRelations<'tcx> {
-    fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool {
+    fn sub_free_regions(
+        &self,
+        _tcx: TyCtxt<'tcx>,
+        shorter: ty::Region<'tcx>,
+        longer: ty::Region<'tcx>,
+    ) -> bool {
         let shorter = shorter.to_region_vid();
         assert!(self.universal_regions.is_universal_region(shorter));
         let longer = longer.to_region_vid();