mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-30 03:57:37 +00:00

This adds panicking Hash impls for several resolver types that don't actually satisfy this condition. It's not obvious to me that rustc_resolve actually upholds the Interned guarantees but fixing that seems pretty hard (the structures have at minimum some interior mutability, so it's not really recursively hashable in place...).
123 lines
3.5 KiB
Rust
123 lines
3.5 KiB
Rust
use std::cmp::Ordering;
|
|
use std::fmt::{self, Debug};
|
|
use std::hash::{Hash, Hasher};
|
|
use std::ops::Deref;
|
|
use std::ptr;
|
|
|
|
use crate::stable_hasher::{HashStable, StableHasher};
|
|
|
|
mod private {
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct PrivateZst;
|
|
}
|
|
|
|
/// A reference to a value that is interned, and is known to be unique.
|
|
///
|
|
/// Note that it is possible to have a `T` and a `Interned<T>` that are (or
|
|
/// refer to) equal but different values. But if you have two different
|
|
/// `Interned<T>`s, they both refer to the same value, at a single location in
|
|
/// memory. This means that equality and hashing can be done on the value's
|
|
/// address rather than the value's contents, which can improve performance.
|
|
///
|
|
/// The `PrivateZst` field means you can pattern match with `Interned(v, _)`
|
|
/// but you can only construct a `Interned` with `new_unchecked`, and not
|
|
/// directly.
|
|
#[rustc_pass_by_value]
|
|
pub struct Interned<'a, T>(pub &'a T, pub private::PrivateZst);
|
|
|
|
impl<'a, T> Interned<'a, T> {
|
|
/// Create a new `Interned` value. The value referred to *must* be interned
|
|
/// and thus be unique, and it *must* remain unique in the future. This
|
|
/// function has `_unchecked` in the name but is not `unsafe`, because if
|
|
/// the uniqueness condition is violated condition it will cause incorrect
|
|
/// behaviour but will not affect memory safety.
|
|
#[inline]
|
|
pub const fn new_unchecked(t: &'a T) -> Self {
|
|
Interned(t, private::PrivateZst)
|
|
}
|
|
}
|
|
|
|
impl<'a, T> Clone for Interned<'a, T> {
|
|
fn clone(&self) -> Self {
|
|
*self
|
|
}
|
|
}
|
|
|
|
impl<'a, T> Copy for Interned<'a, T> {}
|
|
|
|
impl<'a, T> Deref for Interned<'a, T> {
|
|
type Target = T;
|
|
|
|
#[inline]
|
|
fn deref(&self) -> &T {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
impl<'a, T> PartialEq for Interned<'a, T> {
|
|
#[inline]
|
|
fn eq(&self, other: &Self) -> bool {
|
|
// Pointer equality implies equality, due to the uniqueness constraint.
|
|
ptr::eq(self.0, other.0)
|
|
}
|
|
}
|
|
|
|
impl<'a, T> Eq for Interned<'a, T> {}
|
|
|
|
impl<'a, T: PartialOrd> PartialOrd for Interned<'a, T> {
|
|
fn partial_cmp(&self, other: &Interned<'a, T>) -> Option<Ordering> {
|
|
// Pointer equality implies equality, due to the uniqueness constraint,
|
|
// but the contents must be compared otherwise.
|
|
if ptr::eq(self.0, other.0) {
|
|
Some(Ordering::Equal)
|
|
} else {
|
|
let res = self.0.partial_cmp(other.0);
|
|
debug_assert_ne!(res, Some(Ordering::Equal));
|
|
res
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T: Ord> Ord for Interned<'a, T> {
|
|
fn cmp(&self, other: &Interned<'a, T>) -> Ordering {
|
|
// Pointer equality implies equality, due to the uniqueness constraint,
|
|
// but the contents must be compared otherwise.
|
|
if ptr::eq(self.0, other.0) {
|
|
Ordering::Equal
|
|
} else {
|
|
let res = self.0.cmp(other.0);
|
|
debug_assert_ne!(res, Ordering::Equal);
|
|
res
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T> Hash for Interned<'a, T>
|
|
where
|
|
T: Hash,
|
|
{
|
|
#[inline]
|
|
fn hash<H: Hasher>(&self, s: &mut H) {
|
|
// Pointer hashing is sufficient, due to the uniqueness constraint.
|
|
ptr::hash(self.0, s)
|
|
}
|
|
}
|
|
|
|
impl<T, CTX> HashStable<CTX> for Interned<'_, T>
|
|
where
|
|
T: HashStable<CTX>,
|
|
{
|
|
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
|
|
self.0.hash_stable(hcx, hasher);
|
|
}
|
|
}
|
|
|
|
impl<T: Debug> Debug for Interned<'_, T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|