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}