1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! The "Shadow Graph" is maintained on the main thread and which
12 //! tracks each message relating to the dep-graph and applies some
13 //! sanity checks as they go by. If an error results, it means you get
14 //! a nice stack-trace telling you precisely what caused the error.
16 //! NOTE: This is a debugging facility which can potentially have non-trivial
17 //! runtime impact. Therefore, it is largely compiled out if
18 //! debug-assertions are not enabled.
20 //! The basic sanity check, enabled if you have debug assertions
21 //! enabled, is that there is always a task (or ignore) on the stack
22 //! when you do read/write, and that the tasks are pushed/popped
23 //! according to a proper stack discipline.
25 //! Optionally, if you specify RUST_FORBID_DEP_GRAPH_EDGE, you can
26 //! specify an edge filter to be applied to each edge as it is
27 //! created. See `./README.md` for details.
29 use hir::def_id::DefId;
30 use std::cell::RefCell;
34 use super::thread::DepMessage;
35 use super::debug::EdgeFilter;
37 pub struct ShadowGraph {
38 // if you push None onto the stack, that corresponds to an Ignore
39 stack: RefCell<Vec<Option<DepNode<DefId>>>>,
40 forbidden_edge: Option<EdgeFilter>,
43 const ENABLED: bool = cfg!(debug_assertions);
46 pub fn new() -> Self {
47 let forbidden_edge = if !ENABLED {
50 match env::var("RUST_FORBID_DEP_GRAPH_EDGE") {
52 match EdgeFilter::new(&s) {
54 Err(err) => bug!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err),
62 stack: RefCell::new(vec![]),
63 forbidden_edge: forbidden_edge,
68 pub fn enabled(&self) -> bool {
72 pub fn enqueue(&self, message: &DepMessage) {
74 if self.stack.try_borrow().is_err() {
75 // When we apply edge filters, that invokes the Debug trait on
76 // DefIds, which in turn reads from various bits of state and
77 // creates reads! Ignore those recursive reads.
81 let mut stack = self.stack.borrow_mut();
83 // It is ok to READ shared state outside of a
84 // task. That can't do any harm (at least, the only
85 // way it can do harm is by leaking that data into a
86 // query or task, which would be a problem
87 // anyway). What would be bad is WRITING to that
89 DepMessage::Read(_) => { }
90 DepMessage::Write(ref n) => self.check_edge(top(&stack), Some(Some(n))),
91 DepMessage::PushTask(ref n) => stack.push(Some(n.clone())),
92 DepMessage::PushIgnore => stack.push(None),
93 DepMessage::PopTask(ref n) => {
97 bug!("stack mismatch: found {:?} expected {:?}", m, n)
100 Some(None) => bug!("stack mismatch: found Ignore expected {:?}", n),
101 None => bug!("stack mismatch: found empty stack, expected {:?}", n),
104 DepMessage::PopIgnore => {
106 Some(Some(m)) => bug!("stack mismatch: found {:?} expected ignore", m),
108 None => bug!("stack mismatch: found empty stack, expected ignore"),
111 DepMessage::Query => (),
117 source: Option<Option<&DepNode<DefId>>>,
118 target: Option<Option<&DepNode<DefId>>>) {
120 match (source, target) {
121 // cannot happen, one side is always Some(Some(_))
122 (None, None) => unreachable!(),
124 // nothing on top of the stack
125 (None, Some(n)) | (Some(n), None) => bug!("write of {:?} but no current task", n),
127 // this corresponds to an Ignore being top of the stack
128 (Some(None), _) | (_, Some(None)) => (),
130 // a task is on top of the stack
131 (Some(Some(source)), Some(Some(target))) => {
132 if let Some(ref forbidden_edge) = self.forbidden_edge {
133 if forbidden_edge.test(source, target) {
134 bug!("forbidden edge {:?} -> {:?} created", source, target)
142 // Do a little juggling: we get back a reference to an option at the
143 // top of the stack, convert it to an optional reference.
144 fn top<'s>(stack: &'s Vec<Option<DepNode<DefId>>>) -> Option<Option<&'s DepNode<DefId>>> {
146 .map(|n: &'s Option<DepNode<DefId>>| -> Option<&'s DepNode<DefId>> {
148 // (*) type annotation just there to clarify what would
149 // otherwise be some *really* obscure code