// issue-0024: NSLog/foreign-side-effect calls placed as the FIRST statement // of an `if X { ... } else { ... }` branch body do not produce visible // output, even when the branch is provably taken (the SECOND statement in // the same body — also a foreign call — does produce output). // // ── Observed iOS-side symptom (session 59 bisect) ───────────────────────── // // In library/modules/gpu/metal.sx's `metal_create_texture_ios`: // // slot : TextureSlot = .{ tex = tex, bytes_per_pixel = bytes_per_pixel }; // self.textures.append(slot); // NSLog(ns_string("[metal] T6 appended\n".ptr)); // ← fires // // pixels_null := pixels == null; // if pixels_null { // NSLog(ns_string("[metal] T6b null\n".ptr)); // ← never fires // } else { // NSLog(ns_string("[metal] T6a non-null\n".ptr)); // ← never fires // handle : u32 = xx self.textures.len; // metal_update_texture_region_ios(self, handle, 0, 0, w, h, pixels); // // ← DOES fire // // (its first // // NSLog at // // fn entry // // appears in // // the unified // // log) // NSLog(ns_string("[metal] T7 done\n".ptr)); // ← (helper crashed // // before this) // } // // T6 appears in the iOS unified log. T6a/T6b never appear. The else // branch's helper call DOES fire (its own first-statement NSLog inside // the helper appears). So the else-branch IS entered; just its first // NSLog statement produces no output. // // ── Pure-sx repro below does NOT trigger ─────────────────────────────────── // // Running `sx run examples/issue-0024.sx` exits 0 (counter == 4 — all // bumps fired). The bug only manifests with foreign calls (NSLog / ns_string), // and possibly only when the process subsequently crashes (replaceRegion // in the metal.sx case) — which raises the alternative hypothesis that // the missing NSLog output is just iOS unified-logging buffer-loss on // process death, not a sx compiler bug. The runtime sequence between T6 // and the crash was ~500μs; logs within ~1ms of an unhandled exception // can be lost to OSLog's internal buffering on Apple Silicon iOS-sim. // // ── Investigation plan ───────────────────────────────────────────────────── // // Two paths to disambiguate: // 1. Replace NSLog markers with `write(STDERR_FILENO, ...)` calls // (synchronous, no OSLog involvement). If markers still don't appear: // sx compiler bug — likely in src/ir/lower.zig:2166-2196 (the // `is_value` branch of `lowerIfExpr` and downstream `lowerBlockValue` // around 922-948). Possible: side-effecting leading statements // dropped when branches are treated as values. // 2. If markers DO appear with synchronous write: the iOS-side symptom // is unified-logging buffer-loss, not a compiler bug. Close this issue // as "wontfix — diagnostic limitation" and move the iOS debugging to // foreign-write tracing. // // ── Real-world impact ────────────────────────────────────────────────────── // // Bisecting issue-0026 (replaceRegion crash) is currently blocked: without // trustworthy markers inside if/else branches we can't tell which arg // arrives wrong. Resolution unblocks step 3b of the Metal port. #import "modules/std.sx"; counter : s64 = 0; bump :: () { counter = counter + 1; } probe :: (skip: bool) { bump(); if skip { bump(); bump(); } else { bump(); bump(); } bump(); } main :: () -> s32 { probe(false); // counter == 4 (entry + 2 in false branch + exit) → exit 0 if counter == 4 then 0 else 1; }