diff --git a/mir/src/serialize.rs b/mir/src/serialize.rs index dfd8834..8ab4941 100644 --- a/mir/src/serialize.rs +++ b/mir/src/serialize.rs @@ -437,8 +437,12 @@ impl Program { #[cfg(test)] mod tests { use super::Serialize; - use crate::{syntax::*, tyctxt::TyCtxt}; + use crate::{ + syntax::*, + tyctxt::{AdtMeta, TyCtxt}, + }; use config::TyConfig; + use index_vec::IndexVec; #[test] fn serialize_body() { @@ -462,4 +466,74 @@ mod tests { let inf = Literal::Float(f32::INFINITY as f64, FloatTy::F32); assert_eq!(inf.serialize(&tcx), "f32::INFINITY"); } + + fn derive_attrs_for<'a>(serialized: &'a str, type_name: &str) -> &'a str { + let declaration = format!("pub struct {type_name}"); + let declaration_offset = serialized + .find(&declaration) + .expect("serialized ADT declaration"); + + serialized[..declaration_offset] + .lines() + .next_back() + .expect("derive attribute before ADT declaration") + } + + #[test] + fn serialize_hashable_adts() { + let mut tcx = TyCtxt::from_primitives(TyConfig::default()); + + let hashable = tcx.push_adt( + Adt { + variants: IndexVec::from_iter([VariantDef { + fields: IndexVec::from_iter([TyCtxt::U8]), + }]), + }, + AdtMeta { copy: false }, + ); + + let nested = tcx.push_adt( + Adt { + variants: IndexVec::from_iter([VariantDef { + fields: IndexVec::from_iter([hashable]), + }]), + }, + AdtMeta { copy: false }, + ); + + let contains_float = tcx.push_adt( + Adt { + variants: IndexVec::from_iter([VariantDef { + fields: IndexVec::from_iter([TyCtxt::F32]), + }]), + }, + AdtMeta { copy: false }, + ); + + let pointer = tcx.push(TyKind::RawPtr(TyCtxt::U8, Mutability::Not)); + + let contains_pointer = tcx.push_adt( + Adt { + variants: IndexVec::from_iter([VariantDef { + fields: IndexVec::from_iter([pointer]), + }]), + }, + AdtMeta { copy: false }, + ); + + assert!(hashable.hashable(&tcx)); + assert!(nested.hashable(&tcx)); + assert!(!contains_float.hashable(&tcx)); + assert!(!contains_pointer.hashable(&tcx)); + + let serialized = tcx.serialize(); + + assert!(derive_attrs_for(&serialized, &hashable.type_name()).contains("Hash")); + + assert!(derive_attrs_for(&serialized, &nested.type_name()).contains("Hash")); + + assert!(!derive_attrs_for(&serialized, &contains_float.type_name()).contains("Hash")); + + assert!(!derive_attrs_for(&serialized, &contains_pointer.type_name()).contains("Hash")); + } } diff --git a/mir/src/syntax.rs b/mir/src/syntax.rs index 3edf1d7..e016d57 100644 --- a/mir/src/syntax.rs +++ b/mir/src/syntax.rs @@ -428,12 +428,8 @@ impl TyId { } pub fn hashable(self, tcx: &TyCtxt) -> bool { - // TODO: hash Adts maybe - self.kind(tcx).is_structural() - && self.determ_printable(tcx) - && !self.contains(tcx, |_, ty| { - ty == TyCtxt::F32 || ty == TyCtxt::F64 || ty.kind(tcx).is_adt() - }) + self.determ_printable(tcx) + && !self.contains(tcx, |_, ty| ty == TyCtxt::F32 || ty == TyCtxt::F64) && self != TyCtxt::UNIT } diff --git a/mir/src/tyctxt.rs b/mir/src/tyctxt.rs index 88f977a..ba7e13c 100644 --- a/mir/src/tyctxt.rs +++ b/mir/src/tyctxt.rs @@ -14,12 +14,15 @@ pub struct AdtMeta { } impl AdtMeta { - fn derive_attrs(&self) -> String { + fn derive_attrs(&self, hashable: bool) -> String { let mut attrs = vec!["Debug"]; if self.copy { attrs.push("Copy"); attrs.push("Clone"); } + if hashable { + attrs.push("Hash"); + } let list: String = attrs.iter().intersperse(&",").copied().collect(); if list.is_empty() { @@ -124,7 +127,7 @@ impl TyCtxt { let TyKind::Adt(adt) = adt else { panic!("not an adt"); }; - str += &self.adt_meta[&id].derive_attrs(); + str += &self.adt_meta[&id].derive_attrs(id.hashable(self)); if adt.is_enum() { let variants: String = adt .variants