Files
AILang/design/contracts/0017-prelude-classes.md
T
Brummel 4ec8f90b19 docs(contracts): finish the honesty pass (0003, 0008, 0013, 0017)
Third tranche, completing the honesty-rule sweep across the remaining
contracts. Same conservative bar: cut unbacked-verification claims,
change/deletion history, and forward-intent; keep present-state design
rationale.

- 0003: drop "sanitiser-verified" — no TSan/sanitiser test exists in
  the tree, so the claim asserts a verification that does not happen.
  The data-race-freedom property (argued from the non-atomic-but-never-
  shared hot path + atomic-relaxed shared counter) stays.
- 0008: the `Type::Con.name` hash paragraph ("the tightening shifted
  the hashes ... The new pins ... re-asserts the pre-tightening hashes")
  -> present-state: which fixtures carry which hash shape and which test
  pins each.
- 0013: drop "the former codegen-side fallback (at ..., and the
  now-deleted `synth_with_extras`) is retracted" (deletion history; the
  present fact is that codegen does not re-resolve, per the boundary);
  "A future refactor that loosens any one of the four breaks ..." ->
  present-tense statement that the four are load-bearing and each pinned.
- 0017: drop "a real cost surfaced by the `bench_closure_chain`
  regression at the operator-routing-eq-ord milestone" (history) and
  "the symmetric extensions are mechanical when the first such workload
  appears" (forward-intent); keep the present-state allocation cost, the
  pin, and the Int-only-asymmetry rationale.

All 18 contracts now reviewed against the code. Ledger pins green;
honesty sweep clean.
2026-06-02 11:30:46 +02:00

3.9 KiB

Prelude (built-in) classes

Prelude (built-in) classes

The prelude ships the Ordering ADT, the Eq and Ord classes, primitive Eq Int/Bool/Str/Unit and Ord Int/Bool/Str instances, and the five polymorphic free-fn helpers ne/lt/le/gt/ge. The primitive Eq / Ord instance bodies carry the (intrinsic) marker in examples/prelude.ail (the marker is the lockstep partner to the INTERCEPTS registry); the codegen intercept try_emit_primitive_instance_body supplies their single-instruction bodies (icmp eq i64 for eq__Int, icmp eq i1 for eq__Bool, @ail_str_eq for eq__Str, ret i1 1 for eq__Unit; a three-way icmp ladder constructing LT/EQ/GT for compare__T) and attaches alwaysinline so the call folds to the single instruction at every use site.

The five Ord-class free helpers (ne/lt/le/gt/ge) have source-level bodies that route through eq / compare plus match over Ordering — written that way so the surface form is LLM-natural and so user-ADT instances of Eq/Ord automatically pick up working helpers without any per-type opt-in. At Int the codegen intercept short-circuits this indirection: lt__Int, le__Int, gt__Int, ge__Int, and ne__Int are intercept- overridden to emit icmp slt i64 / sle i64 / sgt i64 / sge i64 / ne i64 directly, bypassing the compare__IntOrdering-ctor → match path. Without this short-circuit the Ordering ctor allocates per call inside hot loops; the short-circuit is pinned by crates/ail/tests/ord_int_intercept_ir_pin.rs. It is Int-only by deliberate asymmetry: no current bench or example fixture exercises Ord-lt/le/gt/ge at Bool or Str at sufficient call frequency to justify the intercept-arm maintenance.

Float has neither Eq nor Ord instance per Float semantics; a polymorphic helper invoked at Float fires NoInstance at typecheck with a Float-aware diagnostic that names the explicit float_eq / float_lt alternatives and cross-references this section. The lookup machinery that turns a show x call site into the right monomorphic instance is documented in method dispatch.

Float comparison is a separate surface: the six monomorphic prelude fns float_eq / float_ne / float_lt / float_le / float_gt / float_ge each carry the type (Float, Float) -> Bool without a class constraint. They lower via the same intercept machinery as the primitive Eq instances (single fcmp instruction with the matching predicate oeq / une / olt / ole / ogt / oge, plus alwaysinline). They replace what would otherwise be a polymorphic == / < on Float — Float gets full comparability via explicit named fns, not via an implicit class instance.

The prelude ships class Show a where show : (a borrow) -> Str and primitive Show Int, Show Bool, Show Str, Show Float instances. Float is included in Show (unlike Eq/Ord) — IEEE-754 makes structural equality and total ordering semantically dubious, but textual representation of a Float is well-defined modulo the NaN-spelling caveat in Float semantics. Each Show <T> instance body is a single-application lambda invoking the corresponding runtime primitive (int_to_str, bool_to_str, str_clone, float_to_str; see Str ABI for the heap-Str primitives); no codegen intercept is required. The polymorphic helper print : forall a. Show a => a -> () !IO has body \x -> let s = show x in do io/print_str s (explicit let-binder for heap-Str RC discipline per the Str carve-out in Str ABI). The let-binder is structurally pinned by crates/ail/tests/print_mono_body_shape.rs. Routing through print is the path for non-Str primitives; io/print_str is the only built-in direct-output effect-op.

Ratified by: crates/ail/tests/show_no_instance_e2e.rs.