diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 8f837327ddb15..0c93b86ee4d11 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -1229,7 +1229,7 @@ for traits::VtableGeneratorData<'gcx, N> where N: HashStable for struct infer::canonical::Canonical<'tcx, V> { - variables, value + max_universe, variables, value } ); @@ -1245,7 +1245,8 @@ impl_stable_hash_for!(struct infer::canonical::CanonicalVarInfo { impl_stable_hash_for!(enum infer::canonical::CanonicalVarKind { Ty(k), - Region + Region(ui), + PlaceholderRegion(placeholder), }); impl_stable_hash_for!(enum infer::canonical::CanonicalTyVarKind { diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 2b085a3407ccc..cc6e4df071033 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -107,6 +107,20 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ) } + pub fn canonicalize_user_type_annotation(&self, value: &V) -> Canonicalized<'gcx, V> + where + V: TypeFoldable<'tcx> + Lift<'gcx>, + { + let mut query_state = OriginalQueryValues::default(); + Canonicalizer::canonicalize( + value, + Some(self), + self.tcx, + &CanonicalizeUserTypeAnnotation, + &mut query_state, + ) + } + /// A hacky variant of `canonicalize_query` that does not /// canonicalize `'static`. Unfortunately, the existing leak /// check treaks `'static` differently in some cases (see also @@ -162,11 +176,26 @@ struct CanonicalizeQueryResponse; impl CanonicalizeRegionMode for CanonicalizeQueryResponse { fn canonicalize_free_region( &self, - _canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>, + canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { match r { ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r, + ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( + CanonicalVarInfo { + kind: CanonicalVarKind::PlaceholderRegion(*placeholder), + }, + r, + ), + ty::ReVar(vid) => { + let universe = canonicalizer.region_var_universe(*vid); + canonicalizer.canonical_var_for_region( + CanonicalVarInfo { + kind: CanonicalVarKind::Region(universe), + }, + r, + ) + } _ => { // Other than `'static` or `'empty`, the query // response should be executing in a fully @@ -182,6 +211,29 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { } } +struct CanonicalizeUserTypeAnnotation; + +impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { + fn canonicalize_free_region( + &self, + canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + match r { + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReEmpty | ty::ReStatic => r, + ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), + _ => { + // We only expect region names that the user can type. + bug!("unexpected region in query response: `{:?}`", r) + } + } + } + + fn any(&self) -> bool { + false + } +} + struct CanonicalizeAllFreeRegions; impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { @@ -190,7 +242,7 @@ impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { canonicalizer: &mut Canonicalizer<'_, '_, 'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - canonicalizer.canonical_var_for_region(r) + canonicalizer.canonical_var_for_region_in_root_universe(r) } fn any(&self) -> bool { @@ -209,7 +261,7 @@ impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic { if let ty::ReStatic = r { r } else { - canonicalizer.canonical_var_for_region(r) + canonicalizer.canonical_var_for_region_in_root_universe(r) } } @@ -252,7 +304,8 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> opportunistically resolved to {:?}", vid, r ); - self.canonical_var_for_region(r) + self.canonicalize_region_mode + .canonicalize_free_region(self, r) } ty::ReStatic @@ -261,7 +314,8 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> | ty::ReScope(_) | ty::RePlaceholder(..) | ty::ReEmpty - | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r), + | ty::ReErased => self.canonicalize_region_mode + .canonicalize_free_region(self, r), ty::ReClosureBound(..) | ty::ReCanonical(_) => { bug!("canonical region encountered during canonicalization") @@ -353,6 +407,7 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { if !value.has_type_flags(needs_canonical_flags) { let out_value = gcx.lift(value).unwrap(); let canon_value = Canonical { + max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: out_value, }; @@ -383,7 +438,14 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables); + let max_universe = canonical_variables + .iter() + .map(|cvar| cvar.universe()) + .max() + .unwrap_or(ty::UniverseIndex::ROOT); + Canonical { + max_universe, variables: canonical_variables, value: out_value, } @@ -450,10 +512,46 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { } } - fn canonical_var_for_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - let info = CanonicalVarInfo { - kind: CanonicalVarKind::Region, - }; + /// Shorthand helper that creates a canonical region variable for + /// `r` (always in the root universe). The reason that we always + /// put these variables into the root universe is because this + /// method is used during **query construction:** in that case, we + /// are taking all the regions and just putting them into the most + /// generic context we can. This may generate solutions that don't + /// fit (e.g., that equate some region variable with a placeholder + /// it can't name) on the caller side, but that's ok, the caller + /// can figure that out. In the meantime, it maximizes our + /// caching. + /// + /// (This works because unification never fails -- and hence trait + /// selection is never affected -- due to a universe mismatch.) + fn canonical_var_for_region_in_root_universe( + &mut self, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + self.canonical_var_for_region( + CanonicalVarInfo { + kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + }, + r, + ) + } + + /// Returns the universe in which `vid` is defined. + fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex { + self.infcx + .unwrap() + .borrow_region_constraints() + .var_universe(vid) + } + + /// Create a canonical variable (with the given `info`) + /// representing the region `r`; return a region referencing it. + fn canonical_var_for_region( + &mut self, + info: CanonicalVarInfo, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { let b = self.canonical_var(info, r.into()); debug_assert_eq!(ty::INNERMOST, b.level); self.tcx().mk_region(ty::ReCanonical(b.var)) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index e3bd407d17a90..f2b7c6e0d0d98 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -33,14 +33,14 @@ use infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin}; use rustc_data_structures::indexed_vec::IndexVec; -use smallvec::SmallVec; use rustc_data_structures::sync::Lrc; use serialize::UseSpecializedDecodable; +use smallvec::SmallVec; use std::ops::Index; use syntax::source_map::Span; use ty::fold::TypeFoldable; use ty::subst::Kind; -use ty::{self, BoundTyIndex, Lift, Region, List, TyCtxt}; +use ty::{self, BoundTyIndex, Lift, List, Region, TyCtxt}; mod canonicalizer; @@ -53,6 +53,7 @@ mod substitute; /// numbered starting from 0 in order of first appearance. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub struct Canonical<'gcx, V> { + pub max_universe: ty::UniverseIndex, pub variables: CanonicalVarInfos<'gcx>, pub value: V, } @@ -79,13 +80,31 @@ pub struct CanonicalVarValues<'tcx> { /// various parts of it with canonical variables. This struct stores /// those replaced bits to remember for when we process the query /// result. -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub struct OriginalQueryValues<'tcx> { + /// Map from the universes that appear in the query to the + /// universes in the caller context. For the time being, we only + /// ever put ROOT values into the query, so this map is very + /// simple. + pub universe_map: SmallVec<[ty::UniverseIndex; 4]>, + /// This is equivalent to `CanonicalVarValues`, but using a /// `SmallVec` yields a significant performance win. pub var_values: SmallVec<[Kind<'tcx>; 8]>, } +impl Default for OriginalQueryValues<'tcx> { + fn default() -> Self { + let mut universe_map = SmallVec::default(); + universe_map.push(ty::UniverseIndex::ROOT); + + Self { + universe_map, + var_values: SmallVec::default(), + } + } +} + /// Information about a canonical variable that is included with the /// canonical value. This is sufficient information for code to create /// a copy of the canonical value in some other inference context, @@ -95,6 +114,20 @@ pub struct CanonicalVarInfo { pub kind: CanonicalVarKind, } +impl CanonicalVarInfo { + pub fn universe(&self) -> ty::UniverseIndex { + self.kind.universe() + } + + pub fn is_existential(&self) -> bool { + match self.kind { + CanonicalVarKind::Ty(_) => true, + CanonicalVarKind::Region(_) => true, + CanonicalVarKind::PlaceholderRegion(..) => false, + } + } +} + /// Describes the "kind" of the canonical variable. This is a "kind" /// in the type-theory sense of the term -- i.e., a "meta" type system /// that analyzes type-like values. @@ -104,7 +137,27 @@ pub enum CanonicalVarKind { Ty(CanonicalTyVarKind), /// Region variable `'?R`. - Region, + Region(ty::UniverseIndex), + + /// A "placeholder" that represents "any region". Created when you + /// are solving a goal like `for<'a> T: Foo<'a>` to represent the + /// bound region `'a`. + PlaceholderRegion(ty::Placeholder), +} + +impl CanonicalVarKind { + pub fn universe(self) -> ty::UniverseIndex { + match self { + // At present, we don't support higher-ranked + // quantification over types, so all type variables are in + // the root universe. + CanonicalVarKind::Ty(_) => ty::UniverseIndex::ROOT, + + // Region variables can be created in sub-universes. + CanonicalVarKind::Region(ui) => ui, + CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe, + } + } } /// Rust actually has more than one category of type variables; @@ -220,8 +273,16 @@ impl<'gcx, V> Canonical<'gcx, V> { /// let b: Canonical<'tcx, (T, Ty<'tcx>)> = a.unchecked_map(|v| (v, ty)); /// ``` pub fn unchecked_map(self, map_op: impl FnOnce(V) -> W) -> Canonical<'gcx, W> { - let Canonical { variables, value } = self; - Canonical { variables, value: map_op(value) } + let Canonical { + max_universe, + variables, + value, + } = self; + Canonical { + max_universe, + variables, + value: map_op(value), + } } } @@ -249,35 +310,50 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { where T: TypeFoldable<'tcx>, { + // For each universe that is referred to in the incoming + // query, create a universe in our local inference context. In + // practice, as of this writing, all queries have no universes + // in them, so this code has no effect, but it is looking + // forward to the day when we *do* want to carry universes + // through into queries. + let universes: IndexVec = std::iter::once(ty::UniverseIndex::ROOT) + .chain((0..canonical.max_universe.as_u32()).map(|_| self.create_next_universe())) + .collect(); + let canonical_inference_vars = - self.fresh_inference_vars_for_canonical_vars(span, canonical.variables); + self.instantiate_canonical_vars(span, canonical.variables, |ui| universes[ui]); let result = canonical.substitute(self.tcx, &canonical_inference_vars); (result, canonical_inference_vars) } /// Given the "infos" about the canonical variables from some - /// canonical, creates fresh inference variables with the same - /// characteristics. You can then use `substitute` to instantiate - /// the canonical variable with these inference variables. - fn fresh_inference_vars_for_canonical_vars( + /// canonical, creates fresh variables with the same + /// characteristics (see `instantiate_canonical_var` for + /// details). You can then use `substitute` to instantiate the + /// canonical variable with these inference variables. + fn instantiate_canonical_vars( &self, span: Span, variables: &List, + universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> CanonicalVarValues<'tcx> { let var_values: IndexVec> = variables .iter() - .map(|info| self.fresh_inference_var_for_canonical_var(span, *info)) + .map(|info| self.instantiate_canonical_var(span, *info, &universe_map)) .collect(); CanonicalVarValues { var_values } } /// Given the "info" about a canonical variable, creates a fresh - /// inference variable with the same characteristics. - fn fresh_inference_var_for_canonical_var( + /// variable for it. If this is an existentially quantified + /// variable, then you'll get a new inference variable; if it is a + /// universally quantified variable, you get a placeholder. + fn instantiate_canonical_var( &self, span: Span, cv_info: CanonicalVarInfo, + universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> Kind<'tcx> { match cv_info.kind { CanonicalVarKind::Ty(ty_kind) => { @@ -293,9 +369,21 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ty.into() } - CanonicalVarKind::Region => self - .next_region_var(RegionVariableOrigin::MiscVariable(span)) - .into(), + CanonicalVarKind::Region(ui) => self.next_region_var_in_universe( + RegionVariableOrigin::MiscVariable(span), + universe_map(ui), + ).into(), + + CanonicalVarKind::PlaceholderRegion(ty::Placeholder { universe, name }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = ty::Placeholder { + universe: universe_mapped, + name, + }; + self.tcx + .mk_region(ty::RePlaceholder(placeholder_mapped)) + .into() + } } } } @@ -314,6 +402,7 @@ CloneTypeFoldableImpls! { BraceStructTypeFoldableImpl! { impl<'tcx, C> TypeFoldable<'tcx> for Canonical<'tcx, C> { + max_universe, variables, value, } where C: TypeFoldable<'tcx> @@ -322,7 +411,7 @@ BraceStructTypeFoldableImpl! { BraceStructLiftImpl! { impl<'a, 'tcx, T> Lift<'tcx> for Canonical<'a, T> { type Lifted = Canonical<'tcx, T::Lifted>; - variables, value + max_universe, variables, value } where T: Lift<'tcx> } diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 38788186eb071..b3ce5eb7e56c2 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -394,6 +394,21 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { original_values, query_response, ); + // For each new universe created in the query result that did + // not appear in the original query, create a local + // superuniverse. + let mut universe_map = original_values.universe_map.clone(); + let num_universes_in_query = original_values.universe_map.len(); + let num_universes_in_response = query_response.max_universe.as_usize() + 1; + for _ in num_universes_in_query..num_universes_in_response { + universe_map.push(self.create_next_universe()); + } + assert!(universe_map.len() >= 1); // always have the root universe + assert_eq!( + universe_map[ty::UniverseIndex::ROOT.as_usize()], + ty::UniverseIndex::ROOT + ); + // Every canonical query result includes values for each of // the inputs to the query. Therefore, we begin by unifying // these values with the original inputs that were @@ -440,9 +455,20 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { .variables .iter() .enumerate() - .map(|(index, info)| opt_values[BoundTyIndex::new(index)].unwrap_or_else(|| - self.fresh_inference_var_for_canonical_var(cause.span, *info) - )) + .map(|(index, info)| { + if info.is_existential() { + match opt_values[BoundTyIndex::new(index)] { + Some(k) => k, + None => self.instantiate_canonical_var(cause.span, *info, |u| { + universe_map[u.as_usize()] + }), + } + } else { + self.instantiate_canonical_var(cause.span, *info, |u| { + universe_map[u.as_usize()] + }) + } + }) .collect(), }; diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index fd14e0e40e234..8968c5949b617 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -15,7 +15,6 @@ use super::Subtype; use traits::ObligationCause; use ty::{self, Ty, TyCtxt}; -use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; /// "Greatest lower bound" (common subtype) @@ -76,31 +75,12 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> where T: Relate<'tcx> { debug!("binders(a={:?}, b={:?})", a, b); - let was_error = self.infcx().probe(|_snapshot| { - // Subtle: use a fresh combine-fields here because we recover - // from Err. Doing otherwise could propagate obligations out - // through our `self.obligations` field. - self.infcx() - .combine_fields(self.fields.trace.clone(), self.fields.param_env) - .higher_ranked_glb(a, b, self.a_is_expected) - .is_err() - }); - debug!("binders: was_error={:?}", was_error); // When higher-ranked types are involved, computing the LUB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. - match self.relate_with_variance(ty::Variance::Invariant, a, b) { - Ok(_) => Ok(a.clone()), - Err(err) => { - debug!("binders: error occurred, was_error={:?}", was_error); - if !was_error { - Err(TypeError::OldStyleLUB(Box::new(err))) - } else { - Err(err) - } - } - } + self.relate_with_variance(ty::Variance::Invariant, a, b)?; + Ok(a.clone()) } } diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index d85a3e84f857a..3e08a4e021aed 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -22,7 +22,6 @@ use super::region_constraints::{TaintDirections}; use ty::{self, TyCtxt, Binder, TypeFoldable}; use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; -use std::collections::BTreeMap; use syntax_pos::Span; use util::nodemap::{FxHashMap, FxHashSet}; @@ -202,261 +201,6 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { Ok(HrMatchResult { value: a_value }) }); } - - pub fn higher_ranked_lub(&mut self, a: &Binder, b: &Binder, a_is_expected: bool) - -> RelateResult<'tcx, Binder> - where T: Relate<'tcx> - { - // Start a snapshot so we can examine "all bindings that were - // created as part of this type comparison". - return self.infcx.commit_if_ok(|snapshot| { - // Instantiate each bound region with a fresh region variable. - let span = self.trace.cause.span; - let (a_with_fresh, a_map) = - self.infcx.replace_late_bound_regions_with_fresh_var( - span, HigherRankedType, a); - let (b_with_fresh, _) = - self.infcx.replace_late_bound_regions_with_fresh_var( - span, HigherRankedType, b); - - // Collect constraints. - let result0 = - self.lub(a_is_expected).relate(&a_with_fresh, &b_with_fresh)?; - let result0 = - self.infcx.resolve_type_vars_if_possible(&result0); - debug!("lub result0 = {:?}", result0); - - // Generalize the regions appearing in result0 if possible - let new_vars = self.infcx.region_vars_confined_to_snapshot(snapshot); - let span = self.trace.cause.span; - let result1 = - fold_regions_in( - self.tcx(), - &result0, - |r, debruijn| generalize_region(self.infcx, span, snapshot, debruijn, - &new_vars, &a_map, r)); - - debug!("lub({:?},{:?}) = {:?}", - a, - b, - result1); - - Ok(ty::Binder::bind(result1)) - }); - - fn generalize_region<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - span: Span, - snapshot: &CombinedSnapshot<'a, 'tcx>, - debruijn: ty::DebruijnIndex, - new_vars: &[ty::RegionVid], - a_map: &BTreeMap>, - r0: ty::Region<'tcx>) - -> ty::Region<'tcx> { - // Regions that pre-dated the LUB computation stay as they are. - if !is_var_in_set(new_vars, r0) { - assert!(!r0.is_late_bound()); - debug!("generalize_region(r0={:?}): not new variable", r0); - return r0; - } - - let tainted = infcx.tainted_regions(snapshot, r0, TaintDirections::both()); - - // Variables created during LUB computation which are - // *related* to regions that pre-date the LUB computation - // stay as they are. - if !tainted.iter().all(|&r| is_var_in_set(new_vars, r)) { - debug!("generalize_region(r0={:?}): \ - non-new-variables found in {:?}", - r0, tainted); - assert!(!r0.is_late_bound()); - return r0; - } - - // Otherwise, the variable must be associated with at - // least one of the variables representing bound regions - // in both A and B. Replace the variable with the "first" - // bound region from A that we find it to be associated - // with. - for (a_br, a_r) in a_map { - if tainted.iter().any(|x| x == a_r) { - debug!("generalize_region(r0={:?}): \ - replacing with {:?}, tainted={:?}", - r0, *a_br, tainted); - return infcx.tcx.mk_region(ty::ReLateBound(debruijn, *a_br)); - } - } - - span_bug!( - span, - "region {:?} is not associated with any bound region from A!", - r0) - } - } - - pub fn higher_ranked_glb(&mut self, a: &Binder, b: &Binder, a_is_expected: bool) - -> RelateResult<'tcx, Binder> - where T: Relate<'tcx> - { - debug!("higher_ranked_glb({:?}, {:?})", - a, b); - - // Make a snapshot so we can examine "all bindings that were - // created as part of this type comparison". - return self.infcx.commit_if_ok(|snapshot| { - // Instantiate each bound region with a fresh region variable. - let (a_with_fresh, a_map) = - self.infcx.replace_late_bound_regions_with_fresh_var( - self.trace.cause.span, HigherRankedType, a); - let (b_with_fresh, b_map) = - self.infcx.replace_late_bound_regions_with_fresh_var( - self.trace.cause.span, HigherRankedType, b); - let a_vars = var_ids(self, &a_map); - let b_vars = var_ids(self, &b_map); - - // Collect constraints. - let result0 = - self.glb(a_is_expected).relate(&a_with_fresh, &b_with_fresh)?; - let result0 = - self.infcx.resolve_type_vars_if_possible(&result0); - debug!("glb result0 = {:?}", result0); - - // Generalize the regions appearing in result0 if possible - let new_vars = self.infcx.region_vars_confined_to_snapshot(snapshot); - let span = self.trace.cause.span; - let result1 = - fold_regions_in( - self.tcx(), - &result0, - |r, debruijn| generalize_region(self.infcx, span, snapshot, debruijn, - &new_vars, - &a_map, &a_vars, &b_vars, - r)); - - debug!("glb({:?},{:?}) = {:?}", - a, - b, - result1); - - Ok(ty::Binder::bind(result1)) - }); - - fn generalize_region<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - span: Span, - snapshot: &CombinedSnapshot<'a, 'tcx>, - debruijn: ty::DebruijnIndex, - new_vars: &[ty::RegionVid], - a_map: &BTreeMap>, - a_vars: &[ty::RegionVid], - b_vars: &[ty::RegionVid], - r0: ty::Region<'tcx>) - -> ty::Region<'tcx> { - if !is_var_in_set(new_vars, r0) { - assert!(!r0.is_late_bound()); - return r0; - } - - let tainted = infcx.tainted_regions(snapshot, r0, TaintDirections::both()); - - let mut a_r = None; - let mut b_r = None; - let mut only_new_vars = true; - for r in &tainted { - if is_var_in_set(a_vars, *r) { - if a_r.is_some() { - return fresh_bound_variable(infcx, debruijn); - } else { - a_r = Some(*r); - } - } else if is_var_in_set(b_vars, *r) { - if b_r.is_some() { - return fresh_bound_variable(infcx, debruijn); - } else { - b_r = Some(*r); - } - } else if !is_var_in_set(new_vars, *r) { - only_new_vars = false; - } - } - - // NB---I do not believe this algorithm computes - // (necessarily) the GLB. As written it can - // spuriously fail. In particular, if there is a case - // like: |fn(&a)| and fn(fn(&b)), where a and b are - // free, it will return fn(&c) where c = GLB(a,b). If - // however this GLB is not defined, then the result is - // an error, even though something like - // "fn(fn(&X))" where X is bound would be a - // subtype of both of those. - // - // The problem is that if we were to return a bound - // variable, we'd be computing a lower-bound, but not - // necessarily the *greatest* lower-bound. - // - // Unfortunately, this problem is non-trivial to solve, - // because we do not know at the time of computing the GLB - // whether a GLB(a,b) exists or not, because we haven't - // run region inference (or indeed, even fully computed - // the region hierarchy!). The current algorithm seems to - // works ok in practice. - - if a_r.is_some() && b_r.is_some() && only_new_vars { - // Related to exactly one bound variable from each fn: - return rev_lookup(infcx, span, a_map, a_r.unwrap()); - } else if a_r.is_none() && b_r.is_none() { - // Not related to bound variables from either fn: - assert!(!r0.is_late_bound()); - return r0; - } else { - // Other: - return fresh_bound_variable(infcx, debruijn); - } - } - - fn rev_lookup<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - span: Span, - a_map: &BTreeMap>, - r: ty::Region<'tcx>) -> ty::Region<'tcx> - { - for (a_br, a_r) in a_map { - if *a_r == r { - return infcx.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, *a_br)); - } - } - span_bug!( - span, - "could not find original bound region for {:?}", - r); - } - - fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - debruijn: ty::DebruijnIndex) - -> ty::Region<'tcx> { - infcx.borrow_region_constraints().new_bound(infcx.tcx, debruijn) - } - } -} - -fn var_ids<'a, 'gcx, 'tcx>(fields: &CombineFields<'a, 'gcx, 'tcx>, - map: &BTreeMap>) - -> Vec { - map.iter() - .map(|(_, &r)| match *r { - ty::ReVar(r) => { r } - _ => { - span_bug!( - fields.trace.cause.span, - "found non-region-vid: {:?}", - r); - } - }) - .collect() -} - -fn is_var_in_set(new_vars: &[ty::RegionVid], r: ty::Region<'_>) -> bool { - match *r { - ty::ReVar(ref v) => new_vars.iter().any(|x| x == v), - _ => false - } } fn fold_regions_in<'a, 'gcx, 'tcx, T, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 55c7eef607bbe..8875b4169dd6e 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -15,7 +15,6 @@ use super::Subtype; use traits::ObligationCause; use ty::{self, Ty, TyCtxt}; -use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; /// "Least upper bound" (common supertype) @@ -76,31 +75,12 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> where T: Relate<'tcx> { debug!("binders(a={:?}, b={:?})", a, b); - let was_error = self.infcx().probe(|_snapshot| { - // Subtle: use a fresh combine-fields here because we recover - // from Err. Doing otherwise could propagate obligations out - // through our `self.obligations` field. - self.infcx() - .combine_fields(self.fields.trace.clone(), self.fields.param_env) - .higher_ranked_lub(a, b, self.a_is_expected) - .is_err() - }); - debug!("binders: was_error={:?}", was_error); // When higher-ranked types are involved, computing the LUB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. - match self.relate_with_variance(ty::Variance::Invariant, a, b) { - Ok(_) => Ok(a.clone()), - Err(err) => { - debug!("binders: error occurred, was_error={:?}", was_error); - if !was_error { - Err(TypeError::OldStyleLUB(Box::new(err))) - } else { - Err(err) - } - } - } + self.relate_with_variance(ty::Variance::Invariant, a, b)?; + Ok(a.clone()) } } diff --git a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs index b113a322d370a..d5233851db8c6 100644 --- a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs +++ b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs @@ -38,19 +38,13 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ImpliedOutlivesBounds< tcx: TyCtxt<'_, 'gcx, 'tcx>, canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible> { - // FIXME the query should take a `ImpliedOutlivesBounds` - let Canonical { - variables, - value: - ParamEnvAnd { - param_env, - value: ImpliedOutlivesBounds { ty }, - }, - } = canonicalized; - let canonicalized = Canonical { - variables, - value: param_env.and(ty), - }; + // FIXME this `unchecked_map` is only necessary because the + // query is defined as taking a `ParamEnvAnd`; it should + // take a `ImpliedOutlivesBounds` instead + let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { + let ImpliedOutlivesBounds { ty } = value; + param_env.and(ty) + }); tcx.implied_outlives_bounds(canonicalized) } diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index a36c5accd2a68..cd7c6d76eab57 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs @@ -59,18 +59,10 @@ where // FIXME convert to the type expected by the `dropck_outlives` // query. This should eventually be fixed by changing the // *underlying query*. - let Canonical { - variables, - value: - ParamEnvAnd { - param_env, - value: DropckOutlives { dropped_ty }, - }, - } = canonicalized; - let canonicalized = Canonical { - variables, - value: param_env.and(dropped_ty), - }; + let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { + let DropckOutlives { dropped_ty } = value; + param_env.and(dropped_ty) + }); tcx.dropck_outlives(canonicalized) } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 2ea16823cc65d..e2a5fdd362217 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1368,7 +1368,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // Winnow, but record the exact outcome of evaluation, which // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates.into_iter() + let mut candidates = candidates + .into_iter() .map(|c| match self.evaluate_candidate(stack, &c) { Ok(eval) if eval.may_apply() => Ok(Some(EvaluatedCandidate { candidate: c, @@ -1377,8 +1378,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Ok(_) => Ok(None), Err(OverflowError) => Err(Overflow), }) - .flat_map(Result::transpose) - .collect::, _>>()?; + .flat_map(Result::transpose) + .collect::, _>>()?; debug!( "winnowed to {} candidates for {:?}: {:?}", @@ -3004,9 +3005,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let self_ty = self.infcx .shallow_resolve(*obligation.self_ty().skip_binder()); let poly_trait_ref = match self_ty.sty { - ty::Dynamic(ref data, ..) => { - data.principal().with_self_ty(self.tcx(), self_ty) - } + ty::Dynamic(ref data, ..) => data.principal().with_self_ty(self.tcx(), self_ty), _ => span_bug!(obligation.cause.span, "object candidate with non-object"), }; @@ -3666,8 +3665,17 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { closure_def_id: DefId, substs: ty::ClosureSubsts<'tcx>, ) -> ty::PolyTraitRef<'tcx> { + debug!( + "closure_trait_ref_unnormalized(obligation={:?}, closure_def_id={:?}, substs={:?})", + obligation, closure_def_id, substs, + ); let closure_type = self.infcx.closure_sig(closure_def_id, substs); + debug!( + "closure_trait_ref_unnormalized: closure_type = {:?}", + closure_type + ); + // (1) Feels icky to skip the binder here, but OTOH we know // that the self-type is an unboxed closure type and hence is // in fact unparameterized (or at least does not reference any diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index ed6e372fe7637..855983042c026 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -53,8 +53,6 @@ pub enum TypeError<'tcx> { ProjectionMismatched(ExpectedFound), ProjectionBoundsLength(ExpectedFound), ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), - - OldStyleLUB(Box>), } #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] @@ -166,9 +164,6 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { report_maybe_different(f, &format!("trait `{}`", values.expected), &format!("trait `{}`", values.found)) } - OldStyleLUB(ref err) => { - write!(f, "{}", err) - } } } } @@ -266,12 +261,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } }, - OldStyleLUB(err) => { - db.note("this was previously accepted by the compiler but has been phased out"); - db.note("for more information, see https://github.com/rust-lang/rust/issues/45852"); - - self.note_and_explain_type_err(db, &err, sp); - } CyclicTy(ty) => { // Watch out for various cases of cyclic types and try to explain. if ty.is_closure() || ty.is_generator() { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index f8fc2cc83034f..ad26383df6ac2 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1522,10 +1522,17 @@ impl UniverseIndex { /// True if `self` can name a name from `other` -- in other words, /// if the set of names in `self` is a superset of those in - /// `other`. + /// `other` (`self >= other`). pub fn can_name(self, other: UniverseIndex) -> bool { self.private >= other.private } + + /// True if `self` cannot name some names from `other` -- in other + /// words, if the set of names in `self` is a strict subset of + /// those in `other` (`self < other`). + pub fn cannot_name(self, other: UniverseIndex) -> bool { + self.private < other.private + } } /// The "placeholder index" fully defines a placeholder region. @@ -1540,6 +1547,8 @@ pub struct Placeholder { pub name: BoundRegion, } +impl_stable_hash_for!(struct Placeholder { universe, name }); + /// When type checking, we use the `ParamEnv` to track /// details about the set of where-clauses that are in scope at this /// particular point. diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index ba5b714a0e7d9..05f5f923557ec 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -455,7 +455,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { ProjectionMismatched(x) => ProjectionMismatched(x), ProjectionBoundsLength(x) => ProjectionBoundsLength(x), Sorts(ref x) => return tcx.lift(x).map(Sorts), - OldStyleLUB(ref x) => return tcx.lift(x).map(OldStyleLUB), ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch) }) } @@ -1003,7 +1002,6 @@ EnumTypeFoldableImpl! { (ty::error::TypeError::ProjectionBoundsLength)(x), (ty::error::TypeError::Sorts)(x), (ty::error::TypeError::ExistentialMismatch)(x), - (ty::error::TypeError::OldStyleLUB)(x), } } diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 8fc54b6ff92a3..0c4140caee86f 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -107,7 +107,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // Run the MIR type-checker. let MirTypeckResults { constraints, - placeholder_indices, universal_region_relations, } = type_check::type_check( infcx, @@ -123,8 +122,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( elements, ); - let placeholder_indices = Rc::new(placeholder_indices); - if let Some(all_facts) = &mut all_facts { all_facts .universal_region @@ -136,11 +133,14 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // base constraints generated by the type-check. let var_origins = infcx.take_region_var_origins(); let MirTypeckRegionConstraints { + placeholder_indices, + placeholder_index_to_region: _, mut liveness_constraints, outlives_constraints, closure_bounds_mapping, type_tests, } = constraints; + let placeholder_indices = Rc::new(placeholder_indices); constraint_generation::generate_constraints( infcx, diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index ccb44c670f72f..3358e5851f939 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -8,23 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use borrow_check::nll::ConstraintDescription; -use borrow_check::nll::constraints::{OutlivesConstraint}; +use borrow_check::nll::constraints::OutlivesConstraint; use borrow_check::nll::region_infer::RegionInferenceContext; use borrow_check::nll::type_check::Locations; use borrow_check::nll::universal_regions::DefiningTy; -use util::borrowck_errors::{BorrowckErrors, Origin}; +use borrow_check::nll::ConstraintDescription; use rustc::hir::def_id::DefId; use rustc::infer::error_reporting::nice_region_error::NiceRegionError; use rustc::infer::InferCtxt; +use rustc::infer::NLLRegionVariableOrigin; use rustc::mir::{ConstraintCategory, Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_errors::{Diagnostic, DiagnosticBuilder}; use std::collections::VecDeque; +use syntax::errors::Applicability; use syntax::symbol::keywords; use syntax_pos::Span; -use syntax::errors::Applicability; +use util::borrowck_errors::{BorrowckErrors, Origin}; mod region_name; mod var_name; @@ -76,9 +77,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("best_blame_constraint(from_region={:?})", from_region); // Find all paths - let (path, target_region) = self - .find_constraint_paths_between_regions(from_region, target_test) - .unwrap(); + let (path, target_region) = + self.find_constraint_paths_between_regions(from_region, target_test) + .unwrap(); debug!( "best_blame_constraint: path={:#?}", path.iter() @@ -92,8 +93,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); // Classify each of the constraints along the path. - let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path - .iter() + let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path.iter() .map(|constraint| { if constraint.category == ConstraintCategory::ClosureBounds { self.retrieve_closure_constraint_info(mir, &constraint) @@ -137,13 +137,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { | ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false, - ConstraintCategory::TypeAnnotation - | ConstraintCategory::Return => true, + ConstraintCategory::TypeAnnotation | ConstraintCategory::Return => true, _ => constraint_sup_scc != target_scc, } }); if let Some(i) = best_choice { - return categorized_path[i] + return categorized_path[i]; } // If that search fails, that is.. unusual. Maybe everything @@ -179,6 +178,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { deque.push_back(from_region); while let Some(r) = deque.pop_front() { + debug!( + "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}", + from_region, + r, + self.region_value_str(r), + ); + // Check if we reached the region we were looking for. If so, // we can reconstruct the path that led to it and return it. if target_test(r) { @@ -206,9 +212,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // enqueue any regions we find, keeping track of how we // reached them. let fr_static = self.universal_regions.fr_static; - for constraint in self.constraint_graph.outgoing_edges(r, - &self.constraints, - fr_static) { + for constraint in self.constraint_graph + .outgoing_edges(r, &self.constraints, fr_static) + { assert_eq!(constraint.sup, r); let sub_region = constraint.sub; if let Trace::NotVisited = context[sub_region] { @@ -240,11 +246,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) { debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); - let (category, _, span) = self.best_blame_constraint( - mir, - fr, - |r| r == outlived_fr - ); + let (category, _, span) = self.best_blame_constraint(mir, fr, |r| { + self.provides_universal_region(r, fr, outlived_fr) + }); // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { @@ -260,23 +264,75 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.universal_regions.is_local_free_region(outlived_fr), ); - debug!("report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}", - fr_is_local, outlived_fr_is_local, category); + debug!( + "report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}", + fr_is_local, outlived_fr_is_local, category + ); match (category, fr_is_local, outlived_fr_is_local) { - (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => - self.report_fnmut_error(mir, infcx, mir_def_id, fr, outlived_fr, span, - errors_buffer), - (ConstraintCategory::Assignment, true, false) | - (ConstraintCategory::CallArgument, true, false) => - self.report_escaping_data_error(mir, infcx, mir_def_id, fr, outlived_fr, - category, span, errors_buffer), - _ => - self.report_general_error(mir, infcx, mir_def_id, fr, fr_is_local, - outlived_fr, outlived_fr_is_local, - category, span, errors_buffer), + (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => { + self.report_fnmut_error( + mir, + infcx, + mir_def_id, + fr, + outlived_fr, + span, + errors_buffer, + ) + } + (ConstraintCategory::Assignment, true, false) + | (ConstraintCategory::CallArgument, true, false) => self.report_escaping_data_error( + mir, + infcx, + mir_def_id, + fr, + outlived_fr, + category, + span, + errors_buffer, + ), + _ => self.report_general_error( + mir, + infcx, + mir_def_id, + fr, + fr_is_local, + outlived_fr, + outlived_fr_is_local, + category, + span, + errors_buffer, + ), }; } + /// We have a constraint `fr1: fr2` that is not satisfied, where + /// `fr2` represents some universal region. Here, `r` is some + /// region where we know that `fr1: r` and this function has the + /// job of determining whether `r` is "to blame" for the fact that + /// `fr1: fr2` is required. + /// + /// This is true under two conditions: + /// + /// - `r == fr2` + /// - `fr2` is `'static` and `r` is some placeholder in a universe + /// that cannot be named by `fr1`; in that case, we will require + /// that `fr1: 'static` because it is the only way to `fr1: r` to + /// be satisfied. (See `add_incompatible_universe`.) + fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid) -> bool { + debug!( + "provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", + r, fr1, fr2 + ); + let result = { + r == fr2 || { + fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r) + } + }; + debug!("provides_universal_region: result = {:?}", result); + result + } + /// Report a specialized error when `FnMut` closures return a reference to a captured variable. /// This function expects `fr` to be local and `outlived_fr` to not be local. /// @@ -303,10 +359,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { span: Span, errors_buffer: &mut Vec, ) { - let mut diag = infcx.tcx.sess.struct_span_err( - span, - "captured variable cannot escape `FnMut` closure body", - ); + let mut diag = infcx + .tcx + .sess + .struct_span_err(span, "captured variable cannot escape `FnMut` closure body"); // We should check if the return type of this closure is in fact a closure - in that // case, we can special case the error further. @@ -318,27 +374,28 @@ impl<'tcx> RegionInferenceContext<'tcx> { "returns a reference to a captured variable which escapes the closure body" }; - diag.span_label( - span, - message, - ); + diag.span_label(span, message); - match self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, &mut 1).source { - RegionNameSource::NamedEarlyBoundRegion(fr_span) | - RegionNameSource::NamedFreeRegion(fr_span) | - RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _) | - RegionNameSource::CannotMatchHirTy(fr_span, _) | - RegionNameSource::MatchedHirTy(fr_span) | - RegionNameSource::MatchedAdtAndSegment(fr_span) | - RegionNameSource::AnonRegionFromUpvar(fr_span, _) | - RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => { + match self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, &mut 1) + .source + { + RegionNameSource::NamedEarlyBoundRegion(fr_span) + | RegionNameSource::NamedFreeRegion(fr_span) + | RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _) + | RegionNameSource::CannotMatchHirTy(fr_span, _) + | RegionNameSource::MatchedHirTy(fr_span) + | RegionNameSource::MatchedAdtAndSegment(fr_span) + | RegionNameSource::AnonRegionFromUpvar(fr_span, _) + | RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => { diag.span_label(fr_span, "inferred to be a `FnMut` closure"); - }, - _ => {}, + } + _ => {} } - diag.note("`FnMut` closures only have access to their captured variables while they are \ - executing..."); + diag.note( + "`FnMut` closures only have access to their captured variables while they are \ + executing...", + ); diag.note("...therefore, they cannot allow references to captured variables to escape"); diag.buffer(errors_buffer); @@ -375,7 +432,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { DefiningTy::Closure(..) => "closure", DefiningTy::Generator(..) => "generator", DefiningTy::FnDef(..) => "function", - DefiningTy::Const(..) => "const" + DefiningTy::Const(..) => "const", }; // Revert to the normal error in these cases. @@ -384,12 +441,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { || (category == ConstraintCategory::Assignment && escapes_from == "function") || escapes_from == "const" { - return self.report_general_error(mir, infcx, mir_def_id, - fr, true, outlived_fr, false, - category, span, errors_buffer); + return self.report_general_error( + mir, + infcx, + mir_def_id, + fr, + true, + outlived_fr, + false, + category, + span, + errors_buffer, + ); } - let mut diag = infcx.tcx.borrowed_data_escapes_closure(span, escapes_from, Origin::Mir); + let mut diag = infcx + .tcx + .borrowed_data_escapes_closure(span, escapes_from, Origin::Mir); if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span { diag.span_label( @@ -410,7 +478,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { ), ); - diag.span_label(span, format!("`{}` escapes the {} body here", fr_name, escapes_from)); + diag.span_label( + span, + format!("`{}` escapes the {} body here", fr_name, escapes_from), + ); } diag.buffer(errors_buffer); @@ -452,31 +523,41 @@ impl<'tcx> RegionInferenceContext<'tcx> { let counter = &mut 1; let fr_name = self.give_region_a_name(infcx, mir, mir_def_id, fr, counter); fr_name.highlight_region_name(&mut diag); - let outlived_fr_name = self.give_region_a_name( - infcx, mir, mir_def_id, outlived_fr, counter); + let outlived_fr_name = + self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, counter); outlived_fr_name.highlight_region_name(&mut diag); - let mir_def_name = if infcx.tcx.is_closure(mir_def_id) { "closure" } else { "function" }; + let mir_def_name = if infcx.tcx.is_closure(mir_def_id) { + "closure" + } else { + "function" + }; match (category, outlived_fr_is_local, fr_is_local) { (ConstraintCategory::Return, true, _) => { - diag.span_label(span, format!( - "{} was supposed to return data with lifetime `{}` but it is returning \ - data with lifetime `{}`", - mir_def_name, outlived_fr_name, fr_name - )); - }, + diag.span_label( + span, + format!( + "{} was supposed to return data with lifetime `{}` but it is returning \ + data with lifetime `{}`", + mir_def_name, outlived_fr_name, fr_name + ), + ); + } _ => { - diag.span_label(span, format!( - "{}requires that `{}` must outlive `{}`", - category.description(), fr_name, outlived_fr_name, - )); - }, + diag.span_label( + span, + format!( + "{}requires that `{}` must outlive `{}`", + category.description(), + fr_name, + outlived_fr_name, + ), + ); + } } - self.add_static_impl_trait_suggestion( - infcx, &mut diag, fr, fr_name, outlived_fr, - ); + self.add_static_impl_trait_suggestion(infcx, &mut diag, fr, fr_name, outlived_fr); diag.buffer(errors_buffer); } @@ -499,17 +580,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr_name: RegionName, outlived_fr: RegionVid, ) { - if let ( - Some(f), - Some(ty::RegionKind::ReStatic) - ) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { + if let (Some(f), Some(ty::RegionKind::ReStatic)) = + (self.to_error_region(fr), self.to_error_region(outlived_fr)) + { if let Some(ty::TyS { sty: ty::TyKind::Opaque(did, substs), .. - }) = infcx.tcx.is_suitable_region(f) - .map(|r| r.def_id) - .map(|id| infcx.tcx.return_type_impl_trait(id)) - .unwrap_or(None) + }) = infcx + .tcx + .is_suitable_region(f) + .map(|r| r.def_id) + .map(|id| infcx.tcx.return_type_impl_trait(id)) + .unwrap_or(None) { // Check whether or not the impl trait return type is intended to capture // data with the static lifetime. @@ -522,10 +604,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut found = false; for predicate in bounds.predicates { if let ty::Predicate::TypeOutlives(binder) = predicate { - if let ty::OutlivesPredicate( - _, - ty::RegionKind::ReStatic - ) = binder.skip_binder() { + if let ty::OutlivesPredicate(_, ty::RegionKind::ReStatic) = + binder.skip_binder() + { found = true; break; } @@ -535,18 +616,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { found }; - debug!("add_static_impl_trait_suggestion: has_static_predicate={:?}", - has_static_predicate); + debug!( + "add_static_impl_trait_suggestion: has_static_predicate={:?}", + has_static_predicate + ); let static_str = keywords::StaticLifetime.name(); // If there is a static predicate, then the only sensible suggestion is to replace // fr with `'static`. if has_static_predicate { - diag.help( - &format!( - "consider replacing `{}` with `{}`", - fr_name, static_str, - ), - ); + diag.help(&format!( + "consider replacing `{}` with `{}`", + fr_name, static_str, + )); } else { // Otherwise, we should suggest adding a constraint on the return type. let span = infcx.tcx.def_span(*did); @@ -581,25 +662,48 @@ impl<'tcx> RegionInferenceContext<'tcx> { borrow_region: RegionVid, outlived_region: RegionVid, ) -> (ConstraintCategory, bool, Span, RegionName) { - let (category, from_closure, span) = self.best_blame_constraint( - mir, - borrow_region, - |r| r == outlived_region - ); - let outlived_fr_name = self.give_region_a_name( - infcx, mir, mir_def_id, outlived_region, &mut 1); + let (category, from_closure, span) = + self.best_blame_constraint(mir, borrow_region, |r| r == outlived_region); + let outlived_fr_name = + self.give_region_a_name(infcx, mir, mir_def_id, outlived_region, &mut 1); (category, from_closure, span, outlived_fr_name) } // Finds some region R such that `fr1: R` and `R` is live at // `elem`. crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { - // Find all paths - let (_path, r) = - self.find_constraint_paths_between_regions(fr1, |r| { - self.liveness_constraints.contains(r, elem) - }).unwrap(); - r + debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem); + self.find_constraint_paths_between_regions(fr1, |r| { + // First look for some `r` such that `fr1: r` and `r` is live at `elem` + debug!( + "find_sub_region_live_at: liveness_constraints for {:?} are {:?}", + r, + self.liveness_constraints.region_value_str(r), + ); + self.liveness_constraints.contains(r, elem) + }).or_else(|| { + // If we fail to find that, we may find some `r` such that + // `fr1: r` and `r` is a placeholder from some universe + // `fr1` cannot name. This would force `fr1` to be + // `'static`. + self.find_constraint_paths_between_regions(fr1, |r| { + self.cannot_name_placeholder(fr1, r) + }) + }) + .or_else(|| { + // If we fail to find THAT, it may be that `fr1` is a + // placeholder that cannot "fit" into its SCC. In that + // case, there should be some `r` where `fr1: r`, both + // `fr1` and `r` are in the same SCC, and `fr1` is a + // placeholder that `r` cannot name. We can blame that + // edge. + self.find_constraint_paths_between_regions(fr1, |r| { + self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r) + && self.cannot_name_placeholder(r, fr1) + }) + }) + .map(|(_path, r)| r) + .unwrap() } // Finds a good span to blame for the fact that `fr1` outlives `fr2`. @@ -609,34 +713,30 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr1: RegionVid, fr2: RegionVid, ) -> (ConstraintCategory, Span) { - let (category, _, span) = self.best_blame_constraint(mir, fr1, |r| r == fr2); + let (category, _, span) = + self.best_blame_constraint(mir, fr1, |r| self.provides_universal_region(r, fr1, fr2)); (category, span) } fn retrieve_closure_constraint_info( &self, mir: &Mir<'tcx>, - constraint: &OutlivesConstraint + constraint: &OutlivesConstraint, ) -> (ConstraintCategory, bool, Span) { let loc = match constraint.locations { Locations::All(span) => return (constraint.category, false, span), Locations::Single(loc) => loc, }; - let opt_span_category = self - .closure_bounds_mapping[&loc] - .get(&(constraint.sup, constraint.sub)); + let opt_span_category = + self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub)); opt_span_category .map(|&(category, span)| (category, true, span)) .unwrap_or((constraint.category, false, mir.source_info(loc).span)) } /// Returns `true` if a closure is inferred to be an `FnMut` closure. - crate fn is_closure_fn_mut( - &self, - infcx: &InferCtxt<'_, '_, 'tcx>, - fr: RegionVid, - ) -> bool { + crate fn is_closure_fn_mut(&self, infcx: &InferCtxt<'_, '_, 'tcx>, fr: RegionVid) -> bool { if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) { if let ty::BoundRegion::BrEnv = free_region.bound_region { if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty { @@ -648,4 +748,24 @@ impl<'tcx> RegionInferenceContext<'tcx> { false } + + /// If `r2` represents a placeholder region, then this returns + /// true if `r1` cannot name that placeholder in its + /// value. Otherwise, returns false. + fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool { + debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2); + + match self.definitions[r2].origin { + NLLRegionVariableOrigin::Placeholder(placeholder) => { + let universe1 = self.definitions[r1].universe; + debug!( + "cannot_name_value_of: universe1={:?} placeholder={:?}", + universe1, placeholder + ); + universe1.cannot_name(placeholder.universe) + } + + NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential => false, + } + } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 50fd4afcd7eca..376f445924270 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -345,6 +345,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { if scc_universe.can_name(placeholder.universe) { self.scc_values.add_element(scc, placeholder); } else { + debug!( + "init_free_and_bound_regions: placeholder {:?} is \ + not compatible with universe {:?} of its SCC {:?}", + placeholder, + scc_universe, + scc, + ); self.add_incompatible_universe(scc); } } @@ -471,6 +478,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut constraints: Vec<_> = self.constraints.iter().collect(); constraints.sort(); constraints + .into_iter() + .map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub))) + .collect::>() }); // To propagate constraints, we walk the DAG induced by the @@ -560,6 +570,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `'a` with `'b` and not `'static`. But it will have to do for /// now. fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) { + debug!("add_incompatible_universe(scc={:?})", scc); + let fr_static = self.universal_regions.fr_static; self.scc_values.add_all_points(scc); self.scc_values.add_element(scc, fr_static); @@ -1226,6 +1238,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); let longer_fr_scc = self.constraint_sccs.scc(longer_fr); + debug!( + "check_bound_universal_region: longer_fr_scc={:?}", + longer_fr_scc, + ); // If we have some bound universal region `'a`, then the only // elements it can contain is itself -- we don't know anything @@ -1242,6 +1258,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { Some(v) => v, None => return, }; + debug!("check_bound_universal_region: error_element = {:?}", error_element); // Find the region that introduced this `error_element`. let error_region = match error_element { diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 994f20a011d65..5904138ef603a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -8,21 +8,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; +use borrow_check::nll::constraints::OutlivesConstraint; use borrow_check::nll::region_infer::TypeTest; -use borrow_check::nll::type_check::Locations; +use borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use borrow_check::nll::universal_regions::UniversalRegions; +use borrow_check::nll::ToRegionVid; use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc::infer::region_constraints::{GenericKind, VerifyBound}; -use rustc::infer::{self, SubregionOrigin}; +use rustc::infer::{self, InferCtxt, SubregionOrigin}; use rustc::mir::ConstraintCategory; use rustc::ty::subst::UnpackedKind; use rustc::ty::{self, TyCtxt}; use syntax_pos::DUMMY_SP; crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, @@ -30,32 +32,30 @@ crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'tcx>, locations: Locations, category: ConstraintCategory, - outlives_constraints: &'a mut ConstraintSet, - type_tests: &'a mut Vec>, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, } impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { crate fn new( - tcx: TyCtxt<'a, 'gcx, 'tcx>, + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, locations: Locations, category: ConstraintCategory, - outlives_constraints: &'a mut ConstraintSet, - type_tests: &'a mut Vec>, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, ) -> Self { Self { - tcx, + infcx, + tcx: infcx.tcx, universal_regions, region_bound_pairs, implicit_region_bound, param_env, locations, category, - outlives_constraints, - type_tests, + constraints, } } @@ -113,7 +113,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { } fn verify_to_type_test( - &self, + &mut self, generic_kind: GenericKind<'tcx>, region: ty::Region<'tcx>, verify_bound: VerifyBound<'tcx>, @@ -128,22 +128,30 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { } } - fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { - self.universal_regions.to_region_vid(r) + fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> ty::RegionVid { + if let ty::RePlaceholder(placeholder) = r { + self.constraints + .placeholder_region(self.infcx, *placeholder) + .to_region_vid() + } else { + self.universal_regions.to_region_vid(r) + } } fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) { - self.outlives_constraints.push(OutlivesConstraint { - locations: self.locations, - category: self.category, - sub, - sup, - }); + self.constraints + .outlives_constraints + .push(OutlivesConstraint { + locations: self.locations, + category: self.category, + sub, + sup, + }); } fn add_type_test(&mut self, type_test: TypeTest<'tcx>) { debug!("add_type_test(type_test={:?})", type_test); - self.type_tests.push(type_test); + self.constraints.type_tests.push(type_test); } } @@ -156,8 +164,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) { - let b = self.universal_regions.to_region_vid(b); - let a = self.universal_regions.to_region_vid(a); + let b = self.to_region_vid(b); + let a = self.to_region_vid(a); self.add_outlives(b, a); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs index f8c839e4d3f88..3d0f3d9fc7d8d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -271,15 +271,14 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> { for data in constraint_sets { constraint_conversion::ConstraintConversion::new( - self.infcx.tcx, + self.infcx, &self.universal_regions, &self.region_bound_pairs, self.implicit_region_bound, self.param_env, Locations::All(DUMMY_SP), ConstraintCategory::Internal, - &mut self.constraints.outlives_constraints, - &mut self.constraints.type_tests, + &mut self.constraints, ).convert_all(&data); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 3098acffa23dc..0a873cd84ff3d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -16,6 +16,7 @@ use borrow_check::location::LocationTable; use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::values::LivenessValues; +use borrow_check::nll::region_infer::values::PlaceholderIndex; use borrow_check::nll::region_infer::values::PlaceholderIndices; use borrow_check::nll::region_infer::values::RegionValueElements; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest}; @@ -28,11 +29,12 @@ use borrow_check::nll::ToRegionVid; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedPlaces; +use either::Either; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::outlives::env::RegionBoundPairs; -use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; +use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin}; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext}; @@ -44,14 +46,13 @@ use rustc::traits::{ObligationCause, PredicateObligations}; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::{Subst, Substs, UnpackedKind}; use rustc::ty::{self, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::indexed_vec::IndexVec; use std::rc::Rc; use std::{fmt, iter}; use syntax_pos::{Span, DUMMY_SP}; use transform::{MirPass, MirSource}; -use either::Either; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - macro_rules! span_mirbug { ($context:expr, $elem:expr, $($message:tt)*) => ({ $crate::borrow_check::nll::type_check::mirbug( @@ -126,12 +127,13 @@ pub(crate) fn type_check<'gcx, 'tcx>( ) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { + placeholder_indices: PlaceholderIndices::default(), + placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::new(elements), outlives_constraints: ConstraintSet::default(), closure_bounds_mapping: Default::default(), type_tests: Vec::default(), }; - let mut placeholder_indices = PlaceholderIndices::default(); let CreateResult { universal_region_relations, @@ -151,7 +153,6 @@ pub(crate) fn type_check<'gcx, 'tcx>( borrow_set, all_facts, constraints: &mut constraints, - placeholder_indices: &mut placeholder_indices, }; type_check_internal( @@ -175,7 +176,6 @@ pub(crate) fn type_check<'gcx, 'tcx>( MirTypeckResults { constraints, - placeholder_indices, universal_region_relations, } } @@ -730,18 +730,30 @@ struct BorrowCheckContext<'a, 'tcx: 'a> { all_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - placeholder_indices: &'a mut PlaceholderIndices, } crate struct MirTypeckResults<'tcx> { crate constraints: MirTypeckRegionConstraints<'tcx>, - crate placeholder_indices: PlaceholderIndices, crate universal_region_relations: Rc>, } /// A collection of region constraints that must be satisfied for the /// program to be considered well-typed. crate struct MirTypeckRegionConstraints<'tcx> { + /// Maps from a `ty::Placeholder` to the corresponding + /// `PlaceholderIndex` bit that we will use for it. + /// + /// To keep everything in sync, do not insert this set + /// directly. Instead, use the `placeholder_region` helper. + crate placeholder_indices: PlaceholderIndices, + + /// Each time we add a placeholder to `placeholder_indices`, we + /// also create a corresponding "representative" region vid for + /// that wraps it. This vector tracks those. This way, when we + /// convert the same `ty::RePlaceholder(p)` twice, we can map to + /// the same underlying `RegionVid`. + crate placeholder_index_to_region: IndexVec>, + /// In general, the type-checker is not responsible for enforcing /// liveness constraints; this job falls to the region inferencer, /// which performs a liveness analysis. However, in some limited @@ -759,6 +771,25 @@ crate struct MirTypeckRegionConstraints<'tcx> { crate type_tests: Vec>, } +impl MirTypeckRegionConstraints<'tcx> { + fn placeholder_region( + &mut self, + infcx: &InferCtxt<'_, '_, 'tcx>, + placeholder: ty::Placeholder, + ) -> ty::Region<'tcx> { + let placeholder_index = self.placeholder_indices.insert(placeholder); + match self.placeholder_index_to_region.get(placeholder_index) { + Some(&v) => v, + None => { + let origin = NLLRegionVariableOrigin::Placeholder(placeholder); + let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe); + self.placeholder_index_to_region.push(region); + region + } + } + } +} + /// The `Locations` type summarizes *where* region constraints are /// required to hold. Normally, this is at a particular point which /// created the obligation, but for constraints that the user gave, we @@ -888,15 +919,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(ref mut borrowck_context) = self.borrowck_context { constraint_conversion::ConstraintConversion::new( - self.infcx.tcx, + self.infcx, borrowck_context.universal_regions, self.region_bound_pairs, self.implicit_region_bound, self.param_env, locations, category, - &mut borrowck_context.constraints.outlives_constraints, - &mut borrowck_context.constraints.type_tests, + &mut borrowck_context.constraints, ).convert_all(&data); } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs index 13ebf46bdb149..b82efb29f6e56 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -76,16 +76,20 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> { } fn next_existential_region_var(&mut self) -> ty::Region<'tcx> { - let origin = NLLRegionVariableOrigin::Existential; - self.infcx.next_nll_region_var(origin) + if let Some(_) = &mut self.borrowck_context { + let origin = NLLRegionVariableOrigin::Existential; + self.infcx.next_nll_region_var(origin) + } else { + self.infcx.tcx.types.re_erased + } } fn next_placeholder_region(&mut self, placeholder: ty::Placeholder) -> ty::Region<'tcx> { - let origin = NLLRegionVariableOrigin::Placeholder(placeholder); if let Some(borrowck_context) = &mut self.borrowck_context { - borrowck_context.placeholder_indices.insert(placeholder); + borrowck_context.constraints.placeholder_region(self.infcx, placeholder) + } else { + self.infcx.tcx.types.re_erased } - self.infcx.next_nll_region_var(origin) } fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 3f4d187813d5d..fb7c237a536bd 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -13,10 +13,12 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use astconv::AstConv; +use middle::region; use rustc::hir::def_id::DefId; use rustc::infer::{InferOk, InferResult}; use rustc::infer::LateBoundRegionConversionTime; use rustc::infer::type_variable::TypeVariableOrigin; +use rustc::traits::Obligation; use rustc::traits::error_reporting::ArgKind; use rustc::ty::{self, ToPolyTraitRef, Ty, GenericParamDefKind}; use rustc::ty::fold::TypeFoldable; @@ -479,7 +481,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Along the way, it also writes out entries for types that the user // wrote into our tables, which are then later used by the privacy // check. - match self.check_supplied_sig_against_expectation(expr_def_id, decl, &closure_sigs) { + match self.check_supplied_sig_against_expectation(expr_def_id, decl, body, &closure_sigs) { Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), Err(_) => return self.sig_of_closure_no_expectation(expr_def_id, decl, body), } @@ -523,6 +525,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { &self, expr_def_id: DefId, decl: &hir::FnDecl, + body: &hir::Body, expected_sigs: &ClosureSignatures<'tcx>, ) -> InferResult<'tcx, ()> { // Get the signature S that the user gave. @@ -575,6 +578,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } = self.at(cause, self.param_env) .eq(*expected_ty, supplied_ty)?; all_obligations.extend(obligations); + + // Also, require that the supplied type must outlive + // the closure body. + let closure_body_region = self.tcx.mk_region( + ty::ReScope( + region::Scope { + id: body.value.hir_id.local_id, + data: region::ScopeData::Node, + }, + ), + ); + all_obligations.push( + Obligation::new( + cause.clone(), + self.param_env, + ty::Predicate::TypeOutlives( + ty::Binder::dummy( + ty::OutlivesPredicate( + supplied_ty, + closure_body_region, + ), + ), + ), + ), + ); } let (supplied_output_ty, _) = self.infcx.replace_late_bound_regions_with_fresh_var( diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 17784a4681d00..9ac733b159ba6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -976,7 +976,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> { o_ty }; - let c_ty = self.fcx.inh.infcx.canonicalize_response(&revealed_ty); + let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(&revealed_ty); debug!("visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}", ty.hir_id, o_ty, revealed_ty, c_ty); self.fcx.tables.borrow_mut().user_provided_tys_mut().insert(ty.hir_id, c_ty); @@ -2137,7 +2137,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { method.substs[i] } }); - self.infcx.canonicalize_response(&UserSubsts { + self.infcx.canonicalize_user_type_annotation(&UserSubsts { substs: just_method_substs, user_self_ty: None, // not relevant here }) @@ -2181,7 +2181,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ); if !substs.is_noop() { - let user_substs = self.infcx.canonicalize_response(&UserSubsts { + let user_substs = self.infcx.canonicalize_user_type_annotation(&UserSubsts { substs, user_self_ty, }); diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs index 7ca1d01f20b7b..75f19d133e0e9 100644 --- a/src/test/mir-opt/basic_assignment.rs +++ b/src/test/mir-opt/basic_assignment.rs @@ -56,7 +56,7 @@ fn main() { // StorageLive(_4); // _4 = std::option::Option>::None; // FakeRead(ForLet, _4); -// AscribeUserType(_4, o, UserTypeProjection { base: Ty(Canonical { variables: [], value: std::option::Option> }), projs: [] }); +// AscribeUserType(_4, o, UserTypeProjection { base: Ty(Canonical { max_universe: U0, variables: [], value: std::option::Option> }), projs: [] }); // StorageLive(_5); // StorageLive(_6); // _6 = move _4; diff --git a/src/test/ui/lub-glb/old-lub-glb-hr.stderr b/src/test/ui/lub-glb/old-lub-glb-hr.stderr index 9b40062bd57b9..4182b5258884f 100644 --- a/src/test/ui/lub-glb/old-lub-glb-hr.stderr +++ b/src/test/ui/lub-glb/old-lub-glb-hr.stderr @@ -11,8 +11,6 @@ LL | | }; | = note: expected type `for<'r, 's> fn(&'r u8, &'s u8)` found type `for<'a> fn(&'a u8, &'a u8)` - = note: this was previously accepted by the compiler but has been phased out - = note: for more information, see https://github.com/rust-lang/rust/issues/45852 error: aborting due to previous error diff --git a/src/test/ui/lub-glb/old-lub-glb-object.stderr b/src/test/ui/lub-glb/old-lub-glb-object.stderr index 79442bd108a67..bb8cb45eec397 100644 --- a/src/test/ui/lub-glb/old-lub-glb-object.stderr +++ b/src/test/ui/lub-glb/old-lub-glb-object.stderr @@ -11,8 +11,6 @@ LL | | }; | = note: expected type `&dyn for<'a, 'b> Foo<&'a u8, &'b u8>` found type `&dyn for<'a> Foo<&'a u8, &'a u8>` - = note: this was previously accepted by the compiler but has been phased out - = note: for more information, see https://github.com/rust-lang/rust/issues/45852 error: aborting due to previous error diff --git a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr index 901ace59d33a0..88383190cbce6 100644 --- a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr +++ b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr @@ -1,4 +1,4 @@ -error: user substs: Canonical { variables: [], value: UserSubsts { substs: [u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [], value: UserSubsts { substs: [u32], user_self_ty: None } } --> $DIR/dump-adt-brace-struct.rs:28:5 | LL | SomeStruct:: { t: 22 }; //~ ERROR [u32] diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.stderr b/src/test/ui/nll/user-annotations/dump-fn-method.stderr index a26be359fc4aa..3beb994a4e8a5 100644 --- a/src/test/ui/nll/user-annotations/dump-fn-method.stderr +++ b/src/test/ui/nll/user-annotations/dump-fn-method.stderr @@ -1,22 +1,22 @@ -error: user substs: Canonical { variables: [], value: UserSubsts { substs: [u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [], value: UserSubsts { substs: [u32], user_self_ty: None } } --> $DIR/dump-fn-method.rs:36:13 | LL | let x = foo::; //~ ERROR [u32] | ^^^^^^^^^^ -error: user substs: Canonical { variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [?0, u32, ?1], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [?0, u32, ?1], user_self_ty: None } } --> $DIR/dump-fn-method.rs:42:13 | LL | let x = <_ as Bazoom>::method::<_>; //~ ERROR [?0, u32, ?1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: user substs: Canonical { variables: [], value: UserSubsts { substs: [u8, u16, u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [], value: UserSubsts { substs: [u8, u16, u32], user_self_ty: None } } --> $DIR/dump-fn-method.rs:46:13 | LL | let x = >::method::; //~ ERROR [u8, u16, u32] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: user substs: Canonical { variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [?0, ?1, u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [?0, ?1, u32], user_self_ty: None } } --> $DIR/dump-fn-method.rs:54:5 | LL | y.method::(44, 66); //~ ERROR [?0, ?1, u32]