diff --git a/src/librustc/dep_graph/dep_tracking_map.rs b/src/librustc/dep_graph/dep_tracking_map.rs index 4f1063eae50..501c1aff320 100644 --- a/src/librustc/dep_graph/dep_tracking_map.rs +++ b/src/librustc/dep_graph/dep_tracking_map.rs @@ -13,6 +13,7 @@ use std::cell::RefCell; use std::ops::Index; use std::hash::Hash; use std::marker::PhantomData; +use util::common::MemoizationMap; use super::{DepNode, DepGraph}; @@ -70,6 +71,61 @@ impl DepTrackingMap { } } +impl MemoizationMap for RefCell> { + type Key = M::Key; + type Value = M::Value; + + /// Memoizes an entry in the dep-tracking-map. If the entry is not + /// already present, then `op` will be executed to compute its value. + /// The resulting dependency graph looks like this: + /// + /// [op] -> Map(key) -> CurrentTask + /// + /// Here, `[op]` represents whatever nodes `op` reads in the + /// course of execution; `Map(key)` represents the node for this + /// map; and `CurrentTask` represents the current task when + /// `memoize` is invoked. + /// + /// **Important:* when `op` is invoked, the current task will be + /// switched to `Map(key)`. Therefore, if `op` makes use of any + /// HIR nodes or shared state accessed through its closure + /// environment, it must explicitly read that state. As an + /// example, see `type_scheme_of_item` in `collect`, which looks + /// something like this: + /// + /// ``` + /// fn type_scheme_of_item(..., item: &hir::Item) -> ty::TypeScheme<'tcx> { + /// let item_def_id = ccx.tcx.map.local_def_id(it.id); + /// ccx.tcx.tcache.memoized(item_def_id, || { + /// ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // (*) + /// compute_type_scheme_of_item(ccx, item) + /// }); + /// } + /// ``` + /// + /// The key is the line marked `(*)`: the closure implicitly + /// accesses the body of the item `item`, so we register a read + /// from `Hir(item_def_id)`. + fn memoize(&self, key: M::Key, op: OP) -> M::Value + where OP: FnOnce() -> M::Value + { + let graph; + { + let this = self.borrow(); + if let Some(result) = this.map.get(&key) { + this.read(&key); + return result.clone(); + } + graph = this.graph.clone(); + } + + let _task = graph.in_task(M::to_dep_node(&key)); + let result = op(); + self.borrow_mut().map.insert(key, result.clone()); + result + } +} + impl<'k, M: DepTrackingMapId> Index<&'k M::Key> for DepTrackingMap { type Output = M::Value; diff --git a/src/librustc/middle/ty/contents.rs b/src/librustc/middle/ty/contents.rs index afe88f70d94..619201a4a9f 100644 --- a/src/librustc/middle/ty/contents.rs +++ b/src/librustc/middle/ty/contents.rs @@ -10,7 +10,7 @@ use middle::def_id::{DefId}; use middle::ty::{self, Ty}; -use util::common::{memoized}; +use util::common::MemoizationMap; use util::nodemap::FnvHashMap; use std::fmt; @@ -141,9 +141,7 @@ impl fmt::Debug for TypeContents { impl<'tcx> ty::TyS<'tcx> { pub fn type_contents(&'tcx self, cx: &ty::ctxt<'tcx>) -> TypeContents { - return memoized(&cx.tc_cache, self, |ty| { - tc_ty(cx, ty, &mut FnvHashMap()) - }); + return cx.tc_cache.memoize(self, || tc_ty(cx, self, &mut FnvHashMap())); fn tc_ty<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>, diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index e700abf9db1..8f313120c9d 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -30,10 +30,12 @@ use middle::traits; use middle::ty::{self, TraitRef, Ty, TypeAndMut}; use middle::ty::{TyS, TypeVariants}; use middle::ty::{AdtDef, ClosureSubsts, ExistentialBounds, Region}; -use middle::ty::{FreevarMap, GenericPredicates}; +use middle::ty::{FreevarMap}; use middle::ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, TraitTy}; use middle::ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use middle::ty::TypeVariants::*; +use middle::ty::maps; +use util::common::MemoizationMap; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; use util::nodemap::FnvHashMap; @@ -248,21 +250,23 @@ pub struct ctxt<'tcx> { pub tables: RefCell>, /// Maps from a trait item to the trait item "descriptor" - pub impl_or_trait_items: RefCell>>, + pub impl_or_trait_items: RefCell>>, /// Maps from a trait def-id to a list of the def-ids of its trait items - pub trait_item_def_ids: RefCell>>>, + pub trait_item_def_ids: RefCell>>, - /// A cache for the trait_items() routine - pub trait_items_cache: RefCell>>>>, + /// A cache for the trait_items() routine; note that the routine + /// itself pushes the `TraitItems` dependency node. This cache is + /// "encapsulated" and thus does not need to be itself tracked. + trait_items_cache: RefCell>>>>, - pub impl_trait_refs: RefCell>>>, - pub trait_defs: RefCell>>, - pub adt_defs: RefCell>>, + pub impl_trait_refs: RefCell>>, + pub trait_defs: RefCell>>, + pub adt_defs: RefCell>>, /// Maps from the def-id of an item (trait/struct/enum/fn) to its /// associated predicates. - pub predicates: RefCell>>, + pub predicates: RefCell>>, /// Maps from the def-id of a trait to the list of /// super-predicates. This is a subset of the full list of @@ -270,21 +274,40 @@ pub struct ctxt<'tcx> { /// evaluate them even during type conversion, often before the /// full predicates are available (note that supertraits have /// additional acyclicity requirements). - pub super_predicates: RefCell>>, + pub super_predicates: RefCell>>, pub map: ast_map::Map<'tcx>, + + // Records the free variables refrenced by every closure + // expression. Do not track deps for this, just recompute it from + // scratch every time. pub freevars: RefCell, - pub tcache: RefCell>>, + + // Records the type of every item. + pub tcache: RefCell>>, + + // Internal cache for metadata decoding. No need to track deps on this. pub rcache: RefCell>>, + + // Cache for the type-contents routine. FIXME -- track deps? pub tc_cache: RefCell, ty::contents::TypeContents>>, + + // Cache for various types within a method body and so forth. + // + // FIXME this should be made local to typeck, but it is currently used by one lint pub ast_ty_to_ty_cache: RefCell>>, + + // FIXME no dep tracking, but we should be able to remove this pub ty_param_defs: RefCell>>, + + // FIXME dep tracking -- should be harmless enough pub normalized_cache: RefCell, Ty<'tcx>>>, + pub lang_items: middle::lang_items::LanguageItems, /// Maps from def-id of a type or region parameter to its /// (inferred) variance. - pub item_variance_map: RefCell>>, + pub item_variance_map: RefCell>>, /// True if the variance has been computed yet; false otherwise. pub variance_computed: Cell, @@ -292,13 +315,13 @@ pub struct ctxt<'tcx> { /// Maps a DefId of a type to a list of its inherent impls. /// Contains implementations of methods that are inherent to a type. /// Methods in these implementations don't need to be exported. - pub inherent_impls: RefCell>>>, + pub inherent_impls: RefCell>>, /// Maps a DefId of an impl to a list of its items. /// Note that this contains all of the impls that we know about, /// including ones in other crates. It's not clear that this is the best /// way to do it. - pub impl_items: RefCell>>, + pub impl_items: RefCell>>, /// Set of used unsafe nodes (functions or blocks). Unsafe nodes not /// present in this set can be warned about. @@ -312,6 +335,7 @@ pub struct ctxt<'tcx> { /// The set of external nominal types whose implementations have been read. /// This is used for lazy resolution of methods. pub populated_external_types: RefCell, + /// The set of external primitive types whose implementations have been read. /// FIXME(arielb1): why is this separate from populated_external_types? pub populated_external_primitive_impls: RefCell, @@ -347,7 +371,9 @@ pub struct ctxt<'tcx> { pub fulfilled_predicates: RefCell>, /// Caches the representation hints for struct definitions. - pub repr_hint_cache: RefCell>>>, + /// + /// This is encapsulated by the `ReprHints` task and hence is not tracked. + repr_hint_cache: RefCell>>>, /// Maps Expr NodeId's to their constant qualification. pub const_qualif_map: RefCell>, @@ -499,31 +525,31 @@ impl<'tcx> ctxt<'tcx> { named_region_map: named_region_map, region_maps: region_maps, free_region_maps: RefCell::new(FnvHashMap()), - item_variance_map: RefCell::new(DefIdMap()), + item_variance_map: RefCell::new(DepTrackingMap::new(dep_graph.clone())), variance_computed: Cell::new(false), sess: s, def_map: def_map, tables: RefCell::new(Tables::empty()), - impl_trait_refs: RefCell::new(DefIdMap()), - trait_defs: RefCell::new(DefIdMap()), - adt_defs: RefCell::new(DefIdMap()), - predicates: RefCell::new(DefIdMap()), - super_predicates: RefCell::new(DefIdMap()), + impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + adt_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + super_predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())), fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()), map: map, freevars: RefCell::new(freevars), - tcache: RefCell::new(DefIdMap()), + tcache: RefCell::new(DepTrackingMap::new(dep_graph.clone())), rcache: RefCell::new(FnvHashMap()), tc_cache: RefCell::new(FnvHashMap()), ast_ty_to_ty_cache: RefCell::new(NodeMap()), - impl_or_trait_items: RefCell::new(DefIdMap()), - trait_item_def_ids: RefCell::new(DefIdMap()), + impl_or_trait_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + trait_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())), trait_items_cache: RefCell::new(DefIdMap()), ty_param_defs: RefCell::new(NodeMap()), normalized_cache: RefCell::new(FnvHashMap()), lang_items: lang_items, - inherent_impls: RefCell::new(DefIdMap()), - impl_items: RefCell::new(DefIdMap()), + inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + impl_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())), used_unsafe: RefCell::new(NodeSet()), used_mut_nodes: RefCell::new(NodeSet()), populated_external_types: RefCell::new(DefIdSet()), @@ -1004,4 +1030,38 @@ impl<'tcx> ctxt<'tcx> { pub fn mk_param_from_def(&self, def: &ty::TypeParameterDef) -> Ty<'tcx> { self.mk_param(def.space, def.index, def.name) } + + pub fn trait_items(&self, trait_did: DefId) -> Rc>> { + // since this is cached, pushing a dep-node for the + // computation yields the correct dependencies. + let _task = self.dep_graph.in_task(DepNode::TraitItems(trait_did)); + + let mut trait_items = self.trait_items_cache.borrow_mut(); + match trait_items.get(&trait_did).cloned() { + Some(trait_items) => trait_items, + None => { + let def_ids = self.trait_item_def_ids(trait_did); + let items: Rc> = + Rc::new(def_ids.iter() + .map(|d| self.impl_or_trait_item(d.def_id())) + .collect()); + trait_items.insert(trait_did, items.clone()); + items + } + } + } + + /// Obtain the representation annotation for a struct definition. + pub fn lookup_repr_hints(&self, did: DefId) -> Rc> { + let _task = self.dep_graph.in_task(DepNode::ReprHints(did)); + self.repr_hint_cache.memoize(did, || { + Rc::new(if did.is_local() { + self.get_attrs(did).iter().flat_map(|meta| { + attr::find_repr_attrs(self.sess.diagnostic(), meta).into_iter() + }).collect() + } else { + self.sess.cstore.repr_attrs(did) + }) + }) + } } diff --git a/src/librustc/middle/ty/ivar.rs b/src/librustc/middle/ty/ivar.rs index 73d567d0acf..ffc12aa5aea 100644 --- a/src/librustc/middle/ty/ivar.rs +++ b/src/librustc/middle/ty/ivar.rs @@ -8,7 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use dep_graph::DepNode; use middle::ty::{Ty, TyS}; +use middle::ty::tls; use rustc_data_structures::ivar; @@ -27,6 +29,10 @@ use core::nonzero::NonZero; /// (B) no aliases to this value with a 'tcx longer than this /// value's 'lt exist /// +/// Dependency tracking: each ivar does not know what node in the +/// dependency graph it is associated with, so when you get/fulfill +/// you must supply a `DepNode` id. This should always be the same id! +/// /// NonZero is used rather than Unique because Unique isn't Copy. pub struct TyIVar<'tcx, 'lt: 'tcx>(ivar::Ivar>>, PhantomData)->TyS<'tcx>>); @@ -40,19 +46,28 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> { } #[inline] - pub fn get(&self) -> Option> { + pub fn get(&self, dep_node: DepNode) -> Option> { + tls::with(|tcx| tcx.dep_graph.read(dep_node)); + self.untracked_get() + } + + #[inline] + fn untracked_get(&self) -> Option> { match self.0.get() { None => None, // valid because of invariant (A) Some(v) => Some(unsafe { &*(*v as *const TyS<'tcx>) }) } } + #[inline] - pub fn unwrap(&self) -> Ty<'tcx> { - self.get().unwrap() + pub fn unwrap(&self, dep_node: DepNode) -> Ty<'tcx> { + self.get(dep_node).unwrap() } - pub fn fulfill(&self, value: Ty<'lt>) { + pub fn fulfill(&self, dep_node: DepNode, value: Ty<'lt>) { + tls::with(|tcx| tcx.dep_graph.write(dep_node)); + // Invariant (A) is fulfilled, because by (B), every alias // of this has a 'tcx longer than 'lt. let value: *const TyS<'lt> = value; @@ -64,7 +79,7 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> { impl<'tcx, 'lt> fmt::Debug for TyIVar<'tcx, 'lt> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.get() { + match self.untracked_get() { Some(val) => write!(f, "TyIVar({:?})", val), None => f.write_str("TyIVar()") } diff --git a/src/librustc/middle/ty/maps.rs b/src/librustc/middle/ty/maps.rs new file mode 100644 index 00000000000..e1fea5bba1d --- /dev/null +++ b/src/librustc/middle/ty/maps.rs @@ -0,0 +1,41 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dep_graph::{DepNode, DepTrackingMapId}; +use middle::def_id::DefId; +use middle::ty; +use std::marker::PhantomData; +use std::rc::Rc; + +macro_rules! dep_map_ty { + ($ty_name:ident : $node_name:ident ($key:ty) -> $value:ty) => { + pub struct $ty_name<'tcx> { + data: PhantomData<&'tcx ()> + } + + impl<'tcx> DepTrackingMapId for $ty_name<'tcx> { + type Key = $key; + type Value = $value; + fn to_dep_node(key: &$key) -> DepNode { DepNode::$node_name(*key) } + } + } +} + +dep_map_ty! { ImplOrTraitItems: ImplOrTraitItems(DefId) -> ty::ImplOrTraitItem<'tcx> } +dep_map_ty! { Tcache: ItemSignature(DefId) -> ty::TypeScheme<'tcx> } +dep_map_ty! { Predicates: ItemSignature(DefId) -> ty::GenericPredicates<'tcx> } +dep_map_ty! { SuperPredicates: ItemSignature(DefId) -> ty::GenericPredicates<'tcx> } +dep_map_ty! { TraitItemDefIds: TraitItemDefIds(DefId) -> Rc> } +dep_map_ty! { ImplTraitRefs: ItemSignature(DefId) -> Option> } +dep_map_ty! { TraitDefs: ItemSignature(DefId) -> &'tcx ty::TraitDef<'tcx> } +dep_map_ty! { AdtDefs: ItemSignature(DefId) -> ty::AdtDefMaster<'tcx> } +dep_map_ty! { ItemVariances: ItemSignature(DefId) -> Rc } +dep_map_ty! { InherentImpls: InherentImpls(DefId) -> Rc> } +dep_map_ty! { ImplItems: ImplItems(DefId) -> Vec } diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index f9d18e99297..10682655e63 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -18,6 +18,7 @@ pub use self::ImplOrTraitItem::*; pub use self::IntVarValue::*; pub use self::LvaluePreference::*; +use dep_graph::{self, DepNode}; use front::map as ast_map; use front::map::LinkedPath; use middle; @@ -31,13 +32,13 @@ use middle::traits; use middle::ty; use middle::ty::fold::TypeFolder; use middle::ty::walk::TypeWalker; -use util::common::memoized; -use util::nodemap::{NodeMap, NodeSet, DefIdMap}; +use util::common::MemoizationMap; +use util::nodemap::{NodeMap, NodeSet}; use util::nodemap::FnvHashMap; use serialize::{Encodable, Encoder, Decodable, Decoder}; use std::borrow::{Borrow, Cow}; -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::hash::{Hash, Hasher}; use std::iter; use std::rc::Rc; @@ -76,14 +77,18 @@ pub use self::contents::TypeContents; pub use self::context::{ctxt, tls}; pub use self::context::{CtxtArenas, Lift, Tables}; +pub use self::trait_def::{TraitDef, TraitFlags}; + pub mod adjustment; pub mod cast; pub mod error; pub mod fast_reject; pub mod fold; pub mod _match; +pub mod maps; pub mod outlives; pub mod relate; +pub mod trait_def; pub mod walk; pub mod wf; pub mod util; @@ -1318,161 +1323,6 @@ pub struct TypeScheme<'tcx> { pub ty: Ty<'tcx>, } -bitflags! { - flags TraitFlags: u32 { - const NO_TRAIT_FLAGS = 0, - const HAS_DEFAULT_IMPL = 1 << 0, - const IS_OBJECT_SAFE = 1 << 1, - const OBJECT_SAFETY_VALID = 1 << 2, - const IMPLS_VALID = 1 << 3, - } -} - -/// As `TypeScheme` but for a trait ref. -pub struct TraitDef<'tcx> { - pub unsafety: hir::Unsafety, - - /// If `true`, then this trait had the `#[rustc_paren_sugar]` - /// attribute, indicating that it should be used with `Foo()` - /// sugar. This is a temporary thing -- eventually any trait wil - /// be usable with the sugar (or without it). - pub paren_sugar: bool, - - /// Generic type definitions. Note that `Self` is listed in here - /// as having a single bound, the trait itself (e.g., in the trait - /// `Eq`, there is a single bound `Self : Eq`). This is so that - /// default methods get to assume that the `Self` parameters - /// implements the trait. - pub generics: Generics<'tcx>, - - pub trait_ref: TraitRef<'tcx>, - - /// A list of the associated types defined in this trait. Useful - /// for resolving `X::Foo` type markers. - pub associated_type_names: Vec, - - // Impls of this trait. To allow for quicker lookup, the impls are indexed - // by a simplified version of their Self type: impls with a simplifiable - // Self are stored in nonblanket_impls keyed by it, while all other impls - // are stored in blanket_impls. - - /// Impls of the trait. - pub nonblanket_impls: RefCell< - FnvHashMap> - >, - - /// Blanket impls associated with the trait. - pub blanket_impls: RefCell>, - - /// Various flags - pub flags: Cell -} - -impl<'tcx> TraitDef<'tcx> { - // returns None if not yet calculated - pub fn object_safety(&self) -> Option { - if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) { - Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE)) - } else { - None - } - } - - pub fn set_object_safety(&self, is_safe: bool) { - assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true)); - self.flags.set( - self.flags.get() | if is_safe { - TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE - } else { - TraitFlags::OBJECT_SAFETY_VALID - } - ); - } - - /// Records a trait-to-implementation mapping. - pub fn record_impl(&self, - tcx: &ctxt<'tcx>, - impl_def_id: DefId, - impl_trait_ref: TraitRef<'tcx>) { - debug!("TraitDef::record_impl for {:?}, from {:?}", - self, impl_trait_ref); - - // We don't want to borrow_mut after we already populated all impls, - // so check if an impl is present with an immutable borrow first. - if let Some(sty) = fast_reject::simplify_type(tcx, - impl_trait_ref.self_ty(), false) { - if let Some(is) = self.nonblanket_impls.borrow().get(&sty) { - if is.contains(&impl_def_id) { - return // duplicate - skip - } - } - - self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id) - } else { - if self.blanket_impls.borrow().contains(&impl_def_id) { - return // duplicate - skip - } - self.blanket_impls.borrow_mut().push(impl_def_id) - } - } - - - pub fn for_each_impl(&self, tcx: &ctxt<'tcx>, mut f: F) { - tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); - - for &impl_def_id in self.blanket_impls.borrow().iter() { - f(impl_def_id); - } - - for v in self.nonblanket_impls.borrow().values() { - for &impl_def_id in v { - f(impl_def_id); - } - } - } - - /// Iterate over every impl that could possibly match the - /// self-type `self_ty`. - pub fn for_each_relevant_impl(&self, - tcx: &ctxt<'tcx>, - self_ty: Ty<'tcx>, - mut f: F) - { - tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); - - for &impl_def_id in self.blanket_impls.borrow().iter() { - f(impl_def_id); - } - - // simplify_type(.., false) basically replaces type parameters and - // projections with infer-variables. This is, of course, done on - // the impl trait-ref when it is instantiated, but not on the - // predicate trait-ref which is passed here. - // - // for example, if we match `S: Copy` against an impl like - // `impl Copy for Option`, we replace the type variable - // in `Option` with an infer variable, to `Option<_>` (this - // doesn't actually change fast_reject output), but we don't - // replace `S` with anything - this impl of course can't be - // selected, and as there are hundreds of similar impls, - // considering them would significantly harm performance. - if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) { - if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) { - for &impl_def_id in impls { - f(impl_def_id); - } - } - } else { - for v in self.nonblanket_impls.borrow().values() { - for &impl_def_id in v { - f(impl_def_id); - } - } - } - } - -} - bitflags! { flags AdtFlags: u32 { const NO_ADT_FLAGS = 0, @@ -1514,6 +1364,8 @@ pub struct FieldDefData<'tcx, 'container: 'tcx> { pub vis: hir::Visibility, /// TyIVar is used here to allow for variance (see the doc at /// AdtDefData). + /// + /// Note: direct accesses to `ty` must also add dep edges. ty: ivar::TyIVar<'tcx, 'container> } @@ -1804,11 +1656,11 @@ impl<'tcx, 'container> FieldDefData<'tcx, 'container> { } pub fn unsubst_ty(&self) -> Ty<'tcx> { - self.ty.unwrap() + self.ty.unwrap(DepNode::FieldTy(self.did)) } pub fn fulfill_ty(&self, ty: Ty<'container>) { - self.ty.fulfill(ty); + self.ty.fulfill(DepNode::FieldTy(self.did), ty); } } @@ -1931,31 +1783,20 @@ impl LvaluePreference { /// into the map by the `typeck::collect` phase. If the def-id is external, /// then we have to go consult the crate loading code (and cache the result for /// the future). -fn lookup_locally_or_in_crate_store(descr: &str, +fn lookup_locally_or_in_crate_store(descr: &str, def_id: DefId, - map: &RefCell>, - load_external: F) -> V where - V: Clone, - F: FnOnce() -> V, + map: &M, + load_external: F) + -> M::Value where + M: MemoizationMap, + F: FnOnce() -> M::Value, { - match map.borrow().get(&def_id).cloned() { - Some(v) => { return v; } - None => { } - } - - if def_id.is_local() { - panic!("No def'n found for {:?} in tcx.{}", def_id, descr); - } - let v = load_external(); - - // Don't consider this a write from the current task, since we are - // loading from another crate. (Note that the current task will - // already have registered a read in the call to `get` above.) - dep_graph.with_ignore(|| { - map.borrow_mut().insert(def_id, v.clone()); - }); - - v + map.memoize(def_id, || { + if def_id.is_local() { + panic!("No def'n found for {:?} in tcx.{}", def_id, descr); + } + load_external() + }) } impl BorrowKind { @@ -2231,22 +2072,6 @@ impl<'tcx> ctxt<'tcx> { } } - pub fn trait_items(&self, trait_did: DefId) -> Rc>> { - let mut trait_items = self.trait_items_cache.borrow_mut(); - match trait_items.get(&trait_did).cloned() { - Some(trait_items) => trait_items, - None => { - let def_ids = self.trait_item_def_ids(trait_did); - let items: Rc> = - Rc::new(def_ids.iter() - .map(|d| self.impl_or_trait_item(d.def_id())) - .collect()); - trait_items.insert(trait_did, items.clone()); - items - } - } - } - pub fn trait_impl_polarity(&self, id: DefId) -> Option { if let Some(id) = self.map.as_local_node_id(id) { match self.map.find(id) { @@ -2264,7 +2089,7 @@ impl<'tcx> ctxt<'tcx> { } pub fn custom_coerce_unsized_kind(&self, did: DefId) -> adjustment::CustomCoerceUnsized { - memoized(&self.custom_coerce_unsized_kinds, did, |did: DefId| { + self.custom_coerce_unsized_kinds.memoize(did, || { let (kind, src) = if did.krate != LOCAL_CRATE { (self.sess.cstore.custom_coerce_unsized_kind(did), "external") } else { @@ -2427,19 +2252,6 @@ impl<'tcx> ctxt<'tcx> { || self.lookup_repr_hints(did).contains(&attr::ReprSimd) } - /// Obtain the representation annotation for a struct definition. - pub fn lookup_repr_hints(&self, did: DefId) -> Rc> { - memoized(&self.repr_hint_cache, did, |did: DefId| { - Rc::new(if did.is_local() { - self.get_attrs(did).iter().flat_map(|meta| { - attr::find_repr_attrs(self.sess.diagnostic(), meta).into_iter() - }).collect() - } else { - self.sess.cstore.repr_attrs(did) - }) - }) - } - pub fn item_variances(&self, item_id: DefId) -> Rc { lookup_locally_or_in_crate_store( "item_variance_map", item_id, &self.item_variance_map, diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs new file mode 100644 index 00000000000..db001ce2c44 --- /dev/null +++ b/src/librustc/middle/ty/trait_def.rs @@ -0,0 +1,226 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dep_graph::DepNode; +use middle::def_id::DefId; +use middle::ty; +use middle::ty::fast_reject; +use middle::ty::Ty; +use std::borrow::{Borrow}; +use std::cell::{Cell, Ref, RefCell}; +use syntax::ast::Name; +use rustc_front::hir; +use util::nodemap::FnvHashMap; + +/// As `TypeScheme` but for a trait ref. +pub struct TraitDef<'tcx> { + pub unsafety: hir::Unsafety, + + /// If `true`, then this trait had the `#[rustc_paren_sugar]` + /// attribute, indicating that it should be used with `Foo()` + /// sugar. This is a temporary thing -- eventually any trait wil + /// be usable with the sugar (or without it). + pub paren_sugar: bool, + + /// Generic type definitions. Note that `Self` is listed in here + /// as having a single bound, the trait itself (e.g., in the trait + /// `Eq`, there is a single bound `Self : Eq`). This is so that + /// default methods get to assume that the `Self` parameters + /// implements the trait. + pub generics: ty::Generics<'tcx>, + + pub trait_ref: ty::TraitRef<'tcx>, + + /// A list of the associated types defined in this trait. Useful + /// for resolving `X::Foo` type markers. + pub associated_type_names: Vec, + + // Impls of this trait. To allow for quicker lookup, the impls are indexed + // by a simplified version of their Self type: impls with a simplifiable + // Self are stored in nonblanket_impls keyed by it, while all other impls + // are stored in blanket_impls. + // + // These lists are tracked by `DepNode::TraitImpls`; we don't use + // a DepTrackingMap but instead have the `TraitDef` insert the + // required reads/writes. + + /// Impls of the trait. + nonblanket_impls: RefCell< + FnvHashMap> + >, + + /// Blanket impls associated with the trait. + blanket_impls: RefCell>, + + /// Various flags + pub flags: Cell +} + +impl<'tcx> TraitDef<'tcx> { + pub fn new(unsafety: hir::Unsafety, + paren_sugar: bool, + generics: ty::Generics<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + associated_type_names: Vec) + -> TraitDef<'tcx> { + TraitDef { + paren_sugar: paren_sugar, + unsafety: unsafety, + generics: generics, + trait_ref: trait_ref, + associated_type_names: associated_type_names, + nonblanket_impls: RefCell::new(FnvHashMap()), + blanket_impls: RefCell::new(vec![]), + flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS) + } + } + + pub fn def_id(&self) -> DefId { + self.trait_ref.def_id + } + + // returns None if not yet calculated + pub fn object_safety(&self) -> Option { + if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) { + Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE)) + } else { + None + } + } + + pub fn set_object_safety(&self, is_safe: bool) { + assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true)); + self.flags.set( + self.flags.get() | if is_safe { + TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE + } else { + TraitFlags::OBJECT_SAFETY_VALID + } + ); + } + + fn write_trait_impls(&self, tcx: &ty::ctxt<'tcx>) { + tcx.dep_graph.write(DepNode::TraitImpls(self.trait_ref.def_id)); + } + + fn read_trait_impls(&self, tcx: &ty::ctxt<'tcx>) { + tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id)); + } + + /// Records a trait-to-implementation mapping. + pub fn record_impl(&self, + tcx: &ty::ctxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: ty::TraitRef<'tcx>) { + debug!("TraitDef::record_impl for {:?}, from {:?}", + self, impl_trait_ref); + + // Record the write into the impl set, but only for local + // impls: external impls are handled differently. + if impl_def_id.is_local() { + self.write_trait_impls(tcx); + } + + // We don't want to borrow_mut after we already populated all impls, + // so check if an impl is present with an immutable borrow first. + if let Some(sty) = fast_reject::simplify_type(tcx, + impl_trait_ref.self_ty(), false) { + if let Some(is) = self.nonblanket_impls.borrow().get(&sty) { + if is.contains(&impl_def_id) { + return // duplicate - skip + } + } + + self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id) + } else { + if self.blanket_impls.borrow().contains(&impl_def_id) { + return // duplicate - skip + } + self.blanket_impls.borrow_mut().push(impl_def_id) + } + } + + pub fn for_each_impl(&self, tcx: &ty::ctxt<'tcx>, mut f: F) { + self.read_trait_impls(tcx); + + tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); + + for &impl_def_id in self.blanket_impls.borrow().iter() { + f(impl_def_id); + } + + for v in self.nonblanket_impls.borrow().values() { + for &impl_def_id in v { + f(impl_def_id); + } + } + } + + /// Iterate over every impl that could possibly match the + /// self-type `self_ty`. + pub fn for_each_relevant_impl(&self, + tcx: &ty::ctxt<'tcx>, + self_ty: Ty<'tcx>, + mut f: F) + { + self.read_trait_impls(tcx); + + tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); + + for &impl_def_id in self.blanket_impls.borrow().iter() { + f(impl_def_id); + } + + // simplify_type(.., false) basically replaces type parameters and + // projections with infer-variables. This is, of course, done on + // the impl trait-ref when it is instantiated, but not on the + // predicate trait-ref which is passed here. + // + // for example, if we match `S: Copy` against an impl like + // `impl Copy for Option`, we replace the type variable + // in `Option` with an infer variable, to `Option<_>` (this + // doesn't actually change fast_reject output), but we don't + // replace `S` with anything - this impl of course can't be + // selected, and as there are hundreds of similar impls, + // considering them would significantly harm performance. + if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) { + if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) { + for &impl_def_id in impls { + f(impl_def_id); + } + } + } else { + for v in self.nonblanket_impls.borrow().values() { + for &impl_def_id in v { + f(impl_def_id); + } + } + } + } + + pub fn borrow_impl_lists<'s>(&'s self, tcx: &ty::ctxt<'tcx>) + -> (Ref<'s, Vec>, + Ref<'s, FnvHashMap>>) { + self.read_trait_impls(tcx); + (self.blanket_impls.borrow(), self.nonblanket_impls.borrow()) + } + +} + +bitflags! { + flags TraitFlags: u32 { + const NO_TRAIT_FLAGS = 0, + const HAS_DEFAULT_IMPL = 1 << 0, + const IS_OBJECT_SAFE = 1 << 1, + const OBJECT_SAFETY_VALID = 1 << 2, + const IMPLS_VALID = 1 << 3, + } +} + diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index b5d259d9ac9..2481cab78b4 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -201,46 +201,38 @@ pub fn block_query

(b: &hir::Block, p: P) -> bool where P: FnMut(&hir::Expr) - return v.flag; } -/// Memoizes a one-argument closure using the given RefCell containing -/// a type implementing MutableMap to serve as a cache. -/// -/// In the future the signature of this function is expected to be: -/// ``` -/// pub fn memoized>( -/// cache: &RefCell, -/// f: &|T| -> U -/// ) -> impl |T| -> U { -/// ``` -/// but currently it is not possible. -/// -/// # Examples -/// ``` -/// struct Context { -/// cache: RefCell> -/// } -/// -/// fn factorial(ctxt: &Context, n: usize) -> usize { -/// memoized(&ctxt.cache, n, |n| match n { -/// 0 | 1 => n, -/// _ => factorial(ctxt, n - 2) + factorial(ctxt, n - 1) -/// }) -/// } -/// ``` -#[inline(always)] -pub fn memoized(cache: &RefCell>, arg: T, f: F) -> U - where T: Clone + Hash + Eq, - U: Clone, - S: HashState, - F: FnOnce(T) -> U, +pub trait MemoizationMap { + type Key: Clone; + type Value: Clone; + + /// If `key` is present in the map, return the valuee, + /// otherwise invoke `op` and store the value in the map. + /// + /// NB: if the receiver is a `DepTrackingMap`, special care is + /// needed in the `op` to ensure that the correct edges are + /// added into the dep graph. See the `DepTrackingMap` impl for + /// more details! + fn memoize(&self, key: Self::Key, op: OP) -> Self::Value + where OP: FnOnce() -> Self::Value; +} + +impl MemoizationMap for RefCell> + where K: Hash+Eq+Clone, V: Clone, S: HashState { - let key = arg.clone(); - let result = cache.borrow().get(&key).cloned(); - match result { - Some(result) => result, - None => { - let result = f(arg); - cache.borrow_mut().insert(key, result.clone()); - result + type Key = K; + type Value = V; + + fn memoize(&self, key: K, op: OP) -> V + where OP: FnOnce() -> V + { + let result = self.borrow().get(&key).cloned(); + match result { + Some(result) => result, + None => { + let result = op(); + self.borrow_mut().insert(key, result.clone()); + result + } } } } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 54cccf087eb..0b48cad36ba 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -38,7 +38,7 @@ use middle::ty::{self, RegionEscape, Ty}; use rustc::mir; use rustc::mir::visit::MutVisitor; -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::io::prelude::*; use std::io; use std::rc::Rc; @@ -353,16 +353,11 @@ pub fn get_trait_def<'tcx>(cdata: Cmd, let associated_type_names = parse_associated_type_names(item_doc); let paren_sugar = parse_paren_sugar(item_doc); - ty::TraitDef { - paren_sugar: paren_sugar, - unsafety: unsafety, - generics: generics, - trait_ref: item_trait_ref(item_doc, tcx, cdata), - associated_type_names: associated_type_names, - nonblanket_impls: RefCell::new(FnvHashMap()), - blanket_impls: RefCell::new(vec![]), - flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS) - } + ty::TraitDef::new(unsafety, + paren_sugar, + generics, + item_trait_ref(item_doc, tcx, cdata), + associated_type_names) } pub fn get_adt_def<'tcx>(intr: &IdentInterner, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index d161324f564..5a9b8991758 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -75,11 +75,11 @@ use middle::ty::util::IntTypeExt; use rscope::*; use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; -use util::common::{ErrorReported, memoized}; +use util::common::{ErrorReported, MemoizationMap}; use util::nodemap::{FnvHashMap, FnvHashSet}; use write_ty_to_tcx; -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; use std::collections::HashSet; use std::rc::Rc; @@ -1419,17 +1419,17 @@ fn type_scheme_of_def_id<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } fn type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, - it: &hir::Item) + item: &hir::Item) -> ty::TypeScheme<'tcx> { - // Computing the type scheme of an item is a discrete task: - let item_def_id = ccx.tcx.map.local_def_id(it.id); - let _task = ccx.tcx.dep_graph.in_task(DepNode::TypeScheme(item_def_id)); - ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // we have access to `it` - - memoized(&ccx.tcx.tcache, - ccx.tcx.map.local_def_id(it.id), - |_| compute_type_scheme_of_item(ccx, it)) + let item_def_id = ccx.tcx.map.local_def_id(item.id); + ccx.tcx.tcache.memoize(item_def_id, || { + // NB. Since the `memoized` function enters a new task, and we + // are giving this task access to the item `item`, we must + // register a read. + ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); + compute_type_scheme_of_item(ccx, item) + }) } fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, @@ -1547,14 +1547,14 @@ fn type_scheme_of_foreign_item<'a, 'tcx>( abi: abi::Abi) -> ty::TypeScheme<'tcx> { - // Computing the type scheme of a foreign item is a discrete task: let item_def_id = ccx.tcx.map.local_def_id(item.id); - let _task = ccx.tcx.dep_graph.in_task(DepNode::TypeScheme(item_def_id)); - ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // we have access to `item` - - memoized(&ccx.tcx.tcache, - ccx.tcx.map.local_def_id(item.id), - |_| compute_type_scheme_of_foreign_item(ccx, item, abi)) + ccx.tcx.tcache.memoize(item_def_id, || { + // NB. Since the `memoized` function enters a new task, and we + // are giving this task access to the item `item`, we must + // register a read. + ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); + compute_type_scheme_of_foreign_item(ccx, item, abi) + }) } fn compute_type_scheme_of_foreign_item<'a, 'tcx>(