code wiki / (root) / nx_import_qualified_gate.nx

nx_import_qualified_gate.nx source

↩ module page · 665 lines · 35943 B

1// nx_import_qualified_gate.nx -- LN14 QUALIFIED IMPORTS: the referee for 2// runtime/nx_import.nx's `import "m.nx" as ns` / `ns::name` rung. 3// 4// WHAT IT PROVES, and the shape of the proof: 5// 6// 1. THE CENSUS IS MEASURED, NOT INHERITED. The matrix row this rung hangs off 7// cites "22 shadow collisions found 2026-06-23". That number is a 8// measurement with a date on it, so this gate RE-TAKES it every run over the 9// full population of runtime/ and runtime/_hdl_build/ (composing 10// nx_janitor_dupname, the organ that already owns that walk -- there is no 11// second ruler here) and prints what it finds, with the SHADOW/COPY/ 12// UNREADABLE partition reconciled against the total. 13// 14// 2. A REAL COLLISION FROM THAT CENSUS IS THE FIXTURE. The gate picks the first 15// collision whose two copies genuinely DIFFER and which declares at least one 16// top-level name in BOTH copies, then compiles two programs that import both 17// copies: one plainly, one with the _hdl_build copy qualified. Plain must 18// fail; qualified must build clean. The candidate is checked for solo 19// buildability FIRST, so a RED cannot be a red for some unrelated reason. 20// 21// 3. TWO REAL COMPILERS, NOT A SYNTHESISED MUTANT. Every fixture is built with 22// the CHALLENGER passed in argv AND with the live baseline 23// _offc/nx_cc_sovereign.elf. The qualified program must build under the 24// challenger and must be REFUSED by the baseline: that pair is the bite, and 25// neither half is available to a gate that only ever sees one compiler. 26// 27// 4. EVERY REFUSAL NAMES ITS RULE. The three neg-controls do not merely check 28// "was it refused" -- each greps the compiler's own stderr for the exact 29// rule token nx_import.nx emits (ALIAS-UNKNOWN-NAMESPACE, 30// ALIAS-NO-SUCH-EXPORT, ALIAS-MODULE-ALREADY-IMPORTED). A mutant that 31// disabled one check but still refused for a different reason passes a 32// "was it refused" suite and fails this one. 33// 34// 5. POSITIVE CONTROLS, BECAUSE A GUARD THAT REFUSES EVERYTHING PASSES EVERY 35// NEGATIVE TEST. An ordinary unaliased fixture must BUILD AND RUN under both 36// compilers, and the shipping resolver must still resolve every import edge 37// in the compiler's own closure, bound to the count of files it spliced. 38// Without those, a noexec mount or a missing assembler reads as a verdict 39// about the language. 40// 41// SCOPE STATED PLAINLY, so the next reader does not have to infer it: 42// * The fixture SOURCES are written into runtime/_hdl_build/ and not /tmp, 43// because the import resolver walks up from the importing file's directory 44// and a /tmp source can never reach runtime/nx_syscalls.nx -- a dialect fact 45// pinned by the diff-corpus lane, not a preference. They are written to 46// exactly ONE of the two censused directories, so the census is structurally 47// incapable of counting its own fixtures as a collision. Their content is 48// deterministic, so two concurrent runs write identical bytes. Every .s, 49// .log and .out goes to /tmp/nx_import_qualified_gate/ and every product ELF 50// to _build/ (NAS /tmp is noexec). 51// * When NO alias is declared anywhere in a closure, a stray `::` is left to 52// the tokenizer and produces a parse error rather than a named import 53// refusal. That is deliberate -- scanning every line of every unaliased 54// closure for `::` would charge the whole estate for a feature it is not 55// using -- and it is declared here rather than discovered later. 56// 57// Usage: nx_import_qualified_gate <challenger_cc_elf> 58// The gate CWD is buildroot; /api/build stages into the nishihost ROOT, so the 59// challenger is normally ../nx_compile_x86.sov.elf.new . THERE IS NO DEFAULT: 60// a silent default once made a sibling gate return ten confident verdicts about 61// a stale binary nobody had asked it to test. 62// 63// BITE RESULT, MEASURED 2026-08-25 (planted known-bad on the REAL subject): 64// nx_gate_bite cannot drive this gate. Its END-TO-END mode hands the rebuilt 65// SUBJECT to the gate as argv[1], so the only subject that type-checks here is 66// nx_compile_x86 -- and every mutant build of that DEPLOYS a mutant compiler to 67// the serving root while other seats are compiling through it. Measured, not 68// assumed: a bite with subject=this gate reported 69// `baseline: build_exit=0 gate_exit=3 green=0 ... UNCONTROLLED`, because a gate 70// binary is not a compiler and the gate correctly SKIPped. 71// So the mutation was driven by hand against a subject with ZERO blast radius: 72// imp_alias_exports (nx_import.nx) forced to `return 1`. It is unreachable for 73// any program without an `as` clause, so no other organ in the estate could be 74// affected. Result: 1 valid mutant, 1 KILLED, 0 not-reached -- 75// `export-scanner-...` went PASS -> FAIL and the verdict RED 14/15, while every 76// fork-based tooth stayed PASS because those run the UNMUTATED challenger. That 77// split is what attributes the kill to the subject rather than to the harness. 78// Restore proven by BYTES, not asserted: nx_import.nx returned to sha256 79// 9ba376726307bcb5ef633fdf11d127ef62ee788b0fe11bd6e2f6f65e725329a3 (52253 B) 80// and this gate rebuilt to the pre-mutation artifact e37648120ff44b69 exactly. 81// 82// license_tier: ORIGINAL Read-only outside its own fixtures (Rule 26). 83 84import "nx_syscalls.nx" 85import "nx_gate_verdict.nx" 86import "nx_ccbuild_lib.nx" 87import "nx_import.nx" 88import "nx_janitor_dupname.nx" 89 90// jdn_collect's OWN cap is 30000 entries; this must match it or the buffer it 91// writes into is smaller than the buffer it believes it has. 92const IQ_JDN_MAXK: i64 = 30000 93// POSIX NAME_MAX is 255, plus the NUL jdn_collect writes. Sound, not chosen. 94const IQ_NAME_MAX: i64 = 256 95// The same 4 MiB the compiler driver allocates for an expanded closure 96// (nx_compile_x86.nx NX_COMPILE_X86_EXPAND_CAP). Reused rather than re-guessed. 97const IQ_EXPAND_CAP: i64 = 4194304 98// Largest .nx in the tree is well under this; jdn_pair_differs needs one buffer 99// per side and reports -1 rather than truncating, which the partition counts. 100const IQ_PAIR_CAP: i64 = 4194304 101// No fixture below is more than ~20 short lines; the only variable part is a 102// single filesystem basename (IQ_NAME_MAX). 103const IQ_FIXTURE_CAP: i64 = 4096 104const IQ_PATH_CAP: i64 = 1024 105const IQ_MODE_644: i64 = 420 106const IQ_MODE_755: i64 = 493 107// A wait status carries the exit code in its second byte. 108const IQ_STATUS_SHIFT: i64 = 256 109const IQ_BYTE: i64 = 256 110// NOTE: the closure floor is NOT a constant here. The first draft of this gate 111// guessed 50 against a real closure of 30 and failed a resolver that was working 112// perfectly -- a hand-picked bound that is either unreachable or vacuous, and 113// that one was both. It is DERIVED at runtime from the root file's own declared 114// import edges; see the positive control in main(). 115 116func iq_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 117 118func iq_cat(d: *u8, o: i64, s: *u8) -> i64 { 119 var i: i64 = 0 120 while s[i] != (0 as u8) { d[o + i] = s[i]; i = i + 1 } 121 return o + i 122} 123 124func iq_catb(d: *u8, o: i64, b: i64) -> i64 { d[o] = b as u8; return o + 1 } 125 126// `import "<name>"` + newline. The quote byte is CONSTRUCTED rather than written 127// as an escape inside a literal: a bare escaped quote is the one construct this 128// dialect's lexer is least reliable about, and a fixture that fails to assemble 129// correctly is a gate that measures its own string handling. 130func iq_imp(d: *u8, o: i64, name: *u8) -> i64 { 131 var p: i64 = iq_cat(d, o, "import " as *u8) 132 p = iq_catb(d, p, 0x22) 133 p = iq_cat(d, p, name) 134 p = iq_catb(d, p, 0x22) 135 return iq_catb(d, p, 0x0A) 136} 137 138func iq_imp_as(d: *u8, o: i64, name: *u8, ns: *u8) -> i64 { 139 var p: i64 = iq_cat(d, o, "import " as *u8) 140 p = iq_catb(d, p, 0x22) 141 p = iq_cat(d, p, name) 142 p = iq_catb(d, p, 0x22) 143 p = iq_cat(d, p, " as " as *u8) 144 p = iq_cat(d, p, ns) 145 return iq_catb(d, p, 0x0A) 146} 147 148// Truncate-write. Returns bytes written, or negative. A fixture that failed to 149// land must be a PRECONDITION failure, never a silent pass on stale content from 150// a previous run. 151func iq_put(path: *u8, body: *u8, n: i64) -> i64 { 152 let fd: i64 = sys_openat_wr(path, IQ_MODE_644) 153 if fd < 0 { return 0 - 1 } 154 let w: i64 = sys_write(fd, body, n) 155 sys_close(fd) 156 if w != n { return 0 - 2 } 157 return n 158} 159 160// Does the file at `path` contain `needle`? Composes sys_read_file, which sizes 161// its buffer from the file and cannot short-read, so there is no cap to guess. 162func iq_contains(path: *u8, needle: *u8) -> i64 { 163 let lp: *u8 = sys_mmap(16) 164 let lo: *i64 = lp as *i64 165 *lo = 0 166 let buf: *u8 = sys_read_file(path, lo) 167 if buf == (0 as *u8) { return 0 } 168 let n: i64 = *lo 169 let nl: i64 = iq_slen(needle) 170 if nl == 0 { return 0 } 171 if n < nl { return 0 } 172 var i: i64 = 0 173 while i <= n - nl { 174 var j: i64 = 0 175 var eq: i64 = 1 176 while j < nl { 177 if buf[i + j] != needle[j] { eq = 0 } 178 j = j + 1 179 } 180 if eq == 1 { return 1 } 181 i = i + 1 182 } 183 return 0 184} 185 186func iq_join(out: *u8, a: *u8, b: *u8) -> i64 { 187 var p: i64 = iq_cat(out, 0, a) 188 p = iq_cat(out, p, b) 189 out[p] = 0 as u8 190 return p 191} 192 193func iq_exit_code(st: i64) -> i64 { 194 if st < 0 { return 0 - 1 } 195 return (st / IQ_STATUS_SHIFT) % IQ_BYTE 196} 197 198func iq_kv(k: *u8, v: i64) -> i64 { 199 gv_puts(" " as *u8) 200 gv_puts(k) 201 gv_puts("=" as *u8) 202 gv_num(v) 203 gv_puts("\n" as *u8) 204 return 0 205} 206 207// Compile <src> with <cc>, tagging every temp with <tag>. Returns cb_build's rc 208// (0 ok, 1 compile-fail, 2 asm-fail, 3 rename-fail). The compiler's stderr lands 209// in a REAL log so a refusal can be read for the rule it names, never /dev/null. 210func iq_build(cc: *u8, src: *u8, tag: *u8, envp: *i64, elf_out: *u8, cclog: *u8) -> i64 { 211 let s_tmp: *u8 = sys_mmap(IQ_PATH_CAP) 212 let a_tmp: *u8 = sys_mmap(IQ_PATH_CAP) 213 let a_log: *u8 = sys_mmap(IQ_PATH_CAP) 214 var p: i64 = iq_cat(s_tmp, 0, "/tmp/nx_import_qualified_gate/" as *u8) 215 p = iq_cat(s_tmp, p, tag) 216 p = iq_cat(s_tmp, p, ".s" as *u8) 217 s_tmp[p] = 0 as u8 218 p = iq_cat(a_tmp, 0, "_build/_nxiq_asm_" as *u8) 219 p = iq_cat(a_tmp, p, tag) 220 p = iq_cat(a_tmp, p, ".elf" as *u8) 221 a_tmp[p] = 0 as u8 222 p = iq_cat(a_log, 0, "/tmp/nx_import_qualified_gate/" as *u8) 223 p = iq_cat(a_log, p, tag) 224 p = iq_cat(a_log, p, ".asm.log" as *u8) 225 a_log[p] = 0 as u8 226 let lfd: i64 = sys_openat_wr(cclog, IQ_MODE_644) 227 let rc: i64 = cb_build(cc, src, s_tmp, elf_out, envp, lfd, a_tmp, a_log) 228 sys_close(lfd) 229 return rc 230} 231 232// How many top-level names does alias slot `k` share with alias slot `j`? 233// Composes the SHIPPING export scanner, so the census and the compiler cannot 234// disagree about what a module declares -- there is one definition of "export" 235// in this estate and it is nx_import.nx's. 236func iq_shared_syms(ctx: *ExpandCtx, k: i64, j: i64) -> i64 { 237 let ea: i64 = imp_alias_slot(ctx, k, AL_F_EXPADDR) 238 if ea == 0 { return 0 } 239 let eaddr: *i64 = ea as *i64 240 let elen: *i64 = imp_alias_slot(ctx, k, AL_F_EXPLEN) as *i64 241 let cnt: i64 = imp_alias_slot(ctx, k, AL_F_EXPN) 242 var shared: i64 = 0 243 var i: i64 = 0 244 while i < cnt { 245 if imp_alias_exports(ctx, j, eaddr[i] as *u8, 0, elen[i]) == 1 { shared = shared + 1 } 246 i = i + 1 247 } 248 return shared 249} 250 251func main(argc: i64, argv: *i64) -> i64 { 252 sys_ignore_sigpipe() 253 let ctr: *i64 = gv_ctr() 254 gv_head("nx_import_qualified_gate -- LN14 namespace-qualified imports: two organs may export one identifier" as *u8) 255 256 let have_tree: i64 = cb_anchor_root() 257 var challenger: *u8 = "\x00" as *u8 258 var given: i64 = 0 259 if argc >= 2 { challenger = argv[1] as *u8; given = 1 } 260 if given == 0 { 261 gv_puts(" NO CHALLENGER IN ARGV. This gate compares a CANDIDATE compiler against the live one and\n" as *u8) 262 gv_puts(" will not guess which candidate you mean: a silent default once made a sibling gate return\n" as *u8) 263 gv_puts(" ten confident verdicts about a stale binary nobody had asked it to test. Gate CWD is\n" as *u8) 264 gv_puts(" buildroot and /api/build stages into the nishihost ROOT, so pass ../nx_compile_x86.sov.elf.new\n" as *u8) 265 gv_puts(" MEASURED 2026-08-25: /api/gate_run does NOT forward arguments to the gate, so this gate can\n" as *u8) 266 gv_puts(" only ever report SKIP on that surface -- by construction, not by fault. Drive it with\n" as *u8) 267 gv_puts(" nx_job_run nx_import_qualified_gate /tmp/<out> <challenger>. A MISSING SUBJECT IS NOT A\n" as *u8) 268 gv_puts(" FAILING SUBJECT, so this path returns SKIP and never RED: a gate that is permanently red\n" as *u8) 269 gv_puts(" on a roster is one everybody learns to ignore.\n" as *u8) 270 } 271 let baseline: *u8 = "_offc/nx_cc_sovereign.elf\x00" as *u8 272 gv_puts(" challenger=" as *u8); gv_puts(challenger); gv_puts("\n" as *u8) 273 gv_puts(" baseline=" as *u8); gv_puts(baseline); gv_puts("\n\n" as *u8) 274 275 let envp: *i64 = sys_mmap(8 * 2) as *i64 276 envp[0] = "PATH=/usr/bin:/bin\x00" as *u8 as i64 277 envp[1] = 0 278 sys_mkdir("/tmp/nx_import_qualified_gate\x00" as *u8, IQ_MODE_755) 279 280 // ---- preconditions: I COULD NOT LOOK is not IT IS BROKEN ---------------- 281 var ok: i64 = 1 282 if gv_need("challenger-compiler-named-in-argv" as *u8, given, ctr) == 0 { ok = 0 } 283 if gv_need("buildroot-tree-reachable-from-cwd" as *u8, have_tree, ctr) == 0 { ok = 0 } 284 let fch: i64 = sys_openat_rd(challenger) 285 var have_ch: i64 = 0 286 if fch >= 0 { have_ch = 1; sys_close(fch) } 287 if gv_need("challenger-elf-readable" as *u8, have_ch, ctr) == 0 { ok = 0 } 288 let fbl: i64 = sys_openat_rd(baseline) 289 var have_bl: i64 = 0 290 if fbl >= 0 { have_bl = 1; sys_close(fbl) } 291 if gv_need("baseline-elf-readable" as *u8, have_bl, ctr) == 0 { ok = 0 } 292 let fas: i64 = sys_openat_rd("_offc/nxasm_x86_main.elf\x00" as *u8) 293 var have_as: i64 = 0 294 if fas >= 0 { have_as = 1; sys_close(fas) } 295 if gv_need("sovereign-assembler-present" as *u8, have_as, ctr) == 0 { ok = 0 } 296 if ok == 0 { 297 gv_puts("\n" as *u8) 298 sys_exit(gv_verdict("IMPORT-QUALIFIED" as *u8, ctr, "preconditions absent -- SKIP is not a pass" as *u8)) 299 } 300 301 // ---- 1. THE CENSUS, TAKEN BEFORE ANY FIXTURE EXISTS --------------------- 302 // Order matters: the fixtures are written after this point, so nothing this 303 // gate creates can be inside the population it reports. 304 let fnames: *u8 = sys_mmap(IQ_JDN_MAXK * IQ_NAME_MAX) 305 let fnoff_raw: *u8 = sys_mmap((IQ_JDN_MAXK + 2) * 8) 306 let fnoff: *i64 = fnoff_raw as *i64 307 let hc_raw: *u8 = sys_mmap(16) 308 let hc_out: *i64 = hc_raw as *i64 309 hc_out[0] = 0 310 let NF: i64 = jdn_scan(fnames, fnoff, hc_out) 311 let hcount: i64 = hc_out[0] 312 let ncoll: i64 = jdn_collisions(fnames, fnoff, hcount, NF) 313 gv_puts(" CENSUS runtime/_hdl_build vs runtime, re-measured this run:\n" as *u8) 314 iq_kv("hdl_build_nx_files" as *u8, hcount) 315 iq_kv("runtime_nx_files" as *u8, NF - hcount) 316 iq_kv("shadow_collisions" as *u8, ncoll) 317 iq_kv("corpus_complete" as *u8, 1) 318 319 // partition the collisions by CONTENT, and reconcile the parts to the total 320 let br: *u8 = sys_mmap(IQ_PAIR_CAP) 321 let bh: *u8 = sys_mmap(IQ_PAIR_CAP) 322 var c_differ: i64 = 0 323 var c_same: i64 = 0 324 var c_unread: i64 = 0 325 var ci: i64 = hcount 326 while ci < NF { 327 if jdn_is_shadowed(fnames, fnoff, ci, hcount) == 1 { 328 let nm: *u8 = (fnames as i64 + fnoff[ci]) as *u8 329 let d: i64 = jdn_pair_differs(nm, br, bh, IQ_PAIR_CAP) 330 if d == 1 { c_differ = c_differ + 1 } 331 if d == 0 { c_same = c_same + 1 } 332 if d < 0 { c_unread = c_unread + 1 } 333 } 334 ci = ci + 1 335 } 336 iq_kv("collisions_SHADOW_differing_bytes" as *u8, c_differ) 337 iq_kv("collisions_COPY_identical_bytes" as *u8, c_same) 338 iq_kv("collisions_UNREADABLE" as *u8, c_unread) 339 var partition_ok: i64 = 0 340 if c_differ + c_same + c_unread == ncoll { partition_ok = 1 } 341 gv_check("census-partition-SUMS-to-the-collision-total" as *u8, partition_ok, ctr) 342 343 // ---- 2. PICK A REAL COLLISION AS THE FIXTURE ---------------------------- 344 // Requirements, in the order they are cheap to test: the two copies must 345 // DIFFER (an identical copy collides with nothing), they must declare at 346 // least one top-level name in COMMON (that shared name is the whole point), 347 // and the runtime copy must compile on its own (so a later RED cannot be a 348 // red for an unrelated reason). 349 let realname: *u8 = sys_mmap(IQ_NAME_MAX) 350 realname[0] = 0 as u8 351 var real_shared: i64 = 0 352 var cand_tried: i64 = 0 353 var have_real: i64 = 0 354 let pr: *u8 = sys_mmap(IQ_PATH_CAP) 355 let ph: *u8 = sys_mmap(IQ_PATH_CAP) 356 let lp1: *i64 = sys_mmap(16) as *i64 357 let lp2: *i64 = sys_mmap(16) as *i64 358 let solo_body: *u8 = sys_mmap(IQ_FIXTURE_CAP) 359 let dotdot: *u8 = sys_mmap(IQ_PATH_CAP) 360 ci = hcount 361 while ci < NF { 362 if have_real == 0 { 363 if jdn_is_shadowed(fnames, fnoff, ci, hcount) == 1 { 364 let nm: *u8 = (fnames as i64 + fnoff[ci]) as *u8 365 if jdn_pair_differs(nm, br, bh, IQ_PAIR_CAP) == 1 { 366 cand_tried = cand_tried + 1 367 iq_join(pr, "runtime/\x00" as *u8, nm) 368 iq_join(ph, "runtime/_hdl_build/\x00" as *u8, nm) 369 lp1[0] = 0 370 lp2[0] = 0 371 let s1: *u8 = sys_read_file(pr, lp1) 372 let s2: *u8 = sys_read_file(ph, lp2) 373 if s1 != (0 as *u8) { if s2 != (0 as *u8) { 374 // A FRESH context per candidate: alias slots are 375 // write-once by design, so reusing one would refuse. 376 let cctx: *ExpandCtx = expand_ctx_new(sys_mmap(IQ_FIXTURE_CAP), IQ_FIXTURE_CAP) 377 let ka: i64 = imp_alias_add(cctx, "rt\x00" as *u8, 0, 2) 378 let kb: i64 = imp_alias_add(cctx, "hb\x00" as *u8, 0, 2) 379 if ka >= 0 { if kb >= 0 { 380 imp_alias_scan_exports(cctx, ka, s1, lp1[0]) 381 imp_alias_scan_exports(cctx, kb, s2, lp2[0]) 382 let sh: i64 = iq_shared_syms(cctx, ka, kb) 383 if sh > 0 { 384 // solo-buildability precondition 385 iq_join(dotdot, "../\x00" as *u8, nm) 386 var q: i64 = iq_imp(solo_body, 0, "nx_syscalls.nx\x00" as *u8) 387 q = iq_imp(solo_body, q, dotdot) 388 q = iq_cat(solo_body, q, "func main() -> i64 { sys_exit(0)\n return 0\n}\n" as *u8) 389 if iq_put("runtime/_hdl_build/_nxiq_real_solo.nx\x00" as *u8, solo_body, q) > 0 { 390 let srcb: i64 = iq_build(challenger, "runtime/_hdl_build/_nxiq_real_solo.nx\x00" as *u8, "solo\x00" as *u8, envp, "_build/_nxiq_solo.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/solo.cc.log\x00" as *u8) 391 if srcb == 0 { 392 var w: i64 = 0 393 while nm[w] != (0 as u8) { realname[w] = nm[w]; w = w + 1 } 394 realname[w] = 0 as u8 395 real_shared = sh 396 have_real = 1 397 } 398 } 399 } 400 } } 401 } } 402 } 403 } 404 } 405 ci = ci + 1 406 } 407 gv_puts(" REAL COLLISION FIXTURE:\n" as *u8) 408 gv_puts(" chosen=" as *u8) 409 if have_real == 1 { gv_puts(realname) } else { gv_puts("NONE" as *u8) } 410 gv_puts("\n" as *u8) 411 iq_kv("candidates_tried" as *u8, cand_tried) 412 iq_kv("shared_top_level_names_in_both_copies" as *u8, real_shared) 413 414 // ---- 3. WRITE THE SYNTHETIC FIXTURES ------------------------------------ 415 let b: *u8 = sys_mmap(IQ_FIXTURE_CAP) 416 var n: i64 = 0 417 var writes_ok: i64 = 1 418 419 n = iq_cat(b, 0, "func nxiq_collide(x: i64) -> i64 { return x + 101 }\nfunc nxiq_a_only(x: i64) -> i64 { return nxiq_collide(x) }\n" as *u8) 420 if iq_put("runtime/_hdl_build/_nxiq_a.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 421 422 n = iq_cat(b, 0, "func nxiq_collide(x: i64) -> i64 { return x + 202 }\nfunc nxiq_b_only(x: i64) -> i64 { return nxiq_collide(x) }\n" as *u8) 423 if iq_put("runtime/_hdl_build/_nxiq_b.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 424 425 // control: an ordinary unaliased program that must build and run everywhere 426 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 427 n = iq_imp(b, n, "_nxiq_a.nx\x00" as *u8) 428 n = iq_cat(b, n, "func main() -> i64 {\n var code: i64 = 0\n if nxiq_a_only(0) != 101 { code = 7 }\n sys_exit(code)\n return 0\n}\n" as *u8) 429 if iq_put("runtime/_hdl_build/_nxiq_ctl.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 430 431 // THE QUALIFIED PROGRAM: reaches BOTH definitions of one identifier. 432 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 433 n = iq_imp(b, n, "_nxiq_a.nx\x00" as *u8) 434 n = iq_imp_as(b, n, "_nxiq_b.nx\x00" as *u8, "bee\x00" as *u8) 435 n = iq_cat(b, n, "func main() -> i64 {\n let a: i64 = nxiq_collide(0)\n let bb: i64 = bee::nxiq_collide(0)\n let c: i64 = bee::nxiq_b_only(0)\n var code: i64 = 0\n if a != 101 { code = 1 }\n if bb != 202 { code = 2 }\n if c != 202 { code = 3 }\n sys_exit(code)\n return 0\n}\n" as *u8) 436 if iq_put("runtime/_hdl_build/_nxiq_qual.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 437 438 // THE SAME PROGRAM WITHOUT QUALIFICATION. If it builds at all, `a` and `bb` 439 // are one binding, so one of the two checks MUST fail: it cannot exit 0. 440 // That is structural, not a snapshot of today's resolution order. 441 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 442 n = iq_imp(b, n, "_nxiq_a.nx\x00" as *u8) 443 n = iq_imp(b, n, "_nxiq_b.nx\x00" as *u8) 444 n = iq_cat(b, n, "func main() -> i64 {\n let a: i64 = nxiq_collide(0)\n let bb: i64 = nxiq_collide(0)\n let c: i64 = nxiq_b_only(0)\n var code: i64 = 0\n if a != 101 { code = 1 }\n if bb != 202 { code = 2 }\n if c != 202 { code = 3 }\n sys_exit(code)\n return 0\n}\n" as *u8) 445 if iq_put("runtime/_hdl_build/_nxiq_plain.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 446 447 // the rename must REMOVE the unqualified spelling, not merely add one 448 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 449 n = iq_imp_as(b, n, "_nxiq_b.nx\x00" as *u8, "bee\x00" as *u8) 450 n = iq_cat(b, n, "func main() -> i64 {\n sys_exit(nxiq_b_only(0))\n return 0\n}\n" as *u8) 451 if iq_put("runtime/_hdl_build/_nxiq_strip.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 452 453 // neg-control 1: a namespace nobody declared 454 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 455 n = iq_imp_as(b, n, "_nxiq_b.nx\x00" as *u8, "bee\x00" as *u8) 456 n = iq_cat(b, n, "func main() -> i64 {\n sys_exit(zzz::nxiq_collide(0))\n return 0\n}\n" as *u8) 457 if iq_put("runtime/_hdl_build/_nxiq_unkns.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 458 459 // neg-control 2: a name that module does not declare 460 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 461 n = iq_imp_as(b, n, "_nxiq_b.nx\x00" as *u8, "bee\x00" as *u8) 462 n = iq_cat(b, n, "func main() -> i64 {\n sys_exit(bee::nxiq_not_declared(0))\n return 0\n}\n" as *u8) 463 if iq_put("runtime/_hdl_build/_nxiq_noexport.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 464 465 // neg-control 3: one module reached both plainly and under an alias 466 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 467 n = iq_imp(b, n, "_nxiq_b.nx\x00" as *u8) 468 n = iq_imp_as(b, n, "_nxiq_b.nx\x00" as *u8, "bee\x00" as *u8) 469 n = iq_cat(b, n, "func main() -> i64 {\n sys_exit(0)\n return 0\n}\n" as *u8) 470 if iq_put("runtime/_hdl_build/_nxiq_conflict.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 471 472 // the real pair, both ways 473 if have_real == 1 { 474 iq_join(dotdot, "../\x00" as *u8, realname) 475 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 476 n = iq_imp(b, n, dotdot) 477 n = iq_imp(b, n, realname) 478 n = iq_cat(b, n, "func main() -> i64 {\n sys_exit(0)\n return 0\n}\n" as *u8) 479 if iq_put("runtime/_hdl_build/_nxiq_real_plain.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 480 n = iq_imp(b, 0, "nx_syscalls.nx\x00" as *u8) 481 n = iq_imp(b, n, dotdot) 482 n = iq_imp_as(b, n, realname, "hb\x00" as *u8) 483 n = iq_cat(b, n, "func main() -> i64 {\n sys_exit(0)\n return 0\n}\n" as *u8) 484 if iq_put("runtime/_hdl_build/_nxiq_real_qual.nx\x00" as *u8, b, n) < 0 { writes_ok = 0 } 485 } 486 if gv_need("fixtures-written" as *u8, writes_ok, ctr) == 0 { 487 gv_puts("\n" as *u8) 488 sys_exit(gv_verdict("IMPORT-QUALIFIED" as *u8, ctr, "fixture tree not writable -- SKIP is not a pass" as *u8)) 489 } 490 491 // ---- 4. BUILD AND RUN --------------------------------------------------- 492 let ctl_ch: i64 = iq_build(challenger, "runtime/_hdl_build/_nxiq_ctl.nx\x00" as *u8, "ctlch\x00" as *u8, envp, "_build/_nxiq_ctl_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/ctlch.cc.log\x00" as *u8) 493 var ctl_ch_rc: i64 = 0 - 1 494 if ctl_ch == 0 { ctl_ch_rc = iq_exit_code(cb_run_capture("_build/_nxiq_ctl_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/ctlch.out\x00" as *u8, envp, 2)) } 495 let ctl_bl: i64 = iq_build(baseline, "runtime/_hdl_build/_nxiq_ctl.nx\x00" as *u8, "ctlbl\x00" as *u8, envp, "_build/_nxiq_ctl_bl.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/ctlbl.cc.log\x00" as *u8) 496 var ctl_bl_rc: i64 = 0 - 1 497 if ctl_bl == 0 { ctl_bl_rc = iq_exit_code(cb_run_capture("_build/_nxiq_ctl_bl.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/ctlbl.out\x00" as *u8, envp, 2)) } 498 499 let qual_ch: i64 = iq_build(challenger, "runtime/_hdl_build/_nxiq_qual.nx\x00" as *u8, "qualch\x00" as *u8, envp, "_build/_nxiq_qual_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/qualch.cc.log\x00" as *u8) 500 var qual_ch_rc: i64 = 0 - 1 501 if qual_ch == 0 { qual_ch_rc = iq_exit_code(cb_run_capture("_build/_nxiq_qual_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/qualch.out\x00" as *u8, envp, 2)) } 502 let qual_bl: i64 = iq_build(baseline, "runtime/_hdl_build/_nxiq_qual.nx\x00" as *u8, "qualbl\x00" as *u8, envp, "_build/_nxiq_qual_bl.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/qualbl.cc.log\x00" as *u8) 503 504 let plain_ch: i64 = iq_build(challenger, "runtime/_hdl_build/_nxiq_plain.nx\x00" as *u8, "plainch\x00" as *u8, envp, "_build/_nxiq_plain_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/plainch.cc.log\x00" as *u8) 505 var plain_ch_rc: i64 = 0 - 1 506 if plain_ch == 0 { plain_ch_rc = iq_exit_code(cb_run_capture("_build/_nxiq_plain_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/plainch.out\x00" as *u8, envp, 2)) } 507 508 let strip_ch: i64 = iq_build(challenger, "runtime/_hdl_build/_nxiq_strip.nx\x00" as *u8, "stripch\x00" as *u8, envp, "_build/_nxiq_strip_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/stripch.cc.log\x00" as *u8) 509 let unkns_ch: i64 = iq_build(challenger, "runtime/_hdl_build/_nxiq_unkns.nx\x00" as *u8, "unknsch\x00" as *u8, envp, "_build/_nxiq_unkns_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/unknsch.cc.log\x00" as *u8) 510 let noexp_ch: i64 = iq_build(challenger, "runtime/_hdl_build/_nxiq_noexport.nx\x00" as *u8, "noexpch\x00" as *u8, envp, "_build/_nxiq_noexp_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/noexpch.cc.log\x00" as *u8) 511 let confl_ch: i64 = iq_build(challenger, "runtime/_hdl_build/_nxiq_conflict.nx\x00" as *u8, "conflch\x00" as *u8, envp, "_build/_nxiq_confl_ch.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/conflch.cc.log\x00" as *u8) 512 513 var realp_ch: i64 = 0 - 1 514 var realq_ch: i64 = 0 - 1 515 if have_real == 1 { 516 realp_ch = iq_build(challenger, "runtime/_hdl_build/_nxiq_real_plain.nx\x00" as *u8, "realp\x00" as *u8, envp, "_build/_nxiq_realp.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/realp.cc.log\x00" as *u8) 517 realq_ch = iq_build(challenger, "runtime/_hdl_build/_nxiq_real_qual.nx\x00" as *u8, "realq\x00" as *u8, envp, "_build/_nxiq_realq.elf\x00" as *u8, "/tmp/nx_import_qualified_gate/realq.cc.log\x00" as *u8) 518 } 519 520 gv_puts(" MEASURED (build rc: 0 ok 1 compile-fail 2 asm-fail 3 rename-fail; run rc: -1 not run)\n" as *u8) 521 iq_kv("ctl_challenger_build" as *u8, ctl_ch) 522 iq_kv("ctl_challenger_run" as *u8, ctl_ch_rc) 523 iq_kv("ctl_baseline_build" as *u8, ctl_bl) 524 iq_kv("ctl_baseline_run" as *u8, ctl_bl_rc) 525 iq_kv("qualified_challenger_build" as *u8, qual_ch) 526 iq_kv("qualified_challenger_run" as *u8, qual_ch_rc) 527 iq_kv("qualified_baseline_build" as *u8, qual_bl) 528 iq_kv("unqualified_challenger_build" as *u8, plain_ch) 529 iq_kv("unqualified_challenger_run" as *u8, plain_ch_rc) 530 iq_kv("strip_probe_build" as *u8, strip_ch) 531 iq_kv("negctl_unknown_ns_build" as *u8, unkns_ch) 532 iq_kv("negctl_no_such_export_build" as *u8, noexp_ch) 533 iq_kv("negctl_alias_and_plain_build" as *u8, confl_ch) 534 iq_kv("real_pair_unqualified_build" as *u8, realp_ch) 535 iq_kv("real_pair_qualified_build" as *u8, realq_ch) 536 537 // ---- 5. THE POSITIVE CONTROL ON THE SHIPPING RESOLVER ------------------- 538 // Bound to its denominator: the count of DISTINCT files the expander actually 539 // resolved and spliced. A resolver that silently stopped resolving would 540 // either return a negative rc or splice a handful of files, and the floor 541 // catches the second case. This exercises the in-process copy of the new 542 // nx_import.nx; the forked builds above exercise the challenger's copy. 543 let obuf: *u8 = sys_mmap(IQ_EXPAND_CAP) 544 let ectx: *ExpandCtx = expand_ctx_new(obuf, IQ_EXPAND_CAP) 545 let erc: i64 = expand_imports(ectx, "runtime/nx_compile_x86.nx\x00" as *u8) 546 let enp: *i64 = ectx.n_paths 547 let efiles: i64 = enp[0] 548 // THE FLOOR IS DERIVED FROM THE ROOT ITSELF, using the SHIPPING predicate the 549 // preprocessor uses to recognise an import line -- so the control and the 550 // subject agree on what an import edge IS by construction. The closure must 551 // hold at least the root's own declared edges plus the root, and a resolver 552 // that quietly stopped resolving cannot clear that. 553 let rlp: *i64 = sys_mmap(16) as *i64 554 *rlp = 0 555 let rsrc: *u8 = sys_read_file("runtime/nx_compile_x86.nx\x00" as *u8, rlp) 556 var direct: i64 = 0 557 if rsrc != (0 as *u8) { 558 let rn: i64 = *rlp 559 var rp: i64 = 0 560 while rp < rn { 561 var at: i64 = 0 562 if rp == 0 { at = 1 } 563 if rp > 0 { if rsrc[rp-1] == 0x0A { at = 1 } } 564 if at == 1 { if starts_with_import(rsrc, rp) == 1 { direct = direct + 1 } } 565 rp = rp + 1 566 } 567 } 568 let edge_floor: i64 = direct + 1 569 iq_kv("compiler_closure_expand_rc" as *u8, erc) 570 iq_kv("compiler_closure_files_resolved" as *u8, efiles) 571 iq_kv("root_declared_import_edges" as *u8, direct) 572 iq_kv("derived_closure_floor" as *u8, edge_floor) 573 574 // ---- 6. THE EXPORT SCANNER, UNIT-LEVEL ---------------------------------- 575 let sb: *u8 = sys_mmap(IQ_FIXTURE_CAP) 576 var sn: i64 = iq_cat(sb, 0, "func alpha(x: i64) -> i64 { return x }\nconst beta: i64 = 3\nstruct gamma {\n delta: i64,\n}\nfunc main() -> i64 { return 0 }\n func indented_is_not_top_level() -> i64 { return 0 }\n" as *u8) 577 sb[sn] = 0 as u8 578 let uctx: *ExpandCtx = expand_ctx_new(sys_mmap(IQ_FIXTURE_CAP), IQ_FIXTURE_CAP) 579 let uk: i64 = imp_alias_add(uctx, "u\x00" as *u8, 0, 1) 580 let ucnt: i64 = imp_alias_scan_exports(uctx, uk, sb, sn) 581 var scan_ok: i64 = 0 582 if ucnt == 3 { 583 if imp_alias_exports(uctx, uk, "alpha\x00" as *u8, 0, 5) == 1 { 584 if imp_alias_exports(uctx, uk, "beta\x00" as *u8, 0, 4) == 1 { 585 if imp_alias_exports(uctx, uk, "gamma\x00" as *u8, 0, 5) == 1 { 586 if imp_alias_exports(uctx, uk, "main\x00" as *u8, 0, 4) == 0 { 587 if imp_alias_exports(uctx, uk, "delta\x00" as *u8, 0, 5) == 0 { scan_ok = 1 } 588 } 589 } 590 } 591 } 592 } 593 iq_kv("export_scanner_decls_found" as *u8, ucnt) 594 gv_puts("\n" as *u8) 595 596 // ---- 7. TEETH ----------------------------------------------------------- 597 var t: i64 = 0 598 599 t = 0 600 if ctl_ch == 0 { if ctl_ch_rc == 0 { t = 1 } } 601 gv_check("pos-control-ordinary-unaliased-program-BUILDS-AND-RUNS-under-challenger" as *u8, t, ctr) 602 603 t = 0 604 if ctl_bl == 0 { if ctl_bl_rc == 0 { t = 1 } } 605 gv_check("pos-control-same-program-BUILDS-AND-RUNS-under-baseline-so-a-RED-below-is-the-language-not-the-environment" as *u8, t, ctr) 606 607 t = 0 608 if erc == 0 { if efiles >= edge_floor { t = 1 } } 609 gv_check("pos-control-shipping-resolver-still-resolves-EVERY-import-edge-of-the-compiler-closure" as *u8, t, ctr) 610 611 gv_check("export-scanner-finds-func-const-struct-at-column-0-and-excludes-main-and-struct-fields" as *u8, scan_ok, ctr) 612 613 t = 0 614 if qual_ch == 0 { if qual_ch_rc == 0 { t = 1 } } 615 gv_check("qualified-program-reaches-BOTH-same-named-exports-with-the-right-values" as *u8, t, ctr) 616 617 t = 0 618 if plain_ch != 0 { t = 1 } 619 if plain_ch_rc != 0 { t = 1 } 620 gv_check("same-program-WITHOUT-qualification-cannot-reach-both-bindings" as *u8, t, ctr) 621 622 t = 0 623 if strip_ch != 0 { t = 1 } 624 gv_check("rename-REMOVES-the-unqualified-spelling-rather-than-adding-a-second-one" as *u8, t, ctr) 625 626 t = 0 627 if qual_bl != 0 { t = 1 } 628 gv_check("neg-control-BASELINE-compiler-refuses-the-qualified-program-so-the-green-above-is-the-change" as *u8, t, ctr) 629 630 t = 0 631 if unkns_ch != 0 { 632 if iq_contains("/tmp/nx_import_qualified_gate/unknsch.cc.log\x00" as *u8, "rule=ALIAS-UNKNOWN-NAMESPACE\x00" as *u8) == 1 { t = 1 } 633 } 634 gv_check("neg-control-undeclared-namespace-refused-and-the-log-NAMES-rule-ALIAS-UNKNOWN-NAMESPACE" as *u8, t, ctr) 635 636 t = 0 637 if noexp_ch != 0 { 638 if iq_contains("/tmp/nx_import_qualified_gate/noexpch.cc.log\x00" as *u8, "rule=ALIAS-NO-SUCH-EXPORT\x00" as *u8) == 1 { t = 1 } 639 } 640 gv_check("neg-control-name-the-module-does-not-declare-refused-and-the-log-NAMES-rule-ALIAS-NO-SUCH-EXPORT" as *u8, t, ctr) 641 642 t = 0 643 if confl_ch != 0 { 644 if iq_contains("/tmp/nx_import_qualified_gate/conflch.cc.log\x00" as *u8, "rule=ALIAS-MODULE-ALREADY-IMPORTED\x00" as *u8) == 1 { t = 1 } 645 } 646 gv_check("neg-control-module-imported-plainly-AND-aliased-refused-and-the-log-NAMES-rule-ALIAS-MODULE-ALREADY-IMPORTED" as *u8, t, ctr) 647 648 // The real-collision teeth abstain rather than acquit when the census found 649 // no usable candidate -- a population of zero is the absence of evidence 650 // about the subject, not a pass. 651 if gv_subjects("real shadow collisions usable as a fixture" as *u8, have_real, ctr) == 1 { 652 gv_check("fixture-reached-the-condition-both-copies-declare-a-shared-top-level-name" as *u8, 1, ctr) 653 t = 0 654 if realp_ch != 0 { t = 1 } 655 gv_check("REAL-census-collision-imported-both-ways-UNQUALIFIED-fails-to-build" as *u8, t, ctr) 656 t = 0 657 if realq_ch == 0 { t = 1 } 658 gv_check("REAL-census-collision-imported-both-ways-QUALIFIED-builds-clean" as *u8, t, ctr) 659 } 660 661 gv_puts("\n" as *u8) 662 // gv_verdict journals its own outcome; a second call here would double-count 663 // this gate in every harness-erosion query built on that journal. 664 return gv_verdict("IMPORT-QUALIFIED" as *u8, ctr, "challenger vs live baseline over a re-measured collision census" as *u8) 665}