fix(check): a Math.* call has the type the emitter gives it

Math.max, Math.sqrt and the rest (and the bare min/max/abs/clamp) are computed inline by the
emitter, and L4 gave each the unknown type, which agrees with everything: Maroon Lake's trail
put Math.max(5, n) into Notify's string field a1 and it failed in LLVM ("%t63 defined with type
i32 but expected ptr"). It was never about two dispatches on a line - one is enough. The checker
now mirrors the emitter: a float/double first argument gives that type (sign an int); otherwise
min/max/abs/clamp keep the first argument's type, sign/floor/ceil/round/posmod/wrap/ping_pong are
ints, the rest fixed. Named arguments or an argument it cannot type leave it unknown.

rejected/math_into_text is the case. Reseeded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 14:12:28 +03:00
parent e6729b3123
commit 8e113f749a
6 changed files with 13780 additions and 12630 deletions

View file

@ -178,6 +178,7 @@ function ck_call(e: Node) -> pointer {
if b.kind == E_ID and ck_local(b.s) < 0 and ck_global(b.s) == null {
# an alias (L6) is its target, labels and all, so its arguments are checked in full
if (b.s == "Memory") { ck_raw(e, `Memory.{c.s}`) }
if (b.s == "Math") and is_math_ns(c.s) { return ck_math(e, c.s) }
let al = ns_alias_find(b.s, c.s)
if al >= 0 {
var tf = ck_fn(g_al_target[al])
@ -250,9 +251,47 @@ function ck_call_named(e: Node, name: pointer) -> pointer {
mg_ref(e.a, g) # 0.S2: a call through a var of a function type reads it
return ck_call_sig(e, name, g.ty)
}
if is_math_builtin(name) { return ck_math(e, name) }
ck_walk_args(e)
return "?"
}
# Math.max(a, b), bare max(a, b): the emitter computes these inline, and their type is the one it
# gives them - a float or double argument first is that type (sign an int), else the fixed-point
# path: min, max, abs and clamp keep the first argument's type, the rounding and wrapping ones an
# int, the rest a fixed. Left "?", a Math.max(5, n) went into a string field and failed in LLVM.
function ck_math(e: Node, meth: pointer) -> pointer {
if len(e.kids) == 0 or e.kids[0].kind == E_FINIT {
ck_walk_args(e)
return "?"
}
let tys = new []pointer
var i = 0
while i < len(e.kids) {
push(tys, ck_expr(e.kids[i]))
i += 1
}
if ck_unknown(tys[0]) { return "?" }
if ck_is_float(tys[0]) {
if (meth == "sign") { return "int" }
var t = tys[0]
i = 1
while i < len(tys) {
if (tys[i] == "double") { t = "double" }
i += 1
}
return t
}
# a later float argument moves the emitter onto the float path only when it can see it
i = 1
while i < len(tys) {
if ck_unknown(tys[i]) or ck_is_float(tys[i]) { return "?" }
i += 1
}
if (meth == "min") or (meth == "max") or (meth == "abs") or (meth == "clamp") { return tys[0] }
if (meth == "sign") or (meth == "floor") or (meth == "ceil") or (meth == "round") { return "int" }
if (meth == "posmod") or (meth == "wrap") or (meth == "ping_pong") { return "int" }
return "fixed"
}
# emit E(field: v): each field gets the type the event declares for it
function ck_emit(s: Node) -> void {
let ev = find_event(s.s)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff