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:
2025-12-24 07:44:50 +09:00
parent a47f850d02
commit ab76e39036
60 changed files with 2099 additions and 454 deletions

View File

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

View File

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

View File

@ -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) => {

View File

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

View File

@ -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 { .. }

View 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 を返した場合は Voidnull
let result = weak_value.upgrade_weak().unwrap_or(VMValue::Void);
self.regs.insert(dst, result);
Ok(())
}
}

View File

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

View File

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

View File

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

View File

@ -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(),
)),
}
}