feat(weak): Phase 285A1 - Weak Field Contract (Strict Type Enforcement)

Remove automatic WeakNew conversion and enforce strict compile-time type
checking for weak field assignments. Only 3 assignment types allowed:
1. Result of weak(x) call (WeakRef type)
2. Existing WeakRef variable (e.g., me.parent = other.parent)
3. Void/null (clear operation)

**Implementation**:
- Added MirType::WeakRef to type system (src/mir/types.rs)
- Track WeakRef type in emit_weak_new() even in pure mode
- Weak field reads return WeakRef without auto-upgrade
- Removed automatic WeakNew conversion from field writes
- Implemented check_weak_field_assignment() with actionable errors
- Fixed null literal type tracking (Phase 285A1.1: Unknown → Void)

**Testing**:
- 5 test fixtures (3 OK, 2 NG cases) - all passing
- Smoke test: phase285_weak_field_vm.sh
- Error messages guide users to use weak() or null

**Documentation**:
- Updated lifecycle.md SSOT with weak field contract

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-24 03:17:30 +09:00
parent b0eeb14c54
commit cc8b27a1aa
20 changed files with 802 additions and 29 deletions

View File

@ -73,11 +73,11 @@ pub fn emit_null(b: &mut MirBuilder) -> ValueId {
dst,
value: ConstValue::Null,
});
// Phase 84-1: Null constant type annotation
// Note: MirType has no Null variant, using Unknown as fallback
// Phase 285A1.1: Null constant type annotation
// Null is syntactic sugar for Void (SSOT: lifecycle.md)
b.type_ctx
.value_types
.insert(dst, crate::mir::MirType::Unknown);
.insert(dst, crate::mir::MirType::Void);
dst
}

View File

@ -119,9 +119,17 @@ impl super::MirBuilder {
if let Some(class_name) = inferred_class {
if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) {
if weak_set.contains(&field) {
let loaded = self.emit_weak_load(field_val)?;
let _ = self.emit_barrier_read(loaded);
return Ok(loaded);
// Phase 285A1: Read weak field returns WeakRef (no auto-upgrade)
// field_val is the result of getField, which we treat as WeakRef
let dst = field_val; // The load result is already our return value
// Phase 285A1: Mark the result as WeakRef type
self.type_ctx
.value_types
.insert(dst, crate::mir::types::MirType::WeakRef);
let _ = self.emit_barrier_read(dst);
return Ok(dst); // Return WeakRef directly (no WeakLoad)
}
}
}
@ -145,7 +153,7 @@ impl super::MirBuilder {
// LocalSSA: argument in-block (optional safety)
value_result = self.local_arg(value_result);
// If base is known and field is weak, create WeakRef before store
// Phase 285A1: If field is weak, enforce type contract (3 allowed cases)
if let Some(class_name) = self
.type_ctx
.value_origin_newbox
@ -154,7 +162,8 @@ impl super::MirBuilder {
{
if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) {
if weak_set.contains(&field) {
value_result = self.emit_weak_new(value_result)?;
// Phase 285A1: Strict type check (no automatic conversion)
self.check_weak_field_assignment(&class_name, &field, value_result)?;
}
}
}
@ -219,4 +228,50 @@ impl super::MirBuilder {
Ok(value_result)
}
/// Phase 285A1: Enforce weak field assignment contract
///
/// Allowed assignments:
/// 1. WeakRef (from weak() or weak field read)
/// 2. Void (clear operation)
///
/// Forbidden (Fail-Fast):
/// - BoxRef without weak()
/// - Primitives
/// - Unknown/untracked values
fn check_weak_field_assignment(
&mut self,
box_name: &str,
field_name: &str,
value_id: ValueId,
) -> Result<(), String> {
// Get value type
let value_type = self.type_ctx.value_types.get(&value_id);
match value_type {
// Case 1 & 2: WeakRef allowed
Some(crate::mir::types::MirType::WeakRef) => Ok(()),
// Case 3: Void allowed (clear)
Some(crate::mir::types::MirType::Void) => Ok(()),
// Forbidden: None/Unknown (型追跡漏れ防止)
None => Err(format!(
"Cannot assign untracked value to weak field '{}.{}'. Use weak(...) or Void explicitly.",
box_name, field_name
)),
// Forbidden: BoxRef
Some(crate::mir::types::MirType::Box(box_type)) => Err(format!(
"Cannot assign Box ({}) to weak field '{}.{}'. Use weak(...) to create weak reference: me.{} = weak(value)",
box_type, box_name, field_name, field_name
)),
// Forbidden: Primitives and others
Some(other_type) => Err(format!(
"Cannot assign {:?} to weak field '{}.{}'. Weak fields require WeakRef type. Use weak(...) or Void.",
other_type, box_name, field_name
)),
}
}
}

View File

@ -391,10 +391,19 @@ impl super::MirBuilder {
&mut self,
box_val: super::ValueId,
) -> Result<super::ValueId, String> {
if crate::config::env::mir_core13_pure() {
return Ok(box_val);
}
let dst = self.next_value_id();
// Phase 285A1: Track WeakRef type (even in pure mode)
self.type_ctx
.value_types
.insert(dst, crate::mir::types::MirType::WeakRef);
// Phase 285A1: WeakRef type must be tracked even in pure mode
if crate::config::env::mir_core13_pure() {
// Pure mode: still track type, but skip instruction
return Ok(dst);
}
self.emit_instruction(super::MirInstruction::WeakRef {
dst,
op: WeakRefOp::New,