checker: a function named like a built-in a call always takes is refused at its declaration - function words(st, k) compiled and every call became words(n) with a pointer for n (invalid IR, far from the cause); the table is selfhost/check/check_builtins.ludic, emit_call's built-ins no find_fn guard lets a declared function take, and ludic-dev syntax --check holds it to emit_call both ways; every other built-in (buffer, floats, double, ...) yields to a declared function in the checker as it already did in codegen. string_temps' keep and cross_heap_test's keep renamed (hand, keep_cell); rejected/builtin_call_name and functions/builtin_yield with their cases (added, not run); reseeded, bootstrap-cfree fixpoint holds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-30 01:10:24 +03:00
parent 26b986de5b
commit d4df925b8f
13 changed files with 14830 additions and 13043 deletions

View file

@ -112,6 +112,7 @@ function selfhost_frags() -> []pointer {
push(f, "selfhost/check/check_ops.ludic")
push(f, "selfhost/check/check_call.ludic")
push(f, "selfhost/check/check_stmt.ludic")
push(f, "selfhost/check/check_builtins.ludic")
push(f, "selfhost/check/check_gen.ludic")
push(f, "selfhost/check/check_gen_call.ludic")
push(f, "selfhost/check/check_tynames.ludic")

View file

@ -422,6 +422,53 @@ function sx_parser() -> int {
return n
}
# ---- the built-ins a call always takes, held to emit_call ----
# every `name == "x"` on one of a function's top-level `if` lines (after head, to the next function);
# guarded skips the lines a declared function can take (`find_fn(`)
function sx_name_tests(text: pointer, head: pointer, guarded: bool) -> []pointer {
let out = new []pointer
let n = slen(text)
var inside = false
var i = 0
while i < n {
let line = line_at(text, i)
i = i + slen(line) + 1
if s_starts(line, "function ") { inside = s_starts(line, head) }
else if inside and s_starts(line, " if (") and not (guarded and s_contains(line, "find_fn(")) {
var at = s_index(line, "name == \"", 0)
while at >= 0 {
let a = at + 9
var b = a
while b < slen(line) and line[b] != '"' { b += 1 }
let w = line[a .. b]
if not set_has(out, w) { push(out, w) }
at = s_index(line, "name == \"", b)
}
}
}
return out
}
# selfhost/check/check_builtins.ludic's table against emit_call's unguarded built-ins, both ways
function sx_builtins() -> int {
let ec = read_file("selfhost/backend/emit_call.ludic")
let tb = read_file("selfhost/check/check_builtins.ludic")
if ec == null or tb == null { push(g_sx_prob, "cannot read emit_call.ludic or check_builtins.ludic"); return 1 }
let want = sx_name_tests(ec, "function emit_call(", true)
let have = sx_name_tests(tb, "function is_call_builtin(", false)
var n = 0
var i = 0
while i < len(want) {
if not set_has(have, want[i]) { push(g_sx_prob, `emit_call always takes {want[i]}(...), and check_builtins.ludic does not refuse a function of that name - add it`); n += 1 }
i += 1
}
i = 0
while i < len(have) {
if not set_has(want, have[i]) { push(g_sx_prob, `check_builtins.ludic refuses {have[i]}, which emit_call no longer always takes - drop it`); n += 1 }
i += 1
}
return n
}
# ---- the command ----
function sx_report(what: pointer) -> int {
if len(g_sx_prob) == 0 { return 0 }
@ -457,6 +504,7 @@ function cmd_syntax_gen() -> int {
while i < len(ts) { sx_carries(ts[i]); i += 1 }
sx_docs()
sx_parser()
sx_builtins()
return sx_report("the vocabulary and what is written from it disagree")
}
if changed == 0 { print(" every grammar already says what ludicc --emit-syntax does") }

View file

@ -1375,6 +1375,8 @@ function cmd_dev_test() -> int {
schema_case("lang/scenes", "\"scene\": \"Play\", \"layer\": \"Hud\"", "--emit-schema: code.handlers - a scene's handler, its scene and layer")
schema_case("state/write_reach", "{\"fn\": \"log_note\", \"module\": null, \"at\": \"examples/state/write_reach.ludic:21:37\", \"slot_kind\": \"registry\", \"slot\": \"Kinds.note\", \"entry\": \"box\"}", "--emit-schema: code.fn_refs - a fn in a registry row, the field and row it fills")
schema_case("modules/ports", "\"slot_kind\": \"port_default\", \"slot\": \"Clock.day\"", "--emit-schema: code.fn_refs - a port member's default")
diag_json_case("rejected/builtin_call_name", 2, "words is a built-in of the compiler, and a call to it would never reach this function; choose another name", "a function named like a built-in a call always takes (words, keep) is refused at its declaration")
feat_case("functions/builtin_yield", "", "ab 4", "builtin_yield.ludic (a built-in a declared function may take - buffer - yields to it in the checker as in codegen)")
diag_json_case("rejected/i18n_keys/key_missing", 2, "the key pause.gone is not in examples/rejected/i18n_keys/lang/en.po", "a key literal en.po does not have is refused, at its line and column")
diag_json_case("rejected/i18n_keys/key_missing", 2, "is a plural key, and examples/rejected/i18n_keys/lang/en.po:7 gives pause.resume no msgid_plural", "kn\"...\" is a plural key: en.po's entry has a msgid_plural")
diag_json_case("rejected/i18n_keys/key_missing", 2, "trf: hud.day is given 2 value(s), and its English in examples/rejected/i18n_keys/lang/en.po has 1 hole(s)", "trf given more values than the key's English has holes: a warning")