Bridge canonicalize: add PhiInst.lower_phi and LLVMConstInstructionBox.lower_const diff tests; ArrayBox.pop empty strict tag [array/empty/pop] + smoke; VM README: document [map/missing] and [array/empty/pop] tags

This commit is contained in:
nyash-codex
2025-11-02 11:47:58 +09:00
parent 4011fb2898
commit c81dc20d5c
6 changed files with 133 additions and 1 deletions

View File

@ -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: <key>`VM MapBox.get の既定タグ。従来文言を含むため後方互換)
- `[array/empty/pop] empty array`VM ArrayBox.pop 空時。strict環境HAKO_OOB_STRICT=1で有効
- GateC Direct では、リーダー/検証レイヤの診断をそのまま用いる(例: `unsupported callee type (expected Extern): ModuleFunction`)。
Strict OOB policy (GateC)

View File

@ -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)); }

View File

@ -39,7 +39,21 @@ impl ArrayBox {
pub fn pop(&self) -> Box<dyn NyashBox> {
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())
}
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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