From 31f4b570025f46d0b29c79f2b493d2db65438b30 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Thu, 28 Sep 2017 23:13:43 -0400 Subject: [PATCH] Turn `trans_fulfill_obligation` into a query Part of #44891 --- src/librustc/dep_graph/dep_node.rs | 3 +- src/librustc/ich/impls_ty.rs | 126 ++++++++++++++++++++++++++ src/librustc/traits/mod.rs | 2 + src/librustc/traits/trans/mod.rs | 138 +++++++++++++---------------- src/librustc/ty/instance.rs | 3 +- src/librustc/ty/maps/config.rs | 6 ++ src/librustc/ty/maps/keys.rs | 9 ++ src/librustc/ty/maps/mod.rs | 11 +++ src/librustc/ty/maps/plumbing.rs | 1 + src/librustc_trans/monomorphize.rs | 9 +- 10 files changed, 226 insertions(+), 82 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 36236ff25b0..98e2e72f225 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -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; @@ -486,6 +486,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>> }, diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index fe060aaf426..6e55365980c 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -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> +for traits::Vtable<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + 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> +for traits::VtableImplData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + 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> +for traits::VtableDefaultImplData where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + 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> +for traits::VtableObjectData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + 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> +for traits::VtableBuiltinData where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableBuiltinData { + ref nested, + } = *self; + nested.hash_stable(hcx, hasher); + } +} + +impl<'gcx, N> HashStable> +for traits::VtableClosureData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + 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> +for traits::VtableFnPointerData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableFnPointerData { + fn_ty, + ref nested, + } = *self; + fn_ty.hash_stable(hcx, hasher); + nested.hash_stable(hcx, hasher); + } +} + +impl<'gcx, N> HashStable> +for traits::VtableGeneratorData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + 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); + } +} diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index a1817f18106..bdfbefd1364 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -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 }; } diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs index 947e7117c4e..aa3179dd01f 100644 --- a/src/librustc/traits/trans/mod.rs +++ b/src/librustc/traits/trans/mod.rs @@ -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(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>>, project_cache: RefCell>>, } 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)), } } diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 35ab1cec4cf..600b2572f92 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -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: diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index c8520c5be2d..dee1516e0f8 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -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)) diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index e37cf669797..1bd4f3e657e 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -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 diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 3dcbeda94bc..2001de88803 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -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, [] fn specialization_graph_of: SpecializationGraph(DefId) -> Rc, [] 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) } diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 88b619558d9..88e49369ce6 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -705,6 +705,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 | diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index cd2a881451c..471be439a8f 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -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() }