Auto merge of #44967 - wesleywiser:trans_fulfill_obligation, r=nikomatsakis

Turn `trans_fulfill_obligation` into a query

Part of #44891
This commit is contained in:
bors 2017-10-12 07:48:11 +00:00
commit 39fd958520
10 changed files with 226 additions and 82 deletions

View File

@ -65,7 +65,7 @@ use hir::map::DefPathHash;
use hir::{HirId, ItemLocalId};
use ich::Fingerprint;
use ty::{TyCtxt, Instance, InstanceDef, ParamEnvAnd, Ty};
use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty};
use ty::subst::Substs;
use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
use ich::StableHashingContext;
@ -505,6 +505,7 @@ define_dep_nodes!( <'tcx>
[] InstanceSymbolName { instance: Instance<'tcx> },
[] SpecializationGraph(DefId),
[] ObjectSafety(DefId),
[] FulfillObligation { param_env: ParamEnv<'tcx>, trait_ref: PolyTraitRef<'tcx> },
[] IsCopy { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },
[] IsSized { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> },

View File

@ -841,3 +841,129 @@ impl_stable_hash_for!(struct ::util::common::ErrorReported {});
impl_stable_hash_for!(tuple_struct ::middle::reachable::ReachableSet {
reachable_set
});
impl<'gcx, N> HashStable<StableHashingContext<'gcx>>
for traits::Vtable<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
use traits::Vtable::*;
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
&VtableImpl(ref table_impl) => table_impl.hash_stable(hcx, hasher),
&VtableDefaultImpl(ref table_def_impl) => table_def_impl.hash_stable(hcx, hasher),
&VtableParam(ref table_param) => table_param.hash_stable(hcx, hasher),
&VtableObject(ref table_obj) => table_obj.hash_stable(hcx, hasher),
&VtableBuiltin(ref table_builtin) => table_builtin.hash_stable(hcx, hasher),
&VtableClosure(ref table_closure) => table_closure.hash_stable(hcx, hasher),
&VtableFnPointer(ref table_fn_pointer) => table_fn_pointer.hash_stable(hcx, hasher),
&VtableGenerator(ref table_generator) => table_generator.hash_stable(hcx, hasher),
}
}
}
impl<'gcx, N> HashStable<StableHashingContext<'gcx>>
for traits::VtableImplData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let traits::VtableImplData {
impl_def_id,
substs,
ref nested,
} = *self;
impl_def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
nested.hash_stable(hcx, hasher);
}
}
impl<'gcx, N> HashStable<StableHashingContext<'gcx>>
for traits::VtableDefaultImplData<N> where N: HashStable<StableHashingContext<'gcx>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let traits::VtableDefaultImplData {
trait_def_id,
ref nested,
} = *self;
trait_def_id.hash_stable(hcx, hasher);
nested.hash_stable(hcx, hasher);
}
}
impl<'gcx, N> HashStable<StableHashingContext<'gcx>>
for traits::VtableObjectData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let traits::VtableObjectData {
upcast_trait_ref,
vtable_base,
ref nested,
} = *self;
upcast_trait_ref.hash_stable(hcx, hasher);
vtable_base.hash_stable(hcx, hasher);
nested.hash_stable(hcx, hasher);
}
}
impl<'gcx, N> HashStable<StableHashingContext<'gcx>>
for traits::VtableBuiltinData<N> where N: HashStable<StableHashingContext<'gcx>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let traits::VtableBuiltinData {
ref nested,
} = *self;
nested.hash_stable(hcx, hasher);
}
}
impl<'gcx, N> HashStable<StableHashingContext<'gcx>>
for traits::VtableClosureData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let traits::VtableClosureData {
closure_def_id,
substs,
ref nested,
} = *self;
closure_def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
nested.hash_stable(hcx, hasher);
}
}
impl<'gcx, N> HashStable<StableHashingContext<'gcx>>
for traits::VtableFnPointerData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let traits::VtableFnPointerData {
fn_ty,
ref nested,
} = *self;
fn_ty.hash_stable(hcx, hasher);
nested.hash_stable(hcx, hasher);
}
}
impl<'gcx, N> HashStable<StableHashingContext<'gcx>>
for traits::VtableGeneratorData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let traits::VtableGeneratorData {
closure_def_id,
substs,
ref nested,
} = *self;
closure_def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
nested.hash_stable(hcx, hasher);
}
}

View File

@ -835,6 +835,7 @@ pub fn provide(providers: &mut ty::maps::Providers) {
is_object_safe: object_safety::is_object_safe_provider,
specialization_graph_of: specialize::specialization_graph_provider,
specializes: specialize::specializes,
trans_fulfill_obligation: trans::trans_fulfill_obligation,
..*providers
};
}
@ -844,6 +845,7 @@ pub fn provide_extern(providers: &mut ty::maps::Providers) {
is_object_safe: object_safety::is_object_safe_provider,
specialization_graph_of: specialize::specialization_graph_provider,
specializes: specialize::specializes,
trans_fulfill_obligation: trans::trans_fulfill_obligation,
..*providers
};
}

View File

@ -17,85 +17,77 @@ use dep_graph::{DepGraph, DepKind, DepTrackingMap, DepTrackingMapConfig};
use infer::TransNormalize;
use std::cell::RefCell;
use std::marker::PhantomData;
use syntax::ast;
use syntax_pos::Span;
use syntax_pos::DUMMY_SP;
use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, Vtable};
use ty::{self, Ty, TyCtxt};
use ty::subst::{Subst, Substs};
use ty::fold::{TypeFoldable, TypeFolder};
use util::common::MemoizationMap;
/// Attempts to resolve an obligation to a vtable.. The result is
/// a shallow vtable resolution -- meaning that we do not
/// (necessarily) resolve all nested obligations on the impl. Note
/// that type check should guarantee to us that all nested
/// obligations *could be* resolved if we wanted to.
/// Assumes that this is run after the entire crate has been successfully type-checked.
pub fn trans_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>,
(param_env, trait_ref):
(ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>))
-> Vtable<'tcx, ()>
{
// Remove any references to regions; this helps improve caching.
let trait_ref = ty.erase_regions(&trait_ref);
debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})",
(param_env, trait_ref), trait_ref.def_id());
// Do the initial selection for the obligation. This yields the
// shallow result we are looking for -- that is, what specific impl.
ty.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);
let obligation_cause = ObligationCause::dummy();
let obligation = Obligation::new(obligation_cause,
param_env,
trait_ref.to_poly_trait_predicate());
let selection = match selcx.select(&obligation) {
Ok(Some(selection)) => selection,
Ok(None) => {
// Ambiguity can happen when monomorphizing during trans
// expands to some humongo type that never occurred
// statically -- this humongo type can then overflow,
// leading to an ambiguous result. So report this as an
// overflow bug, since I believe this is the only case
// where ambiguity can result.
bug!("Encountered ambiguity selecting `{:?}` during trans, \
presuming due to overflow",
trait_ref)
}
Err(e) => {
bug!("Encountered error `{:?}` selecting `{:?}` during trans",
e, trait_ref)
}
};
debug!("fulfill_obligation: selection={:?}", selection);
// Currently, we use a fulfillment context to completely resolve
// all nested obligations. This is because they can inform the
// inference of the impl's type parameters.
let mut fulfill_cx = FulfillmentContext::new();
let vtable = selection.map(|predicate| {
debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate);
fulfill_cx.register_predicate_obligation(&infcx, predicate);
});
let vtable = infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable);
info!("Cache miss: {:?} => {:?}", trait_ref, vtable);
vtable
})
}
impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
/// Attempts to resolve an obligation to a vtable.. The result is
/// a shallow vtable resolution -- meaning that we do not
/// (necessarily) resolve all nested obligations on the impl. Note
/// that type check should guarantee to us that all nested
/// obligations *could be* resolved if we wanted to.
/// Assumes that this is run after the entire crate has been successfully type-checked.
pub fn trans_fulfill_obligation(self,
span: Span,
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>)
-> Vtable<'tcx, ()>
{
// Remove any references to regions; this helps improve caching.
let trait_ref = self.erase_regions(&trait_ref);
self.trans_trait_caches.trait_cache.memoize((param_env, trait_ref), || {
debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})",
(param_env, trait_ref), trait_ref.def_id());
// Do the initial selection for the obligation. This yields the
// shallow result we are looking for -- that is, what specific impl.
self.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);
let obligation_cause = ObligationCause::misc(span,
ast::DUMMY_NODE_ID);
let obligation = Obligation::new(obligation_cause,
param_env,
trait_ref.to_poly_trait_predicate());
let selection = match selcx.select(&obligation) {
Ok(Some(selection)) => selection,
Ok(None) => {
// Ambiguity can happen when monomorphizing during trans
// expands to some humongo type that never occurred
// statically -- this humongo type can then overflow,
// leading to an ambiguous result. So report this as an
// overflow bug, since I believe this is the only case
// where ambiguity can result.
debug!("Encountered ambiguity selecting `{:?}` during trans, \
presuming due to overflow",
trait_ref);
self.sess.span_fatal(span,
"reached the recursion limit during monomorphization \
(selection ambiguity)");
}
Err(e) => {
span_bug!(span, "Encountered error `{:?}` selecting `{:?}` during trans",
e, trait_ref)
}
};
debug!("fulfill_obligation: selection={:?}", selection);
// Currently, we use a fulfillment context to completely resolve
// all nested obligations. This is because they can inform the
// inference of the impl's type parameters.
let mut fulfill_cx = FulfillmentContext::new();
let vtable = selection.map(|predicate| {
debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate);
fulfill_cx.register_predicate_obligation(&infcx, predicate);
});
let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable);
info!("Cache miss: {:?} => {:?}", trait_ref, vtable);
vtable
})
})
}
/// Monomorphizes a type from the AST by first applying the in-scope
/// substitutions and then normalizing any associated types.
pub fn trans_apply_param_substs<T>(self,
@ -149,14 +141,12 @@ impl<'a, 'gcx> TypeFolder<'gcx, 'gcx> for AssociatedTypeNormalizer<'a, 'gcx> {
/// Specializes caches used in trans -- in particular, they assume all
/// types are fully monomorphized and that free regions can be erased.
pub struct TransTraitCaches<'tcx> {
trait_cache: RefCell<DepTrackingMap<TraitSelectionCache<'tcx>>>,
project_cache: RefCell<DepTrackingMap<ProjectionCache<'tcx>>>,
}
impl<'tcx> TransTraitCaches<'tcx> {
pub fn new(graph: DepGraph) -> Self {
TransTraitCaches {
trait_cache: RefCell::new(DepTrackingMap::new(graph.clone())),
project_cache: RefCell::new(DepTrackingMap::new(graph)),
}
}

View File

@ -13,7 +13,6 @@ use ty::{self, Ty, TypeFoldable, Substs, TyCtxt};
use ty::subst::{Kind, Subst};
use traits;
use syntax::abi::Abi;
use syntax::codemap::DUMMY_SP;
use util::ppaux;
use std::fmt;
@ -212,7 +211,7 @@ fn resolve_associated_item<'a, 'tcx>(
def_id, trait_id, rcvr_substs);
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs);
let vtbl = tcx.trans_fulfill_obligation(DUMMY_SP, param_env, ty::Binder(trait_ref));
let vtbl = tcx.trans_fulfill_obligation((param_env, ty::Binder(trait_ref)));
// Now that we know which impl is being used, we can dispatch to
// the actual function:

View File

@ -221,6 +221,12 @@ impl<'tcx> QueryDescription for queries::is_mir_available<'tcx> {
}
}
impl<'tcx> QueryDescription for queries::trans_fulfill_obligation<'tcx> {
fn describe(tcx: TyCtxt, key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> String {
format!("checking if `{}` fulfills its obligations", tcx.item_path_str(key.1.def_id()))
}
}
impl<'tcx> QueryDescription for queries::trait_impls_of<'tcx> {
fn describe(tcx: TyCtxt, def_id: DefId) -> String {
format!("trait impls of `{}`", tcx.item_path_str(def_id))

View File

@ -134,6 +134,15 @@ impl Key for (MirSuite, MirPassIndex, DefId) {
}
}
impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) {
fn map_crate(&self) -> CrateNum {
self.1.def_id().krate
}
fn default_span(&self, tcx: TyCtxt) -> Span {
tcx.def_span(self.1.def_id())
}
}
impl<'tcx> Key for Ty<'tcx> {
fn map_crate(&self) -> CrateNum {
LOCAL_CRATE

View File

@ -30,6 +30,7 @@ use middle::trans::{CodegenUnit, Stats};
use mir;
use session::CompileResult;
use session::config::OutputFilenames;
use traits::Vtable;
use traits::specialization_graph;
use ty::{self, CrateInherentImpls, Ty, TyCtxt};
use ty::layout::{Layout, LayoutError};
@ -228,6 +229,8 @@ define_maps! { <'tcx>
[] fn const_is_rvalue_promotable_to_static: ConstIsRvaluePromotableToStatic(DefId) -> bool,
[] fn is_mir_available: IsMirAvailable(DefId) -> bool,
[] fn trans_fulfill_obligation: fulfill_obligation_dep_node(
(ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> Vtable<'tcx, ()>,
[] fn trait_impls_of: TraitImpls(DefId) -> Rc<ty::trait_def::TraitImpls>,
[] fn specialization_graph_of: SpecializationGraph(DefId) -> Rc<specialization_graph::Graph>,
[] fn is_object_safe: ObjectSafety(DefId) -> bool,
@ -347,6 +350,14 @@ fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstr
}
}
fn fulfill_obligation_dep_node<'tcx>((param_env, trait_ref):
(ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> DepConstructor<'tcx> {
DepConstructor::FulfillObligation {
param_env,
trait_ref
}
}
fn coherent_trait_dep_node<'tcx>((_, def_id): (CrateNum, DefId)) -> DepConstructor<'tcx> {
DepConstructor::CoherenceCheckTrait(def_id)
}

View File

@ -748,6 +748,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
DepKind::TypeParamPredicates |
DepKind::CodegenUnit |
DepKind::CompileCodegenUnit |
DepKind::FulfillObligation |
// These are just odd
DepKind::Null |

View File

@ -15,8 +15,6 @@ use rustc::ty::adjustment::CustomCoerceUnsized;
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::ty::{self, Ty, TyCtxt};
use syntax::codemap::DUMMY_SP;
pub use rustc::ty::Instance;
fn fn_once_adapter_instance<'a, 'tcx>(
@ -110,13 +108,14 @@ pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
source_ty: Ty<'tcx>,
target_ty: Ty<'tcx>)
-> CustomCoerceUnsized {
let def_id = tcx.lang_items().coerce_unsized_trait().unwrap();
let trait_ref = ty::Binder(ty::TraitRef {
def_id: tcx.lang_items().coerce_unsized_trait().unwrap(),
def_id: def_id,
substs: tcx.mk_substs_trait(source_ty, &[target_ty])
});
match tcx.trans_fulfill_obligation(
DUMMY_SP, ty::ParamEnv::empty(traits::Reveal::All), trait_ref) {
match tcx.trans_fulfill_obligation( (ty::ParamEnv::empty(traits::Reveal::All), trait_ref)) {
traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => {
tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap()
}