# 機能設計書 84-asyncmacro

## 概要

本ドキュメントは、Nim標準ライブラリのasyncmacroモジュールに関する機能設計書である。async/awaitキーワードを実現するためのマクロを実装し、非同期コードを同期的なスタイルで記述可能にする。

### 本機能の処理概要

asyncmacroモジュールは、Nimのメタプログラミング機能を活用して、非同期プロシージャを通常のプロシージャのように記述できるようにするマクロを提供する。`{.async.}`プラグマで修飾されたプロシージャは、内部的にクロージャイテレータに変換され、`await`キーワードでFutureの完了を待機できるようになる。

**業務上の目的・背景**：従来のコールバックベースの非同期プログラミングは「コールバック地獄」と呼ばれる複雑なネスト構造を生み出しやすい。async/awaitパターンにより、非同期コードを同期コードのように直線的に記述でき、可読性と保守性が大幅に向上する。

**機能の利用シーン**：HTTPサーバー/クライアント、データベースアクセス、ファイルI/O、WebSocket通信など、非同期I/Oを伴う全てのプログラムで使用される。

**主要な処理内容**：
1. asyncマクロによるプロシージャのクロージャイテレータへの変換
2. awaitテンプレートによるFuture待機の実現
3. multisyncマクロによる同期/非同期両対応のプロシージャ生成

**関連システム・外部連携**：asyncfutures（Future型）、asyncdispatch（イベントループ）と連携する。

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

## 関連画面

該当なし（マクロモジュールのため画面との直接的な関連はない）

## 機能種別

メタプログラミング / コード変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| prc | NimNode | Yes | asyncマクロに渡されるプロシージャ定義 | proc/method/lambda |
| f | Future[T] | Yes | awaitテンプレートに渡されるFuture | 非nil |

### 入力データソース

コンパイル時のAST（抽象構文木）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 変換後プロシージャ | NimNode | クロージャイテレータを含む変換済みプロシージャ |
| 変換後awaitコード | NimNode | yield + readによるFuture待機コード |

### 出力先

コンパイラへのAST出力

## 処理フロー

### 処理シーケンス

```
1. asyncマクロ呼び出し
   └─ asyncSingleProc(prc)
      ├─ 戻り値型の検証（Future[T]またはvoid）
      ├─ プロシージャ本体の変換
      │    └─ processBody() - returnの変換、tryブロックの処理
      ├─ クロージャイテレータの生成
      │    └─ iteratorNameSym = genSym(nskIterator)
      ├─ コールバックプロシージャの生成
      │    └─ createCbテンプレート
      └─ 新規Future生成とイテレータ実行

2. await展開
   └─ awaitテンプレート
      ├─ yieldFutureの検証
      ├─ internalTmpFuture = f として保存
      ├─ yield internalTmpFuture
      └─ cast[typeof(f)](internalTmpFuture).read()

3. multisyncマクロ呼び出し
   └─ splitProc(prc)
      ├─ 同期版プロシージャの生成（awaitを除去）
      └─ 非同期版プロシージャの生成（asyncマクロ適用）
```

### フローチャート

```mermaid
flowchart TD
    A["{.async.}プロシージャ"] --> B[asyncマクロ]
    B --> C[asyncSingleProc]
    C --> D[戻り値型検証]
    D --> E{Future[T] or void?}
    E -->|No| F[コンパイルエラー]
    E -->|Yes| G[processBody]
    G --> H[return文変換]
    H --> I[tryブロック処理]
    I --> J[クロージャイテレータ生成]
    J --> K[コールバック生成]
    K --> L[Future生成コード]
    L --> M[変換完了]

    N[await f] --> O[awaitテンプレート]
    O --> P{yield可能?}
    P -->|No| Q[コンパイルエラー]
    P -->|Yes| R[yield f]
    R --> S[f.read]
    S --> T[結果取得]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-84-01 | 戻り値型制約 | async procはFuture[T]またはvoid（暗黙Future[void]） | asyncプラグマ使用時 |
| BR-84-02 | discardable禁止 | async procにdiscardableプラグマは使用不可 | asyncプラグマ使用時 |
| BR-84-03 | awaitコンテキスト | awaitはasync proc内でのみ使用可能 | await呼び出し時 |
| BR-84-04 | varパラメータ禁止 | async procでvarパラメータは使用不可 | asyncプラグマ使用時 |

### 計算ロジック

**プロシージャ変換アルゴリズム**:
1. 元のプロシージャ本体をprocessBodyで変換
   - return文をFuture.complete + iterator returnに変換
   - tryブロック内のreturnは特別処理（needsCompletionフラグ）
2. 変換後のボディをクロージャイテレータとして定義
3. コールバックプロシージャ（cb）を生成
   - イテレータを実行し、完了チェック
   - 未完了ならコールバックを次のFutureに登録
4. 新規Futureを生成し、cbを呼び出すコードを生成

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| コンパイルエラー | 戻り値型エラー | Future以外の戻り値型 | Future[T]に変更 |
| コンパイルエラー | discardableエラー | discardableプラグマ使用 | asyncCheckを使用 |
| コンパイルエラー | awaitコンテキストエラー | async外でawait使用 | waitForを使用 |
| AssertionDefect | nil Future await | nilのFutureをawait | Futureが非nilであることを確認 |

### リトライ仕様

該当なし（コンパイル時マクロ）

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

該当なし

## パフォーマンス要件

- コンパイル時のみ処理されるため、実行時オーバーヘッドは最小限
- 生成されるクロージャイテレータは効率的なステートマシンに変換
- stackTrace:offプラグマでスタックトレースオーバーヘッドを削減

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

- マクロ変換は完全にコンパイル時に行われ、実行時の脆弱性なし
- 例外処理は適切にFuture.failに変換される

## 備考

- -d:nimDumpAsyncで変換後のコードを出力可能
- gcsafeプラグマはイテレータにも継承される
- NimAsyncContinueSuffix定数はスタックトレース改善に使用

---

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

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

### 推奨読解順序

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

まず、マクロで使用される補助的な型と定数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | Context型（14-17行目） |
| 1-2 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | ClosureIt型（26行目） |
| 1-3 | asyncfutures.nim | `lib/pure/asyncfutures.nim` | NimAsyncContinueSuffix定数（51行目） |

**読解のコツ**:
- Context型はprocessBody内でのtry/returnの状態管理に使用
- ClosureIt型はクロージャイテレータの型定義

#### Step 2: awaitテンプレートを理解する

awaitの展開結果を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | await（型エラー版）（161-163行目） |
| 2-2 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | await（Future[T]版）（165-181行目） |

**主要処理フロー**:
- **161-163行目**: Future[T]以外が渡された場合のエラーメッセージ
- **165-181行目**: yieldでFutureを返し、完了後にreadで結果取得

#### Step 3: プロシージャ本体の変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | processBody関数（74-129行目） |
| 3-2 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | nnkReturnStmtの処理（77-99行目） |
| 3-3 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | nnkTryStmtの処理（103-126行目） |

**主要処理フロー**:
- **77-99行目**: return文をcomplete呼び出し + nilリターンに変換
- **88行目**: try内のreturnはneedsCompletionフラグ経由で完了
- **103-126行目**: finally付きtryの特別処理

#### Step 4: asyncSingleProc関数を理解する

これがasyncマクロの中核処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | asyncSingleProc関数（183-326行目） |
| 4-2 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | 戻り値型検証（208-226行目） |
| 4-3 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | イテレータ生成（241-286行目） |
| 4-4 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | コールバック生成（288-298行目） |
| 4-5 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | Future生成（300-314行目） |

**主要処理フロー**:
- **208-226行目**: Future[T]であることを確認、なければvoid想定
- **247-249行目**: processBodyでプロシージャ本体を変換
- **278-281行目**: closure/gcsafeプラグマをイテレータに追加
- **300-308行目**: newFuture[T]を生成するコード生成

#### Step 5: createCbテンプレートを理解する

コールバックプロシージャの生成ロジック。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | createCbテンプレート（28-57行目） |

**主要処理フロー**:
- **33-40行目**: イテレータを実行し、次のFutureを取得
- **41-44行目**: nilチェック（await nilの検出）
- **46-48行目**: 次のFutureにコールバックを登録
- **49-56行目**: 例外処理（Future.fail呼び出し）

#### Step 6: multisyncマクロを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | splitParamType関数（340-349行目） |
| 6-2 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | splitProc関数（359-385行目） |
| 6-3 | asyncmacro.nim | `lib/pure/asyncmacro.nim` | multisyncマクロ（387-396行目） |

**主要処理フロー**:
- **340-349行目**: `Socket | AsyncSocket`のような型を分離
- **359-385行目**: 同期版と非同期版のプロシージャを生成
- **373-377行目**: 同期版ではawaitをノーオップに変換

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

```
コンパイラ（マクロ展開）
    │
    ├─ asyncマクロ(prc)
    │      └─ asyncSingleProc(prc)
    │            ├─ verifyReturnType()
    │            ├─ getFutureVarIdents()
    │            ├─ processBody(prc.body)
    │            │      ├─ [nnkReturnStmt] → complete + nil return
    │            │      ├─ [nnkTryStmt] → needsCompletion処理
    │            │      └─ [other] → 再帰処理
    │            ├─ クロージャイテレータ生成
    │            ├─ createCb展開
    │            ├─ newFuture呼び出しコード生成
    │            └─ cb(retFuture, iter)呼び出しコード生成
    │
    ├─ awaitテンプレート(f)
    │      ├─ [yield可能] yield f; f.read()
    │      └─ [yield不可] コンパイルエラー
    │
    └─ multisyncマクロ(prc)
           └─ splitProc(prc)
                  ├─ 同期版生成（await → 値そのまま）
                  └─ 非同期版生成（asyncSingleProc適用）
```

### データフロー図（AST変換）

```
[入力AST]                    [変換処理]                  [出力AST]

proc foo(): Future[int] {.async.} =
  let x = await bar()           ┌─────────────────────┐
  return x + 1                  │   asyncSingleProc   │
                                └─────────────────────┘
        │                               │
        ▼                               ▼
┌─────────────────────┐    ┌─────────────────────────────────┐
│ NimNode(nnkProcDef) │    │ proc foo(): owned(Future[int]) =│
│   name: foo         │    │   iterator fooIter(retFut):     │
│   params: Future[]  │    │     var result: int             │
│   body:             │    │     var internalTmp = bar()     │
│     await bar()     │ ──▶│     yield internalTmp           │
│     return x + 1    │    │     let x = internalTmp.read()  │
└─────────────────────┘    │     result = x + 1              │
                           │     complete(retFut, result)    │
                           │   proc fooCb(fut, it) = ...     │
                           │   var retFuture = newFuture[]   │
                           │   fooCb(retFuture, fooIter)     │
                           │   return retFuture              │
                           └─────────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| asyncmacro.nim | `lib/pure/asyncmacro.nim` | メインソース | async/awaitマクロ実装 |
| asyncfutures.nim | `lib/pure/asyncfutures.nim` | 依存 | Future型定義 |
| asyncdispatch.nim | `lib/pure/asyncdispatch.nim` | 利用元 | asyncマクロをエクスポート |
| macros.nim | `lib/core/macros.nim` | 依存 | NimNode操作API |
| strutils.nim | `lib/pure/strutils.nim` | 依存 | 文字列処理 |
