Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 107 additions & 3 deletions crates/environ/src/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,7 @@ pub struct RecordField {
/// Variants are close to Rust `enum` declarations where a value is one of many
/// cases and each case has a unique name and an optional payload associated
/// with it.
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TypeVariant {
/// The list of cases that this variant can take.
pub cases: IndexMap<String, Option<InterfaceType>>,
Expand All @@ -1063,6 +1063,20 @@ pub struct TypeVariant {
pub info: VariantInfo,
}

// NB: the order of `cases` matter, so this `PartialEq` disagrees with
// `IndexMap`'s implementation.
impl PartialEq for TypeVariant {
fn eq(&self, other: &TypeVariant) -> bool {
let TypeVariant { cases, abi, info } = self;
cases.len() == other.cases.len()
&& cases.iter().eq(other.cases.iter())
&& *abi == other.abi
&& *info == other.info
}
}

impl Eq for TypeVariant {}

impl Hash for TypeVariant {
fn hash<H: Hasher>(&self, h: &mut H) {
let TypeVariant { cases, abi, info } = self;
Expand Down Expand Up @@ -1091,14 +1105,25 @@ pub struct TypeTuple {
///
/// This can be thought of as a record-of-bools, although the representation is
/// more efficient as bitflags.
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TypeFlags {
/// The names of all flags, all of which are unique.
pub names: IndexSet<String>,
/// Byte information about this type in the canonical ABI.
pub abi: CanonicalAbiInfo,
}

// NB: the order of `names` matter, so this `PartialEq` disagrees with
// `IndexMap`'s implementation.
impl PartialEq for TypeFlags {
fn eq(&self, other: &TypeFlags) -> bool {
let TypeFlags { names, abi } = self;
names.len() == other.names.len() && names.iter().eq(other.names.iter()) && *abi == other.abi
}
}

impl Eq for TypeFlags {}

impl Hash for TypeFlags {
fn hash<H: Hasher>(&self, h: &mut H) {
let TypeFlags { names, abi } = self;
Expand All @@ -1115,7 +1140,7 @@ impl Hash for TypeFlags {
///
/// In interface types enums are simply a bag of names, and can be seen as a
/// variant where all payloads are `Unit`.
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TypeEnum {
/// The names of this enum, all of which are unique.
pub names: IndexSet<String>,
Expand All @@ -1125,6 +1150,20 @@ pub struct TypeEnum {
pub info: VariantInfo,
}

// NB: the order of `names` matter, so this `PartialEq` disagrees with
// `IndexMap`'s implementation.
impl PartialEq for TypeEnum {
fn eq(&self, other: &TypeEnum) -> bool {
let TypeEnum { names, abi, info } = self;
names.len() == other.names.len()
&& names.iter().eq(other.names.iter())
&& *abi == other.abi
&& *info == other.info
}
}

impl Eq for TypeEnum {}

impl Hash for TypeEnum {
fn hash<H: Hasher>(&self, h: &mut H) {
let TypeEnum { names, abi, info } = self;
Expand Down Expand Up @@ -1350,3 +1389,68 @@ pub enum FlatType {
F32,
F64,
}

#[cfg(test)]
mod tests {
use super::*;

fn variant(cases: &[(&str, InterfaceType)]) -> TypeVariant {
TypeVariant {
cases: cases
.iter()
.map(|(name, ty)| (name.to_string(), Some(*ty)))
.collect(),
abi: CanonicalAbiInfo::default(),
info: VariantInfo {
size: DiscriminantSize::Size1,
payload_offset32: 4,
payload_offset64: 8,
},
}
}

fn flags(names: &[&str]) -> TypeFlags {
TypeFlags {
names: names.iter().map(|n| n.to_string()).collect(),
abi: CanonicalAbiInfo::default(),
}
}

fn enum_(names: &[&str]) -> TypeEnum {
TypeEnum {
names: names.iter().map(|n| n.to_string()).collect(),
abi: CanonicalAbiInfo::default(),
info: VariantInfo {
size: DiscriminantSize::Size1,
payload_offset32: 4,
payload_offset64: 8,
},
}
}

#[test]
fn variant_case_order_is_significant() {
let a = variant(&[("n", InterfaceType::U32), ("s", InterfaceType::String)]);
let b = variant(&[("s", InterfaceType::String), ("n", InterfaceType::U32)]);
assert_ne!(a, b);
assert_eq!(a, a.clone());
}

#[test]
fn flags_name_order_is_significant() {
let a = flags(&["a", "b", "c"]);
let b = flags(&["c", "b", "a"]);

assert_ne!(a, b);
assert_eq!(a, a.clone());
}

#[test]
fn enum_name_order_is_significant() {
let a = enum_(&["red", "green", "blue"]);
let b = enum_(&["blue", "green", "red"]);

assert_ne!(a, b);
assert_eq!(a, a.clone());
}
}
Loading