diff --git a/lang/src/compiler/entry/compiler_stageb.hako b/lang/src/compiler/entry/compiler_stageb.hako index 5a15bed2..9ab81bb4 100644 --- a/lang/src/compiler/entry/compiler_stageb.hako +++ b/lang/src/compiler/entry/compiler_stageb.hako @@ -4,6 +4,15 @@ using lang.compiler.parser.box as ParserBox using lang.compiler.pipeline_v2.flow_entry as FlowEntryBox static box StageBMain { + _fallback_enabled() { + // Default ON; set HAKO_STAGEB_ALLOW_FALLBACK=0 (or NYASH_STAGEB_ALLOW_FALLBACK=0) to disable + local v = env.local.get("HAKO_STAGEB_ALLOW_FALLBACK") + if v == null { v = env.local.get("NYASH_STAGEB_ALLOW_FALLBACK") } + if v == null { return 1 } + local s = "" + v + if s == "0" or s == "false" or s == "off" { return 0 } + return 1 + } _fallback_program() { return "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}" } @@ -79,7 +88,12 @@ static box StageBMain { jv0 = FlowEntryBox.emit_v0_from_ast(ast_json, prefer) } if jv0 == null || jv0 == "" { - jv0 = me._fallback_program() + if me._fallback_enabled() == 1 { + jv0 = me._fallback_program() + } else { + // Return empty to surface failure at caller; TTL: enable stricter failure once all paths are green + jv0 = "" + } } return jv0 } @@ -91,7 +105,7 @@ static box StageBMain { local stage3 = flags.stage3 local v1_compat = flags.v1_compat local json = me._do_compile_stage_b(src, prefer, stage3, v1_compat) - if json == null || json == "" { json = me._fallback_program() } + if (json == null || json == "") && me._fallback_enabled() == 1 { json = me._fallback_program() } print(json) return 0 } diff --git a/src/runner/child_env.rs b/src/runner/child_env.rs new file mode 100644 index 00000000..62d0f7ba --- /dev/null +++ b/src/runner/child_env.rs @@ -0,0 +1,21 @@ +/*! + * child_env.rs — Runner helper utilities (OOB strict, quiet exit policies) + */ + +pub fn pre_run_reset_oob_if_strict() { + if crate::config::env::oob_strict_fail() { + crate::runtime::observe::reset(); + } +} + +pub fn post_run_exit_if_oob_strict_triggered() -> ! { + if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() { + eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)"); + std::process::exit(1); + } + // If not strict or no OOB, return to caller path; caller should exit(…) itself. + // This function is defined as diverging only when it actually exits above; otherwise it does nothing. + // To keep signature simple for callers, they should not rely on this returning. + std::process::exit(0) +} + diff --git a/src/runner/pipe_io.rs b/src/runner/pipe_io.rs index 63300eea..b2746997 100644 --- a/src/runner/pipe_io.rs +++ b/src/runner/pipe_io.rs @@ -10,6 +10,7 @@ */ use super::*; +use crate::runner::child_env; impl NyashRunner { /// Try to handle `--ny-parser-pipe` / `--json-file` flow. @@ -47,7 +48,7 @@ impl NyashRunner { Ok(Some(module)) => { super::json_v0_bridge::maybe_dump_mir(&module); // Gate‑C(Core) strict OOB fail‑fast: reset observe flag before run - if crate::config::env::oob_strict_fail() { crate::runtime::observe::reset(); } + child_env::pre_run_reset_oob_if_strict(); let rc = self.execute_mir_module_quiet_exit(&module); if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() { eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)"); @@ -120,7 +121,7 @@ impl NyashRunner { } } // Default: Execute via MIR interpreter (quiet) and exit with rc mirrored from return value - if crate::config::env::oob_strict_fail() { crate::runtime::observe::reset(); } + child_env::pre_run_reset_oob_if_strict(); let rc = self.execute_mir_module_quiet_exit(&module); if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() { eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)"); diff --git a/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_fail_vm.sh b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_fail_vm.sh index bc29f9ff..aed86652 100644 --- a/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_fail_vm.sh +++ b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_fail_vm.sh @@ -4,13 +4,15 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -ROOT="$(cd "$SCRIPT_DIR/../../../../../.." && pwd)" +if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then + ROOT="$ROOT_GIT" +else + ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)" +fi source "$ROOT/tools/smokes/v2/lib/test_runner.sh" require_env || exit 2 -if [ "${SMOKES_ENABLE_BRIDGE_CANON:-0}" != "1" ]; then - test_skip "bridge_canonicalize_fail" "opt-in (set SMOKES_ENABLE_BRIDGE_CANON=1)" && exit 0 -fi +# Default-on: bridge canonicalize fail # v1 JSON with unsupported instruction to assert stable failure message json_path="/tmp/ny_v1_fail_$$.json" @@ -32,4 +34,3 @@ else echo "$output" >&2 exit 1 fi - diff --git a/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_off_vm.sh b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_off_vm.sh index a27e213e..0892d043 100644 --- a/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_off_vm.sh +++ b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_off_vm.sh @@ -4,13 +4,15 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -ROOT="$(cd "$SCRIPT_DIR/../../../../../.." && pwd)" +if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then + ROOT="$ROOT_GIT" +else + ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)" +fi source "$ROOT/tools/smokes/v2/lib/test_runner.sh" require_env || exit 2 -if [ "${SMOKES_ENABLE_BRIDGE_CANON:-0}" != "1" ]; then - test_skip "bridge_canonicalize_off" "opt-in (set SMOKES_ENABLE_BRIDGE_CANON=1)" && exit 0 -fi +# Default-on: bridge canonicalize off # Same v1 minimal JSON (const+ret) without toggles should still run (no mir_call involved) json_path="/tmp/ny_v1_const_off_$$.json" @@ -28,4 +30,3 @@ else echo "[FAIL] bridge_canonicalize_off: expected rc=0, got $rc" >&2 exit 1 fi - diff --git a/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_on_vm.sh b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_on_vm.sh index 992a4cdb..3c36f52b 100644 --- a/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_on_vm.sh +++ b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_on_vm.sh @@ -4,13 +4,15 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -ROOT="$(cd "$SCRIPT_DIR/../../../../../.." && pwd)" +if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then + ROOT="$ROOT_GIT" +else + ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)" +fi source "$ROOT/tools/smokes/v2/lib/test_runner.sh" require_env || exit 2 -if [ "${SMOKES_ENABLE_BRIDGE_CANON:-0}" != "1" ]; then - test_skip "bridge_canonicalize_on" "opt-in (set SMOKES_ENABLE_BRIDGE_CANON=1)" && exit 0 -fi +# Default-on: bridge canonicalize on # Minimal v1 JSON with only const/copy/ret (no mir_call), should run regardless json_path="/tmp/ny_v1_const_$$.json" @@ -29,4 +31,3 @@ else echo "[FAIL] bridge_canonicalize_on: expected rc=0, got $rc" >&2 exit 1 fi - diff --git a/tools/smokes/v2/profiles/quick/core/map/map_len_set_get_vm.sh b/tools/smokes/v2/profiles/quick/core/map/map_len_set_get_vm.sh new file mode 100644 index 00000000..8ed5b17a --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/map/map_len_set_get_vm.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# map_len_set_get_vm.sh — MapBox len/set/get sequence sanity + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then + ROOT="$ROOT_GIT" +else + ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)" +fi +source "$ROOT/tools/smokes/v2/lib/test_runner.sh" +require_env || exit 2 + +code='static box Main { main() { + local m = new MapBox() + print("" + m.size()) + m.set("a", 1) + print("" + m.size()) + m.set("b", 2) + print("" + m.size()) + print("" + m.get("a")) + print("" + m.get("b")) + return 0 +} }' + +output=$(run_nyash_vm -c "$code" --dev) +expected=$'0\n1\n2\n1\n2' + +if [ "$output" = "$expected" ]; then + echo "[PASS] map_len_set_get_vm" + exit 0 +else + echo "[FAIL] map_len_set_get_vm" >&2 + echo "--- expected ---" >&2 + echo "$expected" >&2 + echo "--- actual ---" >&2 + echo "$output" >&2 + exit 1 +fi + diff --git a/tools/smokes/v2/profiles/quick/core/string/index_substring_vm.sh b/tools/smokes/v2/profiles/quick/core/string/index_substring_vm.sh new file mode 100644 index 00000000..1ab062b1 --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/string/index_substring_vm.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# index_substring_vm.sh — String.indexOf/substring boundary behavior + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then + ROOT="$ROOT_GIT" +else + ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)" +fi +source "$ROOT/tools/smokes/v2/lib/test_runner.sh" +require_env || exit 2 + +code='static box Main { main() { + local s = "hello" + print("" + s.indexOf("l")) // 2 + print("" + s.indexOf("z")) // -1 + print(s.substring(0, 2)) // he + print(s.substring(2, 5)) // llo + print("" + s.substring(5, 5).length()) // 0 + return 0 +} }' + +output=$(run_nyash_vm -c "$code" --dev) +expected=$'2\n-1\nhe\nllo\n0' + +if [ "$output" = "$expected" ]; then + echo "[PASS] string_index_substring_vm" + exit 0 +else + echo "[FAIL] string_index_substring_vm" >&2 + echo "--- expected ---" >&2 + echo "$expected" >&2 + echo "--- actual ---" >&2 + echo "$output" >&2 + exit 1 +fi