From e0e1153665855064ef96f82d81e00f40c12cc1b2 Mon Sep 17 00:00:00 2001 From: LesterEvSe Date: Thu, 15 Oct 2026 16:07:49 +0300 Subject: [PATCH 1/3] feat: add builtin field to ResolvedType struct --- src/types/aliased.rs | 6 ++++-- src/types/resolved.rs | 40 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/types/aliased.rs b/src/types/aliased.rs index 1451e3c4..cb614c73 100644 --- a/src/types/aliased.rs +++ b/src/types/aliased.rs @@ -369,7 +369,7 @@ impl BuiltinAlias { use BuiltinAlias as B; use UIntType::*; - match self { + let resolved = match self { B::Ctx8 => ResolvedType::tuple([ ResolvedType::list(U8.into(), NonZeroPow2Usize::new(64).unwrap()), ResolvedType::tuple([U64.into(), U256.into()]), @@ -393,7 +393,9 @@ impl BuiltinAlias { B::Amount1 | B::TokenAmount1 => { ResolvedType::either(ResolvedType::tuple([U1.into(), U256.into()]), U64.into()) } - } + }; + + resolved.with_builtin_alias(self) } } diff --git a/src/types/resolved.rs b/src/types/resolved.rs index 95b3c556..b01aa342 100644 --- a/src/types/resolved.rs +++ b/src/types/resolved.rs @@ -1,4 +1,5 @@ use core::fmt; +use core::hash::{Hash, Hasher}; use std::collections::HashSet; use std::sync::Arc; @@ -7,13 +8,31 @@ use miniscript::iter::{Tree, TreeLike}; use super::{ EnumInfo, StructuralType, TypeConstructible, TypeDeconstructible, TypeInner, UIntType, }; -use crate::num::NonZeroPow2Usize; +use crate::{num::NonZeroPow2Usize, types::BuiltinAlias}; /// SimplicityHL type without type aliases. -#[derive(PartialEq, Eq, Hash, Clone)] +#[derive(Clone)] pub struct ResolvedType { inner: TypeInner>, flags: TypeFlags, + builtin: Option, +} + +/// The builtin alias is metadata for tooling, not part of the type, +/// so `Pubkey` and `u256` stay interchangeable. +impl PartialEq for ResolvedType { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner && self.flags == other.flags + } +} + +impl Eq for ResolvedType {} + +impl Hash for ResolvedType { + fn hash(&self, state: &mut H) { + self.inner.hash(state); + self.flags.hash(state); + } } /// Facts about a type, computed once from its parts when the type is built. @@ -61,13 +80,27 @@ impl ResolvedType { }), }; - Self { inner, flags } + Self { + inner, + flags, + builtin: None, + } } /// Access the inner type primitive. pub fn as_inner(&self) -> &TypeInner> { &self.inner } + + /// Access the builtin alias that this type was resolved from, such as `Pubkey` or `Fe`. + pub const fn builtin_alias(&self) -> Option { + self.builtin + } + + pub(crate) fn with_builtin_alias(mut self, alias: BuiltinAlias) -> Self { + self.builtin = Some(alias); + self + } } /// Nominal enum types. @@ -111,6 +144,7 @@ impl ResolvedType { has_never: true, has_never_in_enum: false, }, + builtin: None, } } From 1cf3cb9900286b9fd61276420c3d6f791a0de22b Mon Sep 17 00:00:00 2001 From: LesterEvSe Date: Thu, 15 Oct 2026 16:08:28 +0300 Subject: [PATCH 2/3] feat: add regression test for comparison trait --- src/types/mod.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/types/mod.rs b/src/types/mod.rs index 3406c634..83aba443 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -134,6 +134,34 @@ mod tests { assert_eq!("Option", &option_never.to_string()); } + #[test] + fn builtin_alias_tag_is_not_part_of_the_type() { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let hash = |ty: &ResolvedType| { + let mut hasher = DefaultHasher::new(); + ty.hash(&mut hasher); + hasher.finish() + }; + + let pubkey = BuiltinAlias::Pubkey.resolve(); + let u256 = ResolvedType::u256(); + assert_eq!(Some(BuiltinAlias::Pubkey), pubkey.builtin_alias()); + assert_eq!(None, u256.builtin_alias()); + assert_eq!(pubkey, u256); + assert!(pubkey.same_as(&u256)); + assert_eq!(hash(&pubkey), hash(&u256)); + assert_eq!("u256", &pubkey.to_string()); + + let point = BuiltinAlias::Point.resolve(); + assert_eq!(Some(BuiltinAlias::Point), point.builtin_alias()); + assert_eq!( + ResolvedType::tuple([ResolvedType::u1(), ResolvedType::u256()]), + point + ); + } + #[test] fn never_type() { assert!(ResolvedType::never().is_never()); From d600cac38fc881865d55ce0500f4f7c28f46af7d Mon Sep 17 00:00:00 2001 From: LesterEvSe Date: Thu, 15 Oct 2026 16:50:09 +0300 Subject: [PATCH 3/3] refactor: rename ast.rs to ast/ in types/README.md --- src/types/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/types/README.md b/src/types/README.md index a313c1fd..40ee3ddf 100644 --- a/src/types/README.md +++ b/src/types/README.md @@ -14,9 +14,8 @@ There are three type structures in SimplicityHL: explicitly call `unify` on the Simplicity type inference engine to enforce this. * `AliasedType` is a copy of `ResolvedType` where everything is a (re)name. Essentially they are - "AST types". They feature primarily in parse.rs and ast.rs. Before these can be used, we call + "AST types". They feature primarily in parse.rs and ast/. Before these can be used, we call `aliased_type.resolve()` to get a `ResolvedType`. SimplicityHL does *not* currently support any form of nominal typing. All structurally equal types are considered interchangeable. -