]> git.proxmox.com Git - rustc.git/blame - src/librustc/dep_graph/shadow.rs
New upstream version 1.19.0+dfsg3
[rustc.git] / src / librustc / dep_graph / shadow.rs
CommitLineData
9e0c209e
SL
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
29use hir::def_id::DefId;
476ff2be 30use std::cell::RefCell;
9e0c209e
SL
31use std::env;
32
33use super::DepNode;
34use super::thread::DepMessage;
35use super::debug::EdgeFilter;
36
37pub 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
43const ENABLED: bool = cfg!(debug_assertions);
44
45impl 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
c30ab7b3
SL
67 #[inline]
68 pub fn enabled(&self) -> bool {
69 ENABLED
70 }
71
9e0c209e
SL
72 pub fn enqueue(&self, message: &DepMessage) {
73 if ENABLED {
476ff2be
SL
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;
9e0c209e
SL
79 }
80
81 let mut stack = self.stack.borrow_mut();
82 match *message {
cc61c64b
XL
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(_) => { }
9e0c209e
SL
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
cc61c64b 125 (None, Some(n)) | (Some(n), None) => bug!("write of {:?} but no current task", n),
9e0c209e
SL
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.
144fn 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}