feat(parser): Phase 285A1.4 & A1.5 - Weak field sugar + Parser hang fix
A1.4: Add sugar syntax `public weak parent` ≡ `public { weak parent }`
A1.5: Fix parser hang on unsupported `param: Type` syntax
Key changes:
- A1.4: Extend visibility parser to handle weak modifier (fields.rs)
- A1.5: Shared helper `parse_param_name_list()` with progress-zero detection
- A1.5: Fix 6 vulnerable parameter parsing loops (methods, constructors, functions)
- Tests: Sugar syntax (OK/NG), parser hang (timeout-based)
- Docs: lifecycle.md, EBNF.md, phase-285a1-boxification.md
Additional changes:
- weak() builtin implementation (handlers/weak.rs)
- Leak tracking improvements (leak_tracker.rs)
- Documentation updates (lifecycle, types, memory-finalization, etc.)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -38,6 +38,8 @@ pub fn to_bool_vm(v: &VMValue) -> Result<bool, String> {
|
||||
Err(format!("cannot coerce BoxRef({}) to bool", b.type_name()))
|
||||
}
|
||||
VMValue::Future(_) => Err("cannot coerce Future to bool".to_string()),
|
||||
// Phase 285A0: WeakRef in boolean context is TypeError
|
||||
VMValue::WeakBox(_) => Err("WeakRef in boolean context - use upgrade() first".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,6 +82,16 @@ pub fn eq_vm(a: &VMValue, b: &VMValue) -> bool {
|
||||
.downcast_ref::<crate::boxes::missing_box::MissingBox>()
|
||||
.is_some()
|
||||
}
|
||||
// Phase 285A0: WeakBox equality
|
||||
(WeakBox(wa), WeakBox(wb)) => {
|
||||
match (wa.upgrade(), wb.upgrade()) {
|
||||
(Some(arc_a), Some(arc_b)) => Arc::ptr_eq(&arc_a, &arc_b),
|
||||
(None, None) => true, // Both dropped
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
// WeakBox == Void when dropped
|
||||
(WeakBox(w), Void) | (Void, WeakBox(w)) => w.upgrade().is_none(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -94,6 +106,7 @@ pub fn tag_of_vm(v: &VMValue) -> &'static str {
|
||||
VMValue::Future(_) => "Future",
|
||||
VMValue::Void => "Void",
|
||||
VMValue::BoxRef(_) => "BoxRef",
|
||||
VMValue::WeakBox(_) => "WeakRef", // Phase 285A0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -173,6 +173,7 @@ impl MirInterpreter {
|
||||
VMValue::Bool(_) => "BoolBox".to_string(),
|
||||
VMValue::Void => "<Void>".to_string(),
|
||||
VMValue::Future(_) => "<Future>".to_string(),
|
||||
VMValue::WeakBox(_) => "<WeakRef>".to_string(), // Phase 285A0
|
||||
};
|
||||
self.box_trace_emit_call(&cls, method, args.len());
|
||||
}
|
||||
@ -190,6 +191,7 @@ impl MirInterpreter {
|
||||
VMValue::String(_) => "String".to_string(),
|
||||
VMValue::Void => "Void".to_string(),
|
||||
VMValue::Future(_) => "Future".to_string(),
|
||||
VMValue::WeakBox(_) => "WeakRef".to_string(), // Phase 285A0
|
||||
};
|
||||
eprintln!("[vm-trace] length dispatch recv_type={}", type_name);
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ pub(super) fn try_handle_object_fields(
|
||||
VV::Void => NV::Void,
|
||||
VV::Future(_) => NV::Void, // not expected in fields
|
||||
VV::BoxRef(_) => NV::Void, // store minimal; complex object fields are not required here
|
||||
VV::WeakBox(_) => NV::Void, // Phase 285A0: WeakBox not expected in this context
|
||||
}
|
||||
}
|
||||
fn nv_to_vm(v: &crate::value::NyashValue) -> VMValue {
|
||||
@ -94,6 +95,7 @@ pub(super) fn try_handle_object_fields(
|
||||
Ok(VMValue::String(_)) => "String".to_string(),
|
||||
Ok(VMValue::Void) => "Void".to_string(),
|
||||
Ok(VMValue::Future(_)) => "Future".to_string(),
|
||||
Ok(VMValue::WeakBox(_)) => "WeakRef".to_string(), // Phase 285A0
|
||||
Err(_) => "<err>".to_string(),
|
||||
};
|
||||
eprintln!("[vm-trace] getField recv_kind={}", rk);
|
||||
@ -229,6 +231,7 @@ pub(super) fn try_handle_object_fields(
|
||||
VMValue::BoxRef(_) => "BoxRef",
|
||||
VMValue::Void => "Void",
|
||||
VMValue::Future(_) => "Future",
|
||||
VMValue::WeakBox(_) => "WeakRef", // Phase 285A0
|
||||
};
|
||||
this.box_trace_emit_get(&inst.class_name, &fname, kind);
|
||||
}
|
||||
@ -339,6 +342,7 @@ pub(super) fn try_handle_object_fields(
|
||||
VMValue::BoxRef(b) => b.type_name(),
|
||||
VMValue::Void => "Void",
|
||||
VMValue::Future(_) => "Future",
|
||||
VMValue::WeakBox(_) => "WeakRef", // Phase 285A0
|
||||
};
|
||||
// class name unknown here; use receiver type name if possible
|
||||
let cls = match this.reg_load(actual_box_val).unwrap_or(VMValue::Void) {
|
||||
@ -438,6 +442,7 @@ pub(super) fn try_handle_object_fields(
|
||||
VMValue::BoxRef(b) => b.type_name(),
|
||||
VMValue::Void => "Void",
|
||||
VMValue::Future(_) => "Future",
|
||||
VMValue::WeakBox(_) => "WeakRef", // Phase 285A0
|
||||
};
|
||||
let cls = match this.reg_load(actual_box_val).unwrap_or(VMValue::Void) {
|
||||
VMValue::BoxRef(b) => {
|
||||
|
||||
@ -458,6 +458,7 @@ impl MirInterpreter {
|
||||
VMValue::Void => "Void",
|
||||
VMValue::Future(_) => "Future",
|
||||
VMValue::BoxRef(bx) => bx.type_name(),
|
||||
VMValue::WeakBox(_) => "WeakRef", // Phase 285A0
|
||||
};
|
||||
|
||||
// 2. Lookup type in TypeRegistry and get slot
|
||||
|
||||
@ -24,6 +24,7 @@ mod externals;
|
||||
mod memory;
|
||||
mod misc;
|
||||
mod type_ops;
|
||||
mod weak; // Phase 285A0: WeakRef handlers
|
||||
|
||||
impl MirInterpreter {
|
||||
pub(super) fn execute_instruction(&mut self, inst: &MirInstruction) -> Result<(), VMError> {
|
||||
@ -118,6 +119,17 @@ impl MirInterpreter {
|
||||
};
|
||||
self.regs.insert(*dst, selected_val);
|
||||
}
|
||||
// Phase 285A0: WeakRef handlers (delegated to weak.rs)
|
||||
MirInstruction::WeakNew { dst, box_val } => {
|
||||
self.handle_weak_new(*dst, *box_val)?;
|
||||
}
|
||||
MirInstruction::WeakLoad { dst, weak_ref } => {
|
||||
self.handle_weak_load(*dst, *weak_ref)?;
|
||||
}
|
||||
MirInstruction::WeakRef { dst, op, value } => match op {
|
||||
WeakRefOp::New => self.handle_weak_new(*dst, *value)?,
|
||||
WeakRefOp::Load => self.handle_weak_load(*dst, *value)?,
|
||||
}
|
||||
MirInstruction::BarrierRead { .. }
|
||||
| MirInstruction::BarrierWrite { .. }
|
||||
| MirInstruction::Barrier { .. }
|
||||
|
||||
59
src/backend/mir_interpreter/handlers/weak.rs
Normal file
59
src/backend/mir_interpreter/handlers/weak.rs
Normal file
@ -0,0 +1,59 @@
|
||||
//! Phase 285A0: WeakRef handlers - 弱参照の作成とアップグレード
|
||||
//!
|
||||
//! SSOT: docs/reference/language/lifecycle.md:179
|
||||
//!
|
||||
//! WeakRef は強参照サイクルを避けるための非所有参照です。
|
||||
//! - `weak(x)` → WeakNew: BoxRef から WeakRef を作成
|
||||
//! - `w.weak_to_strong()` → WeakLoad: WeakRef から BoxRef へアップグレード(失敗時は null/Void)
|
||||
|
||||
use super::*;
|
||||
|
||||
impl MirInterpreter {
|
||||
/// WeakNew: BoxRef → WeakRef 変換
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `dst` - 結果を格納する ValueId
|
||||
/// * `box_val` - 変換元の Box ValueId
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<(), VMError>` - 成功時は Ok、失敗時は Err
|
||||
///
|
||||
/// # Errors
|
||||
/// * `box_val` が BoxRef でない場合はエラー
|
||||
pub(super) fn handle_weak_new(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
box_val: ValueId,
|
||||
) -> Result<(), VMError> {
|
||||
let box_value = self.reg_load(box_val)?;
|
||||
let weak_value = box_value
|
||||
.downgrade_to_weak()
|
||||
.ok_or_else(|| self.err_invalid("WeakNew: target is not a Box"))?;
|
||||
self.regs.insert(dst, weak_value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// WeakLoad: WeakRef → BoxRef | null (= Void) アップグレード
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `dst` - 結果を格納する ValueId
|
||||
/// * `weak_ref` - WeakRef ValueId
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<(), VMError>` - 成功時は Ok、失敗時は Err
|
||||
///
|
||||
/// # Note
|
||||
/// - SSOT: upgrade failure returns null (= Void in VM) - lifecycle.md:179
|
||||
/// - ターゲットが既に drop された場合や Dead 状態の場合は Void を返す
|
||||
pub(super) fn handle_weak_load(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
weak_ref: ValueId,
|
||||
) -> Result<(), VMError> {
|
||||
let weak_value = self.reg_load(weak_ref)?;
|
||||
// upgrade_weak() が None を返した場合は Void(null)
|
||||
let result = weak_value.upgrade_weak().unwrap_or(VMValue::Void);
|
||||
self.regs.insert(dst, result);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -436,6 +436,7 @@ impl MirInterpreter {
|
||||
("BoxRef", b.type_name().to_string(), tag)
|
||||
}
|
||||
}
|
||||
VMValue::WeakBox(_) => ("WeakRef", "".to_string(), None), // Phase 285A0
|
||||
};
|
||||
if let Some(tag) = nullish {
|
||||
eprintln!(
|
||||
|
||||
@ -14,7 +14,7 @@ pub(super) use crate::backend::abi_util::{eq_vm, to_bool_vm};
|
||||
pub(super) use crate::backend::vm::{VMError, VMValue};
|
||||
pub(super) use crate::mir::{
|
||||
BasicBlockId, BinaryOp, Callee, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule,
|
||||
MirType, TypeOpKind, ValueId,
|
||||
MirType, TypeOpKind, ValueId, WeakRefOp,
|
||||
};
|
||||
|
||||
mod exec;
|
||||
|
||||
@ -61,6 +61,7 @@ impl MirInterpreter {
|
||||
))
|
||||
}
|
||||
VMValue::Future(_) => "Future",
|
||||
VMValue::WeakBox(_) => "WeakRef", // Phase 285A0
|
||||
};
|
||||
Err(self.err_type_mismatch("load_as_int", "Integer", type_name))
|
||||
}
|
||||
@ -93,6 +94,7 @@ impl MirInterpreter {
|
||||
return Err(self.err_type_mismatch("load_as_bool", "Bool", &b.type_name()))
|
||||
}
|
||||
VMValue::Future(_) => "Future",
|
||||
VMValue::WeakBox(_) => "WeakRef", // Phase 285A0
|
||||
};
|
||||
Err(self.err_type_mismatch("load_as_bool", "Bool", type_name))
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
use crate::ast::Span;
|
||||
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
|
||||
use crate::mir::{BasicBlockId, ConstValue};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
/// VM execution error
|
||||
#[derive(Debug)]
|
||||
@ -90,7 +90,7 @@ impl std::fmt::Display for VMError {
|
||||
impl std::error::Error for VMError {}
|
||||
|
||||
/// VM value representation
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub enum VMValue {
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
@ -99,6 +99,30 @@ pub enum VMValue {
|
||||
Future(crate::boxes::future::FutureBox),
|
||||
Void,
|
||||
BoxRef(Arc<dyn NyashBox>),
|
||||
/// Phase 285A0: Weak reference to a Box (non-owning)
|
||||
WeakBox(Weak<dyn NyashBox>),
|
||||
}
|
||||
|
||||
// Manual Debug implementation for WeakBox
|
||||
impl std::fmt::Debug for VMValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VMValue::Integer(i) => write!(f, "Integer({})", i),
|
||||
VMValue::Float(v) => write!(f, "Float({})", v),
|
||||
VMValue::Bool(b) => write!(f, "Bool({})", b),
|
||||
VMValue::String(s) => write!(f, "String({:?})", s),
|
||||
VMValue::Future(_) => write!(f, "Future(...)"),
|
||||
VMValue::Void => write!(f, "Void"),
|
||||
VMValue::BoxRef(arc) => write!(f, "BoxRef({})", arc.type_name()),
|
||||
VMValue::WeakBox(weak) => {
|
||||
if weak.upgrade().is_some() {
|
||||
write!(f, "WeakBox(alive)")
|
||||
} else {
|
||||
write!(f, "WeakBox(dropped)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Manual PartialEq implementation to avoid requiring PartialEq on FutureBox
|
||||
@ -112,6 +136,14 @@ impl PartialEq for VMValue {
|
||||
(VMValue::Void, VMValue::Void) => true,
|
||||
(VMValue::Future(_), VMValue::Future(_)) => false,
|
||||
(VMValue::BoxRef(_), VMValue::BoxRef(_)) => false,
|
||||
// Phase 285A0: WeakBox equality (compare by pointer if both alive)
|
||||
(VMValue::WeakBox(a), VMValue::WeakBox(b)) => {
|
||||
match (a.upgrade(), b.upgrade()) {
|
||||
(Some(arc_a), Some(arc_b)) => Arc::ptr_eq(&arc_a, &arc_b),
|
||||
(None, None) => true, // Both dropped
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -128,6 +160,14 @@ impl VMValue {
|
||||
VMValue::Future(f) => Box::new(f.clone()),
|
||||
VMValue::Void => Box::new(VoidBox::new()),
|
||||
VMValue::BoxRef(arc_box) => arc_box.share_box(),
|
||||
VMValue::WeakBox(weak) => {
|
||||
// Upgrade or return void if dropped
|
||||
if let Some(arc) = weak.upgrade() {
|
||||
arc.share_box()
|
||||
} else {
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,6 +181,32 @@ impl VMValue {
|
||||
VMValue::Future(f) => f.to_string_box().value,
|
||||
VMValue::Void => "void".to_string(),
|
||||
VMValue::BoxRef(arc_box) => arc_box.to_string_box().value,
|
||||
VMValue::WeakBox(weak) => {
|
||||
if weak.upgrade().is_some() {
|
||||
"WeakRef(alive)".to_string()
|
||||
} else {
|
||||
"WeakRef(dropped)".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 285A0: Downgrade a strong BoxRef to a weak reference
|
||||
/// Returns None if not a BoxRef
|
||||
pub fn downgrade_to_weak(&self) -> Option<VMValue> {
|
||||
match self {
|
||||
VMValue::BoxRef(arc) => Some(VMValue::WeakBox(Arc::downgrade(arc))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 285A0: Upgrade a weak reference to a strong BoxRef
|
||||
/// Returns Some(BoxRef) if target is alive, None if dropped
|
||||
pub fn upgrade_weak(&self) -> Option<VMValue> {
|
||||
match self {
|
||||
VMValue::WeakBox(weak) => weak.upgrade().map(VMValue::BoxRef),
|
||||
// Non-weak values: return self (already strong)
|
||||
_ => Some(self.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,6 +252,10 @@ impl VMValue {
|
||||
VMValue::Float(f) => Ok(*f != 0.0),
|
||||
VMValue::String(s) => Ok(!s.is_empty()),
|
||||
VMValue::Future(_) => Ok(true),
|
||||
// Phase 285A0: WeakBox truthiness is TypeError (SSOT: types.md:26)
|
||||
VMValue::WeakBox(_) => Err(VMError::TypeError(
|
||||
"WeakRef cannot be used in boolean context; use upgrade() first".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user