//! fingerprint for a given set of node parameters.
use crate::hir::map::DefPathHash;
-use crate::ich::{Fingerprint, StableHashingContext};
+use crate::ich::Fingerprint;
use crate::mir;
use crate::mir::interpret::{GlobalId, LitToConstInput};
use crate::traits;
use crate::ty::subst::SubstsRef;
use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt};
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX};
use rustc_hir::HirId;
use rustc_span::symbol::Symbol;
-use std::fmt;
use std::hash::Hash;
+pub use rustc_query_system::dep_graph::{DepContext, DepNodeParams};
+
// erase!() just makes tokens go away. It's used to specify which macro argument
// is repeated (i.e., which sub-expression of the macro we are in) but don't need
// to actually use any of the arguments.
// tuple args
$({
- return <$tuple_arg_ty as DepNodeParams>
+ return <$tuple_arg_ty as DepNodeParams<TyCtxt<'_>>>
::CAN_RECONSTRUCT_QUERY_KEY;
})*
)*
}
- #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
- RustcEncodable, RustcDecodable)]
- pub struct DepNode {
- pub kind: DepKind,
- pub hash: Fingerprint,
+ pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>;
+
+ pub trait DepNodeExt: Sized {
+ /// Construct a DepNode from the given DepKind and DefPathHash. This
+ /// method will assert that the given DepKind actually requires a
+ /// single DefId/DefPathHash parameter.
+ fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self;
+
+ /// Used in testing
+ fn from_label_string(label: &str, def_path_hash: DefPathHash)
+ -> Result<Self, ()>;
+
+ /// Used in testing
+ fn has_label_string(label: &str) -> bool;
}
- impl DepNode {
+ impl DepNodeExt for DepNode {
/// Construct a DepNode from the given DepKind and DefPathHash. This
/// method will assert that the given DepKind actually requires a
/// single DefId/DefPathHash parameter.
- pub fn from_def_path_hash(def_path_hash: DefPathHash,
- kind: DepKind)
- -> DepNode {
+ fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode {
debug_assert!(kind.can_reconstruct_query_key() && kind.has_params());
DepNode {
kind,
}
}
- /// Creates a new, parameterless DepNode. This method will assert
- /// that the DepNode corresponding to the given DepKind actually
- /// does not require any parameters.
- pub fn new_no_params(kind: DepKind) -> DepNode {
- debug_assert!(!kind.has_params());
- DepNode {
- kind,
- hash: Fingerprint::ZERO,
- }
- }
-
/// Extracts the DefId corresponding to this DepNode. This will work
/// if two conditions are met:
///
/// DepNode. Condition (2) might not be fulfilled if a DepNode
/// refers to something from the previous compilation session that
/// has been removed.
- pub fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
- if self.kind.can_reconstruct_query_key() {
- let def_path_hash = DefPathHash(self.hash);
- tcx.def_path_hash_to_def_id.as_ref()?
- .get(&def_path_hash).cloned()
- } else {
- None
- }
- }
-
/// Used in testing
- pub fn from_label_string(label: &str,
- def_path_hash: DefPathHash)
- -> Result<DepNode, ()> {
+ fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result<DepNode, ()> {
let kind = match label {
$(
stringify!($variant) => DepKind::$variant,
}
/// Used in testing
- pub fn has_label_string(label: &str) -> bool {
+ fn has_label_string(label: &str) -> bool {
match label {
$(
stringify!($variant) => true,
);
}
-impl fmt::Debug for DepNode {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{:?}", self.kind)?;
-
- if !self.kind.has_params() && !self.kind.is_anon() {
- return Ok(());
- }
-
- write!(f, "(")?;
-
- crate::ty::tls::with_opt(|opt_tcx| {
- if let Some(tcx) = opt_tcx {
- if let Some(def_id) = self.extract_def_id(tcx) {
- write!(f, "{}", tcx.def_path_debug_str(def_id))?;
- } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*self) {
- write!(f, "{}", s)?;
- } else {
- write!(f, "{}", self.hash)?;
- }
- } else {
- write!(f, "{}", self.hash)?;
- }
- Ok(())
- })?;
-
- write!(f, ")")
- }
-}
-
rustc_dep_node_append!([define_dep_nodes!][ <'tcx>
// We use this for most things when incr. comp. is turned off.
[] Null,
[] CompileCodegenUnit(Symbol),
]);
-pub(crate) trait DepNodeParams<'tcx>: fmt::Debug + Sized {
- const CAN_RECONSTRUCT_QUERY_KEY: bool;
-
- /// This method turns the parameters of a DepNodeConstructor into an opaque
- /// Fingerprint to be used in DepNode.
- /// Not all DepNodeParams support being turned into a Fingerprint (they
- /// don't need to if the corresponding DepNode is anonymous).
- fn to_fingerprint(&self, _: TyCtxt<'tcx>) -> Fingerprint {
- panic!("Not implemented. Accidentally called on anonymous node?")
- }
-
- fn to_debug_str(&self, _: TyCtxt<'tcx>) -> String {
- format!("{:?}", self)
- }
-
- /// This method tries to recover the query key from the given `DepNode`,
- /// something which is needed when forcing `DepNode`s during red-green
- /// evaluation. The query system will only call this method if
- /// `CAN_RECONSTRUCT_QUERY_KEY` is `true`.
- /// It is always valid to return `None` here, in which case incremental
- /// compilation will treat the query as having changed instead of forcing it.
- fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self>;
-}
-
-impl<'tcx, T> DepNodeParams<'tcx> for T
-where
- T: HashStable<StableHashingContext<'tcx>> + fmt::Debug,
-{
- default const CAN_RECONSTRUCT_QUERY_KEY: bool = false;
-
- default fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
- let mut hcx = tcx.create_stable_hashing_context();
- let mut hasher = StableHasher::new();
-
- self.hash_stable(&mut hcx, &mut hasher);
-
- hasher.finish()
- }
-
- default fn to_debug_str(&self, _: TyCtxt<'tcx>) -> String {
- format!("{:?}", *self)
- }
-
- default fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option<Self> {
- None
- }
-}
-
-impl<'tcx> DepNodeParams<'tcx> for DefId {
+impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for DefId {
const CAN_RECONSTRUCT_QUERY_KEY: bool = true;
- fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint {
+ fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
tcx.def_path_hash(*self).0
}
}
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
- dep_node.extract_def_id(tcx)
+ tcx.extract_def_id(dep_node)
}
}
-impl<'tcx> DepNodeParams<'tcx> for LocalDefId {
+impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for LocalDefId {
const CAN_RECONSTRUCT_QUERY_KEY: bool = true;
- fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint {
+ fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
self.to_def_id().to_fingerprint(tcx)
}
}
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
- dep_node.extract_def_id(tcx).map(|id| id.expect_local())
+ tcx.extract_def_id(dep_node).map(|id| id.expect_local())
}
}
-impl<'tcx> DepNodeParams<'tcx> for CrateNum {
+impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for CrateNum {
const CAN_RECONSTRUCT_QUERY_KEY: bool = true;
- fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint {
+ fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX };
tcx.def_path_hash(def_id).0
}
}
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
- dep_node.extract_def_id(tcx).map(|id| id.krate)
+ tcx.extract_def_id(dep_node).map(|id| id.krate)
}
}
-impl<'tcx> DepNodeParams<'tcx> for (DefId, DefId) {
+impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for (DefId, DefId) {
const CAN_RECONSTRUCT_QUERY_KEY: bool = false;
// We actually would not need to specialize the implementation of this
// method but it's faster to combine the hashes than to instantiate a full
// hashing context and stable-hashing state.
- fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint {
+ fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
let (def_id_0, def_id_1) = *self;
let def_path_hash_0 = tcx.def_path_hash(def_id_0);
}
}
-impl<'tcx> DepNodeParams<'tcx> for HirId {
+impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId {
const CAN_RECONSTRUCT_QUERY_KEY: bool = false;
// We actually would not need to specialize the implementation of this
// method but it's faster to combine the hashes than to instantiate a full
// hashing context and stable-hashing state.
- fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint {
+ fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
let HirId { owner, local_id } = *self;
let def_path_hash = tcx.def_path_hash(owner.to_def_id());
def_path_hash.0.combine(local_id)
}
}
-
-/// A "work product" corresponds to a `.o` (or other) file that we
-/// save in between runs. These IDs do not have a `DefId` but rather
-/// some independent path or string that persists between runs without
-/// the need to be mapped or unmapped. (This ensures we can serialize
-/// them even in the absence of a tcx.)
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
-#[derive(HashStable)]
-pub struct WorkProductId {
- hash: Fingerprint,
-}
-
-impl WorkProductId {
- pub fn from_cgu_name(cgu_name: &str) -> WorkProductId {
- let mut hasher = StableHasher::new();
- cgu_name.len().hash(&mut hasher);
- cgu_name.hash(&mut hasher);
- WorkProductId { hash: hasher.finish() }
- }
-
- pub fn from_fingerprint(fingerprint: Fingerprint) -> WorkProductId {
- WorkProductId { hash: fingerprint }
- }
-}
--- /dev/null
+use crate::hir::map::definitions::DefPathHash;
+use crate::ich::StableHashingContext;
+use crate::ty::{self, TyCtxt};
+use rustc_data_structures::profiling::SelfProfilerRef;
+use rustc_data_structures::sync::Lock;
+use rustc_data_structures::thin_vec::ThinVec;
+use rustc_errors::Diagnostic;
+use rustc_hir::def_id::DefId;
+
+mod dep_node;
+mod safe;
+
+pub(crate) use rustc_query_system::dep_graph::DepNodeParams;
+pub use rustc_query_system::dep_graph::{
+ debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex,
+ WorkProduct, WorkProductFileKind, WorkProductId,
+};
+
+pub use dep_node::{label_strs, DepConstructor, DepKind, DepNode, DepNodeExt};
+pub use safe::AssertDepGraphSafe;
+pub use safe::DepGraphSafe;
+
+pub type DepGraph = rustc_query_system::dep_graph::DepGraph<DepKind>;
+pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps<DepKind>;
+pub type DepGraphQuery = rustc_query_system::dep_graph::DepGraphQuery<DepKind>;
+pub type PreviousDepGraph = rustc_query_system::dep_graph::PreviousDepGraph<DepKind>;
+pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph<DepKind>;
+
+impl rustc_query_system::dep_graph::DepKind for DepKind {
+ fn is_eval_always(&self) -> bool {
+ DepKind::is_eval_always(self)
+ }
+
+ fn has_params(&self) -> bool {
+ DepKind::has_params(self)
+ }
+
+ fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{:?}", node.kind)?;
+
+ if !node.kind.has_params() && !node.kind.is_anon() {
+ return Ok(());
+ }
+
+ write!(f, "(")?;
+
+ ty::tls::with_opt(|opt_tcx| {
+ if let Some(tcx) = opt_tcx {
+ if let Some(def_id) = tcx.extract_def_id(node) {
+ write!(f, "{}", tcx.def_path_debug_str(def_id))?;
+ } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*node) {
+ write!(f, "{}", s)?;
+ } else {
+ write!(f, "{}", node.hash)?;
+ }
+ } else {
+ write!(f, "{}", node.hash)?;
+ }
+ Ok(())
+ })?;
+
+ write!(f, ")")
+ }
+
+ fn assert_ignored() {
+ ty::tls::with_context_opt(|icx| {
+ let icx = if let Some(icx) = icx { icx } else { return };
+ assert!(icx.task_deps.is_none(), "expected no task dependency tracking");
+ })
+ }
+
+ fn with_ignore_deps<OP, R>(op: OP) -> R
+ where
+ OP: FnOnce() -> R,
+ {
+ ty::tls::with_context(|icx| {
+ let icx = ty::tls::ImplicitCtxt { task_deps: None, ..icx.clone() };
+
+ ty::tls::enter_context(&icx, |_| op())
+ })
+ }
+
+ fn with_deps<OP, R>(task_deps: Option<&Lock<TaskDeps>>, op: OP) -> R
+ where
+ OP: FnOnce() -> R,
+ {
+ ty::tls::with_context(|icx| {
+ let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() };
+
+ ty::tls::enter_context(&icx, |_| op())
+ })
+ }
+
+ fn read_deps<OP>(op: OP) -> ()
+ where
+ OP: for<'a> FnOnce(Option<&'a Lock<TaskDeps>>) -> (),
+ {
+ ty::tls::with_context_opt(|icx| {
+ let icx = if let Some(icx) = icx { icx } else { return };
+ op(icx.task_deps)
+ })
+ }
+}
+
+impl<'tcx> DepContext for TyCtxt<'tcx> {
+ type DepKind = DepKind;
+ type StableHashingContext = StableHashingContext<'tcx>;
+
+ fn create_stable_hashing_context(&self) -> Self::StableHashingContext {
+ TyCtxt::create_stable_hashing_context(*self)
+ }
+
+ fn force_from_dep_node(&self, node: &DepNode) -> bool {
+ ty::query::force_from_dep_node(*self, node)
+ }
+
+ /// Extracts the DefId corresponding to this DepNode. This will work
+ /// if two conditions are met:
+ ///
+ /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and
+ /// 2. the item that the DefPath refers to exists in the current tcx.
+ ///
+ /// Condition (1) is determined by the DepKind variant of the
+ /// DepNode. Condition (2) might not be fulfilled if a DepNode
+ /// refers to something from the previous compilation session that
+ /// has been removed.
+ fn extract_def_id(&self, node: &DepNode) -> Option<DefId> {
+ if node.kind.can_reconstruct_query_key() {
+ let def_path_hash = DefPathHash(node.hash);
+ self.def_path_hash_to_def_id.as_ref()?.get(&def_path_hash).cloned()
+ } else {
+ None
+ }
+ }
+
+ fn ensure_node_can_be_forced(&self, dep_dep_node: &DepNode) -> Option<()> {
+ // FIXME: This match is just a workaround for incremental bugs and should
+ // be removed. https://github.com/rust-lang/rust/issues/62649 is one such
+ // bug that must be fixed before removing this.
+ match dep_dep_node.kind {
+ DepKind::hir_owner | DepKind::hir_owner_nodes | DepKind::CrateMetadata => {
+ if let Some(def_id) = self.extract_def_id(dep_dep_node) {
+ if def_id_corresponds_to_hir_dep_node(*self, def_id) {
+ if dep_dep_node.kind == DepKind::CrateMetadata {
+ // The `DefPath` has corresponding node,
+ // and that node should have been marked
+ // either red or green in `data.colors`.
+ bug!(
+ "DepNode {:?} should have been \
+ pre-marked as red or green but wasn't.",
+ dep_dep_node
+ );
+ }
+ } else {
+ // This `DefPath` does not have a
+ // corresponding `DepNode` (e.g. a
+ // struct field), and the ` DefPath`
+ // collided with the `DefPath` of a
+ // proper item that existed in the
+ // previous compilation session.
+ //
+ // Since the given `DefPath` does not
+ // denote the item that previously
+ // existed, we just fail to mark green.
+ return None;
+ }
+ } else {
+ // If the node does not exist anymore, we
+ // just fail to mark green.
+ return None;
+ }
+ }
+ _ => {
+ // For other kinds of nodes it's OK to be
+ // forced.
+ }
+ }
+ Some(())
+ }
+
+ fn has_errors_or_delayed_span_bugs(&self) -> bool {
+ self.sess.has_errors_or_delayed_span_bugs()
+ }
+
+ fn diagnostic(&self) -> &rustc_errors::Handler {
+ self.sess.diagnostic()
+ }
+
+ // Interactions with on_disk_cache
+ fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) {
+ use crate::mir::interpret::GlobalId;
+ use crate::ty::query::queries;
+ use crate::ty::query::QueryDescription;
+ rustc_dep_node_try_load_from_on_disk_cache!(dep_node, *self)
+ }
+
+ fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec<Diagnostic> {
+ self.queries.on_disk_cache.load_diagnostics(*self, prev_dep_node_index)
+ }
+
+ fn store_diagnostics(&self, dep_node_index: DepNodeIndex, diagnostics: ThinVec<Diagnostic>) {
+ self.queries.on_disk_cache.store_diagnostics(dep_node_index, diagnostics)
+ }
+
+ fn profiler(&self) -> &SelfProfilerRef {
+ &self.prof
+ }
+}
+
+fn def_id_corresponds_to_hir_dep_node(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+ def_id.index == hir_id.owner.local_def_index
+}
+
+impl rustc_query_system::HashStableContext for StableHashingContext<'_> {
+ fn debug_dep_tasks(&self) -> bool {
+ self.sess().opts.debugging_opts.dep_tasks
+ }
+}
+
+impl rustc_query_system::HashStableContextProvider<StableHashingContext<'tcx>> for TyCtxt<'tcx> {
+ fn get_stable_hashing_context(&self) -> StableHashingContext<'tcx> {
+ self.create_stable_hashing_context()
+ }
+}
+
+impl rustc_query_system::HashStableContextProvider<StableHashingContext<'a>>
+ for StableHashingContext<'a>
+{
+ fn get_stable_hashing_context(&self) -> Self {
+ self.clone()
+ }
+}
--- /dev/null
+//! The `DepGraphSafe` trait
+
+use crate::ty::TyCtxt;
+
+pub use rustc_query_system::dep_graph::{AssertDepGraphSafe, DepGraphSafe};
+
+/// The type context itself can be used to access all kinds of tracked
+/// state, but those accesses should always generate read events.
+impl<'tcx> DepGraphSafe for TyCtxt<'tcx> {}
false
}
-
-impl DepNode {
- /// Check whether the query invocation corresponding to the given
- /// DepNode is eligible for on-disk-caching. If so, this is method
- /// will execute the query corresponding to the given DepNode.
- /// Also, as a sanity check, it expects that the corresponding query
- /// invocation has been marked as green already.
- pub fn try_load_from_on_disk_cache<'tcx>(&self, tcx: TyCtxt<'tcx>) {
- use crate::dep_graph::DepKind;
-
- rustc_dep_node_try_load_from_on_disk_cache!(self, tcx)
- }
-}
use graphviz as dot;
use rustc::dep_graph::debug::{DepNodeFilter, EdgeFilter};
-use rustc::dep_graph::{DepGraphQuery, DepKind, DepNode};
+use rustc::dep_graph::{DepGraphQuery, DepKind, DepNode, DepNodeExt};
use rustc::hir::map::Map;
use rustc::ty::TyCtxt;
use rustc_ast::ast;
//! Errors are reported if we are in the suitable configuration but
//! the required condition is not met.
-use rustc::dep_graph::{label_strs, DepNode};
+use rustc::dep_graph::{label_strs, DepContext, DepNode, DepNodeExt};
use rustc::hir::map::Map;
use rustc::ty::TyCtxt;
use rustc_ast::ast::{self, Attribute, NestedMetaItem};
}
fn dep_node_str(&self, dep_node: &DepNode) -> String {
- if let Some(def_id) = dep_node.extract_def_id(self.tcx) {
+ if let Some(def_id) = self.tcx.extract_def_id(dep_node) {
format!("{:?}({})", dep_node.kind, self.tcx.def_path_str(def_id))
} else {
format!("{:?}({:?})", dep_node.kind, dep_node.hash)
try_load_from_on_disk_cache_stream.extend(quote! {
DepKind::#name => {
- if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY {
+ if <#arg as DepNodeParams<TyCtxt<'_>>>::CAN_RECONSTRUCT_QUERY_KEY {
debug_assert!($tcx.dep_graph
.node_color($dep_node)
.map(|c| c.is_green())
.unwrap_or(false));
- let key = <#arg as DepNodeParams>::recover($tcx, $dep_node).unwrap();
+ let key = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
if queries::#name::cache_on_disk($tcx, key, None) {
let _ = $tcx.#name(key);
}
// Add a match arm to force the query given the dep node
dep_node_force_stream.extend(quote! {
DepKind::#name => {
- if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY {
- if let Some(key) = <#arg as DepNodeParams>::recover($tcx, $dep_node) {
+ if <#arg as DepNodeParams<TyCtxt<'_>>>::CAN_RECONSTRUCT_QUERY_KEY {
+ if let Some(key) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
$tcx.force_query::<crate::ty::query::queries::#name<'_>>(
key,
DUMMY_SP,
use crate::rmeta::table::{FixedSizeEncoding, Table};
use crate::rmeta::*;
-use rustc::dep_graph::{self, DepNode, DepNodeIndex};
+use rustc::dep_graph::{self, DepNode, DepNodeExt, DepNodeIndex};
use rustc::hir::exports::Export;
use rustc::middle::cstore::{CrateSource, ExternCrate};
use rustc::middle::cstore::{ForeignModule, LinkagePreference, NativeLibrary};
/// some independent path or string that persists between runs without
/// the need to be mapped or unmapped. (This ensures we can serialize
/// them even in the absence of a tcx.)
-#[derive(
- Clone,
- Copy,
- Debug,
- PartialEq,
- Eq,
- PartialOrd,
- Ord,
- Hash,
- RustcEncodable,
- RustcDecodable,
- HashStable_Generic
-)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
+#[derive(HashStable_Generic)]
pub struct WorkProductId {
hash: Fingerprint,
}