Convert implied_outlives_bounds to a query
This commit is contained in:
parent
ef9781358d
commit
e5286d9fa6
@ -649,6 +649,7 @@ define_dep_nodes!( <'tcx>
|
||||
[input] OutputFilenames,
|
||||
[] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>),
|
||||
[] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>),
|
||||
[] ImpliedOutlivesBounds(CanonicalTyGoal<'tcx>),
|
||||
[] DropckOutlives(CanonicalTyGoal<'tcx>),
|
||||
[] EvaluateObligation(CanonicalPredicateGoal<'tcx>),
|
||||
[] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>),
|
||||
|
@ -1,216 +0,0 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use infer::InferCtxt;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
use traits::{FulfillmentContext, TraitEngine, TraitEngineExt};
|
||||
use ty::{self, Ty, TypeFoldable};
|
||||
use ty::outlives::Component;
|
||||
use ty::wf;
|
||||
|
||||
/// Outlives bounds are relationships between generic parameters,
|
||||
/// whether they both be regions (`'a: 'b`) or whether types are
|
||||
/// involved (`T: 'a`). These relationships can be extracted from the
|
||||
/// full set of predicates we understand or also from types (in which
|
||||
/// case they are called implied bounds). They are fed to the
|
||||
/// `OutlivesEnv` which in turn is supplied to the region checker and
|
||||
/// other parts of the inference system.
|
||||
#[derive(Debug)]
|
||||
pub enum OutlivesBound<'tcx> {
|
||||
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
|
||||
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
|
||||
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
|
||||
/// Implied bounds are region relationships that we deduce
|
||||
/// automatically. The idea is that (e.g.) a caller must check that a
|
||||
/// function's argument types are well-formed immediately before
|
||||
/// calling that fn, and hence the *callee* can assume that its
|
||||
/// argument types are well-formed. This may imply certain relationships
|
||||
/// between generic parameters. For example:
|
||||
///
|
||||
/// fn foo<'a,T>(x: &'a T)
|
||||
///
|
||||
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
|
||||
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `param_env`, the where-clauses in scope
|
||||
/// - `body_id`, the body-id to use when normalizing assoc types.
|
||||
/// Note that this may cause outlives obligations to be injected
|
||||
/// into the inference context with this body-id.
|
||||
/// - `ty`, the type that we are supposed to assume is WF.
|
||||
/// - `span`, a span to use when normalizing, hopefully not important,
|
||||
/// might be useful if a `bug!` occurs.
|
||||
pub fn implied_outlives_bounds(
|
||||
&self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
) -> Vec<OutlivesBound<'tcx>> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
// Sometimes when we ask what it takes for T: WF, we get back that
|
||||
// U: WF is required; in that case, we push U onto this stack and
|
||||
// process it next. Currently (at least) these resulting
|
||||
// predicates are always guaranteed to be a subset of the original
|
||||
// type, so we need not fear non-termination.
|
||||
let mut wf_types = vec![ty];
|
||||
|
||||
let mut implied_bounds = vec![];
|
||||
|
||||
let mut fulfill_cx = FulfillmentContext::new();
|
||||
|
||||
while let Some(ty) = wf_types.pop() {
|
||||
// Compute the obligations for `ty` to be well-formed. If `ty` is
|
||||
// an unresolved inference variable, just substituted an empty set
|
||||
// -- because the return type here is going to be things we *add*
|
||||
// to the environment, it's always ok for this set to be smaller
|
||||
// than the ultimate set. (Note: normally there won't be
|
||||
// unresolved inference variables here anyway, but there might be
|
||||
// during typeck under some circumstances.)
|
||||
let obligations = wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]);
|
||||
|
||||
// NB: All of these predicates *ought* to be easily proven
|
||||
// true. In fact, their correctness is (mostly) implied by
|
||||
// other parts of the program. However, in #42552, we had
|
||||
// an annoying scenario where:
|
||||
//
|
||||
// - Some `T::Foo` gets normalized, resulting in a
|
||||
// variable `_1` and a `T: Trait<Foo=_1>` constraint
|
||||
// (not sure why it couldn't immediately get
|
||||
// solved). This result of `_1` got cached.
|
||||
// - These obligations were dropped on the floor here,
|
||||
// rather than being registered.
|
||||
// - Then later we would get a request to normalize
|
||||
// `T::Foo` which would result in `_1` being used from
|
||||
// the cache, but hence without the `T: Trait<Foo=_1>`
|
||||
// constraint. As a result, `_1` never gets resolved,
|
||||
// and we get an ICE (in dropck).
|
||||
//
|
||||
// Therefore, we register any predicates involving
|
||||
// inference variables. We restrict ourselves to those
|
||||
// involving inference variables both for efficiency and
|
||||
// to avoids duplicate errors that otherwise show up.
|
||||
fulfill_cx.register_predicate_obligations(
|
||||
self,
|
||||
obligations
|
||||
.iter()
|
||||
.filter(|o| o.predicate.has_infer_types())
|
||||
.cloned(),
|
||||
);
|
||||
|
||||
// From the full set of obligations, just filter down to the
|
||||
// region relationships.
|
||||
implied_bounds.extend(obligations.into_iter().flat_map(|obligation| {
|
||||
assert!(!obligation.has_escaping_regions());
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Subtype(..) |
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::ConstEvaluatable(..) => vec![],
|
||||
|
||||
ty::Predicate::WellFormed(subty) => {
|
||||
wf_types.push(subty);
|
||||
vec![]
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref data) => match data.no_late_bound_regions() {
|
||||
None => vec![],
|
||||
Some(ty::OutlivesPredicate(r_a, r_b)) => {
|
||||
vec![OutlivesBound::RegionSubRegion(r_b, r_a)]
|
||||
}
|
||||
},
|
||||
|
||||
ty::Predicate::TypeOutlives(ref data) => match data.no_late_bound_regions() {
|
||||
None => vec![],
|
||||
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
|
||||
let ty_a = self.resolve_type_vars_if_possible(&ty_a);
|
||||
let components = tcx.outlives_components(ty_a);
|
||||
Self::implied_bounds_from_components(r_b, components)
|
||||
}
|
||||
},
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Ensure that those obligations that we had to solve
|
||||
// get solved *here*.
|
||||
match fulfill_cx.select_all_or_error(self) {
|
||||
Ok(()) => (),
|
||||
Err(errors) => self.report_fulfillment_errors(&errors, None, false),
|
||||
}
|
||||
|
||||
implied_bounds
|
||||
}
|
||||
|
||||
/// When we have an implied bound that `T: 'a`, we can further break
|
||||
/// this down to determine what relationships would have to hold for
|
||||
/// `T: 'a` to hold. We get to assume that the caller has validated
|
||||
/// those relationships.
|
||||
fn implied_bounds_from_components(
|
||||
sub_region: ty::Region<'tcx>,
|
||||
sup_components: Vec<Component<'tcx>>,
|
||||
) -> Vec<OutlivesBound<'tcx>> {
|
||||
sup_components
|
||||
.into_iter()
|
||||
.flat_map(|component| {
|
||||
match component {
|
||||
Component::Region(r) =>
|
||||
vec![OutlivesBound::RegionSubRegion(sub_region, r)],
|
||||
Component::Param(p) =>
|
||||
vec![OutlivesBound::RegionSubParam(sub_region, p)],
|
||||
Component::Projection(p) =>
|
||||
vec![OutlivesBound::RegionSubProjection(sub_region, p)],
|
||||
Component::EscapingProjection(_) =>
|
||||
// If the projection has escaping regions, don't
|
||||
// try to infer any implied bounds even for its
|
||||
// free components. This is conservative, because
|
||||
// the caller will still have to prove that those
|
||||
// free components outlive `sub_region`. But the
|
||||
// idea is that the WAY that the caller proves
|
||||
// that may change in the future and we want to
|
||||
// give ourselves room to get smarter here.
|
||||
vec![],
|
||||
Component::UnresolvedInferenceVariable(..) =>
|
||||
vec![],
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn explicit_outlives_bounds<'tcx>(
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
|
||||
debug!("explicit_outlives_bounds()");
|
||||
param_env
|
||||
.caller_bounds
|
||||
.into_iter()
|
||||
.filter_map(move |predicate| match predicate {
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Subtype(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::TypeOutlives(..) |
|
||||
ty::Predicate::ConstEvaluatable(..) => None,
|
||||
ty::Predicate::RegionOutlives(ref data) => data.no_late_bound_regions().map(
|
||||
|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a),
|
||||
),
|
||||
})
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
|
||||
use infer::{GenericKind, InferCtxt};
|
||||
use infer::outlives::free_region_map::FreeRegionMap;
|
||||
use infer::outlives::bounds::{self, OutlivesBound};
|
||||
use traits::query::outlives_bounds::{self, OutlivesBound};
|
||||
use ty::{self, Ty};
|
||||
|
||||
use syntax::ast;
|
||||
@ -50,7 +50,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
|
||||
region_bound_pairs: vec![],
|
||||
};
|
||||
|
||||
env.add_outlives_bounds(None, bounds::explicit_outlives_bounds(param_env));
|
||||
env.add_outlives_bounds(None, outlives_bounds::explicit_outlives_bounds(param_env));
|
||||
|
||||
env
|
||||
}
|
||||
|
@ -12,5 +12,4 @@
|
||||
|
||||
pub mod env;
|
||||
pub mod free_region_map;
|
||||
pub mod bounds;
|
||||
pub mod obligations;
|
||||
|
@ -23,6 +23,7 @@ pub mod dropck_outlives;
|
||||
pub mod evaluate_obligation;
|
||||
pub mod normalize;
|
||||
pub mod normalize_erasing_regions;
|
||||
pub mod outlives_bounds;
|
||||
pub mod type_op;
|
||||
|
||||
pub type CanonicalProjectionGoal<'tcx> =
|
||||
|
169
src/librustc/traits/query/outlives_bounds.rs
Normal file
169
src/librustc/traits/query/outlives_bounds.rs
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use infer::InferCtxt;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
use rustc_data_structures::small_vec::SmallVec;
|
||||
use traits::{FulfillmentContext, ObligationCause, TraitEngine, TraitEngineExt};
|
||||
use traits::query::NoSolution;
|
||||
use ty::{self, Ty, TyCtxt};
|
||||
|
||||
use ich::StableHashingContext;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
|
||||
StableHasherResult};
|
||||
use std::mem;
|
||||
|
||||
/// Outlives bounds are relationships between generic parameters,
|
||||
/// whether they both be regions (`'a: 'b`) or whether types are
|
||||
/// involved (`T: 'a`). These relationships can be extracted from the
|
||||
/// full set of predicates we understand or also from types (in which
|
||||
/// case they are called implied bounds). They are fed to the
|
||||
/// `OutlivesEnv` which in turn is supplied to the region checker and
|
||||
/// other parts of the inference system.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum OutlivesBound<'tcx> {
|
||||
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
|
||||
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
|
||||
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
|
||||
}
|
||||
|
||||
EnumLiftImpl! {
|
||||
impl<'a, 'tcx> Lift<'tcx> for self::OutlivesBound<'a> {
|
||||
type Lifted = self::OutlivesBound<'tcx>;
|
||||
(self::OutlivesBound::RegionSubRegion)(a, b),
|
||||
(self::OutlivesBound::RegionSubParam)(a, b),
|
||||
(self::OutlivesBound::RegionSubProjection)(a, b),
|
||||
}
|
||||
}
|
||||
|
||||
EnumTypeFoldableImpl! {
|
||||
impl<'tcx> TypeFoldable<'tcx> for self::OutlivesBound<'tcx> {
|
||||
(self::OutlivesBound::RegionSubRegion)(a, b),
|
||||
(self::OutlivesBound::RegionSubParam)(a, b),
|
||||
(self::OutlivesBound::RegionSubProjection)(a, b),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for OutlivesBound<'tcx> {
|
||||
fn hash_stable<W: StableHasherResult>(&self,
|
||||
hcx: &mut StableHashingContext<'a>,
|
||||
hasher: &mut StableHasher<W>) {
|
||||
mem::discriminant(self).hash_stable(hcx, hasher);
|
||||
match *self {
|
||||
OutlivesBound::RegionSubRegion(ref a, ref b) => {
|
||||
a.hash_stable(hcx, hasher);
|
||||
b.hash_stable(hcx, hasher);
|
||||
}
|
||||
OutlivesBound::RegionSubParam(ref a, ref b) => {
|
||||
a.hash_stable(hcx, hasher);
|
||||
b.hash_stable(hcx, hasher);
|
||||
}
|
||||
OutlivesBound::RegionSubProjection(ref a, ref b) => {
|
||||
a.hash_stable(hcx, hasher);
|
||||
b.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
|
||||
/// Implied bounds are region relationships that we deduce
|
||||
/// automatically. The idea is that (e.g.) a caller must check that a
|
||||
/// function's argument types are well-formed immediately before
|
||||
/// calling that fn, and hence the *callee* can assume that its
|
||||
/// argument types are well-formed. This may imply certain relationships
|
||||
/// between generic parameters. For example:
|
||||
///
|
||||
/// fn foo<'a,T>(x: &'a T)
|
||||
///
|
||||
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
|
||||
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `param_env`, the where-clauses in scope
|
||||
/// - `body_id`, the body-id to use when normalizing assoc types.
|
||||
/// Note that this may cause outlives obligations to be injected
|
||||
/// into the inference context with this body-id.
|
||||
/// - `ty`, the type that we are supposed to assume is WF.
|
||||
/// - `span`, a span to use when normalizing, hopefully not important,
|
||||
/// might be useful if a `bug!` occurs.
|
||||
pub fn implied_outlives_bounds(
|
||||
&self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
_body_id: ast::NodeId,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
) -> Vec<OutlivesBound<'tcx>> {
|
||||
let mut orig_values = SmallVec::new();
|
||||
let key = self.canonicalize_query(¶m_env.and(ty), &mut orig_values);
|
||||
let result = match self.tcx.global_tcx().implied_outlives_bounds(key) {
|
||||
Ok(r) => r,
|
||||
Err(NoSolution) => {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
"implied_outlives_bounds failed to solve all obligations"
|
||||
);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
assert!(result.value.is_proven());
|
||||
|
||||
let result = self.instantiate_query_result_and_region_obligations(
|
||||
&ObligationCause::dummy(), param_env, &orig_values, &result);
|
||||
debug!("implied_outlives_bounds for {:?}: {:?}", ty, result);
|
||||
let result = match result {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
"implied_outlives_bounds failed to instantiate"
|
||||
);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
|
||||
// Instantiation may have produced new inference variables and constraints on those
|
||||
// variables. Process these constraints.
|
||||
let mut fulfill_cx = FulfillmentContext::new();
|
||||
fulfill_cx.register_predicate_obligations(self, result.obligations);
|
||||
if let Err(_) = fulfill_cx.select_all_or_error(self) {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
"implied_outlives_bounds failed to solve obligations from instantiation"
|
||||
);
|
||||
}
|
||||
|
||||
result.value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn explicit_outlives_bounds<'tcx>(
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
|
||||
debug!("explicit_outlives_bounds()");
|
||||
param_env
|
||||
.caller_bounds
|
||||
.into_iter()
|
||||
.filter_map(move |predicate| match predicate {
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Subtype(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::TypeOutlives(..) |
|
||||
ty::Predicate::ConstEvaluatable(..) => None,
|
||||
ty::Predicate::RegionOutlives(ref data) => data.no_late_bound_regions().map(
|
||||
|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a),
|
||||
),
|
||||
})
|
||||
}
|
@ -87,6 +87,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::normalize_projection_ty<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::implied_outlives_bounds<'tcx> {
|
||||
fn describe(_tcx: TyCtxt, goal: CanonicalTyGoal<'tcx>) -> String {
|
||||
format!("computing implied outlives bounds for `{:?}`", goal)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::dropck_outlives<'tcx> {
|
||||
fn describe(_tcx: TyCtxt, goal: CanonicalTyGoal<'tcx>) -> String {
|
||||
format!("computing dropck types for `{:?}`", goal)
|
||||
|
@ -38,6 +38,7 @@ use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal,
|
||||
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal, NoSolution};
|
||||
use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult};
|
||||
use traits::query::normalize::NormalizationResult;
|
||||
use traits::query::outlives_bounds::OutlivesBound;
|
||||
use traits::specialization_graph;
|
||||
use traits::Clauses;
|
||||
use ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
|
||||
@ -551,6 +552,13 @@ define_queries! { <'tcx>
|
||||
ParamEnvAnd<'tcx, Ty<'tcx>>
|
||||
) -> Ty<'tcx>,
|
||||
|
||||
[] fn implied_outlives_bounds: ImpliedOutlivesBounds(
|
||||
CanonicalTyGoal<'tcx>
|
||||
) -> Result<
|
||||
Lrc<Canonical<'tcx, canonical::QueryResult<'tcx, Vec<OutlivesBound<'tcx>>>>>,
|
||||
NoSolution,
|
||||
>,
|
||||
|
||||
/// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead.
|
||||
[] fn dropck_outlives: DropckOutlives(
|
||||
CanonicalTyGoal<'tcx>
|
||||
|
@ -1036,6 +1036,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
|
||||
DepKind::ConstValueToAllocation |
|
||||
DepKind::NormalizeProjectionTy |
|
||||
DepKind::NormalizeTyAfterErasingRegions |
|
||||
DepKind::ImpliedOutlivesBounds |
|
||||
DepKind::DropckOutlives |
|
||||
DepKind::EvaluateObligation |
|
||||
DepKind::TypeOpEq |
|
||||
|
@ -56,6 +56,7 @@ CloneTypeFoldableAndLiftImpls! {
|
||||
::ty::BoundRegion,
|
||||
::ty::ClosureKind,
|
||||
::ty::IntVarValue,
|
||||
::ty::ParamTy,
|
||||
::syntax_pos::Span,
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,10 @@
|
||||
use either::Either;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::{self, BodyOwnerKind, HirId};
|
||||
use rustc::infer::outlives::bounds::{self, OutlivesBound};
|
||||
use rustc::infer::outlives::free_region_map::FreeRegionRelations;
|
||||
use rustc::infer::region_constraints::GenericKind;
|
||||
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
|
||||
use rustc::traits::query::outlives_bounds::{self, OutlivesBound};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::{self, ClosureSubsts, GeneratorSubsts, RegionVid, Ty, TyCtxt};
|
||||
@ -494,7 +494,10 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
|
||||
let num_universals = self.infcx.num_region_vars();
|
||||
|
||||
// Insert the facts we know from the predicates. Why? Why not.
|
||||
self.add_outlives_bounds(&indices, bounds::explicit_outlives_bounds(param_env));
|
||||
self.add_outlives_bounds(
|
||||
&indices,
|
||||
outlives_bounds::explicit_outlives_bounds(param_env),
|
||||
);
|
||||
|
||||
// Add the implied bounds from inputs and outputs.
|
||||
for ty in inputs_and_output {
|
||||
|
185
src/librustc_traits/implied_outlives_bounds.rs
Normal file
185
src/librustc_traits/implied_outlives_bounds.rs
Normal file
@ -0,0 +1,185 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Provider for the `implied_outlives_bounds` query.
|
||||
//! Do not call this query directory. See [`rustc::traits::query::implied_outlives_bounds`].
|
||||
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::infer::canonical::{self, Canonical};
|
||||
use rustc::traits::{TraitEngine, TraitEngineExt};
|
||||
use rustc::traits::query::outlives_bounds::OutlivesBound;
|
||||
use rustc::traits::query::{CanonicalTyGoal, Fallible, NoSolution};
|
||||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc::ty::outlives::Component;
|
||||
use rustc::ty::query::Providers;
|
||||
use rustc::ty::wf;
|
||||
use syntax::ast::DUMMY_NODE_ID;
|
||||
use syntax::codemap::DUMMY_SP;
|
||||
use rustc::traits::FulfillmentContext;
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
||||
crate fn provide(p: &mut Providers) {
|
||||
*p = Providers {
|
||||
implied_outlives_bounds,
|
||||
..*p
|
||||
};
|
||||
}
|
||||
|
||||
fn implied_outlives_bounds<'tcx>(
|
||||
tcx: TyCtxt<'_, 'tcx, 'tcx>,
|
||||
goal: CanonicalTyGoal<'tcx>,
|
||||
) -> Result<
|
||||
Lrc<Canonical<'tcx, canonical::QueryResult<'tcx, Vec<OutlivesBound<'tcx>>>>>,
|
||||
NoSolution,
|
||||
> {
|
||||
tcx.infer_ctxt()
|
||||
.enter_canonical_trait_query(&goal, |infcx, _fulfill_cx, key| {
|
||||
let (param_env, ty) = key.into_parts();
|
||||
compute_implied_outlives_bounds(&infcx, param_env, ty)
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_implied_outlives_bounds<'tcx>(
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: Ty<'tcx>
|
||||
) -> Fallible<Vec<OutlivesBound<'tcx>>> {
|
||||
let tcx = infcx.tcx;
|
||||
|
||||
// Sometimes when we ask what it takes for T: WF, we get back that
|
||||
// U: WF is required; in that case, we push U onto this stack and
|
||||
// process it next. Currently (at least) these resulting
|
||||
// predicates are always guaranteed to be a subset of the original
|
||||
// type, so we need not fear non-termination.
|
||||
let mut wf_types = vec![ty];
|
||||
|
||||
let mut implied_bounds = vec![];
|
||||
|
||||
let mut fulfill_cx = FulfillmentContext::new();
|
||||
|
||||
while let Some(ty) = wf_types.pop() {
|
||||
// Compute the obligations for `ty` to be well-formed. If `ty` is
|
||||
// an unresolved inference variable, just substituted an empty set
|
||||
// -- because the return type here is going to be things we *add*
|
||||
// to the environment, it's always ok for this set to be smaller
|
||||
// than the ultimate set. (Note: normally there won't be
|
||||
// unresolved inference variables here anyway, but there might be
|
||||
// during typeck under some circumstances.)
|
||||
let obligations =
|
||||
wf::obligations(infcx, param_env, DUMMY_NODE_ID, ty, DUMMY_SP).unwrap_or(vec![]);
|
||||
|
||||
// NB: All of these predicates *ought* to be easily proven
|
||||
// true. In fact, their correctness is (mostly) implied by
|
||||
// other parts of the program. However, in #42552, we had
|
||||
// an annoying scenario where:
|
||||
//
|
||||
// - Some `T::Foo` gets normalized, resulting in a
|
||||
// variable `_1` and a `T: Trait<Foo=_1>` constraint
|
||||
// (not sure why it couldn't immediately get
|
||||
// solved). This result of `_1` got cached.
|
||||
// - These obligations were dropped on the floor here,
|
||||
// rather than being registered.
|
||||
// - Then later we would get a request to normalize
|
||||
// `T::Foo` which would result in `_1` being used from
|
||||
// the cache, but hence without the `T: Trait<Foo=_1>`
|
||||
// constraint. As a result, `_1` never gets resolved,
|
||||
// and we get an ICE (in dropck).
|
||||
//
|
||||
// Therefore, we register any predicates involving
|
||||
// inference variables. We restrict ourselves to those
|
||||
// involving inference variables both for efficiency and
|
||||
// to avoids duplicate errors that otherwise show up.
|
||||
fulfill_cx.register_predicate_obligations(
|
||||
infcx,
|
||||
obligations
|
||||
.iter()
|
||||
.filter(|o| o.predicate.has_infer_types())
|
||||
.cloned(),
|
||||
);
|
||||
|
||||
// From the full set of obligations, just filter down to the
|
||||
// region relationships.
|
||||
implied_bounds.extend(obligations.into_iter().flat_map(|obligation| {
|
||||
assert!(!obligation.has_escaping_regions());
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Subtype(..) |
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::ConstEvaluatable(..) => vec![],
|
||||
|
||||
ty::Predicate::WellFormed(subty) => {
|
||||
wf_types.push(subty);
|
||||
vec![]
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref data) => match data.no_late_bound_regions() {
|
||||
None => vec![],
|
||||
Some(ty::OutlivesPredicate(r_a, r_b)) => {
|
||||
vec![OutlivesBound::RegionSubRegion(r_b, r_a)]
|
||||
}
|
||||
},
|
||||
|
||||
ty::Predicate::TypeOutlives(ref data) => match data.no_late_bound_regions() {
|
||||
None => vec![],
|
||||
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
|
||||
let ty_a = infcx.resolve_type_vars_if_possible(&ty_a);
|
||||
let components = tcx.outlives_components(ty_a);
|
||||
implied_bounds_from_components(r_b, components)
|
||||
}
|
||||
},
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Ensure that those obligations that we had to solve
|
||||
// get solved *here*.
|
||||
match fulfill_cx.select_all_or_error(infcx) {
|
||||
Ok(()) => Ok(implied_bounds),
|
||||
Err(_) => Err(NoSolution),
|
||||
}
|
||||
}
|
||||
|
||||
/// When we have an implied bound that `T: 'a`, we can further break
|
||||
/// this down to determine what relationships would have to hold for
|
||||
/// `T: 'a` to hold. We get to assume that the caller has validated
|
||||
/// those relationships.
|
||||
fn implied_bounds_from_components(
|
||||
sub_region: ty::Region<'tcx>,
|
||||
sup_components: Vec<Component<'tcx>>,
|
||||
) -> Vec<OutlivesBound<'tcx>> {
|
||||
sup_components
|
||||
.into_iter()
|
||||
.flat_map(|component| {
|
||||
match component {
|
||||
Component::Region(r) =>
|
||||
vec![OutlivesBound::RegionSubRegion(sub_region, r)],
|
||||
Component::Param(p) =>
|
||||
vec![OutlivesBound::RegionSubParam(sub_region, p)],
|
||||
Component::Projection(p) =>
|
||||
vec![OutlivesBound::RegionSubProjection(sub_region, p)],
|
||||
Component::EscapingProjection(_) =>
|
||||
// If the projection has escaping regions, don't
|
||||
// try to infer any implied bounds even for its
|
||||
// free components. This is conservative, because
|
||||
// the caller will still have to prove that those
|
||||
// free components outlive `sub_region`. But the
|
||||
// idea is that the WAY that the caller proves
|
||||
// that may change in the future and we want to
|
||||
// give ourselves room to get smarter here.
|
||||
vec![],
|
||||
Component::UnresolvedInferenceVariable(..) =>
|
||||
vec![],
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
@ -33,6 +33,7 @@ extern crate syntax_pos;
|
||||
mod chalk_context;
|
||||
mod dropck_outlives;
|
||||
mod evaluate_obligation;
|
||||
mod implied_outlives_bounds;
|
||||
mod normalize_projection_ty;
|
||||
mod normalize_erasing_regions;
|
||||
pub mod lowering;
|
||||
@ -43,6 +44,7 @@ use rustc::ty::query::Providers;
|
||||
pub fn provide(p: &mut Providers) {
|
||||
dropck_outlives::provide(p);
|
||||
evaluate_obligation::provide(p);
|
||||
implied_outlives_bounds::provide(p);
|
||||
lowering::provide(p);
|
||||
normalize_projection_ty::provide(p);
|
||||
normalize_erasing_regions::provide(p);
|
||||
|
Loading…
Reference in New Issue
Block a user