# 機能設計書 84-ローカル可変変数

## 概要

本ドキュメントは、F#コンパイラにおけるローカル可変変数の自動ヒープ昇格（Autobox）機能の設計について記述する。この機能は、ラムダ式やオブジェクト式によってキャプチャされる可変変数を参照セル（ref cell）に自動変換し、正しいクロージャセマンティクスを保証する。

### 本機能の処理概要

**業務上の目的・背景**：
F#では、ラムダ式がローカルの可変変数をキャプチャする場合、その変数は複数のクロージャ間で共有される必要がある。CLRのクロージャ実装では値型変数はコピーされるため、可変変数を参照セル（`FSharpRef<T>`）に変換することで、共有セマンティクスを実現する。この変換は自動的に行われ、ユーザーに警告を発する。

**機能の利用シーン**：
- ラムダ式内で外部スコープの可変変数を参照・変更する場合
- オブジェクト式内で外部スコープの可変変数を参照・変更する場合
- 再帰関数内でクロージャにキャプチャされる可変変数がある場合

**主要な処理内容**：
1. エスケープする可変変数の検出（DecideImplFile）
2. 対象変数のヒープ変数（参照セル型）への変換
3. 値の読み取り・書き込み・アドレス取得の書き換え
4. バインディングの書き換え

**関連システム・外部連携**：
- TypedTree: 型付き抽象構文木の入力元
- TypedTreeOps: TAST操作ユーティリティ
- FSharp.Core.Ref<T>: 参照セル型

**権限による制御**：
特になし。コンパイラ内部の変換フェーズとして自動的に適用される。

## 関連画面

該当なし（コンパイラ内部機能）

## 機能種別

コンパイラ変換機能（可変変数のヒープ昇格）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| g | TcGlobals | Yes | コンパイラグローバル状態 | null不可 |
| amap | ImportMap | Yes | インポートマップ | null不可 |
| implFile | TypedImplFile | Yes | 変換対象の型付き実装ファイル | 有効なTAST |

### 入力データソース

- 型検査済みTAST（TypedTree）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| implFile | TypedImplFile | 変換後の型付き実装ファイル |

### 出力先

変換済みTASTとしてコンパイルパイプラインに渡される

## 処理フロー

### 処理シーケンス

```
1. エスケープする可変変数の検出
   └─ DecideImplFile
      ├─ FoldImplFileでフォールド
      ├─ DecideExpr: ラムダ/オブジェクト式を検出
      ├─ DecideLambda: ラムダ内のエスケープ変数を判定
      └─ DecideEscapes: 条件を満たす可変変数を収集

2. 変換対象がなければ終了
   └─ Zset.isEmpty localsToTransform

3. 警告の発行
   └─ 各変数に対してFS3180警告を発行

4. ヒープ変数マップの作成
   └─ 各localsToTransformに対してRef<T>型の変数を作成

5. 実装ファイルの書き換え
   └─ RewriteImplFile
      ├─ TransformExpr: 読み取り/書き込み/アドレス取得の書き換え
      └─ TransformBinding: バインディングの書き換え
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[DecideImplFile]
    B --> C[エスケープ可変変数を収集]
    C --> D{変換対象あり?}
    D -->|No| Z[変換なしで終了]
    D -->|Yes| E[警告を発行]
    E --> F[heapValMapを作成]
    F --> G[RewriteImplFile]
    G --> H[TransformExpr]
    H --> I[TransformBinding]
    I --> J[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-84-01 | エスケープ判定 | 関数の引数として渡された可変変数はエスケープしない | syntacticArgsに含まれる場合 |
| BR-84-02 | ValReprInfo判定 | ValReprInfoがNoneの場合のみエスケープ対象 | ローカル変数の場合 |
| BR-84-03 | 既知のみ使用前変更 | IsKnownOnlyMutableBeforeUseがtrueの変数は除外 | オプティマイザで判定済みの場合 |
| BR-84-04 | 警告発行 | 暗黙のヒープ割り当てについて警告FS3180を発行 | 変換対象が存在する場合 |

### 計算ロジック

- **参照セル型**: `mkRefCellTy g localVal.Type` → `FSharpRef<元の型>`
- **読み取り変換**: `mkRefCellGet g m v.Type nve`
- **書き込み変換**: `mkRefCellSet g m v.Type nve arg`
- **アドレス取得変換**: `mkRecdFieldGetAddrViaExprAddr`

## データベース操作仕様

該当なし（メモリ内変換のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| FS3180 | 警告 | 暗黙のヒープ割り当てが発生 | ユーザーへの情報提供（警告） |

### リトライ仕様

リトライなし。

## トランザクション仕様

該当なし（ステートレス変換）

## パフォーマンス要件

- 変換処理はコンパイル時のみ実行
- 参照セルへの変換は最小限の変数に対してのみ実行
- ランタイムでは参照セルアクセスのオーバーヘッドが発生

## セキュリティ考慮事項

該当なし

## 備考

- 警告FS3180（abImplicitHeapAllocation）は、明示的なrefを使用することで回避可能
- StackGuard("AutoboxRewriteStackGuardDepth")で深い再帰を保護
- 特殊なケース（While、TryFinally、IntegerForLoop、TryWith）はDecideExprOpで処理

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、ローカル可変変数変換で使用される主要なデータ型を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | cenv型（18-22行目） |

**主要な型定義**:
- **18-22行目**: `cenv` - コンパイラ環境
  - `g`: TcGlobals
  - `amap`: ImportMap

**読解のコツ**: このモジュールは比較的小さく（200行未満）、単一の目的（可変変数のヒープ昇格）に特化している。

#### Step 2: エスケープ検出を理解する

エスケープする可変変数の検出ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | DecideEscapes関数（25-34行目） |
| 2-2 | LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | DecideLambda関数（37-50行目） |
| 2-3 | LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | DecideExpr関数（75-108行目） |

**主要処理フロー**:
- **25-34行目**: `DecideEscapes` - エスケープ条件の判定
  - 引数として渡されていない（`passedIn`）
  - `IsMutable`がtrue
  - `ValReprInfo`がNone
  - `IsKnownOnlyMutableBeforeUse`がfalse
- **37-50行目**: `DecideLambda` - ラムダ式内のエスケープ変数を検出
- **75-108行目**: `DecideExpr` - 各種式パターンの処理

#### Step 3: 特殊ケースの処理を理解する

特殊な式パターンの処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | DecideExprOp関数（54-72行目） |

**主要処理フロー**:
- **57-58行目**: `TOp.While` - whileループの処理
- **60-61行目**: `TOp.TryFinally` - try/finallyの処理
- **63-64行目**: `TOp.IntegerForLoop` - 整数forループの処理
- **66-67行目**: `TOp.TryWith` - try/withの処理

**読解のコツ**: これらの特殊ケースは内部でラムダを使用するが、その中の可変変数はエスケープとはみなさない。

#### Step 4: 変換実行を理解する

実際の変換処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | TransformImplFile関数（172-197行目） |
| 4-2 | LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | TransformExpr関数（138-159行目） |
| 4-3 | LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | TransformBinding関数（162-169行目） |

**主要処理フロー**:
- **172-197行目**: `TransformImplFile` - メインエントリーポイント
  - 変換対象がなければ早期終了
  - 警告を発行（178行目）
  - heapValMapを作成（180-189行目）
  - RewriteImplFileで書き換え
- **142-145行目**: 値の読み取りを`mkRefCellGet`に変換
- **148-152行目**: 値の書き込みを`mkRefCellSet`に変換
- **155-157行目**: アドレス取得を`mkRecdFieldGetAddrViaExprAddr`に変換
- **162-169行目**: バインディングを`mkRefCell`でラップ

### プログラム呼び出し階層図

```
TransformImplFile (g, amap, implFile)
    │
    ├─ DecideImplFile (g, amap, implFile)
    │      │
    │      └─ FoldImplFile (folder, implFile)
    │             │
    │             ├─ DecideBinding (cenv, z, bind)
    │             │      └─ DecideLambda
    │             │             └─ DecideEscapes
    │             │
    │             ├─ DecideBindings (cenv, z, binds)
    │             │
    │             └─ DecideExpr (cenv, exprF, noInterceptF, z, expr)
    │                    ├─ Expr.Lambda → DecideLambda
    │                    ├─ Expr.TyLambda → DecideLambda
    │                    ├─ Expr.Obj → CheckMethod → DecideEscapes
    │                    └─ Expr.Op → DecideExprOp
    │
    ├─ Zset.isEmpty localsToTransform → 早期終了
    │
    ├─ warning (Error(...))  [各変数に対して]
    │
    ├─ heapValMap作成
    │      └─ mkRefCellTy / mkCompGenLocal / mkLocal
    │
    └─ RewriteImplFile
           │
           ├─ TransformExpr (g, heapValMap, exprF, expr)
           │      ├─ Expr.Val → mkRefCellGet
           │      ├─ TOp.LValueOp(LSet) → mkRefCellSet
           │      └─ TOp.LValueOp(LAddrOf) → mkRecdFieldGetAddrViaExprAddr
           │
           └─ TransformBinding (g, heapValMap, exprF, bind)
                  └─ mkRefCell
```

### データフロー図

```
[入力]                        [検出]                         [変換]

TypedImplFile
    │
    ▼
DecideImplFile ────────────▶ localsToTransform (Zset<Val>)
                                    │
                                    ├─▶ エスケープ条件判定
                                    │     - not passedIn
                                    │     - IsMutable
                                    │     - ValReprInfo = None
                                    │     - not IsKnownOnlyMutableBeforeUse
                                    │
                                    ▼
                            heapValMap (ValMap<Val * Expr>)
                                    │
                                    ├─▶ 元変数 → (新Ref変数, 新Ref式)
                                    │
                                    ▼
                            RewriteImplFile
                                    │
                                    ├─▶ TransformExpr
                                    │       val v   → !nve (mkRefCellGet)
                                    │       v := x  → nve := x (mkRefCellSet)
                                    │       &v      → &nve.contents
                                    │
                                    └─▶ TransformBinding
                                            let v = e → let nv = ref e
                                                  │
                                                  ▼
                                        変換済みTypedImplFile
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| LowerLocalMutables.fs | `src/Compiler/Optimize/LowerLocalMutables.fs` | ソース | 可変変数ヒープ昇格の実装 |
| TypedTree.fs | `src/Compiler/TypedTree/TypedTree.fs` | ソース | TAST型定義 |
| TypedTreeOps.fs | `src/Compiler/TypedTree/TypedTreeOps.fs` | ソース | TAST操作ユーティリティ |
| TcGlobals.fs | `src/Compiler/TypedTree/TcGlobals.fs` | ソース | グローバル型情報（mkRefCellTy等） |
| Optimizer.fs | `src/Compiler/Optimize/Optimizer.fs` | ソース | IsKnownOnlyMutableBeforeUse関数 |
