diff --git a/lang/src/vm/README.md b/lang/src/vm/README.md index 396999b1..a8dcf361 100644 --- a/lang/src/vm/README.md +++ b/lang/src/vm/README.md @@ -96,6 +96,8 @@ Diagnostics (stable tags) - `[core/mir_call] map set missing key|bad key|missing value|bad value` - `[core/mir_call] map get missing key|bad key` - `[core/mir_call] unsupported callee type: Closure` + - `[map/missing] Key not found: `(VM MapBox.get の既定タグ。従来文言を含むため後方互換) + - `[array/empty/pop] empty array`(VM ArrayBox.pop 空時。strict環境(HAKO_OOB_STRICT=1)で有効) - Gate‑C Direct では、リーダー/検証レイヤの診断をそのまま用いる(例: `unsupported callee type (expected Extern): ModuleFunction`)。 Strict OOB policy (Gate‑C) diff --git a/src/backend/mir_interpreter/handlers/boxes_array.rs b/src/backend/mir_interpreter/handlers/boxes_array.rs index 5b61027c..33490993 100644 --- a/src/backend/mir_interpreter/handlers/boxes_array.rs +++ b/src/backend/mir_interpreter/handlers/boxes_array.rs @@ -30,6 +30,12 @@ pub(super) fn try_handle_array_box( if let Some(d) = dst { this.regs.insert(d, VMValue::Void); } return Ok(true); } + "pop" => { + if !args.is_empty() { return Err(VMError::InvalidInstruction("pop expects 0 args".into())); } + let ret = ab.pop(); + if let Some(d) = dst { this.regs.insert(d, VMValue::from_nyash_box(ret)); } + return Ok(true); + } "len" | "length" | "size" => { let ret = ab.length(); if let Some(d) = dst { this.regs.insert(d, VMValue::from_nyash_box(ret)); } diff --git a/src/boxes/array/mod.rs b/src/boxes/array/mod.rs index 01063f19..49d6bad5 100644 --- a/src/boxes/array/mod.rs +++ b/src/boxes/array/mod.rs @@ -39,7 +39,21 @@ impl ArrayBox { pub fn pop(&self) -> Box { match self.items.write().unwrap().pop() { Some(item) => item, - None => Box::new(crate::boxes::null_box::NullBox::new()), + None => { + let strict = std::env::var("HAKO_OOB_STRICT") + .ok() + .map(|v| matches!(v.as_str(), "1"|"true"|"on")) + .unwrap_or(false) + || std::env::var("NYASH_OOB_STRICT") + .ok() + .map(|v| matches!(v.as_str(), "1"|"true"|"on")) + .unwrap_or(false); + if strict { + Box::new(StringBox::new("[array/empty/pop] empty array")) + } else { + Box::new(crate::boxes::null_box::NullBox::new()) + } + } } } diff --git a/tools/smokes/v2/profiles/quick/core/array/array_empty_pop_tag_vm.sh b/tools/smokes/v2/profiles/quick/core/array/array_empty_pop_tag_vm.sh new file mode 100644 index 00000000..53956549 --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/array/array_empty_pop_tag_vm.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# array_empty_pop_tag_vm.sh — Array.pop on empty array emits [array/empty/pop] under strict + +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 a=[]; print(a.pop()); return 0 } }' +out=$(HAKO_OOB_STRICT=1 NYASH_OOB_STRICT=1 run_nyash_vm -c "$code") +if echo "$out" | grep -q "\[array/empty/pop\]"; then + echo "[PASS] array_empty_pop_tag_vm" + exit 0 +else + echo "[FAIL] array_empty_pop_tag_vm" >&2 + echo "--- output ---" >&2 + echo "$out" >&2 + exit 1 +fi + diff --git a/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_static_lower_const_on_off_vm.sh b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_static_lower_const_on_off_vm.sh new file mode 100644 index 00000000..7a7e0f5b --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_static_lower_const_on_off_vm.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# canonicalize_static_lower_const_on_off_vm.sh — Verify LLVMConstInstructionBox.lower_const rewrite + +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 + +json_path="/tmp/ny_v1_lower_const_$$.json" +mut_on="/tmp/ny_v1_lower_const_on_$$.json" +mut_off="/tmp/ny_v1_lower_const_off_$$.json" + +cat >"$json_path" <<'JSON' +{"schema_version":"1.0","functions":[{"name":"main","blocks":[{"id":0,"instructions":[{"op":"mir_call","mir_call":{"callee":{"type":"ModuleFunction","name":"LLVMConstInstructionBox.lower_const"},"args":[1]}},{"op":"ret"}]}]}]} +JSON + +set +e +HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_BRIDGE_INJECT_SINGLETON=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_on" \ + "$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1 +set -e || true +if [ ! -f "$mut_on" ] || ! grep -q '"type":"Method"' "$mut_on" || ! grep -q '"box_name":"LLVMConstInstructionBox"' "$mut_on" || ! grep -q '"method":"lower_const"' "$mut_on"; then + echo "[FAIL] canonicalize_static_lower_const_on_off_vm (ON)" >&2; exit 1 +fi + +set +e +HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_off" \ + "$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1 +set -e || true +if [ -f "$mut_off" ] && ! grep -q '"type":"ModuleFunction"' "$mut_off"; then + echo "[FAIL] canonicalize_static_lower_const_on_off_vm (OFF)" >&2; exit 1 +fi + +echo "[PASS] canonicalize_static_lower_const_on_off_vm" +rm -f "$json_path" "$mut_on" "$mut_off" +exit 0 + diff --git a/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_static_lower_phi_on_off_vm.sh b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_static_lower_phi_on_off_vm.sh new file mode 100644 index 00000000..85d67324 --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/bridge/canonicalize_static_lower_phi_on_off_vm.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# canonicalize_static_lower_phi_on_off_vm.sh — Verify PhiInst.lower_phi rewrite (ModuleFunction → Method) + +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 + +json_path="/tmp/ny_v1_lower_phi_$$.json" +mut_on="/tmp/ny_v1_lower_phi_on_$$.json" +mut_off="/tmp/ny_v1_lower_phi_off_$$.json" + +cat >"$json_path" <<'JSON' +{"schema_version":"1.0","functions":[{"name":"main","blocks":[{"id":0,"instructions":[{"op":"mir_call","mir_call":{"callee":{"type":"ModuleFunction","name":"PhiInst.lower_phi"},"args":[1]}},{"op":"ret"}]}]}]} +JSON + +set +e +HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_BRIDGE_INJECT_SINGLETON=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_on" \ + "$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1 +set -e || true +if [ ! -f "$mut_on" ] || ! grep -q '"type":"Method"' "$mut_on" || ! grep -q '"box_name":"PhiInst"' "$mut_on" || ! grep -q '"method":"lower_phi"' "$mut_on"; then + echo "[FAIL] canonicalize_static_lower_phi_on_off_vm (ON)" >&2; exit 1 +fi + +set +e +HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_off" \ + "$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1 +set -e || true +if [ -f "$mut_off" ] && ! grep -q '"type":"ModuleFunction"' "$mut_off"; then + echo "[FAIL] canonicalize_static_lower_phi_on_off_vm (OFF)" >&2; exit 1 +fi + +echo "[PASS] canonicalize_static_lower_phi_on_off_vm" +rm -f "$json_path" "$mut_on" "$mut_off" +exit 0 +