Refactor compiler to make use of dep-tracking-maps. Also, in cases where

we were using interior mutability (RefCells, TyIvar), add some reads/writes.
This commit is contained in:
Niko Matsakis 2015-12-22 16:39:33 -05:00
parent 5d9dd7cf33
commit d48f48f61f
10 changed files with 511 additions and 316 deletions

View file

@ -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<M: DepTrackingMapId> DepTrackingMap<M> {
}
}
impl<M: DepTrackingMapId> MemoizationMap for RefCell<DepTrackingMap<M>> {
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<OP>(&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<M> {
type Output = M::Value;

View file

@ -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>,

View file

@ -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<Tables<'tcx>>,
/// Maps from a trait item to the trait item "descriptor"
pub impl_or_trait_items: RefCell<DefIdMap<ty::ImplOrTraitItem<'tcx>>>,
pub impl_or_trait_items: RefCell<DepTrackingMap<maps::ImplOrTraitItems<'tcx>>>,
/// Maps from a trait def-id to a list of the def-ids of its trait items
pub trait_item_def_ids: RefCell<DefIdMap<Rc<Vec<ty::ImplOrTraitItemId>>>>,
pub trait_item_def_ids: RefCell<DepTrackingMap<maps::TraitItemDefIds<'tcx>>>,
/// A cache for the trait_items() routine
pub trait_items_cache: RefCell<DefIdMap<Rc<Vec<ty::ImplOrTraitItem<'tcx>>>>>,
/// 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<DefIdMap<Rc<Vec<ty::ImplOrTraitItem<'tcx>>>>>,
pub impl_trait_refs: RefCell<DefIdMap<Option<TraitRef<'tcx>>>>,
pub trait_defs: RefCell<DefIdMap<&'tcx ty::TraitDef<'tcx>>>,
pub adt_defs: RefCell<DefIdMap<ty::AdtDefMaster<'tcx>>>,
pub impl_trait_refs: RefCell<DepTrackingMap<maps::ImplTraitRefs<'tcx>>>,
pub trait_defs: RefCell<DepTrackingMap<maps::TraitDefs<'tcx>>>,
pub adt_defs: RefCell<DepTrackingMap<maps::AdtDefs<'tcx>>>,
/// Maps from the def-id of an item (trait/struct/enum/fn) to its
/// associated predicates.
pub predicates: RefCell<DefIdMap<GenericPredicates<'tcx>>>,
pub predicates: RefCell<DepTrackingMap<maps::Predicates<'tcx>>>,
/// 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<DefIdMap<GenericPredicates<'tcx>>>,
pub super_predicates: RefCell<DepTrackingMap<maps::Predicates<'tcx>>>,
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<FreevarMap>,
pub tcache: RefCell<DefIdMap<ty::TypeScheme<'tcx>>>,
// Records the type of every item.
pub tcache: RefCell<DepTrackingMap<maps::Tcache<'tcx>>>,
// Internal cache for metadata decoding. No need to track deps on this.
pub rcache: RefCell<FnvHashMap<ty::CReaderCacheKey, Ty<'tcx>>>,
// Cache for the type-contents routine. FIXME -- track deps?
pub tc_cache: RefCell<FnvHashMap<Ty<'tcx>, 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<NodeMap<Ty<'tcx>>>,
// FIXME no dep tracking, but we should be able to remove this
pub ty_param_defs: RefCell<NodeMap<ty::TypeParameterDef<'tcx>>>,
// FIXME dep tracking -- should be harmless enough
pub normalized_cache: RefCell<FnvHashMap<Ty<'tcx>, 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<DefIdMap<Rc<ty::ItemVariances>>>,
pub item_variance_map: RefCell<DepTrackingMap<maps::ItemVariances<'tcx>>>,
/// True if the variance has been computed yet; false otherwise.
pub variance_computed: Cell<bool>,
@ -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<DefIdMap<Rc<Vec<DefId>>>>,
pub inherent_impls: RefCell<DepTrackingMap<maps::InherentImpls<'tcx>>>,
/// 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<DefIdMap<Vec<ty::ImplOrTraitItemId>>>,
pub impl_items: RefCell<DepTrackingMap<maps::ImplItems<'tcx>>>,
/// 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<DefIdSet>,
/// 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<DefIdSet>,
@ -347,7 +371,9 @@ pub struct ctxt<'tcx> {
pub fulfilled_predicates: RefCell<traits::FulfilledPredicates<'tcx>>,
/// Caches the representation hints for struct definitions.
pub repr_hint_cache: RefCell<DefIdMap<Rc<Vec<attr::ReprAttr>>>>,
///
/// This is encapsulated by the `ReprHints` task and hence is not tracked.
repr_hint_cache: RefCell<DefIdMap<Rc<Vec<attr::ReprAttr>>>>,
/// Maps Expr NodeId's to their constant qualification.
pub const_qualif_map: RefCell<NodeMap<middle::check_const::ConstQualif>>,
@ -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<Vec<ty::ImplOrTraitItem<'tcx>>> {
// 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<Vec<_>> =
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<Vec<attr::ReprAttr>> {
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)
})
})
}
}

View file

@ -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<NonZero<*const TyS<'static>>>,
PhantomData<fn(TyS<'lt>)->TyS<'tcx>>);
@ -40,19 +46,28 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> {
}
#[inline]
pub fn get(&self) -> Option<Ty<'tcx>> {
pub fn get(&self, dep_node: DepNode) -> Option<Ty<'tcx>> {
tls::with(|tcx| tcx.dep_graph.read(dep_node));
self.untracked_get()
}
#[inline]
fn untracked_get(&self) -> Option<Ty<'tcx>> {
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(<unfulfilled>)")
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use 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<Vec<ty::ImplOrTraitItemId>> }
dep_map_ty! { ImplTraitRefs: ItemSignature(DefId) -> Option<ty::TraitRef<'tcx>> }
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<ty::ItemVariances> }
dep_map_ty! { InherentImpls: InherentImpls(DefId) -> Rc<Vec<DefId>> }
dep_map_ty! { ImplItems: ImplItems(DefId) -> Vec<ty::ImplOrTraitItemId> }

View file

@ -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<Name>,
// 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<fast_reject::SimplifiedType, Vec<DefId>>
>,
/// Blanket impls associated with the trait.
pub blanket_impls: RefCell<Vec<DefId>>,
/// Various flags
pub flags: Cell<TraitFlags>
}
impl<'tcx> TraitDef<'tcx> {
// returns None if not yet calculated
pub fn object_safety(&self) -> Option<bool> {
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<F: FnMut(DefId)>(&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<F: FnMut(DefId)>(&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<T:Copy> Copy for Option<T>`, we replace the type variable
// in `Option<T>` 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<V, F>(descr: &str,
fn lookup_locally_or_in_crate_store<M, F>(descr: &str,
def_id: DefId,
map: &RefCell<DefIdMap<V>>,
load_external: F) -> V where
V: Clone,
F: FnOnce() -> V,
map: &M,
load_external: F)
-> M::Value where
M: MemoizationMap<Key=DefId>,
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<Vec<ImplOrTraitItem<'tcx>>> {
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<Vec<ImplOrTraitItem>> =
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<hir::ImplPolarity> {
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<Vec<attr::ReprAttr>> {
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<ItemVariances> {
lookup_locally_or_in_crate_store(
"item_variance_map", item_id, &self.item_variance_map,

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use 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<Name>,
// 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<fast_reject::SimplifiedType, Vec<DefId>>
>,
/// Blanket impls associated with the trait.
blanket_impls: RefCell<Vec<DefId>>,
/// Various flags
pub flags: Cell<TraitFlags>
}
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<Name>)
-> 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<bool> {
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<F: FnMut(DefId)>(&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<F: FnMut(DefId)>(&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<T:Copy> Copy for Option<T>`, we replace the type variable
// in `Option<T>` 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<DefId>>,
Ref<'s, FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>>) {
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,
}
}

View file

@ -201,46 +201,38 @@ pub fn block_query<P>(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<T: Clone, U: Clone, M: MutableMap<T, U>>(
/// cache: &RefCell<M>,
/// f: &|T| -> U
/// ) -> impl |T| -> U {
/// ```
/// but currently it is not possible.
///
/// # Examples
/// ```
/// struct Context {
/// cache: RefCell<HashMap<usize, usize>>
/// }
///
/// 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<T, U, S, F>(cache: &RefCell<HashMap<T, U, S>>, 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<OP>(&self, key: Self::Key, op: OP) -> Self::Value
where OP: FnOnce() -> Self::Value;
}
impl<K, V, S> MemoizationMap for RefCell<HashMap<K,V,S>>
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<OP>(&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
}
}
}
}

View file

@ -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,

View file

@ -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>(