From 504f2c09dc271388cfe4ee8e1ba1687ab49e1817 Mon Sep 17 00:00:00 2001 From: emilyaherbert Date: Tue, 14 Feb 2023 16:40:14 -0600 Subject: [PATCH] Implement OrdWithEngines for some types. --- sway-core/src/engine_threading.rs | 41 +++++- sway-core/src/language/ty/declaration/enum.rs | 31 ++++- .../src/language/ty/declaration/struct.rs | 27 +++- .../semantic_analysis/namespace/trait_map.rs | 25 ++-- sway-core/src/type_system/info.rs | 124 +++++++++++++++++- sway-core/src/type_system/trait_constraint.rs | 22 +++- sway-core/src/type_system/type_argument.rs | 24 +++- sway-core/src/type_system/type_parameter.rs | 31 +++++ sway-types/src/integer_bits.rs | 2 +- 9 files changed, 299 insertions(+), 28 deletions(-) diff --git a/sway-core/src/engine_threading.rs b/sway-core/src/engine_threading.rs index a48ccd15bb7..e1986312353 100644 --- a/sway-core/src/engine_threading.rs +++ b/sway-core/src/engine_threading.rs @@ -87,6 +87,24 @@ impl PartialEq for WithEngines<'_, T> { impl Eq for WithEngines<'_, T> {} +impl PartialOrd for WithEngines<'_, T> +where + T: PartialEqWithEngines, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.thing.cmp(&other.thing, self.engines.te())) + } +} + +impl Ord for WithEngines<'_, T> +where + T: EqWithEngines, +{ + fn cmp(&self, other: &Self) -> Ordering { + self.thing.cmp(&other.thing, self.engines.te()) + } +} + pub(crate) trait DisplayWithEngines { fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: Engines<'_>) -> fmt::Result; } @@ -141,7 +159,7 @@ pub trait PartialEqWithEngines { } pub trait OrdWithEngines { - fn cmp(&self, rhs: &Self, type_engine: &TypeEngine) -> Ordering; + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering; } impl EqWithEngines for &T {} @@ -151,8 +169,19 @@ impl PartialEqWithEngines for &T { } } impl OrdWithEngines for &T { - fn cmp(&self, rhs: &Self, type_engine: &TypeEngine) -> Ordering { - (*self).cmp(*rhs, type_engine) + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { + (*self).cmp(*other, type_engine) + } +} + +impl OrdWithEngines for Option { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { + match (self, other) { + (Some(x), Some(y)) => x.cmp(y, type_engine), + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, + (None, None) => Ordering::Equal, + } } } @@ -174,11 +203,11 @@ impl PartialEqWithEngines for [T] { } } impl OrdWithEngines for [T] { - fn cmp(&self, rhs: &Self, type_engine: &TypeEngine) -> Ordering { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { self.iter() - .zip(rhs.iter()) + .zip(other.iter()) .map(|(x, y)| x.cmp(y, type_engine)) .find(|o| o.is_ne()) - .unwrap_or_else(|| self.len().cmp(&rhs.len())) + .unwrap_or_else(|| self.len().cmp(&other.len())) } } diff --git a/sway-core/src/language/ty/declaration/enum.rs b/sway-core/src/language/ty/declaration/enum.rs index 91936ab1ff1..b3a09c9a40f 100644 --- a/sway-core/src/language/ty/declaration/enum.rs +++ b/sway-core/src/language/ty/declaration/enum.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + cmp::Ordering, + hash::{Hash, Hasher}, +}; use sway_error::error::CompileError; use sway_types::{Ident, Span, Spanned}; @@ -154,6 +157,32 @@ impl PartialEqWithEngines for TyEnumVariant { } } +impl OrdWithEngines for TyEnumVariant { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { + let TyEnumVariant { + name: ln, + type_argument: lta, + tag: lt, + // these fields are not compared because they aren't relevant/a + // reliable source of obj v. obj distinction + span: _, + attributes: _, + } = self; + let TyEnumVariant { + name: rn, + type_argument: rta, + tag: rt, + // these fields are not compared because they aren't relevant/a + // reliable source of obj v. obj distinction + span: _, + attributes: _, + } = other; + ln.cmp(rn) + .then_with(|| lta.cmp(rta, type_engine)) + .then_with(|| lt.cmp(rt)) + } +} + impl SubstTypes for TyEnumVariant { fn subst_inner(&mut self, type_mapping: &TypeSubstMap, engines: Engines<'_>) { self.type_argument.subst_inner(type_mapping, engines); diff --git a/sway-core/src/language/ty/declaration/struct.rs b/sway-core/src/language/ty/declaration/struct.rs index 2fc9b39d03c..52cdcafc5cd 100644 --- a/sway-core/src/language/ty/declaration/struct.rs +++ b/sway-core/src/language/ty/declaration/struct.rs @@ -1,4 +1,7 @@ -use std::hash::{Hash, Hasher}; +use std::{ + cmp::Ordering, + hash::{Hash, Hasher}, +}; use sway_error::error::CompileError; use sway_types::{Ident, Span, Spanned}; @@ -161,6 +164,28 @@ impl PartialEqWithEngines for TyStructField { } } +impl OrdWithEngines for TyStructField { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { + let TyStructField { + name: ln, + type_argument: lta, + // these fields are not compared because they aren't relevant/a + // reliable source of obj v. obj distinction + span: _, + attributes: _, + } = self; + let TyStructField { + name: rn, + type_argument: rta, + // these fields are not compared because they aren't relevant/a + // reliable source of obj v. obj distinction + span: _, + attributes: _, + } = other; + ln.cmp(rn).then_with(|| lta.cmp(rta, type_engine)) + } +} + impl SubstTypes for TyStructField { fn subst_inner(&mut self, type_mapping: &TypeSubstMap, engines: Engines<'_>) { self.type_argument.subst_inner(type_mapping, engines); diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index 295ff9273c7..e8d9eee793b 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -1,4 +1,7 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::{ + cmp::Ordering, + collections::{BTreeMap, BTreeSet}, +}; use sway_error::error::CompileError; use sway_types::{Ident, Span, Spanned}; @@ -23,10 +26,10 @@ impl PartialEqWithEngines for TraitSuffix { } } impl OrdWithEngines for TraitSuffix { - fn cmp(&self, rhs: &Self, type_engine: &TypeEngine) -> std::cmp::Ordering { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> std::cmp::Ordering { self.name - .cmp(&rhs.name) - .then_with(|| self.args.cmp(&rhs.args, type_engine)) + .cmp(&other.name) + .then_with(|| self.args.cmp(&other.args, type_engine)) } } @@ -38,11 +41,11 @@ impl PartialEqWithEngines for CallPath { } } impl OrdWithEngines for CallPath { - fn cmp(&self, rhs: &Self, type_engine: &TypeEngine) -> std::cmp::Ordering { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { self.prefixes - .cmp(&rhs.prefixes) - .then_with(|| self.suffix.cmp(&rhs.suffix, type_engine)) - .then_with(|| self.is_absolute.cmp(&rhs.is_absolute)) + .cmp(&other.prefixes) + .then_with(|| self.suffix.cmp(&other.suffix, type_engine)) + .then_with(|| self.is_absolute.cmp(&other.is_absolute)) } } @@ -55,10 +58,10 @@ struct TraitKey { } impl OrdWithEngines for TraitKey { - fn cmp(&self, rhs: &Self, type_engine: &TypeEngine) -> std::cmp::Ordering { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> std::cmp::Ordering { self.name - .cmp(&rhs.name, type_engine) - .then_with(|| self.type_id.cmp(&rhs.type_id)) + .cmp(&other.name, type_engine) + .then_with(|| self.type_id.cmp(&other.type_id)) } } diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index 1ae0958989c..660b3e6b067 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -8,12 +8,13 @@ use sway_error::error::CompileError; use sway_types::{integer_bits::IntegerBits, span::Span, Spanned}; use std::{ + cmp::Ordering, collections::HashSet, fmt, hash::{Hash, Hasher}, }; -#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] pub enum AbiName { Deferred, Known(CallPath), @@ -146,7 +147,7 @@ pub enum TypeInfo { impl HashWithEngines for TypeInfo { fn hash(&self, state: &mut H, engines: Engines<'_>) { - std::mem::discriminant(self).hash(state); + self.discriminant_value().hash(state); match self { TypeInfo::Str(len) => { len.hash(state); @@ -313,7 +314,95 @@ impl PartialEqWithEngines for TypeInfo { (TypeInfo::Storage { fields: l_fields }, TypeInfo::Storage { fields: r_fields }) => { l_fields.eq(r_fields, engines) } - (l, r) => std::mem::discriminant(l) == std::mem::discriminant(r), + (l, r) => l.discriminant_value() == r.discriminant_value(), + } + } +} + +impl OrdWithEngines for TypeInfo { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { + match (self, other) { + ( + Self::UnknownGeneric { + name: l, + trait_constraints: ltc, + }, + Self::UnknownGeneric { + name: r, + trait_constraints: rtc, + }, + ) => l.cmp(r).then_with(|| ltc.cmp(rtc, type_engine)), + (Self::Placeholder(l), Self::Placeholder(r)) => l.cmp(r, type_engine), + ( + Self::Custom { + call_path: l_call_path, + type_arguments: l_type_args, + }, + Self::Custom { + call_path: r_call_path, + type_arguments: r_type_args, + }, + ) => l_call_path.suffix.cmp(&r_call_path.suffix).then_with(|| { + l_type_args + .as_deref() + .cmp(&r_type_args.as_deref(), type_engine) + }), + (Self::Str(l), Self::Str(r)) => l.val().cmp(&r.val()), + (Self::UnsignedInteger(l), Self::UnsignedInteger(r)) => l.cmp(r), + ( + Self::Enum { + call_path: l_call_path, + type_parameters: ltp, + variant_types: lvt, + }, + Self::Enum { + call_path: r_call_path, + type_parameters: rtp, + variant_types: rvt, + }, + ) => l_call_path + .suffix + .cmp(&r_call_path.suffix) + .then_with(|| ltp.cmp(rtp, type_engine)) + .then_with(|| lvt.cmp(rvt, type_engine)), + ( + Self::Struct { + call_path: l_call_path, + type_parameters: ltp, + fields: lf, + }, + Self::Struct { + call_path: r_call_path, + type_parameters: rtp, + fields: rf, + }, + ) => l_call_path + .suffix + .cmp(&r_call_path.suffix) + .then_with(|| ltp.cmp(rtp, type_engine)) + .then_with(|| lf.cmp(rf, type_engine)), + (Self::Tuple(l), Self::Tuple(r)) => l.cmp(r, type_engine), + ( + Self::ContractCaller { + abi_name: l_abi_name, + address: _, + }, + Self::ContractCaller { + abi_name: r_abi_name, + address: _, + }, + ) => { + // NOTE: we assume all contract callers are unique + l_abi_name.cmp(r_abi_name) + } + (Self::Array(l0, l1), Self::Array(r0, r1)) => type_engine + .get(l0.type_id) + .cmp(&type_engine.get(r0.type_id), type_engine) + .then_with(|| l1.val().cmp(&r1.val())), + (TypeInfo::Storage { fields: l_fields }, TypeInfo::Storage { fields: r_fields }) => { + l_fields.cmp(r_fields, type_engine) + } + (l, r) => l.discriminant_value().cmp(&r.discriminant_value()), } } } @@ -505,6 +594,35 @@ impl UnconstrainedTypeParameters for TypeInfo { } impl TypeInfo { + /// Returns a discriminant for the variant. + // NOTE: This is approach is not the most straightforward, but is needed + // because of this missing feature on Rust's `Discriminant` type: + // https://github.com/rust-lang/rust/pull/106418 + fn discriminant_value(&self) -> u8 { + match self { + TypeInfo::Unknown => 0, + TypeInfo::UnknownGeneric { .. } => 1, + TypeInfo::Placeholder(_) => 2, + TypeInfo::Str(_) => 3, + TypeInfo::UnsignedInteger(_) => 4, + TypeInfo::Enum { .. } => 5, + TypeInfo::Struct { .. } => 6, + TypeInfo::Boolean => 7, + TypeInfo::Tuple(_) => 8, + TypeInfo::ContractCaller { .. } => 9, + TypeInfo::Custom { .. } => 10, + TypeInfo::SelfType => 11, + TypeInfo::B256 => 12, + TypeInfo::Numeric => 13, + TypeInfo::Contract => 14, + TypeInfo::ErrorRecovery => 15, + TypeInfo::Array(_, _) => 16, + TypeInfo::Storage { .. } => 17, + TypeInfo::RawUntypedPtr => 18, + TypeInfo::RawUntypedSlice => 19, + } + } + /// maps a type to a name that is used when constructing function selectors pub(crate) fn to_selector_name( &self, diff --git a/sway-core/src/type_system/trait_constraint.rs b/sway-core/src/type_system/trait_constraint.rs index 3bd7c8c6bb6..a4b86f89a48 100644 --- a/sway-core/src/type_system/trait_constraint.rs +++ b/sway-core/src/type_system/trait_constraint.rs @@ -1,4 +1,7 @@ -use std::hash::Hash; +use std::{ + cmp::Ordering, + hash::{Hash, Hasher}, +}; use sway_error::error::CompileError; use sway_types::{Span, Spanned}; @@ -19,11 +22,12 @@ pub struct TraitConstraint { } impl HashWithEngines for TraitConstraint { - fn hash(&self, state: &mut H, engines: Engines<'_>) { + fn hash(&self, state: &mut H, engines: Engines<'_>) { self.trait_name.hash(state); self.type_arguments.hash(state, engines); } } + impl EqWithEngines for TraitConstraint {} impl PartialEqWithEngines for TraitConstraint { fn eq(&self, other: &Self, engines: Engines<'_>) -> bool { @@ -32,6 +36,20 @@ impl PartialEqWithEngines for TraitConstraint { } } +impl OrdWithEngines for TraitConstraint { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { + let TraitConstraint { + trait_name: ltn, + type_arguments: lta, + } = self; + let TraitConstraint { + trait_name: rtn, + type_arguments: rta, + } = other; + ltn.cmp(rtn).then_with(|| lta.cmp(rta, type_engine)) + } +} + impl Spanned for TraitConstraint { fn span(&self) -> sway_types::Span { self.trait_name.span() diff --git a/sway-core/src/type_system/type_argument.rs b/sway-core/src/type_system/type_argument.rs index 99de4c69147..d9e831b1d5a 100644 --- a/sway-core/src/type_system/type_argument.rs +++ b/sway-core/src/type_system/type_argument.rs @@ -1,5 +1,5 @@ use crate::{engine_threading::*, language::CallPathTree, type_system::*}; -use std::{fmt, hash::Hasher}; +use std::{cmp::Ordering, fmt, hash::Hasher}; use sway_types::{Span, Spanned}; #[derive(Debug, Clone)] @@ -53,8 +53,26 @@ impl PartialEqWithEngines for TypeArgument { } impl OrdWithEngines for TypeArgument { - fn cmp(&self, rhs: &Self, _: &TypeEngine) -> std::cmp::Ordering { - self.type_id.cmp(&rhs.type_id) + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { + let TypeArgument { + type_id: lti, + // these fields are not compared because they aren't relevant/a + // reliable source of obj v. obj distinction + initial_type_id: _, + span: _, + call_path_tree: _, + } = self; + let TypeArgument { + type_id: rti, + // these fields are not compared because they aren't relevant/a + // reliable source of obj v. obj distinction + initial_type_id: _, + span: _, + call_path_tree: _, + } = other; + type_engine + .get(*lti) + .cmp(&type_engine.get(*rti), type_engine) } } diff --git a/sway-core/src/type_system/type_parameter.rs b/sway-core/src/type_system/type_parameter.rs index ab5b3b058db..010b91f8f2b 100644 --- a/sway-core/src/type_system/type_parameter.rs +++ b/sway-core/src/type_system/type_parameter.rs @@ -11,6 +11,7 @@ use sway_error::error::CompileError; use sway_types::{ident::Ident, span::Span, Spanned}; use std::{ + cmp::Ordering, collections::BTreeMap, fmt, hash::{Hash, Hasher}, @@ -55,6 +56,36 @@ impl PartialEqWithEngines for TypeParameter { } } +impl OrdWithEngines for TypeParameter { + fn cmp(&self, other: &Self, type_engine: &TypeEngine) -> Ordering { + let TypeParameter { + type_id: lti, + name_ident: ln, + trait_constraints: ltc, + // these fields are not compared because they aren't relevant/a + // reliable source of obj v. obj distinction + trait_constraints_span: _, + initial_type_id: _, + } = self; + let TypeParameter { + type_id: rti, + name_ident: rn, + trait_constraints: rtc, + // these fields are not compared because they aren't relevant/a + // reliable source of obj v. obj distinction + trait_constraints_span: _, + initial_type_id: _, + } = other; + ln.cmp(rn) + .then_with(|| { + type_engine + .get(*lti) + .cmp(&type_engine.get(*rti), type_engine) + }) + .then_with(|| ltc.cmp(rtc, type_engine)) + } +} + impl SubstTypes for TypeParameter { fn subst_inner(&mut self, type_mapping: &TypeSubstMap, engines: Engines<'_>) { self.type_id.subst(type_mapping, engines); diff --git a/sway-types/src/integer_bits.rs b/sway-types/src/integer_bits.rs index fc3bbb1f434..0b2f30dc7b9 100644 --- a/sway-types/src/integer_bits.rs +++ b/sway-types/src/integer_bits.rs @@ -1,6 +1,6 @@ use std::fmt; -#[derive(Eq, PartialEq, Hash, Debug, Clone, Copy)] +#[derive(Eq, PartialEq, Hash, Debug, Clone, Copy, PartialOrd, Ord)] pub enum IntegerBits { Eight, Sixteen,