From 31f4b570025f46d0b29c79f2b493d2db65438b30 Mon Sep 17 00:00:00 2001
From: Wesley Wiser <wwiser@gmail.com>
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<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);
+    }
+}
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<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)),
         }
     }
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<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)
 }
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()
         }