# 機能設計書 54-ジェネリクス (Generics)

## 概要

本ドキュメントは、Lua Language Serverにおけるジェネリクス機能の設計を記載する。この機能は、`---@generic`アノテーションで定義された型パラメータを、関数呼び出し時の実引数から解決し、具体的な型に置き換える機能を提供する。

### 本機能の処理概要

ジェネリクス機能は、型パラメータ（例：`T`、`K`、`V`）を持つ関数やクラスにおいて、呼び出し時の実引数から型パラメータを具体的な型に解決（resolve）する機能である。これにより、汎用的な関数定義でありながら、呼び出し箇所では具体的な型情報を得ることができる。

**業務上の目的・背景**：汎用的なユーティリティ関数（pairs、ipairs、map、filterなど）は、様々な型に対して動作する。ジェネリクスなしでは、これらの関数の戻り値型は`any`や`unknown`となり、型情報が失われる。ジェネリクス機能により、呼び出し時の具体的な型を推論し、型安全性を維持できる。

**機能の利用シーン**：
- イテレータ関数（pairs、ipairs）の型推論
- コレクション操作関数（map、filter）の型推論
- ファクトリパターンの型推論
- 汎用コンテナクラスの型推論

**主要な処理内容**：
1. `---@generic`アノテーションから型パラメータを抽出
2. 関数の引数シグネチャから型パラメータの出現位置を特定
3. 実引数の型から型パラメータを解決
4. 解決された型を使用して戻り値型を具体化
5. vm.genericオブジェクトとして結果を返却

**関連システム・外部連携**：型推論機能（vm/infer.lua）、LuaDoc解析（parser/luadoc.lua）と連携。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ホバー情報表示 | 補助機能 | ジェネリック関数の戻り値型を表示 |
| 2 | コード補完 | 補助機能 | ジェネリック型のフィールド補完 |

## 機能種別

計算処理 / 型解決

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | ファイルURI | 有効なfile:// URI |
| args | parser.object | Yes | 関数呼び出しの実引数 | - |
| sign | vm.sign | No | 関数のシグネチャ情報 | - |

### 入力データソース

- LuaDocアノテーション（`---@generic`）
- 関数呼び出しの実引数
- 型推論結果（vm.node）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| resolved | table<string, vm.node> | 型パラメータ名から具体的型へのマッピング |
| vm.generic | object | 解決されたジェネリック型オブジェクト |

### 出力先

- 型推論結果に統合
- ホバー情報、補完候補に反映

## 処理フロー

### 処理シーケンス

```
1. vm.getSign呼び出し
   └─ 関数ソースからシグネチャ情報を取得

2. 型パラメータ抽出
   └─ doc.genericから型パラメータ名を収集

3. シグネチャ構築
   └─ 各引数の型ノードをsignListに追加

4. sign:resolve呼び出し
   └─ 実引数から型パラメータを解決

5. 再帰的解決
   └─ 配列型、テーブル型、関数型の型パラメータを解決

6. 結果返却
   └─ resolved（型パラメータ→具体型マッピング）を返却
```

### フローチャート

```mermaid
flowchart TD
    A[関数呼び出し検出] --> B[vm.getSign]
    B --> C{シグネチャ存在?}
    C -->|No| D[通常の型推論]
    C -->|Yes| E[sign:resolve実行]
    E --> F{引数ループ}
    F --> G[argNode取得]
    G --> H[signから既知型を除外]
    H --> I[resolve再帰呼び出し]
    I --> J{オブジェクト種別}
    J -->|doc.generic.name| K[型パラメータ解決]
    J -->|doc.type.array| L[配列要素解決]
    J -->|doc.type.table| M[テーブル解決]
    J -->|doc.type.function| N[関数引数/戻り値解決]
    K --> O[resolved更新]
    L --> O
    M --> O
    N --> O
    O --> F
    F -->|完了| P[resolved返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-54-01 | 引数順解決 | 型パラメータは引数の順序で解決 | 常時 |
| BR-54-02 | 既知型除外 | シグネチャ内の既知型は解決対象から除外 | 常時 |
| BR-54-03 | 配列要素 | T[]の場合、配列要素からTを解決 | 配列型 |
| BR-54-04 | テーブルKV | {[K]:V}の場合、キーと値からK,Vを解決 | テーブル型 |
| BR-54-05 | 関数型 | fun(T):Rの場合、引数からT、戻り値からRを解決 | 関数型 |
| BR-54-06 | リテラル型 | `T`（バッククォート）はリテラル値を型として解決 | リテラル指定 |
| BR-54-07 | Optional継承 | 引数がoptionalなら解決結果もoptional | optional引数 |

### 計算ロジック

**型パラメータ解決アルゴリズム**：
```
resolve(object, node):
  if object is doc.generic.name:
    if object.literal:
      for n in node: resolved[key] = type(n.value)
    else:
      for n in node: resolved[key].merge(n)
  if object is doc.type.array:
    resolve(object.node, getArrayElement(node))
  if object is doc.type.table:
    resolve(field.key, getTableKey(node))
    resolve(field.value, getTableValue(node))
  if object is doc.type.function:
    for each arg: resolve(arg, functionArg(node))
    for each ret: resolve(ret, functionRet(node))
```

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

### 操作別データベース影響一覧

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 解決不可 | 型パラメータが解決できない | unknown型として扱う |
| - | 循環参照 | 解決処理が循環 | visitedマークで中断 |

### リトライ仕様

リトライは行わない。

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

トランザクションは使用しない。

## パフォーマンス要件

- visitedテーブルで重複解決を防止
- 同一ソースに対するシグネチャはキャッシュ（_sign）

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

特になし。

## 備考

- ジェネリクスはLua標準機能ではなく、LuaDoc拡張機能
- 複雑なジェネリック型の解決は計算コストが高い場合がある

---

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

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

### 推奨読解順序

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

ジェネリクスで使用するデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sign.lua | `script/vm/sign.lua` | vm.signクラス定義（5-11行目） |
| 1-2 | generic.lua | `script/vm/generic.lua` | vm.genericクラス定義（8-13行目） |

**読解のコツ**:
- `vm.sign`は関数のジェネリックシグネチャを管理
  - `signList`: 各引数の型ノード配列
  - `docGeneric`: `---@generic`ドキュメント配列
- `vm.generic`は解決されたジェネリック型を表現
  - `sign`: シグネチャ情報
  - `proto`: プロトタイプとなる型

#### Step 2: シグネチャ取得を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sign.lua | `script/vm/sign.lua` | vm.getSign関数（294-350行目） |

**主要処理フロー**:
1. **295-298行目**: キャッシュ確認（`_sign`）
2. **299-323行目**: function型の場合、bindDocsからdoc.genericを抽出
3. **314-322行目**: 各引数のノードをsignListに追加
4. **324-348行目**: doc.type.function/table/arrayの場合、ジェネリック名をスキャン

#### Step 3: 型パラメータ解決を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sign.lua | `script/vm/sign.lua` | mt:resolve関数（26-272行目） |

**主要処理フロー**:
- **38-179行目**: resolve内部関数 - 再帰的に型パラメータを解決
- **55-84行目**: doc.generic.nameの場合の解決
- **85-111行目**: 配列型の解決
- **112-149行目**: テーブル型の解決
- **150-178行目**: 関数型の解決
- **184-216行目**: getSignInfo - シグネチャから既知型とジェネリック名を抽出
- **223-246行目**: buildArgNode - 既知型を除外した引数ノードを構築
- **258-269行目**: 各引数に対して解決を実行

#### Step 4: ジェネリック型の適用を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | generic.lua | `script/vm/generic.lua` | mt:resolve関数（167-188行目） |
| 4-2 | generic.lua | `script/vm/generic.lua` | cloneObject関数（18-162行目） |

**主要処理フロー**:
- **167-188行目**: 解決された型をプロトタイプに適用
- **18-162行目**: 型オブジェクトをクローンして型パラメータを置換

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

```
関数呼び出し検出（compiler.lua内）
    │
    └─ vm.getSign(source)
           │
           ├─ [キャッシュ確認] source._sign
           │
           ├─ [function型]
           │      ├─ vm.createSign()
           │      ├─ sign:addDocGeneric(doc)
           │      └─ sign:addSign(argNode)
           │
           └─ [doc.type.function型]
                  └─ guide.eachSourceType(..., 'doc.generic.name', ...)

sign:resolve(uri, args)
    │
    ├─ resolve(sign, argNode) [各引数に対して]
    │      │
    │      ├─ [doc.generic.name]
    │      │      └─ resolved[key] = vm.createNode(n)
    │      │
    │      ├─ [doc.type.array]
    │      │      └─ resolve(object.node, arrayElement)
    │      │
    │      ├─ [doc.type.table]
    │      │      ├─ resolve(field.key, tableKey)
    │      │      └─ resolve(field.value, tableValue)
    │      │
    │      └─ [doc.type.function]
    │             ├─ resolve(arg.extends, funcArg)
    │             └─ resolve(ret, funcRet)
    │
    └─ return resolved

vm.generic:resolve(uri, args)
    │
    ├─ self.sign:resolve(uri, args)
    │
    └─ cloneObject(proto, resolved)
           │
           └─ 型パラメータを具体的型に置換
```

### データフロー図

```
[入力]                      [処理]                      [出力]

---@generic T              vm.getSign              ───▶  vm.sign
function foo(x: T)         シグネチャ構築                {signList, docGeneric}
                                │
foo(myValue)              sign:resolve            ───▶  resolved
実引数                      型パラメータ解決            {T: vm.node}
                                │
                          cloneObject             ───▶  vm.node
                          型適用                        (具体的な型)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sign.lua | `script/vm/sign.lua` | ソース | シグネチャ管理・解決 |
| generic.lua | `script/vm/generic.lua` | ソース | ジェネリック型クラス |
| compiler.lua | `script/vm/compiler.lua` | ソース | 型推論エンジン |
| infer.lua | `script/vm/infer.lua` | ソース | 型ビュー生成 |
| node.lua | `script/vm/node.lua` | ソース | vm.nodeクラス |
| luadoc.lua | `script/parser/luadoc.lua` | ソース | LuaDoc解析 |
| guide.lua | `script/parser/guide.lua` | ソース | ASTユーティリティ |
