Fix content hashing

Fixes #2800
This commit is contained in:
Laurenz 2023-11-30 13:49:03 +01:00
parent 5bdec9e1d8
commit de40124adb
5 changed files with 30 additions and 26 deletions

View File

@ -964,8 +964,11 @@ fn create_native_elem_impl(element: &Elem) -> TokenStream {
#foundations::Element::of::<Self>()
}
fn dyn_hash(&self, mut hasher: &mut dyn ::std::hash::Hasher) {
<Self as ::std::hash::Hash>::hash(self, &mut hasher);
fn dyn_hash(&self, mut state: &mut dyn ::std::hash::Hasher) {
// Also hash the TypeId since values with different types but
// equal data should be different.
::std::hash::Hash::hash(&::std::any::TypeId::of::<Self>(), &mut state);
::std::hash::Hash::hash(self, &mut state);
}
fn dyn_eq(&self, other: &#foundations::Content) -> bool {

View File

@ -66,7 +66,8 @@ use crate::util::fat;
/// elements the content is composed of and what fields they have.
/// Alternatively, you can inspect the output of the [`repr`]($repr) function.
#[ty(scope)]
#[derive(Clone)]
#[derive(Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Content(Arc<dyn NativeElement>);
impl Content {
@ -607,12 +608,6 @@ impl From<Arc<dyn NativeElement>> for Content {
}
}
impl std::hash::Hash for Content {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.dyn_hash(state);
}
}
impl PartialEq for Content {
fn eq(&self, other: &Self) -> bool {
// Additional short circuit for different elements.

View File

@ -2,7 +2,7 @@ use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt::{self, Debug};
use std::hash::Hasher;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use ecow::EcoString;
@ -275,6 +275,12 @@ pub trait NativeElement: Debug + Repr + Construct + Set + Send + Sync + 'static
fn fields(&self) -> Dict;
}
impl Hash for dyn NativeElement {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
}
}
/// An element's constructor function.
pub trait Construct {
/// Construct an element from the arguments.

View File

@ -1,4 +1,4 @@
use std::any::Any;
use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
@ -258,6 +258,7 @@ impl Debug for Property {
/// We're using a `Box` since values will either be contained in an `Arc` and
/// therefore already on the heap or they will be small enough that we can just
/// clone them.
#[derive(Hash)]
struct Block(Box<dyn Blockable>);
impl Block {
@ -278,12 +279,6 @@ impl Debug for Block {
}
}
impl Hash for Block {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.dyn_hash(state);
}
}
impl Clone for Block {
fn clone(&self) -> Self {
self.0.dyn_clone()
@ -318,6 +313,9 @@ impl<T: Debug + Clone + Hash + Send + Sync + 'static> Blockable for T {
}
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
// Also hash the TypeId since values with different types but
// equal data should be different.
TypeId::of::<Self>().hash(&mut state);
self.hash(&mut state);
}
@ -326,6 +324,12 @@ impl<T: Debug + Clone + Hash + Send + Sync + 'static> Blockable for T {
}
}
impl Hash for dyn Blockable {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
}
}
/// A show rule recipe.
#[derive(Clone, PartialEq, Hash)]
pub struct Recipe {

View File

@ -1,4 +1,4 @@
use std::any::Any;
use std::any::{Any, TypeId};
use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
@ -8,7 +8,6 @@ use ecow::{eco_format, EcoString};
use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer};
use serde::de::{Error, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use siphasher::sip128::{Hasher128, SipHasher13};
use crate::diag::StrResult;
use crate::eval::ops;
@ -544,7 +543,7 @@ trait Bounds: Debug + Repr + Sync + Send + 'static {
fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &Dynamic) -> bool;
fn dyn_ty(&self) -> Type;
fn hash128(&self) -> u128;
fn dyn_hash(&self, state: &mut dyn Hasher);
}
impl<T> Bounds for T
@ -564,20 +563,17 @@ where
Type::of::<T>()
}
#[tracing::instrument(skip_all)]
fn hash128(&self) -> u128 {
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
// Also hash the TypeId since values with different types but
// equal data should be different.
let mut state = SipHasher13::new();
self.type_id().hash(&mut state);
TypeId::of::<Self>().hash(&mut state);
self.hash(&mut state);
state.finish128().as_u128()
}
}
impl Hash for dyn Bounds {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u128(self.hash128());
self.dyn_hash(state);
}
}