Auto merge of #55305 - nikomatsakis:universes-refactor-3, r=scalexm

universes refactor 3

Some more refactorings from my universe branch. These are getting a bit more "invasive" -- they start to plumb the universe information through the canonicalization process. As of yet though I don't **believe** this branch changes our behavior in any notable way, though I'm marking the branch as `WIP` to give myself a chance to verify this.

r? @scalexm
This commit is contained in:
bors 2018-11-02 01:19:17 +00:00
commit 5eda136f62
27 changed files with 667 additions and 557 deletions

View File

@ -1229,7 +1229,7 @@ for traits::VtableGeneratorData<'gcx, N> where N: HashStable<StableHashingContex
impl_stable_hash_for!(
impl<'tcx, V> 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 {

View File

@ -107,6 +107,20 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
)
}
pub fn canonicalize_user_type_annotation<V>(&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))

View File

@ -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<W>(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<ty::UniverseIndex, _> = 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<CanonicalVarInfo>,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> CanonicalVarValues<'tcx> {
let var_values: IndexVec<BoundTyIndex, Kind<'tcx>> = 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>
}

View File

@ -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(),
};

View File

@ -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())
}
}

View File

@ -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<T>(&mut self, a: &Binder<T>, b: &Binder<T>, a_is_expected: bool)
-> RelateResult<'tcx, Binder<T>>
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<ty::BoundRegion, ty::Region<'tcx>>,
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<T>(&mut self, a: &Binder<T>, b: &Binder<T>, a_is_expected: bool)
-> RelateResult<'tcx, Binder<T>>
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<ty::BoundRegion, ty::Region<'tcx>>,
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<X>(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<ty::BoundRegion, ty::Region<'tcx>>,
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<ty::BoundRegion, ty::Region<'tcx>>)
-> Vec<ty::RegionVid> {
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>,

View File

@ -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())
}
}

View File

@ -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<CanonicalizedQueryResponse<'gcx, Self::QueryResponse>> {
// 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<Ty>`; 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)
}

View File

@ -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)
}

View File

@ -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::<Result<Vec<_>, _>>()?;
.flat_map(Result::transpose)
.collect::<Result<Vec<_>, _>>()?;
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

View File

@ -53,8 +53,6 @@ pub enum TypeError<'tcx> {
ProjectionMismatched(ExpectedFound<DefId>),
ProjectionBoundsLength(ExpectedFound<usize>),
ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>),
OldStyleLUB(Box<TypeError<'tcx>>),
}
#[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() {

View File

@ -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.

View File

@ -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),
}
}

View File

@ -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,

View File

@ -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<Diagnostic>,
) {
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,
}
}
}

View File

@ -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::<Vec<_>>()
});
// 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 {

View File

@ -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<TypeTest<'tcx>>,
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<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
locations: Locations,
category: ConstraintCategory,
outlives_constraints: &'a mut ConstraintSet,
type_tests: &'a mut Vec<TypeTest<'tcx>>,
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);
}

View File

@ -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);
}

View File

@ -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<AllFacts>,
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<UniversalRegionRelations<'tcx>>,
}
/// 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<PlaceholderIndex, ty::Region<'tcx>>,
/// 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<TypeTest<'tcx>>,
}
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);
}
}

View File

@ -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> {

View File

@ -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(

View File

@ -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,
});

View File

@ -56,7 +56,7 @@ fn main() {
// StorageLive(_4);
// _4 = std::option::Option<std::boxed::Box<u32>>::None;
// FakeRead(ForLet, _4);
// AscribeUserType(_4, o, UserTypeProjection { base: Ty(Canonical { variables: [], value: std::option::Option<std::boxed::Box<u32>> }), projs: [] });
// AscribeUserType(_4, o, UserTypeProjection { base: Ty(Canonical { max_universe: U0, variables: [], value: std::option::Option<std::boxed::Box<u32>> }), projs: [] });
// StorageLive(_5);
// StorageLive(_6);
// _6 = move _4;

View File

@ -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

View File

@ -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

View File

@ -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::<u32> { t: 22 }; //~ ERROR [u32]

View File

@ -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::<u32>; //~ 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<u32>>::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 = <u8 as Bazoom<u16>>::method::<u32>; //~ 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::<u32>(44, 66); //~ ERROR [?0, ?1, u32]