diff --git a/src/ir/lower.test.zig b/src/ir/lower.test.zig index 9a364a5c..220f8c4c 100644 --- a/src/ir/lower.test.zig +++ b/src/ir/lower.test.zig @@ -1582,6 +1582,15 @@ test "lower: scan populates source-keyed caches per declaring source (E0)" { // migrating `#foreign` call sites until this is locked: a sample fn / global / // runtime-class written with `#foreign` must lower to byte-identical IR as the // same decl written with `extern`. This test is the gate — keep it green. +// +// Phase 5.0 POST-FLIP NOTE: the fn-decl and data-global `#foreign` parser paths now +// build the *same* extern-named AST that postfix `extern` produces (commits e5ddfbe +// global, 6b94bb6 fn-body), so cases 1/2/4 below are STRUCTURALLY identical at the +// AST level — equivalence is guaranteed by construction, not coincidence. The test +// stays as a regression tripwire: if a future change re-diverges the two spellings +// (a reader that branches on `foreign_expr` structurally, or a revert of the flip), +// this gate catches it. Case 3 (runtime class) was always coalesced onto the single +// `is_foreign_eff` field, so it is behaviorally — not structurally — equal. /// Lower a single self-contained `.sx` source (no stdlib imports) all the way to /// the printed module IR text. Mirrors the full pipeline in the fix-0102b tests @@ -1694,6 +1703,21 @@ test "lower: GATE A→B — #foreign and extern/export lower to identical IR" { try std.testing.expectEqualStrings(foreign_ir, extern_ir); } + // 2b. FUNCTION RENAME — sx name bound to a different C symbol (`extern_name` axis). + { + const foreign_ir = try lowerSrcToIr(alloc, io, + \\c_abs :: (a: i32) -> i32 #foreign "abs"; + \\main :: () -> i32 { c_abs(-7) } + \\ + ); + const extern_ir = try lowerSrcToIr(alloc, io, + \\c_abs :: (a: i32) -> i32 extern "abs"; + \\main :: () -> i32 { c_abs(-7) } + \\ + ); + try std.testing.expectEqualStrings(foreign_ir, extern_ir); + } + // 3. RUNTIME CLASS — Obj-C reference (import) class with dispatch. { const foreign_ir = try lowerSrcToIr(alloc, io,