diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc/dep_graph/graph.rs | 353 | ||||
| -rw-r--r-- | src/librustc/dep_graph/mod.rs | 3 | ||||
| -rw-r--r-- | src/librustc/dep_graph/raii.rs | 33 | ||||
| -rw-r--r-- | src/librustc/hir/map/collector.rs | 26 | ||||
| -rw-r--r-- | src/librustc/ty/context.rs | 7 | ||||
| -rw-r--r-- | src/librustc/ty/maps/plumbing.rs | 1 | ||||
| -rw-r--r-- | src/librustc/ty/mod.rs | 40 | ||||
| -rw-r--r-- | src/librustc_mir/hair/cx/expr.rs | 78 | ||||
| -rw-r--r-- | src/test/ui/const-eval/enum_discr.rs | 36 | ||||
| -rw-r--r-- | src/test/ui/issue-23302-1.stderr | 12 | ||||
| -rw-r--r-- | src/test/ui/issue-23302-2.stderr | 12 | ||||
| -rw-r--r-- | src/test/ui/issue-36163.stderr | 17 |
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 |
