]> git.lizzy.rs Git - rust.git/blob - src/librustc/dep_graph/shadow.rs
Alias std::cmp::max/min to Ord::max/min
[rust.git] / src / librustc / dep_graph / shadow.rs
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.
4 //
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.
10
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.
15 //!
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.
19 //!
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.
24 //!
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.
28
29 use hir::def_id::DefId;
30 use std::cell::RefCell;
31 use std::env;
32
33 use super::DepNode;
34 use super::thread::DepMessage;
35 use super::debug::EdgeFilter;
36
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>,
41 }
42
43 const ENABLED: bool = cfg!(debug_assertions);
44
45 impl ShadowGraph {
46     pub fn new() -> Self {
47         let forbidden_edge = if !ENABLED {
48             None
49         } else {
50             match env::var("RUST_FORBID_DEP_GRAPH_EDGE") {
51                 Ok(s) => {
52                     match EdgeFilter::new(&s) {
53                         Ok(f) => Some(f),
54                         Err(err) => bug!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err),
55                     }
56                 }
57                 Err(_) => None,
58             }
59         };
60
61         ShadowGraph {
62             stack: RefCell::new(vec![]),
63             forbidden_edge: forbidden_edge,
64         }
65     }
66
67     #[inline]
68     pub fn enabled(&self) -> bool {
69         ENABLED
70     }
71
72     pub fn enqueue(&self, message: &DepMessage) {
73         if ENABLED {
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.
78                 return;
79             }
80
81             let mut stack = self.stack.borrow_mut();
82             match *message {
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
88                 // state.
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) => {
94                     match stack.pop() {
95                         Some(Some(m)) => {
96                             if *n != m {
97                                 bug!("stack mismatch: found {:?} expected {:?}", m, n)
98                             }
99                         }
100                         Some(None) => bug!("stack mismatch: found Ignore expected {:?}", n),
101                         None => bug!("stack mismatch: found empty stack, expected {:?}", n),
102                     }
103                 }
104                 DepMessage::PopIgnore => {
105                     match stack.pop() {
106                         Some(Some(m)) => bug!("stack mismatch: found {:?} expected ignore", m),
107                         Some(None) => (),
108                         None => bug!("stack mismatch: found empty stack, expected ignore"),
109                     }
110                 }
111                 DepMessage::Query => (),
112             }
113         }
114     }
115
116     fn check_edge(&self,
117                   source: Option<Option<&DepNode<DefId>>>,
118                   target: Option<Option<&DepNode<DefId>>>) {
119         assert!(ENABLED);
120         match (source, target) {
121             // cannot happen, one side is always Some(Some(_))
122             (None, None) => unreachable!(),
123
124             // nothing on top of the stack
125             (None, Some(n)) | (Some(n), None) => bug!("write of {:?} but no current task", n),
126
127             // this corresponds to an Ignore being top of the stack
128             (Some(None), _) | (_, Some(None)) => (),
129
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)
135                     }
136                 }
137             }
138         }
139     }
140 }
141
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>>> {
145     stack.last()
146         .map(|n: &'s Option<DepNode<DefId>>| -> Option<&'s DepNode<DefId>> {
147             // (*)
148             // (*) type annotation just there to clarify what would
149             // otherwise be some *really* obscure code
150             n.as_ref()
151         })
152 }