iter 22b.3.2: mono_symbol — primitive surface forms + hash for compound
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
//! `examples_dir()` pattern — `cargo test`-cwd is the crate dir,
|
||||
//! the fixtures live in `<repo>/examples/`.
|
||||
|
||||
use ailang_core::ast::Type;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn examples_dir() -> PathBuf {
|
||||
@@ -93,3 +94,44 @@ fn monomorphise_workspace_is_identity_when_no_concrete_call_sites() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mono_symbol_primitive_types_use_surface_form() {
|
||||
// Primitive-type forms must be human-readable in diagnostics
|
||||
// and ABI symbols. Per the spec's mono-symbol naming guidance:
|
||||
// <method>#<surface-name> for primitives.
|
||||
let int_ty = Type::int();
|
||||
let bool_ty = Type::bool_();
|
||||
let str_ty = Type::str_();
|
||||
let unit_ty = Type::unit();
|
||||
|
||||
assert_eq!(ailang_check::mono::mono_symbol("show", &int_ty), "show#Int");
|
||||
assert_eq!(ailang_check::mono::mono_symbol("eq", &bool_ty), "eq#Bool");
|
||||
assert_eq!(ailang_check::mono::mono_symbol("show", &str_ty), "show#Str");
|
||||
assert_eq!(ailang_check::mono::mono_symbol("foo", &unit_ty), "foo#Unit");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mono_symbol_compound_type_uses_hash_suffix() {
|
||||
// Compound types fall through to canonical type_hash with an
|
||||
// 8-hex-char prefix. Stability matters more than legibility
|
||||
// here — same input must produce the same name across runs.
|
||||
let pair_ty = Type::Con {
|
||||
name: "Pair".into(),
|
||||
args: vec![Type::int(), Type::bool_()],
|
||||
};
|
||||
let name = ailang_check::mono::mono_symbol("show", &pair_ty);
|
||||
|
||||
// Format: `show#<8-hex-chars>`.
|
||||
assert!(name.starts_with("show#"), "name = {name}");
|
||||
let suffix = &name["show#".len()..];
|
||||
assert_eq!(suffix.len(), 8, "compound suffix is 8 hex chars: {suffix}");
|
||||
assert!(
|
||||
suffix.chars().all(|c| c.is_ascii_hexdigit()),
|
||||
"compound suffix must be hex: {suffix}"
|
||||
);
|
||||
|
||||
// Stability: a second call with the same type produces the same name.
|
||||
let again = ailang_check::mono::mono_symbol("show", &pair_ty);
|
||||
assert_eq!(name, again, "mono_symbol must be deterministic");
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ fn unify(a: &Type, b: &Type, subst: &mut Subst) -> Result<()> {
|
||||
pub mod builtins;
|
||||
pub mod diagnostic;
|
||||
pub mod lift;
|
||||
mod mono;
|
||||
pub mod mono;
|
||||
|
||||
pub use diagnostic::{Diagnostic, Severity};
|
||||
pub use lift::lift_letrecs;
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
//! the implementation so the constraint is visible to anyone
|
||||
//! extending the skeleton.
|
||||
|
||||
use ailang_core::ast::Def;
|
||||
use ailang_core::ast::{Def, Type};
|
||||
use ailang_core::workspace::Workspace;
|
||||
use crate::Result;
|
||||
|
||||
@@ -87,3 +87,46 @@ fn workspace_has_typeclasses(ws: &Workspace) -> bool {
|
||||
m.defs.iter().any(|d| matches!(d, Def::Class(_) | Def::Instance(_)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Iter 22b.3: deterministic mono-symbol name for a `(method,
|
||||
/// type)` pair. Primitive types (`Int`, `Bool`, `Str`, `Float`,
|
||||
/// `Unit`) produce `<method>#<surface-name>` for diagnostic and
|
||||
/// ABI legibility. All other types — parameterised cons,
|
||||
/// user-defined ADTs, function types — fall to
|
||||
/// `<method>#<8-hex-prefix-of-canonical-type-hash>`. The hash
|
||||
/// route ensures uniqueness without requiring a flattened
|
||||
/// surface form for arbitrarily nested types.
|
||||
///
|
||||
/// Determinism: `ailang_core::canonical::type_hash` is the same
|
||||
/// function `workspace::build_registry` uses to key
|
||||
/// [`Registry::entries`], so a registry-key match implies a
|
||||
/// `mono_symbol` match.
|
||||
pub fn mono_symbol(method: &str, ty: &Type) -> String {
|
||||
if let Some(prim) = primitive_surface_name(ty) {
|
||||
return format!("{method}#{prim}");
|
||||
}
|
||||
let full_hash = ailang_core::canonical::type_hash(ty);
|
||||
// 8-hex prefix is enough for low-collision keying across the
|
||||
// workspace; the full hash remains in the registry for
|
||||
// disambiguation should one ever be needed.
|
||||
format!("{method}#{}", &full_hash[..8])
|
||||
}
|
||||
|
||||
/// Iter 22b.3: returns the surface name iff `ty` is a zero-arity
|
||||
/// primitive `Type::Con`. Used by [`mono_symbol`] to gate the
|
||||
/// human-readable form. The match is intentionally narrow:
|
||||
/// `Int<args>` (which is malformed but parser-accepting) is
|
||||
/// treated as compound, so it falls to the hash form.
|
||||
fn primitive_surface_name(ty: &Type) -> Option<&'static str> {
|
||||
match ty {
|
||||
Type::Con { name, args } if args.is_empty() => match name.as_str() {
|
||||
"Int" => Some("Int"),
|
||||
"Bool" => Some("Bool"),
|
||||
"Str" => Some("Str"),
|
||||
"Float" => Some("Float"),
|
||||
"Unit" => Some("Unit"),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user