// Copyright 2017 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.

//! This module contains `HashStable` implementations for various data types
//! from rustc::ty in no particular order.

use ich::{Fingerprint, StableHashingContext, NodeIdHashingMode};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
                                           StableHasher, StableHasherResult};
use std::cell::RefCell;
use std::hash as std_hash;
use std::mem;
use middle::region;
use infer;
use traits;
use ty;
use mir;

impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>>
for &'gcx ty::List<T>
    where T: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        thread_local! {
            static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> =
                RefCell::new(FxHashMap());
        }

        let hash = CACHE.with(|cache| {
            let key = (self.as_ptr() as usize, self.len());
            if let Some(&hash) = cache.borrow().get(&key) {
                return hash;
            }

            let mut hasher = StableHasher::new();
            (&self[..]).hash_stable(hcx, &mut hasher);

            let hash: Fingerprint = hasher.finish();
            cache.borrow_mut().insert(key, hash);
            hash
        });

        hash.hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx, T> ToStableHashKey<StableHashingContext<'a>> for &'gcx ty::List<T>
    where T: HashStable<StableHashingContext<'a>>
{
    type KeyType = Fingerprint;

    #[inline]
    fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint {
        let mut hasher = StableHasher::new();
        let mut hcx: StableHashingContext<'a> = hcx.clone();
        self.hash_stable(&mut hcx, &mut hasher);
        hasher.finish()
    }
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::subst::Kind<'gcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.unpack().hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::subst::UnpackedKind<'gcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match self {
            ty::subst::UnpackedKind::Lifetime(lt) => lt.hash_stable(hcx, hasher),
            ty::subst::UnpackedKind::Type(ty) => ty.hash_stable(hcx, hasher),
        }
    }
}

impl<'a> HashStable<StableHashingContext<'a>>
for ty::RegionKind {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::ReErased |
            ty::ReStatic |
            ty::ReEmpty => {
                // No variant fields to hash for these ...
            }
            ty::ReCanonical(c) => {
                c.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BrAnon(i)) => {
                db.hash_stable(hcx, hasher);
                i.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BrNamed(def_id, name)) => {
                db.hash_stable(hcx, hasher);
                def_id.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BrEnv) => {
                db.hash_stable(hcx, hasher);
            }
            ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => {
                def_id.hash_stable(hcx, hasher);
                index.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReScope(scope) => {
                scope.hash_stable(hcx, hasher);
            }
            ty::ReFree(ref free_region) => {
                free_region.hash_stable(hcx, hasher);
            }
            ty::ReClosureBound(vid) => {
                vid.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(..) |
            ty::ReVar(..) |
            ty::ReSkolemized(..) => {
                bug!("StableHasher: unexpected region {:?}", *self)
            }
        }
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid {
    #[inline]
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::CanonicalVar {
    #[inline]
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'gcx>,
                                          hasher: &mut StableHasher<W>) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::adjustment::AutoBorrow<'gcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::adjustment::AutoBorrow::Ref(ref region, mutability) => {
                region.hash_stable(hcx, hasher);
                mutability.hash_stable(hcx, hasher);
            }
            ty::adjustment::AutoBorrow::RawPtr(mutability) => {
                mutability.hash_stable(hcx, hasher);
            }
        }
    }
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::adjustment::Adjust<'gcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::adjustment::Adjust::NeverToAny |
            ty::adjustment::Adjust::ReifyFnPointer |
            ty::adjustment::Adjust::UnsafeFnPointer |
            ty::adjustment::Adjust::ClosureFnPointer |
            ty::adjustment::Adjust::MutToConstPointer |
            ty::adjustment::Adjust::Unsize => {}
            ty::adjustment::Adjust::Deref(ref overloaded) => {
                overloaded.hash_stable(hcx, hasher);
            }
            ty::adjustment::Adjust::Borrow(ref autoref) => {
                autoref.hash_stable(hcx, hasher);
            }
        }
    }
}

impl_stable_hash_for!(struct ty::adjustment::Adjustment<'tcx> { kind, target });
impl_stable_hash_for!(struct ty::adjustment::OverloadedDeref<'tcx> { region, mutbl });
impl_stable_hash_for!(struct ty::UpvarBorrow<'tcx> { kind, region });
impl_stable_hash_for!(enum ty::adjustment::AllowTwoPhase {
    Yes,
    No
});

impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::adjustment::AutoBorrowMutability {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'gcx>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::adjustment::AutoBorrowMutability::Mutable { ref allow_two_phase_borrow } => {
                allow_two_phase_borrow.hash_stable(hcx, hasher);
            }
            ty::adjustment::AutoBorrowMutability::Immutable => {}
        }
    }
}

impl_stable_hash_for!(struct ty::UpvarId { var_id, closure_expr_id });

impl_stable_hash_for!(enum ty::BorrowKind {
    ImmBorrow,
    UniqueImmBorrow,
    MutBorrow
});

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::UpvarCapture<'gcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::UpvarCapture::ByValue => {}
            ty::UpvarCapture::ByRef(ref up_var_borrow) => {
                up_var_borrow.hash_stable(hcx, hasher);
            }
        }
    }
}

impl_stable_hash_for!(struct ty::GenSig<'tcx> {
    yield_ty,
    return_ty
});

impl_stable_hash_for!(struct ty::FnSig<'tcx> {
    inputs_and_output,
    variadic,
    unsafety,
    abi
});

impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for ty::Binder<T>
    where T: HashStable<StableHashingContext<'a>>
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.skip_binder().hash_stable(hcx, hasher);
    }
}

impl_stable_hash_for!(enum ty::ClosureKind { Fn, FnMut, FnOnce });

impl_stable_hash_for!(enum ty::Visibility {
    Public,
    Restricted(def_id),
    Invisible
});

impl_stable_hash_for!(struct ty::TraitRef<'tcx> { def_id, substs });
impl_stable_hash_for!(struct ty::TraitPredicate<'tcx> { trait_ref });
impl_stable_hash_for!(struct ty::SubtypePredicate<'tcx> { a_is_expected, a, b });

impl<'a, 'gcx, A, B> HashStable<StableHashingContext<'a>>
for ty::OutlivesPredicate<A, B>
    where A: HashStable<StableHashingContext<'a>>,
          B: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        let ty::OutlivesPredicate(ref a, ref b) = *self;
        a.hash_stable(hcx, hasher);
        b.hash_stable(hcx, hasher);
    }
}

impl_stable_hash_for!(struct ty::ProjectionPredicate<'tcx> { projection_ty, ty });
impl_stable_hash_for!(struct ty::ProjectionTy<'tcx> { substs, item_def_id });


impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::Predicate<'gcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::Predicate::Trait(ref pred) => {
                pred.hash_stable(hcx, hasher);
            }
            ty::Predicate::Subtype(ref pred) => {
                pred.hash_stable(hcx, hasher);
            }
            ty::Predicate::RegionOutlives(ref pred) => {
                pred.hash_stable(hcx, hasher);
            }
            ty::Predicate::TypeOutlives(ref pred) => {
                pred.hash_stable(hcx, hasher);
            }
            ty::Predicate::Projection(ref pred) => {
                pred.hash_stable(hcx, hasher);
            }
            ty::Predicate::WellFormed(ty) => {
                ty.hash_stable(hcx, hasher);
            }
            ty::Predicate::ObjectSafe(def_id) => {
                def_id.hash_stable(hcx, hasher);
            }
            ty::Predicate::ClosureKind(def_id, closure_substs, closure_kind) => {
                def_id.hash_stable(hcx, hasher);
                closure_substs.hash_stable(hcx, hasher);
                closure_kind.hash_stable(hcx, hasher);
            }
            ty::Predicate::ConstEvaluatable(def_id, substs) => {
                def_id.hash_stable(hcx, hasher);
                substs.hash_stable(hcx, hasher);
            }
        }
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::AdtFlags {
    fn hash_stable<W: StableHasherResult>(&self,
                                          _: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        std_hash::Hash::hash(self, hasher);
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::VariantFlags {
    fn hash_stable<W: StableHasherResult>(&self,
                                          _: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        std_hash::Hash::hash(self, hasher);
    }
}

impl_stable_hash_for!(enum ty::VariantDiscr {
    Explicit(def_id),
    Relative(distance)
});

impl_stable_hash_for!(struct ty::FieldDef {
    did,
    ident -> (ident.name),
    vis,
});

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ::mir::interpret::ConstValue<'gcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use mir::interpret::ConstValue::*;

        mem::discriminant(self).hash_stable(hcx, hasher);

        match *self {
            Unevaluated(def_id, substs) => {
                def_id.hash_stable(hcx, hasher);
                substs.hash_stable(hcx, hasher);
            }
            Scalar(val) => {
                val.hash_stable(hcx, hasher);
            }
            ScalarPair(a, b) => {
                a.hash_stable(hcx, hasher);
                b.hash_stable(hcx, hasher);
            }
            ByRef(id, alloc, offset) => {
                id.hash_stable(hcx, hasher);
                alloc.hash_stable(hcx, hasher);
                offset.hash_stable(hcx, hasher);
            }
        }
    }
}

impl_stable_hash_for!(struct mir::interpret::Pointer {
    alloc_id,
    offset
});

impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        ty::tls::with_opt(|tcx| {
            trace!("hashing {:?}", *self);
            let tcx = tcx.expect("can't hash AllocIds during hir lowering");
            let alloc_kind = tcx.alloc_map.lock().get(*self);
            alloc_kind.hash_stable(hcx, hasher);
        });
    }
}

impl<'a, 'gcx, M: HashStable<StableHashingContext<'a>>> HashStable<StableHashingContext<'a>>
for mir::interpret::AllocType<'gcx, M> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use mir::interpret::AllocType::*;

        mem::discriminant(self).hash_stable(hcx, hasher);

        match *self {
            Function(instance) => instance.hash_stable(hcx, hasher),
            Static(def_id) => def_id.hash_stable(hcx, hasher),
            Memory(ref mem) => mem.hash_stable(hcx, hasher),
        }
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::Allocation {
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        self.bytes.hash_stable(hcx, hasher);
        for reloc in self.relocations.iter() {
            reloc.hash_stable(hcx, hasher);
        }
        self.undef_mask.hash_stable(hcx, hasher);
        self.align.hash_stable(hcx, hasher);
        self.mutability.hash_stable(hcx, hasher);
    }
}

impl_stable_hash_for!(enum ::syntax::ast::Mutability {
    Immutable,
    Mutable
});


impl<'a> HashStable<StableHashingContext<'a>>
for ::mir::interpret::Scalar {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use mir::interpret::Scalar::*;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            Bits { bits, size } => {
                bits.hash_stable(hcx, hasher);
                size.hash_stable(hcx, hasher);
            },
            Ptr(ptr) => ptr.hash_stable(hcx, hasher),
        }
    }
}

impl_stable_hash_for!(struct ty::Const<'tcx> {
    ty,
    val
});

impl_stable_hash_for!(struct ::mir::interpret::ConstEvalErr<'tcx> {
    span,
    stacktrace,
    error
});

impl_stable_hash_for!(struct ::mir::interpret::FrameInfo {
    span,
    lint_root,
    location
});

impl_stable_hash_for!(struct ty::ClosureSubsts<'tcx> { substs });
impl_stable_hash_for!(struct ty::GeneratorSubsts<'tcx> { substs });

impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> {
    parent,
    predicates
});

impl_stable_hash_for!(struct ::mir::interpret::EvalError<'tcx> { kind });

impl<'a, 'gcx, O: HashStable<StableHashingContext<'a>>> HashStable<StableHashingContext<'a>>
for ::mir::interpret::EvalErrorKind<'gcx, O> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use mir::interpret::EvalErrorKind::*;

        mem::discriminant(&self).hash_stable(hcx, hasher);

        match *self {
            FunctionArgCountMismatch |
            DanglingPointerDeref |
            DoubleFree |
            InvalidMemoryAccess |
            InvalidFunctionPointer |
            InvalidBool |
            InvalidNullPointerUsage |
            ReadPointerAsBytes |
            ReadBytesAsPointer |
            ReadForeignStatic |
            InvalidPointerMath |
            DeadLocal |
            StackFrameLimitReached |
            OutOfTls |
            TlsOutOfBounds |
            CalledClosureAsFunction |
            VtableForArgumentlessMethod |
            ModifiedConstantMemory |
            AssumptionNotHeld |
            InlineAsm |
            ReallocateNonBasePtr |
            DeallocateNonBasePtr |
            HeapAllocZeroBytes |
            Unreachable |
            ReadFromReturnPointer |
            UnimplementedTraitSelection |
            TypeckError |
            TooGeneric |
            CheckMatchError |
            DerefFunctionPointer |
            ExecuteMemory |
            OverflowNeg |
            RemainderByZero |
            DivisionByZero |
            GeneratorResumedAfterReturn |
            GeneratorResumedAfterPanic |
            InfiniteLoop => {}
            ReadUndefBytes(offset) => offset.hash_stable(hcx, hasher),
            InvalidDiscriminant(val) => val.hash_stable(hcx, hasher),
            Panic { ref msg, ref file, line, col } => {
                msg.hash_stable(hcx, hasher);
                file.hash_stable(hcx, hasher);
                line.hash_stable(hcx, hasher);
                col.hash_stable(hcx, hasher);
            },
            ReferencedConstant(ref err) => err.hash_stable(hcx, hasher),
            MachineError(ref err) => err.hash_stable(hcx, hasher),
            FunctionAbiMismatch(a, b) => {
                a.hash_stable(hcx, hasher);
                b.hash_stable(hcx, hasher)
            },
            FunctionArgMismatch(a, b) => {
                a.hash_stable(hcx, hasher);
                b.hash_stable(hcx, hasher)
            },
            NoMirFor(ref s) => s.hash_stable(hcx, hasher),
            UnterminatedCString(ptr) => ptr.hash_stable(hcx, hasher),
            PointerOutOfBounds {
                ptr,
                access,
                allocation_size,
            } => {
                ptr.hash_stable(hcx, hasher);
                access.hash_stable(hcx, hasher);
                allocation_size.hash_stable(hcx, hasher)
            },
            InvalidBoolOp(bop) => bop.hash_stable(hcx, hasher),
            Unimplemented(ref s) => s.hash_stable(hcx, hasher),
            BoundsCheck { ref len, ref index } => {
                len.hash_stable(hcx, hasher);
                index.hash_stable(hcx, hasher)
            },
            Intrinsic(ref s) => s.hash_stable(hcx, hasher),
            InvalidChar(c) => c.hash_stable(hcx, hasher),
            AbiViolation(ref s) => s.hash_stable(hcx, hasher),
            AlignmentCheckFailed {
                required,
                has,
            } => {
                required.hash_stable(hcx, hasher);
                has.hash_stable(hcx, hasher)
            },
            MemoryLockViolation {
                ptr,
                len,
                frame,
                access,
                ref lock,
            } =>  {
                ptr.hash_stable(hcx, hasher);
                len.hash_stable(hcx, hasher);
                frame.hash_stable(hcx, hasher);
                access.hash_stable(hcx, hasher);
                lock.hash_stable(hcx, hasher)
            },
            MemoryAcquireConflict {
                ptr,
                len,
                kind,
                ref lock,
            } =>  {
                ptr.hash_stable(hcx, hasher);
                len.hash_stable(hcx, hasher);
                kind.hash_stable(hcx, hasher);
                lock.hash_stable(hcx, hasher)
            },
            InvalidMemoryLockRelease {
                ptr,
                len,
                frame,
                ref lock,
            } =>  {
                ptr.hash_stable(hcx, hasher);
                len.hash_stable(hcx, hasher);
                frame.hash_stable(hcx, hasher);
                lock.hash_stable(hcx, hasher)
            },
            DeallocatedLockedMemory {
                ptr,
                ref lock,
            } => {
                ptr.hash_stable(hcx, hasher);
                lock.hash_stable(hcx, hasher)
            },
            ValidationFailure(ref s) => s.hash_stable(hcx, hasher),
            TypeNotPrimitive(ty) => ty.hash_stable(hcx, hasher),
            ReallocatedWrongMemoryKind(ref a, ref b) => {
                a.hash_stable(hcx, hasher);
                b.hash_stable(hcx, hasher)
            },
            DeallocatedWrongMemoryKind(ref a, ref b) => {
                a.hash_stable(hcx, hasher);
                b.hash_stable(hcx, hasher)
            },
            IncorrectAllocationInformation(a, b, c, d) => {
                a.hash_stable(hcx, hasher);
                b.hash_stable(hcx, hasher);
                c.hash_stable(hcx, hasher);
                d.hash_stable(hcx, hasher)
            },
            Layout(lay) => lay.hash_stable(hcx, hasher),
            HeapAllocNonPowerOfTwoAlignment(n) => n.hash_stable(hcx, hasher),
            PathNotFound(ref v) => v.hash_stable(hcx, hasher),
            Overflow(op) => op.hash_stable(hcx, hasher),
        }
    }
}

impl_stable_hash_for!(enum mir::interpret::Lock {
    NoLock,
    WriteLock(dl),
    ReadLock(v)
});

impl_stable_hash_for!(struct mir::interpret::DynamicLifetime {
    frame,
    region
});

impl_stable_hash_for!(enum mir::interpret::AccessKind {
    Read,
    Write
});

impl_stable_hash_for!(enum ty::Variance {
    Covariant,
    Invariant,
    Contravariant,
    Bivariant
});

impl_stable_hash_for!(enum ty::adjustment::CustomCoerceUnsized {
    Struct(index)
});

impl_stable_hash_for!(struct ty::Generics {
    parent,
    parent_count,
    params,
    // Reverse map to each param's `index` field, from its `def_id`.
    param_def_id_to_index -> _, // Don't hash this
    has_self,
    has_late_bound_regions,
});

impl_stable_hash_for!(struct ty::GenericParamDef {
    name,
    def_id,
    index,
    pure_wrt_drop,
    kind
});

impl<'a> HashStable<StableHashingContext<'a>> for ty::GenericParamDefKind {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::GenericParamDefKind::Lifetime => {}
            ty::GenericParamDefKind::Type {
                has_default,
                ref object_lifetime_default,
                ref synthetic,
            } => {
                has_default.hash_stable(hcx, hasher);
                object_lifetime_default.hash_stable(hcx, hasher);
                synthetic.hash_stable(hcx, hasher);
            }
        }
    }
}

impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>>
for ::middle::resolve_lifetime::Set1<T>
    where T: HashStable<StableHashingContext<'a>>
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use middle::resolve_lifetime::Set1;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            Set1::Empty |
            Set1::Many => {
                // Nothing to do.
            }
            Set1::One(ref value) => {
                value.hash_stable(hcx, hasher);
            }
        }
    }
}

impl_stable_hash_for!(enum ::middle::resolve_lifetime::LifetimeDefOrigin {
    Explicit,
    InBand
});

impl_stable_hash_for!(enum ::middle::resolve_lifetime::Region {
    Static,
    EarlyBound(index, decl, is_in_band),
    LateBound(db_index, decl, is_in_band),
    LateBoundAnon(db_index, anon_index),
    Free(call_site_scope_data, decl)
});

impl_stable_hash_for!(enum ty::cast::CastKind {
    CoercionCast,
    PtrPtrCast,
    PtrAddrCast,
    AddrPtrCast,
    NumericCast,
    EnumCast,
    PrimIntCast,
    U8CharCast,
    ArrayPtrCast,
    FnPtrPtrCast,
    FnPtrAddrCast
});

impl_stable_hash_for!(struct ::middle::region::Scope { id, data });

impl_stable_hash_for!(enum ::middle::region::ScopeData {
    Node,
    CallSite,
    Arguments,
    Destruction,
    Remainder(first_statement_index)
});

impl<'a> ToStableHashKey<StableHashingContext<'a>> for region::Scope {
    type KeyType = region::Scope;

    #[inline]
    fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> region::Scope {
        *self
    }
}

impl_stable_hash_for!(struct ty::adjustment::CoerceUnsizedInfo {
    custom_kind
});

impl_stable_hash_for!(struct ty::FreeRegion {
    scope,
    bound_region
});

impl_stable_hash_for!(enum ty::BoundRegion {
    BrAnon(index),
    BrNamed(def_id, name),
    BrFresh(index),
    BrEnv
});

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::TyKind<'gcx>
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use ty::TyKind::*;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            Bool  |
            Char  |
            Str   |
            Error |
            Never => {
                // Nothing more to hash.
            }
            Int(int_ty) => {
                int_ty.hash_stable(hcx, hasher);
            }
            Uint(uint_ty) => {
                uint_ty.hash_stable(hcx, hasher);
            }
            Float(float_ty)  => {
                float_ty.hash_stable(hcx, hasher);
            }
            Adt(adt_def, substs) => {
                adt_def.hash_stable(hcx, hasher);
                substs.hash_stable(hcx, hasher);
            }
            Array(inner_ty, len) => {
                inner_ty.hash_stable(hcx, hasher);
                len.hash_stable(hcx, hasher);
            }
            Slice(inner_ty) => {
                inner_ty.hash_stable(hcx, hasher);
            }
            RawPtr(pointee_ty) => {
                pointee_ty.hash_stable(hcx, hasher);
            }
            Ref(region, pointee_ty, mutbl) => {
                region.hash_stable(hcx, hasher);
                pointee_ty.hash_stable(hcx, hasher);
                mutbl.hash_stable(hcx, hasher);
            }
            FnDef(def_id, substs) => {
                def_id.hash_stable(hcx, hasher);
                substs.hash_stable(hcx, hasher);
            }
            FnPtr(ref sig) => {
                sig.hash_stable(hcx, hasher);
            }
            Dynamic(ref existential_predicates, region) => {
                existential_predicates.hash_stable(hcx, hasher);
                region.hash_stable(hcx, hasher);
            }
            Closure(def_id, closure_substs) => {
                def_id.hash_stable(hcx, hasher);
                closure_substs.hash_stable(hcx, hasher);
            }
            Generator(def_id, generator_substs, movability) => {
                def_id.hash_stable(hcx, hasher);
                generator_substs.hash_stable(hcx, hasher);
                movability.hash_stable(hcx, hasher);
            }
            GeneratorWitness(types) => {
                types.hash_stable(hcx, hasher)
            }
            Tuple(inner_tys) => {
                inner_tys.hash_stable(hcx, hasher);
            }
            Projection(ref data) | UnnormalizedProjection(ref data) => {
                data.hash_stable(hcx, hasher);
            }
            Opaque(def_id, substs) => {
                def_id.hash_stable(hcx, hasher);
                substs.hash_stable(hcx, hasher);
            }
            Param(param_ty) => {
                param_ty.hash_stable(hcx, hasher);
            }
            Foreign(def_id) => {
                def_id.hash_stable(hcx, hasher);
            }
            Infer(infer_ty) => {
                infer_ty.hash_stable(hcx, hasher);
            }
        }
    }
}

impl_stable_hash_for!(enum ty::InferTy {
    TyVar(a),
    IntVar(a),
    FloatVar(a),
    FreshTy(a),
    FreshIntTy(a),
    FreshFloatTy(a),
    CanonicalTy(a),
});

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::TyVid
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // TyVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a TyVid {:?}.", *self)
    }
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::IntVid
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // IntVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash an IntVid {:?}.", *self)
    }
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::FloatVid
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // FloatVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a FloatVid {:?}.", *self)
    }
}

impl_stable_hash_for!(struct ty::ParamTy {
    idx,
    name
});

impl_stable_hash_for!(struct ty::TypeAndMut<'tcx> {
    ty,
    mutbl
});

impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
for ty::ExistentialPredicate<'gcx>
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::ExistentialPredicate::Trait(ref trait_ref) => {
                trait_ref.hash_stable(hcx, hasher);
            }
            ty::ExistentialPredicate::Projection(ref projection) => {
                projection.hash_stable(hcx, hasher);
            }
            ty::ExistentialPredicate::AutoTrait(def_id) => {
                def_id.hash_stable(hcx, hasher);
            }
        }
    }
}

impl_stable_hash_for!(struct ty::ExistentialTraitRef<'tcx> {
    def_id,
    substs
});

impl_stable_hash_for!(struct ty::ExistentialProjection<'tcx> {
    item_def_id,
    substs,
    ty
});

impl_stable_hash_for!(struct ty::Instance<'tcx> {
    def,
    substs
});

impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::InstanceDef<'gcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);

        match *self {
            ty::InstanceDef::Item(def_id) => {
                def_id.hash_stable(hcx, hasher);
            }
            ty::InstanceDef::Intrinsic(def_id) => {
                def_id.hash_stable(hcx, hasher);
            }
            ty::InstanceDef::FnPtrShim(def_id, ty) => {
                def_id.hash_stable(hcx, hasher);
                ty.hash_stable(hcx, hasher);
            }
            ty::InstanceDef::Virtual(def_id, n) => {
                def_id.hash_stable(hcx, hasher);
                n.hash_stable(hcx, hasher);
            }
            ty::InstanceDef::ClosureOnceShim { call_once } => {
                call_once.hash_stable(hcx, hasher);
            }
            ty::InstanceDef::DropGlue(def_id, ty) => {
                def_id.hash_stable(hcx, hasher);
                ty.hash_stable(hcx, hasher);
            }
            ty::InstanceDef::CloneShim(def_id, ty) => {
                def_id.hash_stable(hcx, hasher);
                ty.hash_stable(hcx, hasher);
            }
        }
    }
}

impl_stable_hash_for!(struct ty::TraitDef {
    // We already have the def_path_hash below, no need to hash it twice
    def_id -> _,
    unsafety,
    paren_sugar,
    has_auto_impl,
    is_marker,
    def_path_hash,
});

impl_stable_hash_for!(struct ty::Destructor {
    did
});

impl_stable_hash_for!(struct ty::CrateVariancesMap {
    variances,
    // This is just an irrelevant helper value.
    empty_variance -> _,
});

impl_stable_hash_for!(struct ty::CratePredicatesMap<'tcx> {
    predicates,
    // This is just an irrelevant helper value.
    empty_predicate -> _,
});

impl_stable_hash_for!(struct ty::AssociatedItem {
    def_id,
    ident -> (ident.name),
    kind,
    vis,
    defaultness,
    container,
    method_has_self_argument
});

impl_stable_hash_for!(enum ty::AssociatedKind {
    Const,
    Method,
    Existential,
    Type
});

impl_stable_hash_for!(enum ty::AssociatedItemContainer {
    TraitContainer(def_id),
    ImplContainer(def_id)
});


impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>>
for ty::steal::Steal<T>
    where T: HashStable<StableHashingContext<'a>>
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.borrow().hash_stable(hcx, hasher);
    }
}

impl_stable_hash_for!(struct ty::ParamEnv<'tcx> {
    caller_bounds,
    reveal
});

impl_stable_hash_for!(enum traits::Reveal {
    UserFacing,
    All
});

impl_stable_hash_for!(enum ::middle::privacy::AccessLevel {
    ReachableFromImplTrait,
    Reachable,
    Exported,
    Public
});

impl<'a> HashStable<StableHashingContext<'a>>
for ::middle::privacy::AccessLevels {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
            let ::middle::privacy::AccessLevels {
                ref map
            } = *self;

            map.hash_stable(hcx, hasher);
        });
    }
}

impl_stable_hash_for!(struct ty::CrateInherentImpls {
    inherent_impls
});

impl_stable_hash_for!(enum ::session::CompileIncomplete {
    Stopped,
    Errored(error_reported)
});

impl_stable_hash_for!(struct ::util::common::ErrorReported {});

impl_stable_hash_for!(tuple_struct ::middle::reachable::ReachableSet {
    reachable_set
});

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::Vtable<'gcx, N> where N: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use traits::Vtable::*;

        mem::discriminant(self).hash_stable(hcx, hasher);

        match self {
            &VtableImpl(ref table_impl) => table_impl.hash_stable(hcx, hasher),
            &VtableAutoImpl(ref table_def_impl) => table_def_impl.hash_stable(hcx, hasher),
            &VtableParam(ref table_param) => table_param.hash_stable(hcx, hasher),
            &VtableObject(ref table_obj) => table_obj.hash_stable(hcx, hasher),
            &VtableBuiltin(ref table_builtin) => table_builtin.hash_stable(hcx, hasher),
            &VtableClosure(ref table_closure) => table_closure.hash_stable(hcx, hasher),
            &VtableFnPointer(ref table_fn_pointer) => table_fn_pointer.hash_stable(hcx, hasher),
            &VtableGenerator(ref table_generator) => table_generator.hash_stable(hcx, hasher),
        }
    }
}

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::VtableImplData<'gcx, N> where N: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        let traits::VtableImplData {
            impl_def_id,
            substs,
            ref nested,
        } = *self;
        impl_def_id.hash_stable(hcx, hasher);
        substs.hash_stable(hcx, hasher);
        nested.hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::VtableAutoImplData<N> where N: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        let traits::VtableAutoImplData {
            trait_def_id,
            ref nested,
        } = *self;
        trait_def_id.hash_stable(hcx, hasher);
        nested.hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::VtableObjectData<'gcx, N> where N: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        let traits::VtableObjectData {
            upcast_trait_ref,
            vtable_base,
            ref nested,
        } = *self;
        upcast_trait_ref.hash_stable(hcx, hasher);
        vtable_base.hash_stable(hcx, hasher);
        nested.hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::VtableBuiltinData<N> where N: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        let traits::VtableBuiltinData {
            ref nested,
        } = *self;
        nested.hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::VtableClosureData<'gcx, N> where N: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        let traits::VtableClosureData {
            closure_def_id,
            substs,
            ref nested,
        } = *self;
        closure_def_id.hash_stable(hcx, hasher);
        substs.hash_stable(hcx, hasher);
        nested.hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::VtableFnPointerData<'gcx, N> where N: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        let traits::VtableFnPointerData {
            fn_ty,
            ref nested,
        } = *self;
        fn_ty.hash_stable(hcx, hasher);
        nested.hash_stable(hcx, hasher);
    }
}

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::VtableGeneratorData<'gcx, N> where N: HashStable<StableHashingContext<'a>> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        let traits::VtableGeneratorData {
            generator_def_id,
            substs,
            ref nested,
        } = *self;
        generator_def_id.hash_stable(hcx, hasher);
        substs.hash_stable(hcx, hasher);
        nested.hash_stable(hcx, hasher);
    }
}

impl_stable_hash_for!(
    impl<'tcx, V> for struct infer::canonical::Canonical<'tcx, V> {
        variables, value
    }
);

impl_stable_hash_for!(
    impl<'tcx> for struct infer::canonical::CanonicalVarValues<'tcx> {
        var_values
    }
);

impl_stable_hash_for!(struct infer::canonical::CanonicalVarInfo {
    kind
});

impl_stable_hash_for!(enum infer::canonical::CanonicalVarKind {
    Ty(k),
    Region
});

impl_stable_hash_for!(enum infer::canonical::CanonicalTyVarKind {
    General,
    Int,
    Float
});

impl_stable_hash_for!(
    impl<'tcx, R> for struct infer::canonical::QueryResult<'tcx, R> {
        var_values, region_constraints, certainty, value
    }
);

impl_stable_hash_for!(enum infer::canonical::Certainty {
    Proven, Ambiguous
});

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::WhereClause<'tcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use traits::WhereClause::*;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match self {
            Implemented(trait_ref) => trait_ref.hash_stable(hcx, hasher),
            ProjectionEq(projection) => projection.hash_stable(hcx, hasher),
            TypeOutlives(ty_outlives) => ty_outlives.hash_stable(hcx, hasher),
            RegionOutlives(region_outlives) => region_outlives.hash_stable(hcx, hasher),
        }
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::WellFormed<'tcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use traits::WellFormed::*;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match self {
            Trait(trait_ref) => trait_ref.hash_stable(hcx, hasher),
            Ty(ty) => ty.hash_stable(hcx, hasher),
        }
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::FromEnv<'tcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use traits::FromEnv::*;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match self {
            Trait(trait_ref) => trait_ref.hash_stable(hcx, hasher),
            Ty(ty) => ty.hash_stable(hcx, hasher),
        }
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::DomainGoal<'tcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use traits::DomainGoal::*;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match self {
            Holds(wc) => wc.hash_stable(hcx, hasher),
            WellFormed(wf) => wf.hash_stable(hcx, hasher),
            FromEnv(from_env) => from_env.hash_stable(hcx, hasher),
            Normalize(projection) => projection.hash_stable(hcx, hasher),
        }
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::Goal<'tcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use traits::Goal::*;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match self {
            Implies(hypotheses, goal) => {
                hypotheses.hash_stable(hcx, hasher);
                goal.hash_stable(hcx, hasher);
            },
            And(goal1, goal2) => {
                goal1.hash_stable(hcx, hasher);
                goal2.hash_stable(hcx, hasher);
            }
            Not(goal) => goal.hash_stable(hcx, hasher),
            DomainGoal(domain_goal) => domain_goal.hash_stable(hcx, hasher),
            Quantified(quantifier, goal) => {
                quantifier.hash_stable(hcx, hasher);
                goal.hash_stable(hcx, hasher);
            },
            CannotProve => { },
        }
    }
}

impl_stable_hash_for!(
    impl<'tcx> for struct traits::ProgramClause<'tcx> {
        goal, hypotheses
    }
);

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::Clause<'tcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        use traits::Clause::*;

        mem::discriminant(self).hash_stable(hcx, hasher);
        match self {
            Implies(clause) => clause.hash_stable(hcx, hasher),
            ForAll(clause) => clause.hash_stable(hcx, hasher),
        }
    }
}

impl_stable_hash_for!(enum traits::QuantifierKind {
    Universal,
    Existential
});
