about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/dep_graph/graph.rs353
-rw-r--r--src/librustc/dep_graph/mod.rs3
-rw-r--r--src/librustc/dep_graph/raii.rs33
-rw-r--r--src/librustc/hir/map/collector.rs26
-rw-r--r--src/librustc/ty/context.rs7
-rw-r--r--src/librustc/ty/maps/plumbing.rs1
-rw-r--r--src/librustc/ty/mod.rs40
-rw-r--r--src/librustc_mir/hair/cx/expr.rs78
-rw-r--r--src/test/ui/const-eval/enum_discr.rs36
-rw-r--r--src/test/ui/issue-23302-1.stderr12
-rw-r--r--src/test/ui/issue-23302-2.stderr12
-rw-r--r--src/test/ui/issue-36163.stderr17
12 files changed, 377 insertions, 241 deletions
diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs
index d60c22064d3..22ab1b15c8b 100644
--- a/src/librustc/dep_graph/graph.rs
+++ b/src/librustc/dep_graph/graph.rs
@@ -12,11 +12,10 @@ use errors::DiagnosticBuilder;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
-use rustc_data_structures::sync::Lrc;
-use std::cell::{Ref, RefCell};
+use rustc_data_structures::sync::{Lrc, RwLock, ReadGuard, Lock};
 use std::env;
 use std::hash::Hash;
-use ty::TyCtxt;
+use ty::{self, TyCtxt};
 use util::common::{ProfileQueriesMsg, profq_msg};
 
 use ich::{StableHashingContext, StableHashingContextProvider, Fingerprint};
@@ -24,7 +23,6 @@ use ich::{StableHashingContext, StableHashingContextProvider, Fingerprint};
 use super::debug::EdgeFilter;
 use super::dep_node::{DepNode, DepKind, WorkProductId};
 use super::query::DepGraphQuery;
-use super::raii;
 use super::safe::DepGraphSafe;
 use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex};
 use super::prev::PreviousDepGraph;
@@ -37,7 +35,7 @@ pub struct DepGraph {
     // result value fingerprints. Do not rely on the length of this vector
     // being the same as the number of nodes in the graph. The vector can
     // contain an arbitrary number of zero-entries at the end.
-    fingerprints: Lrc<RefCell<IndexVec<DepNodeIndex, Fingerprint>>>
+    fingerprints: Lrc<Lock<IndexVec<DepNodeIndex, Fingerprint>>>
 }
 
 
@@ -67,27 +65,27 @@ struct DepGraphData {
     /// tracking. The `current` field is the dependency graph of only the
     /// current compilation session: We don't merge the previous dep-graph into
     /// current one anymore.
-    current: RefCell<CurrentDepGraph>,
+    current: Lock<CurrentDepGraph>,
 
     /// The dep-graph from the previous compilation session. It contains all
     /// nodes and edges as well as all fingerprints of nodes that have them.
     previous: PreviousDepGraph,
 
-    colors: RefCell<DepNodeColorMap>,
+    colors: Lock<DepNodeColorMap>,
 
     /// When we load, there may be `.o` files, cached mir, or other such
     /// things available to us. If we find that they are not dirty, we
     /// load the path to the file storing those work-products here into
     /// this map. We can later look for and extract that data.
-    previous_work_products: RefCell<FxHashMap<WorkProductId, WorkProduct>>,
+    previous_work_products: RwLock<FxHashMap<WorkProductId, WorkProduct>>,
 
     /// Work-products that we generate in this run.
-    work_products: RefCell<FxHashMap<WorkProductId, WorkProduct>>,
+    work_products: RwLock<FxHashMap<WorkProductId, WorkProduct>>,
 
-    dep_node_debug: RefCell<FxHashMap<DepNode, String>>,
+    dep_node_debug: Lock<FxHashMap<DepNode, String>>,
 
     // Used for testing, only populated when -Zquery-dep-graph is specified.
-    loaded_from_cache: RefCell<FxHashMap<DepNodeIndex, bool>>,
+    loaded_from_cache: Lock<FxHashMap<DepNodeIndex, bool>>,
 }
 
 impl DepGraph {
@@ -102,22 +100,22 @@ impl DepGraph {
                                                  (prev_graph_node_count * 115) / 100);
         DepGraph {
             data: Some(Lrc::new(DepGraphData {
-                previous_work_products: RefCell::new(FxHashMap()),
-                work_products: RefCell::new(FxHashMap()),
-                dep_node_debug: RefCell::new(FxHashMap()),
-                current: RefCell::new(CurrentDepGraph::new()),
+                previous_work_products: RwLock::new(FxHashMap()),
+                work_products: RwLock::new(FxHashMap()),
+                dep_node_debug: Lock::new(FxHashMap()),
+                current: Lock::new(CurrentDepGraph::new()),
                 previous: prev_graph,
-                colors: RefCell::new(DepNodeColorMap::new(prev_graph_node_count)),
-                loaded_from_cache: RefCell::new(FxHashMap()),
+                colors: Lock::new(DepNodeColorMap::new(prev_graph_node_count)),
+                loaded_from_cache: Lock::new(FxHashMap()),
             })),
-            fingerprints: Lrc::new(RefCell::new(fingerprints)),
+            fingerprints: Lrc::new(Lock::new(fingerprints)),
         }
     }
 
     pub fn new_disabled() -> DepGraph {
         DepGraph {
             data: None,
-            fingerprints: Lrc::new(RefCell::new(IndexVec::new())),
+            fingerprints: Lrc::new(Lock::new(IndexVec::new())),
         }
     }
 
@@ -144,21 +142,32 @@ impl DepGraph {
 
     pub fn assert_ignored(&self)
     {
-        if let Some(ref data) = self.data {
-            match data.current.borrow().task_stack.last() {
-                Some(&OpenTask::Ignore) | None => {
-                    // ignored
+        if let Some(..) = self.data {
+            ty::tls::with_context_opt(|icx| {
+                let icx = if let Some(icx) = icx { icx } else { return };
+                match *icx.task {
+                    OpenTask::Ignore => {
+                        // ignored
+                    }
+                    _ => panic!("expected an ignore context")
                 }
-                _ => panic!("expected an ignore context")
-            }
+            })
         }
     }
 
     pub fn with_ignore<OP,R>(&self, op: OP) -> R
         where OP: FnOnce() -> R
     {
-        let _task = self.data.as_ref().map(|data| raii::IgnoreTask::new(&data.current));
-        op()
+        ty::tls::with_context(|icx| {
+            let icx = ty::tls::ImplicitCtxt {
+                task: &OpenTask::Ignore,
+                ..icx.clone()
+            };
+
+            ty::tls::enter_context(&icx, |_| {
+                op()
+            })
+        })
     }
 
     /// Starts a new dep-graph task. Dep-graph tasks are specified
@@ -197,24 +206,51 @@ impl DepGraph {
         where C: DepGraphSafe + StableHashingContextProvider<'gcx>,
               R: HashStable<StableHashingContext<'gcx>>,
     {
-        self.with_task_impl(key, cx, arg, task,
-            |data, key| data.borrow_mut().push_task(key),
-            |data, key| data.borrow_mut().pop_task(key))
+        self.with_task_impl(key, cx, arg, false, task,
+            |key| OpenTask::Regular(Lock::new(RegularOpenTask {
+                node: key,
+                reads: Vec::new(),
+                read_set: FxHashSet(),
+            })),
+            |data, key, task| data.borrow_mut().complete_task(key, task))
     }
 
-    fn with_task_impl<'gcx, C, A, R>(&self,
-                                    key: DepNode,
-                                    cx: C,
-                                    arg: A,
-                                    task: fn(C, A) -> R,
-                                    push: fn(&RefCell<CurrentDepGraph>, DepNode),
-                                    pop: fn(&RefCell<CurrentDepGraph>, DepNode) -> DepNodeIndex)
-                                    -> (R, DepNodeIndex)
+    /// Creates a new dep-graph input with value `input`
+    pub fn input_task<'gcx, C, R>(&self,
+                                   key: DepNode,
+                                   cx: C,
+                                   input: R)
+                                   -> (R, DepNodeIndex)
         where C: DepGraphSafe + StableHashingContextProvider<'gcx>,
               R: HashStable<StableHashingContext<'gcx>>,
     {
+        fn identity_fn<C, A>(_: C, arg: A) -> A {
+            arg
+        }
+
+        self.with_task_impl(key, cx, input, true, identity_fn,
+            |_| OpenTask::Ignore,
+            |data, key, _| data.borrow_mut().alloc_node(key, Vec::new()))
+    }
+
+    fn with_task_impl<'gcx, C, A, R>(
+        &self,
+        key: DepNode,
+        cx: C,
+        arg: A,
+        no_tcx: bool,
+        task: fn(C, A) -> R,
+        create_task: fn(DepNode) -> OpenTask,
+        finish_task_and_alloc_depnode: fn(&Lock<CurrentDepGraph>,
+                                          DepNode,
+                                          OpenTask) -> DepNodeIndex
+    ) -> (R, DepNodeIndex)
+    where
+        C: DepGraphSafe + StableHashingContextProvider<'gcx>,
+        R: HashStable<StableHashingContext<'gcx>>,
+    {
         if let Some(ref data) = self.data {
-            push(&data.current, key);
+            let open_task = create_task(key);
 
             // In incremental mode, hash the result of the task. We don't
             // do anything with the hash yet, but we are computing it
@@ -227,13 +263,26 @@ impl DepGraph {
                 profq_msg(hcx.sess(), ProfileQueriesMsg::TaskBegin(key.clone()))
             };
 
-            let result = task(cx, arg);
+            let result = if no_tcx {
+                task(cx, arg)
+            } else {
+                ty::tls::with_context(|icx| {
+                    let icx = ty::tls::ImplicitCtxt {
+                        task: &open_task,
+                        ..icx.clone()
+                    };
+
+                    ty::tls::enter_context(&icx, |_| {
+                        task(cx, arg)
+                    })
+                })
+            };
 
             if cfg!(debug_assertions) {
                 profq_msg(hcx.sess(), ProfileQueriesMsg::TaskEnd)
             };
 
-            let dep_node_index = pop(&data.current, key);
+            let dep_node_index = finish_task_and_alloc_depnode(&data.current, key, open_task);
 
             let mut stable_hasher = StableHasher::new();
             result.hash_stable(&mut hcx, &mut stable_hasher);
@@ -302,11 +351,28 @@ impl DepGraph {
         where OP: FnOnce() -> R
     {
         if let Some(ref data) = self.data {
-            data.current.borrow_mut().push_anon_task();
-            let result = op();
+            let (result, open_task) = ty::tls::with_context(|icx| {
+                let task = OpenTask::Anon(Lock::new(AnonOpenTask {
+                    reads: Vec::new(),
+                    read_set: FxHashSet(),
+                }));
+
+                let r = {
+                    let icx = ty::tls::ImplicitCtxt {
+                        task: &task,
+                        ..icx.clone()
+                    };
+
+                    ty::tls::enter_context(&icx, |_| {
+                        op()
+                    })
+                };
+
+                (r, task)
+            });
             let dep_node_index = data.current
                                      .borrow_mut()
-                                     .pop_anon_task(dep_kind);
+                                     .pop_anon_task(dep_kind, open_task);
             (result, dep_node_index)
         } else {
             (op(), DepNodeIndex::INVALID)
@@ -324,9 +390,9 @@ impl DepGraph {
         where C: DepGraphSafe + StableHashingContextProvider<'gcx>,
               R: HashStable<StableHashingContext<'gcx>>,
     {
-        self.with_task_impl(key, cx, arg, task,
-            |data, key| data.borrow_mut().push_eval_always_task(key),
-            |data, key| data.borrow_mut().pop_eval_always_task(key))
+        self.with_task_impl(key, cx, arg, false, task,
+            |key| OpenTask::EvalAlways { node: key },
+            |data, key, task| data.borrow_mut().complete_eval_always_task(key, task))
     }
 
     #[inline]
@@ -432,13 +498,13 @@ impl DepGraph {
 
     /// Access the map of work-products created during this run. Only
     /// used during saving of the dep-graph.
-    pub fn work_products(&self) -> Ref<FxHashMap<WorkProductId, WorkProduct>> {
+    pub fn work_products(&self) -> ReadGuard<FxHashMap<WorkProductId, WorkProduct>> {
         self.data.as_ref().unwrap().work_products.borrow()
     }
 
     /// Access the map of work-products created during the cached run. Only
     /// used during saving of the dep-graph.
-    pub fn previous_work_products(&self) -> Ref<FxHashMap<WorkProductId, WorkProduct>> {
+    pub fn previous_work_products(&self) -> ReadGuard<FxHashMap<WorkProductId, WorkProduct>> {
         self.data.as_ref().unwrap().previous_work_products.borrow()
     }
 
@@ -528,6 +594,7 @@ impl DepGraph {
         debug!("try_mark_green({:?}) - BEGIN", dep_node);
         let data = self.data.as_ref().unwrap();
 
+        #[cfg(not(parallel_queries))]
         debug_assert!(!data.current.borrow().node_to_node_index.contains_key(dep_node));
 
         if dep_node.kind.is_input() {
@@ -668,16 +735,24 @@ impl DepGraph {
             }
         }
 
-
         // If we got here without hitting a `return` that means that all
         // dependencies of this DepNode could be marked as green. Therefore we
-        // can also mark this DepNode as green. We do so by...
+        // can also mark this DepNode as green.
 
-        // ... allocating an entry for it in the current dependency graph and
-        // adding all the appropriate edges imported from the previous graph ...
-        let dep_node_index = data.current
-                                 .borrow_mut()
-                                 .alloc_node(*dep_node, current_deps);
+        // There may be multiple threads trying to mark the same dep node green concurrently
+
+        let (dep_node_index, did_allocation) = {
+            let mut current = data.current.borrow_mut();
+
+            if let Some(&dep_node_index) = current.node_to_node_index.get(&dep_node) {
+                // Someone else allocated it before us
+                (dep_node_index, false)
+            } else {
+                // We allocating an entry for the node in the current dependency graph and
+                // adding all the appropriate edges imported from the previous graph
+                (current.alloc_node(*dep_node, current_deps), true)
+            }
+        };
 
         // ... copying the fingerprint from the previous graph too, so we don't
         // have to recompute it ...
@@ -689,6 +764,8 @@ impl DepGraph {
                 fingerprints.resize(dep_node_index.index() + 1, Fingerprint::ZERO);
             }
 
+            // Multiple threads can all write the same fingerprint here
+            #[cfg(not(parallel_queries))]
             debug_assert!(fingerprints[dep_node_index] == Fingerprint::ZERO,
                 "DepGraph::try_mark_green() - Duplicate fingerprint \
                 insertion for {:?}", dep_node);
@@ -697,7 +774,14 @@ impl DepGraph {
         }
 
         // ... emitting any stored diagnostic ...
-        {
+        if did_allocation {
+            // Only the thread which did the allocation emits the error messages
+
+            // FIXME: Ensure that these are printed before returning for all threads.
+            // Currently threads where did_allocation = false can continue on
+            // and emit other diagnostics before these diagnostics are emitted.
+            // Such diagnostics should be emitted after these.
+            // See https://github.com/rust-lang/rust/issues/48685
             let diagnostics = tcx.on_disk_query_result_cache
                                  .load_diagnostics(tcx, prev_dep_node_index);
 
@@ -716,6 +800,8 @@ impl DepGraph {
 
         // ... and finally storing a "Green" entry in the color map.
         let mut colors = data.colors.borrow_mut();
+        // Multiple threads can all write the same color here
+        #[cfg(not(parallel_queries))]
         debug_assert!(colors.get(prev_dep_node_index).is_none(),
                       "DepGraph::try_mark_green() - Duplicate DepNodeColor \
                       insertion for {:?}", dep_node);
@@ -839,7 +925,6 @@ pub(super) struct CurrentDepGraph {
     nodes: IndexVec<DepNodeIndex, DepNode>,
     edges: IndexVec<DepNodeIndex, Vec<DepNodeIndex>>,
     node_to_node_index: FxHashMap<DepNode, DepNodeIndex>,
-    task_stack: Vec<OpenTask>,
     forbidden_edge: Option<EdgeFilter>,
 
     // Anonymous DepNodes are nodes the ID of which we compute from the list of
@@ -888,38 +973,19 @@ impl CurrentDepGraph {
             edges: IndexVec::new(),
             node_to_node_index: FxHashMap(),
             anon_id_seed: stable_hasher.finish(),
-            task_stack: Vec::new(),
             forbidden_edge,
             total_read_count: 0,
             total_duplicate_read_count: 0,
         }
     }
 
-    pub(super) fn push_ignore(&mut self) {
-        self.task_stack.push(OpenTask::Ignore);
-    }
-
-    pub(super) fn pop_ignore(&mut self) {
-        let popped_node = self.task_stack.pop().unwrap();
-        debug_assert_eq!(popped_node, OpenTask::Ignore);
-    }
-
-    pub(super) fn push_task(&mut self, key: DepNode) {
-        self.task_stack.push(OpenTask::Regular {
-            node: key,
-            reads: Vec::new(),
-            read_set: FxHashSet(),
-        });
-    }
-
-    pub(super) fn pop_task(&mut self, key: DepNode) -> DepNodeIndex {
-        let popped_node = self.task_stack.pop().unwrap();
-
-        if let OpenTask::Regular {
-            node,
-            read_set: _,
-            reads
-        } = popped_node {
+    fn complete_task(&mut self, key: DepNode, task: OpenTask) -> DepNodeIndex {
+        if let OpenTask::Regular(task) = task {
+            let RegularOpenTask {
+                node,
+                read_set: _,
+                reads
+            } = task.into_inner();
             assert_eq!(node, key);
 
             // If this is an input node, we expect that it either has no
@@ -946,24 +1012,16 @@ impl CurrentDepGraph {
 
             self.alloc_node(node, reads)
         } else {
-            bug!("pop_task() - Expected regular task to be popped")
+            bug!("complete_task() - Expected regular task to be popped")
         }
     }
 
-    fn push_anon_task(&mut self) {
-        self.task_stack.push(OpenTask::Anon {
-            reads: Vec::new(),
-            read_set: FxHashSet(),
-        });
-    }
-
-    fn pop_anon_task(&mut self, kind: DepKind) -> DepNodeIndex {
-        let popped_node = self.task_stack.pop().unwrap();
-
-        if let OpenTask::Anon {
-            read_set: _,
-            reads
-        } = popped_node {
+    fn pop_anon_task(&mut self, kind: DepKind, task: OpenTask) -> DepNodeIndex {
+        if let OpenTask::Anon(task) = task {
+            let AnonOpenTask {
+                read_set: _,
+                reads
+            } = task.into_inner();
             debug_assert!(!kind.is_input());
 
             let mut fingerprint = self.anon_id_seed;
@@ -997,62 +1055,54 @@ impl CurrentDepGraph {
         }
     }
 
-    fn push_eval_always_task(&mut self, key: DepNode) {
-        self.task_stack.push(OpenTask::EvalAlways { node: key });
-    }
-
-    fn pop_eval_always_task(&mut self, key: DepNode) -> DepNodeIndex {
-        let popped_node = self.task_stack.pop().unwrap();
-
+    fn complete_eval_always_task(&mut self, key: DepNode, task: OpenTask) -> DepNodeIndex {
         if let OpenTask::EvalAlways {
             node,
-        } = popped_node {
+        } = task {
             debug_assert_eq!(node, key);
             let krate_idx = self.node_to_node_index[&DepNode::new_no_params(DepKind::Krate)];
             self.alloc_node(node, vec![krate_idx])
         } else {
-            bug!("pop_eval_always_task() - Expected eval always task to be popped");
+            bug!("complete_eval_always_task() - Expected eval always task to be popped");
         }
     }
 
     fn read_index(&mut self, source: DepNodeIndex) {
-        match self.task_stack.last_mut() {
-            Some(&mut OpenTask::Regular {
-                ref mut reads,
-                ref mut read_set,
-                node: ref target,
-            }) => {
-                self.total_read_count += 1;
-                if read_set.insert(source) {
-                    reads.push(source);
-
-                    if cfg!(debug_assertions) {
-                        if let Some(ref forbidden_edge) = self.forbidden_edge {
-                            let source = self.nodes[source];
-                            if forbidden_edge.test(&source, &target) {
-                                bug!("forbidden edge {:?} -> {:?} created",
-                                     source,
-                                     target)
+        ty::tls::with_context_opt(|icx| {
+            let icx = if let Some(icx) = icx { icx } else { return };
+            match *icx.task {
+                OpenTask::Regular(ref task) => {
+                    let mut task = task.lock();
+                    self.total_read_count += 1;
+                    if task.read_set.insert(source) {
+                        task.reads.push(source);
+
+                        if cfg!(debug_assertions) {
+                            if let Some(ref forbidden_edge) = self.forbidden_edge {
+                                let target = &task.node;
+                                let source = self.nodes[source];
+                                if forbidden_edge.test(&source, &target) {
+                                    bug!("forbidden edge {:?} -> {:?} created",
+                                        source,
+                                        target)
+                                }
                             }
                         }
+                    } else {
+                        self.total_duplicate_read_count += 1;
                     }
-                } else {
-                    self.total_duplicate_read_count += 1;
                 }
-            }
-            Some(&mut OpenTask::Anon {
-                ref mut reads,
-                ref mut read_set,
-            }) => {
-                if read_set.insert(source) {
-                    reads.push(source);
+                OpenTask::Anon(ref task) => {
+                    let mut task = task.lock();
+                    if task.read_set.insert(source) {
+                        task.reads.push(source);
+                    }
+                }
+                OpenTask::Ignore | OpenTask::EvalAlways { .. } => {
+                    // ignore
                 }
             }
-            Some(&mut OpenTask::Ignore) |
-            Some(&mut OpenTask::EvalAlways { .. }) | None => {
-                // ignore
-            }
-        }
+        })
     }
 
     fn alloc_node(&mut self,
@@ -1070,17 +1120,20 @@ impl CurrentDepGraph {
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
-enum OpenTask {
-    Regular {
-        node: DepNode,
-        reads: Vec<DepNodeIndex>,
-        read_set: FxHashSet<DepNodeIndex>,
-    },
-    Anon {
-        reads: Vec<DepNodeIndex>,
-        read_set: FxHashSet<DepNodeIndex>,
-    },
+pub struct RegularOpenTask {
+    node: DepNode,
+    reads: Vec<DepNodeIndex>,
+    read_set: FxHashSet<DepNodeIndex>,
+}
+
+pub struct AnonOpenTask {
+    reads: Vec<DepNodeIndex>,
+    read_set: FxHashSet<DepNodeIndex>,
+}
+
+pub enum OpenTask {
+    Regular(Lock<RegularOpenTask>),
+    Anon(Lock<AnonOpenTask>),
     Ignore,
     EvalAlways {
         node: DepNode,
diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs
index a472183698a..8a6f66911ec 100644
--- a/src/librustc/dep_graph/mod.rs
+++ b/src/librustc/dep_graph/mod.rs
@@ -14,13 +14,12 @@ mod dep_tracking_map;
 mod graph;
 mod prev;
 mod query;
-mod raii;
 mod safe;
 mod serialized;
 
 pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig};
 pub use self::dep_node::{DepNode, DepKind, DepConstructor, WorkProductId, label_strs};
-pub use self::graph::{DepGraph, WorkProduct, DepNodeIndex, DepNodeColor};
+pub use self::graph::{DepGraph, WorkProduct, DepNodeIndex, DepNodeColor, OpenTask};
 pub use self::graph::WorkProductFileKind;
 pub use self::prev::PreviousDepGraph;
 pub use self::query::DepGraphQuery;
diff --git a/src/librustc/dep_graph/raii.rs b/src/librustc/dep_graph/raii.rs
deleted file mode 100644
index 5728bcc7d27..00000000000
--- a/src/librustc/dep_graph/raii.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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 super::graph::CurrentDepGraph;
-
-use std::cell::RefCell;
-
-pub struct IgnoreTask<'graph> {
-    graph: &'graph RefCell<CurrentDepGraph>,
-}
-
-impl<'graph> IgnoreTask<'graph> {
-    pub(super) fn new(graph: &'graph RefCell<CurrentDepGraph>) -> IgnoreTask<'graph> {
-        graph.borrow_mut().push_ignore();
-        IgnoreTask {
-            graph,
-        }
-    }
-}
-
-impl<'graph> Drop for IgnoreTask<'graph> {
-    fn drop(&mut self) {
-        self.graph.borrow_mut().pop_ignore();
-    }
-}
-
diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs
index f77275926eb..38de8548f30 100644
--- a/src/librustc/hir/map/collector.rs
+++ b/src/librustc/hir/map/collector.rs
@@ -79,26 +79,23 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
                 body_ids: _,
             } = *krate;
 
-            root_mod_sig_dep_index = dep_graph.with_task(
+            root_mod_sig_dep_index = dep_graph.input_task(
                 root_mod_def_path_hash.to_dep_node(DepKind::Hir),
                 &hcx,
                 HirItemLike { item_like: (module, attrs, span), hash_bodies: false },
-                identity_fn
             ).1;
-            root_mod_full_dep_index = dep_graph.with_task(
+            root_mod_full_dep_index = dep_graph.input_task(
                 root_mod_def_path_hash.to_dep_node(DepKind::HirBody),
                 &hcx,
                 HirItemLike { item_like: (module, attrs, span), hash_bodies: true },
-                identity_fn
             ).1;
         }
 
         {
-            dep_graph.with_task(
+            dep_graph.input_task(
                 DepNode::new_no_params(DepKind::AllLocalTraitImpls),
                 &hcx,
                 &krate.trait_impls,
-                identity_fn
             );
         }
 
@@ -169,12 +166,11 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
 
         let (_, crate_dep_node_index) = self
             .dep_graph
-            .with_task(DepNode::new_no_params(DepKind::Krate),
+            .input_task(DepNode::new_no_params(DepKind::Krate),
                        &self.hcx,
                        (((node_hashes, upstream_crates), source_file_names),
                         (commandline_args_hash,
-                         crate_disambiguator.to_fingerprint())),
-                       identity_fn);
+                         crate_disambiguator.to_fingerprint())));
 
         let svh = Svh::new(self.dep_graph
                                .fingerprint_of(crate_dep_node_index)
@@ -267,18 +263,16 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
 
         let def_path_hash = self.definitions.def_path_hash(dep_node_owner);
 
-        self.current_signature_dep_index = self.dep_graph.with_task(
+        self.current_signature_dep_index = self.dep_graph.input_task(
             def_path_hash.to_dep_node(DepKind::Hir),
             &self.hcx,
             HirItemLike { item_like, hash_bodies: false },
-            identity_fn
         ).1;
 
-        self.current_full_dep_index = self.dep_graph.with_task(
+        self.current_full_dep_index = self.dep_graph.input_task(
             def_path_hash.to_dep_node(DepKind::HirBody),
             &self.hcx,
             HirItemLike { item_like, hash_bodies: true },
-            identity_fn
         ).1;
 
         self.hir_body_nodes.push((def_path_hash, self.current_full_dep_index));
@@ -520,12 +514,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
     }
 }
 
-// We use this with DepGraph::with_task(). Since we are handling only input
-// values here, the "task" computing them just passes them through.
-fn identity_fn<T>(_: &StableHashingContext, item_like: T) -> T {
-    item_like
-}
-
 // This is a wrapper structure that allows determining if span values within
 // the wrapped item should be hashed or not.
 struct HirItemLike<T> {
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 56669243f27..c35f515a99e 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1562,6 +1562,7 @@ impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> {
                 tcx,
                 query: icx.query.clone(),
                 layout_depth: icx.layout_depth,
+                task: icx.task,
             };
             ty::tls::enter_context(&new_icx, |new_icx| {
                 f(new_icx.tcx)
@@ -1741,6 +1742,7 @@ pub mod tls {
     use errors::{Diagnostic, TRACK_DIAGNOSTICS};
     use rustc_data_structures::OnDrop;
     use rustc_data_structures::sync::Lrc;
+    use dep_graph::OpenTask;
 
     /// This is the implicit state of rustc. It contains the current
     /// TyCtxt and query. It is updated when creating a local interner or
@@ -1759,6 +1761,10 @@ pub mod tls {
 
         /// Used to prevent layout from recursing too deeply.
         pub layout_depth: usize,
+
+        /// The current dep graph task. This is used to add dependencies to queries
+        /// when executing them
+        pub task: &'a OpenTask,
     }
 
     // A thread local value which stores a pointer to the current ImplicitCtxt
@@ -1845,6 +1851,7 @@ pub mod tls {
                 tcx,
                 query: None,
                 layout_depth: 0,
+                task: &OpenTask::Ignore,
             };
             enter_context(&icx, |_| {
                 f(tcx)
diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs
index 70fbd17e6cb..61a4eb58531 100644
--- a/src/librustc/ty/maps/plumbing.rs
+++ b/src/librustc/ty/maps/plumbing.rs
@@ -530,6 +530,7 @@ macro_rules! define_maps {
                             tcx,
                             query: Some(job.clone()),
                             layout_depth: icx.layout_depth,
+                            task: icx.task,
                         };
 
                         // Use the ImplicitCtxt while we execute the query
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index d8aba024502..de043668410 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1982,32 +1982,38 @@ impl<'a, 'gcx, 'tcx> AdtDef {
                                     tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                     variant_index: usize)
                                     -> Discr<'tcx> {
-        let repr_type = self.repr.discr_type();
-        let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx());
+        let (val, offset) = self.discriminant_def_for_variant(variant_index);
+        let explicit_value = val
+            .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did))
+            .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx.global_tcx()));
+        explicit_value.checked_add(tcx, offset as u128).0
+    }
+
+    /// Yields a DefId for the discriminant and an offset to add to it
+    /// Alternatively, if there is no explicit discriminant, returns the
+    /// inferred discriminant directly
+    pub fn discriminant_def_for_variant(
+        &self,
+        variant_index: usize,
+    ) -> (Option<DefId>, usize) {
         let mut explicit_index = variant_index;
+        let expr_did;
         loop {
             match self.variants[explicit_index].discr {
-                ty::VariantDiscr::Relative(0) => break,
+                ty::VariantDiscr::Relative(0) => {
+                    expr_did = None;
+                    break;
+                },
                 ty::VariantDiscr::Relative(distance) => {
                     explicit_index -= distance;
                 }
-                ty::VariantDiscr::Explicit(expr_did) => {
-                    match self.eval_explicit_discr(tcx, expr_did) {
-                        Some(discr) => {
-                            explicit_value = discr;
-                            break;
-                        },
-                        None => {
-                            if explicit_index == 0 {
-                                break;
-                            }
-                            explicit_index -= 1;
-                        }
-                    }
+                ty::VariantDiscr::Explicit(did) => {
+                    expr_did = Some(did);
+                    break;
                 }
             }
         }
-        explicit_value.checked_add(tcx, (variant_index - explicit_index) as u128).0
+        (expr_did, variant_index - explicit_index)
     }
 
     pub fn destructor(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Destructor> {
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index c0d28280946..d0c352319c8 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -589,12 +589,84 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
             // Check to see if this cast is a "coercion cast", where the cast is actually done
             // using a coercion (or is a no-op).
             if let Some(&TyCastKind::CoercionCast) = cx.tables()
-                                                       .cast_kinds()
-                                                       .get(source.hir_id) {
+                                                    .cast_kinds()
+                                                    .get(source.hir_id) {
                 // Convert the lexpr to a vexpr.
                 ExprKind::Use { source: source.to_ref() }
             } else {
-                ExprKind::Cast { source: source.to_ref() }
+                // check whether this is casting an enum variant discriminant
+                // to prevent cycles, we refer to the discriminant initializer
+                // which is always an integer and thus doesn't need to know the
+                // enum's layout (or its tag type) to compute it during const eval
+                // Example:
+                // enum Foo {
+                //     A,
+                //     B = A as isize + 4,
+                // }
+                // The correct solution would be to add symbolic computations to miri,
+                // so we wouldn't have to compute and store the actual value
+                let var = if let hir::ExprPath(ref qpath) = source.node {
+                    let def = cx.tables().qpath_def(qpath, source.hir_id);
+                    cx
+                        .tables()
+                        .node_id_to_type(source.hir_id)
+                        .ty_adt_def()
+                        .and_then(|adt_def| {
+                        match def {
+                            Def::VariantCtor(variant_id, CtorKind::Const) => {
+                                let idx = adt_def.variant_index_with_id(variant_id);
+                                let (d, o) = adt_def.discriminant_def_for_variant(idx);
+                                use rustc::ty::util::IntTypeExt;
+                                let ty = adt_def.repr.discr_type().to_ty(cx.tcx());
+                                Some((d, o, ty))
+                            }
+                            _ => None,
+                        }
+                    })
+                } else {
+                    None
+                };
+                let source = if let Some((did, offset, ty)) = var {
+                    let mk_const = |val| Expr {
+                        temp_lifetime,
+                        ty,
+                        span: expr.span,
+                        kind: ExprKind::Literal {
+                            literal: Literal::Value {
+                                value: cx.tcx().mk_const(ty::Const {
+                                    val,
+                                    ty,
+                                }),
+                            },
+                        },
+                    }.to_ref();
+                    let offset = mk_const(
+                        ConstVal::Value(Value::ByVal(PrimVal::Bytes(offset as u128))),
+                    );
+                    match did {
+                        Some(did) => {
+                            // in case we are offsetting from a computed discriminant
+                            // and not the beginning of discriminants (which is always `0`)
+                            let substs = Substs::identity_for_item(cx.tcx(), did);
+                            let lhs = mk_const(ConstVal::Unevaluated(did, substs));
+                            let bin = ExprKind::Binary {
+                                op: BinOp::Add,
+                                lhs,
+                                rhs: offset,
+                            };
+                            Expr {
+                                temp_lifetime,
+                                ty,
+                                span: expr.span,
+                                kind: bin,
+                            }.to_ref()
+                        },
+                        None => offset,
+                    }
+                } else {
+                    source.to_ref()
+                };
+                ExprKind::Cast { source }
             }
         }
         hir::ExprType(ref source, _) => return source.make_mirror(cx),
diff --git a/src/test/ui/const-eval/enum_discr.rs b/src/test/ui/const-eval/enum_discr.rs
new file mode 100644
index 00000000000..ba38a42092e
--- /dev/null
+++ b/src/test/ui/const-eval/enum_discr.rs
@@ -0,0 +1,36 @@
+// Copyright 2018 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.
+
+// compile-pass
+// run-pass
+
+enum Foo {
+    X = 42,
+    Y = Foo::X as isize - 3,
+}
+
+enum Bar {
+    X,
+    Y = Bar::X as isize + 2,
+}
+
+enum Boo {
+    X = Boo::Y as isize * 2,
+    Y = 9,
+}
+
+fn main() {
+    assert_eq!(Foo::X as isize, 42);
+    assert_eq!(Foo::Y as isize, 39);
+    assert_eq!(Bar::X as isize, 0);
+    assert_eq!(Bar::Y as isize, 2);
+    assert_eq!(Boo::X as isize, 18);
+    assert_eq!(Boo::Y as isize, 9);
+}
diff --git a/src/test/ui/issue-23302-1.stderr b/src/test/ui/issue-23302-1.stderr
index 0fbe2f7a411..6ff46a7e21f 100644
--- a/src/test/ui/issue-23302-1.stderr
+++ b/src/test/ui/issue-23302-1.stderr
@@ -1,11 +1,15 @@
-error[E0391]: cycle detected when const-evaluating `X::A::{{initializer}}`
+error[E0391]: cycle detected when processing `X::A::{{initializer}}`
   --> $DIR/issue-23302-1.rs:14:9
    |
 LL |     A = X::A as isize, //~ ERROR E0391
-   |         ^^^^
+   |         ^^^^^^^^^^^^^
    |
-note: ...which requires computing layout of `X`...
-   = note: ...which again requires const-evaluating `X::A::{{initializer}}`, completing the cycle
+   = note: ...which again requires processing `X::A::{{initializer}}`, completing the cycle
+note: cycle used when const-evaluating `X::A::{{initializer}}`
+  --> $DIR/issue-23302-1.rs:14:9
+   |
+LL |     A = X::A as isize, //~ ERROR E0391
+   |         ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issue-23302-2.stderr b/src/test/ui/issue-23302-2.stderr
index 313cfa0c162..6b72a5b544d 100644
--- a/src/test/ui/issue-23302-2.stderr
+++ b/src/test/ui/issue-23302-2.stderr
@@ -1,11 +1,15 @@
-error[E0391]: cycle detected when const-evaluating `Y::A::{{initializer}}`
+error[E0391]: cycle detected when processing `Y::A::{{initializer}}`
   --> $DIR/issue-23302-2.rs:14:9
    |
 LL |     A = Y::B as isize, //~ ERROR E0391
-   |         ^^^^
+   |         ^^^^^^^^^^^^^
    |
-note: ...which requires computing layout of `Y`...
-   = note: ...which again requires const-evaluating `Y::A::{{initializer}}`, completing the cycle
+   = note: ...which again requires processing `Y::A::{{initializer}}`, completing the cycle
+note: cycle used when const-evaluating `Y::A::{{initializer}}`
+  --> $DIR/issue-23302-2.rs:14:9
+   |
+LL |     A = Y::B as isize, //~ ERROR E0391
+   |         ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issue-36163.stderr b/src/test/ui/issue-36163.stderr
index 541f54ca768..7199fffd386 100644
--- a/src/test/ui/issue-36163.stderr
+++ b/src/test/ui/issue-36163.stderr
@@ -1,21 +1,20 @@
-error[E0391]: cycle detected when const-evaluating `Foo::B::{{initializer}}`
+error[E0391]: cycle detected when processing `Foo::B::{{initializer}}`
   --> $DIR/issue-36163.rs:14:9
    |
 LL |     B = A, //~ ERROR E0391
    |         ^
    |
-note: ...which requires processing `Foo::B::{{initializer}}`...
+note: ...which requires processing `A`...
+  --> $DIR/issue-36163.rs:11:18
+   |
+LL | const A: isize = Foo::B as isize;
+   |                  ^^^^^^^^^^^^^^^
+   = note: ...which again requires processing `Foo::B::{{initializer}}`, completing the cycle
+note: cycle used when const-evaluating `Foo::B::{{initializer}}`
   --> $DIR/issue-36163.rs:14:9
    |
 LL |     B = A, //~ ERROR E0391
    |         ^
-note: ...which requires const-evaluating `A`...
-  --> $DIR/issue-36163.rs:11:18
-   |
-LL | const A: isize = Foo::B as isize;
-   |                  ^^^^^^
-note: ...which requires computing layout of `Foo`...
-   = note: ...which again requires const-evaluating `Foo::B::{{initializer}}`, completing the cycle
 
 error: aborting due to previous error