1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
// 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 rustc_data_structures::fx::FxHashMap; use std::cell::RefCell; use std::hash::Hash; use std::marker::PhantomData; use util::common::MemoizationMap; use super::{DepKind, DepNodeIndex, DepGraph}; /// A DepTrackingMap offers a subset of the `Map` API and ensures that /// we make calls to `read` and `write` as appropriate. We key the /// maps with a unique type for brevity. pub struct DepTrackingMap<M: DepTrackingMapConfig> { phantom: PhantomData<M>, graph: DepGraph, map: FxHashMap<M::Key, (M::Value, DepNodeIndex)>, } pub trait DepTrackingMapConfig { type Key: Eq + Hash + Clone; type Value: Clone; fn to_dep_kind() -> DepKind; } impl<M: DepTrackingMapConfig> DepTrackingMap<M> { pub fn new(graph: DepGraph) -> DepTrackingMap<M> { DepTrackingMap { phantom: PhantomData, graph, map: FxHashMap(), } } } impl<M: DepTrackingMapConfig> 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 register a read of that /// state. As an example, see `type_of_item` in `collect`, /// which looks something like this: /// /// ``` /// fn type_of_item(..., item: &hir::Item) -> Ty<'tcx> { /// let item_def_id = ccx.tcx.hir.local_def_id(it.id); /// ccx.tcx.item_types.memoized(item_def_id, || { /// ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // (*) /// compute_type_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(&(ref result, dep_node)) = this.map.get(&key) { this.graph.read_index(dep_node); return result.clone(); } graph = this.graph.clone(); } let (result, dep_node) = graph.with_anon_task(M::to_dep_kind(), op); self.borrow_mut().map.insert(key, (result.clone(), dep_node)); graph.read_index(dep_node); result } }