fix(emit): const 声明 STORE_VAR 恒 b=0,修复寄存器复用触发误抛(B011)
This commit is contained in:
@@ -49,7 +49,11 @@ impl Emitter {
|
|||||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(src_reg as u32), Operand::Imm(cell_idx as u16), Operand::None));
|
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(src_reg as u32), Operand::Imm(cell_idx as u16), Operand::None));
|
||||||
} else {
|
} else {
|
||||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg as u32), Operand::Reg(src_reg as u32), Operand::Imm(if is_const { 1 } else { 0 })));
|
// B011: const 声明路径 STORE_VAR 恒 b=0,不查运行时 guard。
|
||||||
|
// guard 读 regs[rd] 判断槽是否已初始化,依赖槽初始为 undefined;
|
||||||
|
// 声明冗余检查由编译期 declare() 完成(重复声明编译报错),
|
||||||
|
// 运行时 guard 只服务"对 const 再赋值"(赋值路径,见 emit_assign_target)。
|
||||||
|
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg as u32), Operand::Reg(src_reg as u32), Operand::Imm(0)));
|
||||||
}
|
}
|
||||||
ctx.init_var(name);
|
ctx.init_var(name);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//! B011 const guard 回归测试:const 声明路径不依赖寄存器槽新鲜度。
|
||||||
|
//!
|
||||||
|
//! 背景:`dispatch_store_var` 的 const guard(b!=0)读 `regs[rd]` 判断目标槽是否已初始化。
|
||||||
|
//! 声明路径带 b=1 时,checkpoint/寄存器复用残留陈旧值会误抛 "Assignment to constant variable"。
|
||||||
|
//! Phase 5 RegAlloc 复用寄存器后此 bug 必现——本测试锁死声明路径恒 b=0 的行为。
|
||||||
|
|
||||||
|
use oxide_compiler::compiler::Compiler;
|
||||||
|
use oxide_parser::Allocator;
|
||||||
|
use oxide_vm::vm::Vm;
|
||||||
|
|
||||||
|
fn eval(source: &str) -> String {
|
||||||
|
let allocator = Allocator::default();
|
||||||
|
let program = match oxide_parser::parse(&allocator, source) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => return format!("parse error: {}", e[0].message),
|
||||||
|
};
|
||||||
|
let module = match Compiler::new().compile(&program) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => return format!("compile error: {e}"),
|
||||||
|
};
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
match vm.run(&module) {
|
||||||
|
Ok(result) => format!("{result}"),
|
||||||
|
Err(e) => format!("vm error: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// B011 复现样例:switch 语句(checkpoint 复用槽)后跟 const 声明。
|
||||||
|
// 修复前误抛 "Assignment to constant variable",修复后输出 5。
|
||||||
|
#[test]
|
||||||
|
fn switch_then_const_declaration_no_false_positive() {
|
||||||
|
assert_eq!(eval("switch (1) { case 1: break; } const x = 5; x"), "5");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 锚:对已声明 const 再赋值仍必须抛错(guard 保留在赋值路径)。
|
||||||
|
#[test]
|
||||||
|
fn const_reassignment_still_throws() {
|
||||||
|
let out = eval("const x = 5; x = 6");
|
||||||
|
assert!(out.contains("Assignment to constant variable"), "got: {out}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 锚:块级 const 遮蔽互不影响(内层 const 不污染外层槽)。
|
||||||
|
#[test]
|
||||||
|
fn block_scoped_const_shadowing_independent() {
|
||||||
|
assert_eq!(eval("const x = 5; { const x = 1; } x"), "5");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 锚:解构 const 声明正常(emit_object_binding → emit_bind_target)。
|
||||||
|
#[test]
|
||||||
|
fn const_destructuring_declaration_works() {
|
||||||
|
assert_eq!(eval("const { a } = { a: 7 }; a"), "7");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user