floats iter 3: JOURNAL — iteration close (typecheck + builtins)

This commit is contained in:
2026-05-10 15:43:31 +02:00
parent d6da5c26b1
commit dbec90cefd
+101
View File
@@ -13249,3 +13249,104 @@ milestone:
- DESIGN.md §"Float semantics" subsection (A5 determinism
contract) and line-2033 "supported primitive types" list
update — iter 5.
## 2026-05-10 — Iteration Floats.3: typecheck + builtins
Widened the existing arithmetic and comparison builtins (`+`, `-`,
`*`, `/`, `!=`, `<`, `<=`, `>`, `>=`) from monomorphic
`(Int, Int) -> {Int,Bool}` to polymorphic `forall a. (a, a) -> a`
(resp. `... -> Bool`). Same shape `==` already had since iter 16e.
The `{Int, Float}` filter is enforced at codegen (iter 4); typecheck
accepts any matching pair, mirroring how `==` works today. `%` stays
monomorphic-Int (no fmod yet — `%` semantics for Float require an
explicit decision on sign-of-result and ±0/±Inf edge cases that has
not been made). The widening is type-side only; codegen lowering for
`+` etc. continues to use the monomorphic-Int `synth::builtin_binop`
table (iter 4 converts that to type-dispatched). The lockstep partner
table at `crates/ailang-codegen/src/synth.rs::builtin_ail_type` was
widened in the same commit so no codegen call site sees an
out-of-date polymorphic shape.
Stale comment caught and corrected during the iter-3.1 quality
review: the `==` install block carried "the other comparison ops
(`<`, `<=`, `>`, `>=`, `!=`) stay Int-only" since iter 16e.
Replaced with the actual current shape — and the same comment now
documents the codegen-dispatch table's Float arm that lands in
iter 4 (`Float → fcmp oeq double`), so the future-state pointer
sits next to the typecheck-side install rather than only in the
spec.
Five new Float builtins installed: `neg : forall a. (a) -> a`
(polymorphic; parallel to widened `+`), `int_to_float : (Int) ->
Float`, `float_to_int_truncate : (Float) -> Int`, `float_to_str :
(Float) -> Str`, `is_nan : (Float) -> Bool`. The polymorphic `neg`
rationale: spec section A3 calls out that the `(- 0.0 x)` desugar
is wrong for `-0.0` (returns `+0.0` per IEEE rounding), so Float
negation needs its own builtin name. Reusing the `forall a. (a) -> a`
shape lets Int negation share the symbol.
Three Float bit-pattern constants installed as bare values
(parallel to `__unreachable__` but with concrete `Type::float()`
rather than `forall a. a`): `nan`, `inf`, `neg_inf`. Reference site
is `(var nan)`, not `(app nan)`. Codegen emits `double 0x7FF8...` /
`double 0x7FF0...` / `double 0xFFF0...` at the use site in iter 4.
One new effect op installed: `io/print_float : (Float) -> Unit !IO`.
Mirrors `io/print_int|bool|str`. Codegen lowering through runtime C
glue `@ail_print_float` lands in iter 4.
Pattern-matching on Float literals is now typecheck-rejected via the
new `CheckError::FloatPatternNotAllowed` variant per spec line
723-735 recommendation (a). The surface lex / parser still ACCEPT
Float patterns (iter 2 left this open); the typecheck diagnostic
fires at the `Pattern::Lit { lit }` arm in
`crates/ailang-check/src/lib.rs`. The error message points the
LLM-author to the documented alternative — ordering operators and
`is_nan` for Float discrimination. The variant gets a parallel arm
in `CheckError::code()` returning `"float-pattern-not-allowed"`
mechanical compile-completeness driven by the variant addition.
`ail builtins` reflects all twelve new entries (4 widened arithmetic
display strings + 5 Float fn builtins + 3 Float constants + 1
io/print_float effect op) via the refreshed `list()` table.
Per-task commits:
- `0981804` floats iter 3.1: widen +/-/*/// and !=/</<=/>/>= to polymorphic forall a
- `6890aa2` floats iter 3.1 fixup: correct stale ==-comparison comment + asymmetric Float test args + drop spec-section reference
- `60a4c68` floats iter 3.2: install neg/int_to_float/float_to_int_truncate/float_to_str/is_nan
- `fd3f74c` floats iter 3.3: install Float constants nan/inf/neg_inf + io/print_float effect op
- `d6da5c2` floats iter 3.4: typecheck rejects Pattern::Lit Float with FloatPatternNotAllowed
Process note: the Floats milestone keeps centralising the lockstep
between `crates/ailang-check/src/builtins.rs::install` and
`crates/ailang-codegen/src/synth.rs::{builtin_ail_type,
builtin_effect_op_ret}`. After iter 3 these two tables carry four
near-identical `Forall { vars: ["a"], …, body: Fn { params, ret, … } }`
constructions per crate. The two-table convention is established and
deliberate (different crates, no `ailang-core` dependency in `synth.rs`
on `ailang-check`-style helpers), but if the duplication crosses a
fifth pair of entries in iter 4 a small `ailang-core::ast::poly_a_a_to_a`
constructor starts to earn its keep. Queued as a roadmap item, not
fix-now.
Known debt deliberately deferred to later iterations of this
milestone:
- Codegen LOWERING for the widened `+`/`-`/`*`/`/` etc. — iter 4
converts the monomorphic-Int `synth::builtin_binop` table to
type-dispatched; emits `fadd double` / `fcmp olt double` / `fcmp
une double` (for `!=`!) / etc. for the Float arm.
- Codegen LOWERING for `neg` (Int → `sub i64 0, %x`, Float →
`fneg double %x`) — iter 4. The `fneg` instruction (LLVM 8+) is
needed for correct `-0.0` handling.
- Codegen LOWERING for `int_to_float` (`sitofp`),
`float_to_int_truncate` (`@llvm.fptosi.sat.i64.f64`),
`float_to_str` (runtime C glue), `is_nan` (`fcmp uno`) — iter 4.
- Codegen LOWERING for `nan`/`inf`/`neg_inf` constants (LLVM
hex-float literals at the use site) — iter 4.
- Codegen LOWERING for `io/print_float` (parallel to
`io/print_int`, runtime glue `@ail_print_float`) — iter 4.
- Prose round-trip for Float literals — iter 5.
- DESIGN.md §"Float semantics" subsection (A5 determinism contract)
and the line-2033 "supported primitive types" list update — iter 5.