code wiki / (root) / nx_closure_gate.nx

nx_closure_gate.nx source

↩ module page · 197 lines · 11072 B

1// nx_closure_gate.nx -- THE REFEREE FOR LN13b: the non-capturing function literal. 2// 3// WHAT IS ACTUALLY AT RISK HERE, AND WHY THE TEETH ARE SHAPED THIS WAY. Admitting a new construct 4// is easy to observe: the compiler stops refusing it. That is the LEAST interesting thing this 5// gate could measure, because the pre-fix compiler refused the construct outright and any check of 6// compile status would go green the moment the parser stopped erroring -- including on an 7// implementation that lifted the wrong body. The failures worth guarding survive a clean compile: 8// a literal bound to the wrong code address, a parameter read from the wrong frame slot, or a 9// synthesized fall-off-the-end return carrying value id 0, which IS the first parameter. None of 10// those change the compiler's exit status; all of them change what the compiled program COMPUTES. 11// 12// SO THE BEHAVIOUR AXIS IS A RUN, NOT AN ASSERTION ABOUT ASSEMBLY. nx_probe_closure_lift is a 13// self-checking program: it returns 0 only if every arithmetic identity in it held, and a distinct 14// non-zero code otherwise, so a failure names itself. This gate reads that exit code. It does NOT 15// inspect emitted assembly -- an asm-shaped tooth passes on a compiler that emits the right 16// instructions in the wrong order and fails on a compiler that improves its register allocation. 17// 18// AND THE REFUSAL AXIS IS HALF THE RUNG. Rung 1 admits the EMPTY capture set and defers capture to 19// LN13c, so a literal reading a local of the function around it must be refused BY NAME. Without 20// that, the rung would silently compile a name that resolves to nothing, and this parser answers 21// an unresolved name with the CONSTANT ZERO -- a wrong number with no diagnostic. The refusal is 22// therefore not politeness; it is the only honest answer available at this rung, and the tooth 23// asserts the offending NAME appears in the message, not merely that something was refused. 24// 25// THE DISCRIMINATION CONTROL IS LOAD-BEARING. A compiler that refused every func-typed construct 26// would pass the capture tooth perfectly. nx_probe_closure_named uses the PRE-EXISTING bare-name 27// and &name paths through the same func-typed slot; it must still compile and still run to 0. If 28// only that tooth fails, the lift broke a path that shipped long before it. 29// license_tier: ORIGINAL. Forks the compiler under test; writes only under its own /tmp dir. 30// No hw writes (Rule 26). 31import "nx_syscalls.nx" 32import "nx_gate_verdict.nx" 33import "nx_gatekit_lib.nx" 34import "nx_tool_run.nx" 35 36const CG_CAP: i64 = 262144 37const CG_PATH: i64 = 4096 38const CG_SPAN: i64 = 8 39const CG_ARGV: i64 = 4 40// The compiler under test. argv[1] overrides, so this gate can judge a STAGED build before it is 41// promoted -- the nx_cc_equiv_gate / nx_langdiag_gate shape. 42const CG_SUBJ_DEFAULT: *u8 = "_offc/nx_cc_sovereign.elf" 43const CG_ASM_A: *u8 = "_offc/nxasm_x86_main.elf" 44const CG_ASM_B: *u8 = "_offc/nxasm_x86.elf" 45const CG_P_LIFT: *u8 = "runtime/nx_probe_closure_lift.nx" 46const CG_P_CAP: *u8 = "runtime/nx_probe_closure_capture.nx" 47const CG_P_NAMED: *u8 = "runtime/nx_probe_closure_named.nx" 48const CG_EXIT_UNRESOLVED: i64 = 3 49const CG_MODE_0755: i64 = 493 50 51func cg_exists(p: *u8) -> i64 { 52 let fd: i64 = sys_openat_rd(p) 53 if fd < 0 { return 0 } 54 sys_close(fd) 55 return 1 56} 57 58// Compile `src` with `subject`. THE ASSEMBLY GOES TO A FILE AND THE DIAGNOSTICS TO A SEPARATE ONE: 59// this compiler writes its artifact on stdout and its commentary on stderr, so capturing them 60// merged and calling the result a .s file produces a source with progress output glued to the 61// front. The assembler then rejects it and blames the SOURCE -- a plumbing mistake wearing a 62// compiler bug's clothes. tr_run_redirect keeps the two apart; `buf` receives the diagnostics, 63// which is what every message tooth below scans. 64func cg_compile(subject: *u8, src: *u8, out_s: *u8, err_p: *u8, buf: *u8, ln: *i64) -> i64 { 65 let av: *i64 = sys_mmap(CG_SPAN * CG_ARGV) as *i64 66 av[0] = subject as i64 67 av[1] = src as i64 68 av[2] = 0 69 let rc: i64 = tr_run_redirect(subject, av, out_s, err_p) 70 let n: i64 = gk_read(err_p, buf, CG_CAP - 1) 71 var m: i64 = n 72 if m < 0 { m = 0 } 73 buf[m] = 0 as u8 74 ln[0] = gk_size(out_s) 75 return rc 76} 77 78func main(argc: i64, argv: **u8) -> i64 { 79 let ctr: *i64 = gv_ctr() 80 gv_head("NX-CLOSURE-GATE -- LN13b: the non-capturing function literal" as *u8) 81 82 var subject: *u8 = CG_SUBJ_DEFAULT 83 if argc > 1 { subject = argv[1] as *u8 } 84 85 // ---- PRECONDITIONS. A missing subject or probe means NOTHING WAS MEASURED, which must never 86 // be reported in the same word as a failure of the subject. 87 gv_need("subject-compiler-present" as *u8, cg_exists(subject), ctr) 88 gv_need("probe-lift-present" as *u8, cg_exists(CG_P_LIFT), ctr) 89 gv_need("probe-capture-present" as *u8, cg_exists(CG_P_CAP), ctr) 90 gv_need("probe-named-present" as *u8, cg_exists(CG_P_NAMED), ctr) 91 92 var asmp: *u8 = CG_ASM_A 93 if cg_exists(asmp) == 0 { asmp = CG_ASM_B } 94 gv_need("assembler-present" as *u8, cg_exists(asmp), ctr) 95 96 let dir: *u8 = sys_mmap(CG_PATH) 97 gk_fixture_dir("nx_closure_gate" as *u8, dir) 98 let s_lift: *u8 = sys_mmap(CG_PATH) 99 let s_named: *u8 = sys_mmap(CG_PATH) 100 let e_lift: *u8 = sys_mmap(CG_PATH) 101 let e_named: *u8 = sys_mmap(CG_PATH) 102 gk_join(s_lift, dir, "lift.s" as *u8) 103 gk_join(s_named, dir, "named.s" as *u8) 104 gk_join(e_lift, dir, "lift.elf" as *u8) 105 gk_join(e_named, dir, "named.elf" as *u8) 106 107 let buf: *u8 = sys_mmap(CG_CAP) 108 let cbuf: *u8 = sys_mmap(CG_CAP) 109 let ln: *i64 = sys_mmap(CG_SPAN) as *i64 110 111 // ---- THE LIFT COMPILES ---------------------------------------------------------------- 112 let d_lift: *u8 = sys_mmap(CG_PATH) 113 gk_join(d_lift, dir, "lift.err" as *u8) 114 let rc_lift: i64 = cg_compile(subject, CG_P_LIFT, s_lift, d_lift, buf, ln) 115 let n_lift: i64 = ln[0] 116 gv_check_eq("literal-compiles" as *u8, rc_lift, 0, ctr) 117 gv_check("literal-emits-assembly-not-an-empty-file" as *u8, 118 gk_contains(s_lift, "att_syntax" as *u8), ctr) 119 120 // ---- THE LIFT RUNS AND COMPUTES. This is the tooth the whole gate exists for: the probe's 121 // exit code is 0 only if a literal called through a local, two distinct literals, parameter 122 // order, the six- and seven-argument frames, and a literal crossing a call boundary all held. 123 let av2: *i64 = sys_mmap(CG_SPAN * CG_ARGV) as *i64 124 av2[0] = asmp as i64 125 av2[1] = s_lift as i64 126 av2[2] = e_lift as i64 127 av2[3] = 0 128 let arc: i64 = tr_run_capture(asmp, av2, cbuf, CG_CAP - 1, ln) 129 gv_check_eq("literal-assembles-to-an-elf" as *u8, arc, 0, ctr) 130 gv_check("assembled-artifact-exists" as *u8, cg_exists(e_lift), ctr) 131 // A BYTE-PERFECT ELF WITHOUT +x IS INERT, and forking it yields zero bytes and rc=127 -- which 132 // reads as a broken COMPILER rather than a broken fixture. The assembler leaves the mode to 133 // whatever its open() gave it, so the gate must set it. Measured 2026-09-04: development /tmp 134 // tolerated the omission and the estate host did not, i.e. it passed exactly where it did not 135 // matter and failed exactly where it did. 136 sys_fchmodat(e_lift, CG_MODE_0755) 137 let av3: *i64 = sys_mmap(CG_SPAN * CG_ARGV) as *i64 138 av3[0] = e_lift as i64 139 av3[1] = 0 140 let run_rc: i64 = tr_run_capture(e_lift, av3, cbuf, CG_CAP - 1, ln) 141 gv_check_eq("BEHAVIOUR-every-arithmetic-identity-in-the-probe-held" as *u8, run_rc, 0, ctr) 142 143 // ---- CAPTURE IS REFUSED, BY NAME --------------------------------------------------------- 144 let s_cap: *u8 = sys_mmap(CG_PATH) 145 gk_join(s_cap, dir, "capture.s" as *u8) 146 let d_cap: *u8 = sys_mmap(CG_PATH) 147 gk_join(d_cap, dir, "capture.err" as *u8) 148 let rc_cap: i64 = cg_compile(subject, CG_P_CAP, s_cap, d_cap, buf, ln) 149 gv_check("neg-control-capture-is-refused-not-compiled" as *u8, (rc_cap != 0) as i64, ctr) 150 gv_check("neg-control-refusal-names-the-captured-variable-n" as *u8, 151 gk_has(buf, "reads `n` from the function around it" as *u8), ctr) 152 gv_check("neg-control-refusal-says-CAPTURE-in-words" as *u8, 153 gk_has(buf, "which is a CAPTURE" as *u8), ctr) 154 gv_check("neg-control-refusal-carries-its-capability-slug" as *u8, 155 gk_has(buf, "capability=closure-capture" as *u8), ctr) 156 // A refusal must emit NO program. A compiler that printed a diagnostic and an object anyway 157 // would satisfy every check above while still shipping the miscompile. 158 gv_check("neg-control-refused-capture-emits-no-assembly" as *u8, 159 (gk_contains(s_cap, "att_syntax" as *u8) == 0) as i64, ctr) 160 161 // ---- DISCRIMINATION: the compiler must not simply refuse everything ---------------------- 162 let d_named: *u8 = sys_mmap(CG_PATH) 163 gk_join(d_named, dir, "named.err" as *u8) 164 let rc_named: i64 = cg_compile(subject, CG_P_NAMED, s_named, d_named, buf, ln) 165 let n_named: i64 = ln[0] 166 gv_check_eq("neg-control-named-function-through-a-func-slot-still-compiles" as *u8, rc_named, 0, ctr) 167 gv_check("neg-control-named-path-emits-assembly" as *u8, 168 gk_contains(s_named, "att_syntax" as *u8), ctr) 169 gv_check("neg-control-named-path-is-NOT-refused-as-a-capture" as *u8, 170 (gk_has(buf, "which is a CAPTURE" as *u8) == 0) as i64, ctr) 171 av2[1] = s_named as i64 172 av2[2] = e_named as i64 173 let arc2: i64 = tr_run_capture(asmp, av2, cbuf, CG_CAP - 1, ln) 174 gv_check_eq("neg-control-named-path-assembles" as *u8, arc2, 0, ctr) 175 sys_fchmodat(e_named, CG_MODE_0755) 176 av3[0] = e_named as i64 177 let run2: i64 = tr_run_capture(e_named, av3, cbuf, CG_CAP - 1, ln) 178 gv_check_eq("neg-control-named-path-still-RUNS-correctly" as *u8, run2, 0, ctr) 179 180 // ---- ANTI-VACUITY. Every "does not contain" tooth above passes for free on an empty capture, 181 // and a compiler that emitted nothing at all would score them all. Bind the denominator. 182 gv_check("outputs-are-non-empty-so-the-absence-teeth-are-not-vacuous" as *u8, 183 (n_lift > 0) as i64, ctr) 184 gv_check("named-control-output-non-empty" as *u8, (n_named > 0) as i64, ctr) 185 186 gv_values_head() 187 gv_kv("lift_compile_rc" as *u8, rc_lift) 188 gv_kv("lift_run_exit" as *u8, run_rc) 189 gv_kv("capture_compile_rc" as *u8, rc_cap) 190 gv_kv("named_compile_rc" as *u8, rc_named) 191 gv_kv("named_run_exit" as *u8, run2) 192 gv_kv("lift_output_bytes" as *u8, n_lift) 193 gv_kv("named_output_bytes" as *u8, n_named) 194 195 return gv_verdict("CLOSURE-GATE" as *u8, ctr, 196 "a non-capturing function literal compiles, assembles and RUNS to a self-checking exit 0 covering the lift, two distinct literals, parameter order, the six- and seven-argument frames and a literal crossing a call boundary; a literal reading an enclosing local is refused by name with its capability slug and emits no program; and the pre-existing named and &name paths through the same func-typed slot still compile and still run, so the refusal discriminates rather than blocking everything" as *u8) 197}