# 通知設計書 33-main戻り値エラー

## 概要

本ドキュメントは、Julia の `@main` エントリーポイント機能において、`main` 関数の戻り値が `nothing` でも `Cint` に変換可能でもない場合に出力されるエラー通知の設計を記述する。

### 本通知の処理概要

本通知は、Julia 1.11 以降で導入された `@main` マクロにより指定されたエントリーポイント関数の戻り値が不正な場合にエラーレベルのログを出力する処理である。

**業務上の目的・背景**：Julia 1.11 では `@main` マクロにより `Main.main` 関数をスクリプトのエントリーポイントとして指定できる。スクリプト実行完了後、`main` 関数の戻り値はプロセスの終了コードとして使用される。戻り値は `nothing`（終了コード 0 として扱われる）または `Cint`（C の `int32` 型）に変換可能である必要がある。この制約を満たさない戻り値が返された場合、ユーザーにその旨を通知し、デフォルトの終了コード 1（エラー）を使用する。

**通知の送信タイミング**：`_start` 関数内で `main` 関数の戻り値を `Cint` に変換する `try` ブロックが失敗した時点で送信される。具体的には `base/client.jl` の602行目で発火する。

**通知の受信者**：Julia プロセスの標準エラー出力 (stderr) を監視する開発者。ログレベルは `Error`。

**通知内容の概要**：`"The return value of main should be nothing or convertible to Cint"` というメッセージが出力される。

**期待されるアクション**：開発者は `main` 関数の戻り値の型を修正し、`nothing` を返すか、`Int32` / `Cint` に変換可能な値を返すようにコードを変更する。

## 通知種別

ログ（Error） -- Julia の標準ログシステム（`@error` マクロ）による stderr へのエラーレベル出力

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（`@error` マクロによる即座のログ出力） |
| 優先度 | 高 |
| リトライ | 無し |

### 送信先決定ロジック

Julia の標準ログシステムにより、現在アクティブなログハンドラに送信される。

## 通知テンプレート

### メール通知の場合

該当なし（本通知はログ出力であり、メール送信は行わない）

### 本文テンプレート

```
The return value of `main` should be `nothing` or convertible to `Cint`
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| （なし） | 本通知は固定文字列のみ | -- | -- |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| スクリプト実行完了 | `main` 関数の戻り値を `Cint` に変換する試みが失敗 | `ret !== nothing` かつ `Cint(ret)` が例外をスロー | `_start` 関数内の `try-catch` で検出 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `ret === nothing` | 戻り値が `nothing` の場合、終了コード 0 が設定され本通知は発生しない |
| `Cint(ret)` が成功 | 戻り値が `Cint` に変換可能な場合、正常に終了コードが設定される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A["_start(): main 関数を呼び出し"] --> B["ret = invokelatest(main, ARGS)"]
    B --> C{"ret === nothing か"}
    C -->|Yes| D["ret = 0"]
    C -->|No| E{"Cint(ret) が成功するか"}
    E -->|成功| F["ret = Cint(ret)"]
    E -->|失敗 catch| G["@error 戻り値エラー通知"]
    G --> H["ret = Cint(1)"]
    D --> I["プロセス終了コードとして ret を使用"]
    F --> I
    H --> I
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（データベースは使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 戻り値変換失敗 | `main` の戻り値が `Cint` に変換不能 | @error 通知を発行し、終了コード 1 をデフォルト設定 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 無し |
| リトライ間隔 | -- |
| リトライ対象エラー | -- |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（スクリプト実行ごとに最大1回） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

本通知は固定メッセージのみを出力し、機密情報は含まない。`main` 関数の実際の戻り値は出力されない。

## 備考

- `@main` マクロは Julia 1.11 で導入された機能。詳細は `base/client.jl` の629-696行目のドキュメントを参照。
- 本通知が発行された場合、プロセスの終了コードは `Cint(1)`（エラー）に設定される。
- `should_use_main_entrypoint()` が `false` を返す場合（`@main` が使われていない場合）、`main` 関数は呼び出されないため、本通知は発生しない。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | client.jl | `base/client.jl` | `@main` マクロ（680-696行目）により設定される `#__main_is_entrypoint__#` フラグの構造を理解する |

**読解のコツ**: `@main` マクロは対象モジュールに `main` グローバル変数と `#__main_is_entrypoint__#` フラグを設定する。`should_use_main_entrypoint()`（568-573行目）がこのフラグを確認する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | client.jl | `base/client.jl` | `_start` 関数（575-613行目）が Julia プロセスのエントリーポイント |

**主要処理フロー**:
1. **586行目**: `should_use_main_entrypoint()` で `@main` の有無を確認
2. **591行目**: `ret = invokelatest(main, ARGS)` で `main` 関数を呼び出し
3. **598行目**: `ret === nothing` の場合 `ret = 0` に設定
4. **599-604行目**: `Cint(ret)` への変換を試行、失敗時に本通知を発行

#### Step 3: @main マクロの定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | client.jl | `base/client.jl` | `@main` マクロ（680-696行目）と `should_use_main_entrypoint`（568-573行目） |

**主要処理フロー**:
- **686-690行目**: `@main` はモジュール内に `global main` と `#__main_is_entrypoint__#` = `true` を設定
- **569行目**: `should_use_main_entrypoint` は `Main.main` が定義されているか確認
- **571行目**: `#__main_is_entrypoint__#` フラグが `true` であることを確認

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

```
_start() [base/client.jl:575]
    |
    +-- exec_options() [行585]
    |
    +-- should_use_main_entrypoint() [行586]
    |       |
    |       +-- binding_module(Main, :main) [行570]
    |       +-- #__main_is_entrypoint__# チェック [行571]
    |
    +-- invokelatest(main, ARGS) [行591]
    |       |
    |       +-- ret = main の戻り値
    |
    +-- Cint(ret) [行600]
            |
            +-- 失敗時: @error (No.33 本通知) [行602]
            +-- ret = Cint(1) [行603]
```

### データフロー図

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

ARGS (コマンドライン引数)
    |
    +--> main(ARGS) ---------> ret (戻り値)
                                  |
                                  +--> nothing チェック
                                  |       |
                                  |       +--> ret = 0
                                  |
                                  +--> Cint(ret) 変換
                                          |
                                          +-- 成功 --> 終了コード
                                          +-- 失敗 --> @error ログ --> stderr
                                                       ret = Cint(1)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| client.jl | `base/client.jl` | ソース | `_start` 関数、`@main` マクロ、`should_use_main_entrypoint` の定義 |
| logging.jl | `base/logging.jl` | ソース | `@error` マクロの定義 |
