Files
hakorune/docs/development/current/main/phases/phase-285/phase-285a1-boxification.md
tomoaki ab76e39036 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>
2025-12-24 07:44:50 +09:00

10 KiB
Raw Blame History

Phase 285A1: Weak Field Validator Boxification

Status: Complete (2025-12-24)

Goal

Phase 285A1 の weak field 処理を専用 Box に箱化し、以下を実現:

  • 単一責任: weak field 契約検証のみを担当
  • テスト容易性: 独立してテスト可能
  • 再利用性: 他の箇所でも使える

Implementation

新規ファイル

src/mir/builder/weak_field_validator.rs

/// Phase 285A1: Weak Field Contract Validator Box
///
/// 単一責任: weak field の契約検証のみ
/// - 読み込み時: WeakRef 型注釈
/// - 書き込み時: 型契約検証WeakRef/Void のみ許可)
pub(super) struct WeakFieldValidatorBox;

impl WeakFieldValidatorBox {
    /// Weak field 読み込み時の型注釈を追加
    pub(super) fn annotate_read_result(
        type_ctx: &mut TypeContext,
        dst: ValueId,
    ) {
        // Phase 285A1: Mark the result as WeakRef type
        type_ctx.value_types.insert(dst, MirType::WeakRef);
    }

    /// Weak field への代入を検証3つの許可ケース
    ///
    /// Phase 285A1 Fail-Fast 契約:
    /// - **許可**: WeakRef (weak() または weak field 読み込み)
    /// - **許可**: Void (クリア操作)
    /// - **禁止**: BoxRef (weak() なしの Box)
    /// - **禁止**: プリミティブ型
    /// - **禁止**: 型追跡されていない値
    pub(super) fn validate_assignment(
        value_type: Option<&MirType>,
        box_name: &str,
        field_name: &str,
    ) -> Result<(), String> {
        // 3つの許可ケースを検証
        // Fail-Fast: エラーは即座に返す
    }
}

特徴:

  • Phase 33 の箱化モジュール化と同じ思想(単一責任、明確な境界)
  • 充実したコメント(目的、契約、例)
  • 単体テスト完備5つのテストケース

変更ファイル

src/mir/builder/fields.rs

Before (277行):

  • check_weak_field_assignment() メソッド232-277行
  • 読み込み時の型注釈inline
  • 書き込み時の型検証inline

After (237行):

  • WeakFieldValidatorBox::annotate_read_result() 呼び出し
  • WeakFieldValidatorBox::validate_assignment() 呼び出し
  • 40行削減-14.4%

src/mir/builder.rs

モジュール宣言追加:

mod weak_field_validator; // Phase 285A1: Weak field contract validator

Contract Enforcement

読み込み契約

// Phase 285A1: weak field 読み込みは WeakRef 型を返す(自動昇格なし)
WeakFieldValidatorBox::annotate_read_result(&mut self.type_ctx, dst);

書き込み契約

3つの許可ケース:

  1. WeakRef: weak() または weak field 読み込みの結果
  2. Void: クリア操作(me.next = Void
  3. BoxRef: 禁止 - エラーメッセージで weak() 使用を提案

エラーメッセージ例:

Cannot assign Box (StringBox) to weak field 'Node.next'.
Use weak(...) to create weak reference: me.next = weak(value)

Tests

単体テスト5つ

  1. test_validate_weakref_allowed: WeakRef 許可
  2. test_validate_void_allowed: Void 許可
  3. test_validate_boxref_forbidden: BoxRef 禁止
  4. test_validate_untracked_forbidden: 型追跡なし禁止
  5. test_validate_primitive_forbidden: プリミティブ禁止

動作確認

# ビルド成功
cargo build --release
# Finished `release` profile [optimized] target(s) in 1m 23s

# 基本機能確認
echo 'static box Main { main() { print("Hello"); return 0 } }' > test.hako
./target/release/hakorune test.hako
# Output: Hello
# RC: 0

# フィールドアクセス確認
# Box with fields works correctly

Architecture Benefits

単一責任SRP

  • Before: fields.rs が weak field 処理を含む(複数責任)
  • After: WeakFieldValidatorBox が検証のみ担当

テスト容易性

  • Before: fields.rs 全体のテストが必要
  • After: WeakFieldValidatorBox 単独でテスト可能

再利用性

  • Before: check_weak_field_assignment()MirBuilder メソッド
  • After: WeakFieldValidatorBox はどこからでも使える

明確な境界

fields.rs
  ├─ フィールド読み込み/書き込みロジック
  └─ WeakFieldValidatorBox::validate_assignment() 呼び出し
      ↓
weak_field_validator.rs
  └─ weak field 契約検証のみ

Code Quality

Phase 33 思想の継承

  • 単一責任: 1つの Box = 1つの関心事
  • 明確な境界: 入力/出力が明確
  • 充実したコメント: 目的、契約、例
  • Fail-Fast: エラーは即座に返す(フォールバック禁止)

メトリクス

  • 行数削減: 277行 → 237行-40行、-14.4%
  • モジュール化: 1ファイル → 2ファイル責任分離
  • テストカバレッジ: 0 → 5単体テスト

Phase 285 Context

Phase 285A1 は Phase 285 の一部として、weak field 処理の基盤を整備:

  • Phase 285 Goal: Box lifecycle / weakref / finalization / GC conformance
  • Phase 285A1 Scope: weak field 契約検証の箱化
  • Status: P0 (docs-only) → 実装進行中

Note: weak field 構文(weak next: Nodeは未実装。Phase 285 P1 で実装予定。


A1.5: Parser Hang Fix - Parameter Type Annotations

Status: Complete (2025-12-24)

Problem

During Phase 285A1.4 implementation, discovered critical parser bug:

  • User writes: setParent(p: Node) { ... }
  • Parser hangs infinitely at COLON token (no advance on unexpected token)
  • Workaround required removing type annotation → setParent(p) { ... } → Works

Root Cause

6 identical vulnerable parameter parsing loops across the parser codebase:

  1. src/parser/declarations/box_def/members/methods.rs:21-30
  2. src/parser/declarations/box_def/members/constructors.rs:27-34 (init)
  3. src/parser/declarations/box_def/members/constructors.rs:101-108 (pack)
  4. src/parser/declarations/box_def/members/constructors.rs:138-145 (birth)
  5. src/parser/items/functions.rs:34-51
  6. src/parser/items/static_items.rs:72-87

Vulnerable Code Pattern:

while !p.match_token(&TokenType::RPAREN) && !p.is_at_end() {
    must_advance!(p, _unused, "method parameter parsing");
    if let TokenType::IDENTIFIER(param) = &p.current_token().token_type {
        params.push(param.clone());
        p.advance();  // ← Only advances on IDENTIFIER
    }
    // ⚠️ COLON token: not IDENTIFIER, not COMMA, not RPAREN → NO ADVANCE → INFINITE LOOP
    if p.match_token(&TokenType::COMMA) {
        p.advance();
    }
}

EBNF Spec Finding: params grammar undefined in EBNF.md, : TYPE is for return type only

Solution: Shared Helper Function (DRY Principle)

Created src/parser/common/params.rs with common parameter parsing logic:

/// Parse parameter name list with Fail-Fast on unexpected tokens
///
/// Parses: IDENT (',' IDENT)*
/// Rejects: Type annotations, unexpected tokens, malformed comma sequences
pub(crate) fn parse_param_name_list(
    p: &mut NyashParser,
    context: &str,  // "method", "constructor", "function" for error messages
) -> Result<Vec<String>, ParseError>

Key Features:

  • Progress-zero detection: Tracks token position, errors if stuck (prevents infinite loops)
  • Explicit token handling: All token types (IDENTIFIER, COMMA, other) explicitly matched
  • Fail-Fast: Either advances or errors (no infinite loop possible)
  • Unified error messages: Context parameter customizes messages per call site

Files Modified

New Files (2):

  • src/parser/common/params.rs (~90 lines) - Helper function
  • tools/smokes/v2/profiles/quick/parser/phase285_param_type_annotation_nohang.sh - Timeout smoke test

Modified Files (8):

  1. src/parser/common/mod.rs - Module declaration (moved common.rs → common/mod.rs)
  2. src/parser/declarations/box_def/members/methods.rs - Replaced 12 lines with 1 call
  3. src/parser/declarations/box_def/members/constructors.rs - Replaced 3 loops (init/pack/birth)
  4. src/parser/items/functions.rs - Replaced 20 lines with 2 lines
  5. src/parser/items/static_items.rs - Replaced 22 lines with 2 lines
  6. apps/tests/phase285_parser_param_type_annot_should_not_hang.hako - Regression test

Net Change: +90 new - 72 removed + 6 calls = +24 lines (with better error handling!)

Tests

Regression Test: apps/tests/phase285_parser_param_type_annot_should_not_hang.hako

box TestNode {
    value: IntegerBox
    // ❌ Should error immediately (type annotation not supported)
    setParent(p: Node) {
        return 0
    }
}

Smoke Test: tools/smokes/v2/profiles/quick/parser/phase285_param_type_annotation_nohang.sh

  • Timeout: 3 seconds (detects hang)
  • Expected: Parse error within 1 second
  • Validation: Error message mentions "Unexpected token COLON" and "Parameter type annotations not supported"

Result: PASS

✅ PASS: Parser correctly rejects param type annotations without hanging

Error Message Examples

Before (infinite hang):

🚨 PARSER INFINITE LOOP DETECTED at method parameter parsing

After (clear error):

❌ Parse error: Unexpected token COLON, expected ',' or ')' in method parameter list.
Note: Parameter type annotations are not supported.

Architecture Benefits

DRY Principle:

  • Before: 6 identical vulnerable loops (72 lines total)
  • After: 1 helper function (~90 lines) + 6 one-line calls

Maintainability:

  • Future fixes only need 1 location
  • Unified error messages (single source of truth)
  • Progress-zero detection guaranteed in one place

Safety:

  • No infinite loop possible (Fail-Fast guaranteed)
  • All token types explicitly handled (no silent fallthrough)
  • Context-aware error messages (better UX)

References

  • Phase 33: Box Theory Modularization (phase-33-modularization.md)
  • Phase 285: Box lifecycle (phase-285/README.md)
  • src/mir/builder/weak_field_validator.rs: A1.1 実装本体
  • src/mir/builder/fields.rs: A1.1 呼び出し側
  • src/parser/common/params.rs: A1.5 実装本体
  • src/parser/declarations/box_def/members/fields.rs: A1.2-A1.4 実装本体