diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index b0a9e0afa70a..cd4d2257de89 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 fe3a5d149f67..c86f9f66ec83 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 355f949b8700..62ccd9467448 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 42f506606e69..4cd6b4cfadb0 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 f0dc94f417c1..4caab458025b 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();