# 機能設計書：10-モジュールグラフ管理

## 1. 機能概要

### 1.1 目的
本機能は、Nimプロジェクト全体のモジュール間依存関係を管理するモジュールグラフデータ構造を提供する。コンパイラの各フェーズ（セマンティック解析、コード生成など）がモジュール情報にアクセスするための中央リポジトリとして機能する。

### 1.2 機能範囲
- モジュール依存グラフの構築と管理
- 型のインスタンス化キャッシュ
- アタッチされた演算子（デストラクタ等）の管理
- メソッドテーブルの管理
- コンパイラプロシージャ（compilerprocs）の管理
- マクロキャッシュAPI（cacheSeqs、cacheTables等）のサポート
- nimsuggestモードでの差分コンパイルサポート
- NIF（Nimony Intermediate Format）との連携

### 1.3 関連モジュール
| モジュール | 役割 |
|-----------|------|
| modulegraphs.nim | モジュールグラフ本体 |
| ast.nim | AST定義とシンボル型 |
| options.nim | コンパイラ設定 |
| idents.nim | 識別子キャッシュ |
| lineinfos.nim | 行情報・ファイルインデックス |
| passes.nim | コンパイルパス管理 |
| typekeys.nim | 型キー生成 |


## 2. アーキテクチャ設計

### 2.1 システム構成図
```
+------------------+
|  ModuleGraph     |
+------------------+
       |
       +-- ifaces: seq[Iface]          -- モジュールインターフェース
       |     |
       |     +-- module: PSym          -- モジュールシンボル
       |     +-- converters: seq[PSym] -- コンバータ一覧
       |     +-- patterns: seq[PSym]   -- パターン一覧
       |     +-- interf: TStrTable     -- 公開シンボル
       |     +-- interfHidden: TStrTable -- 全シンボル
       |
       +-- deps: IntSet                 -- 依存関係グラフ
       |
       +-- typeInstCache                -- 型インスタンスキャッシュ
       +-- procInstCache                -- プロシージャインスタンスキャッシュ
       +-- attachedOps                  -- アタッチ演算子
       |
       +-- methods                      -- メソッドテーブル
       +-- dispatchers                  -- ディスパッチャ
       |
       +-- compilerprocs: TStrTable     -- コンパイラプロシージャ
       +-- exposed: TStrTable           -- 公開シンボル
       +-- systemModule: PSym           -- systemモジュール
       |
       +-- config: ConfigRef            -- 設定
       +-- cache: IdentCache            -- 識別子キャッシュ
       +-- vm: RootRef                  -- VM状態
       +-- idgen: IdGenerator           -- ID生成
```

### 2.2 モジュール間依存関係
```
      main.nim
         |
    +----+----+
    |         |
  a.nim     b.nim
    |         |
    +----+----+
         |
     system.nim
```
上記の依存関係は`deps: IntSet`でエンコードされ、`dependsOn(a, b)`関数で `(a shl 15) + b` の形式で格納される。


## 3. データ構造設計

### 3.1 主要データ型

#### ModuleGraph型（モジュールグラフ本体）
```nim
# compiler/modulegraphs.nim 57-127行目
ModuleGraph* {.acyclic.} = ref object
  ifaces*: seq[Iface]  # FileIndexでインデックス

  # インスタンス化キャッシュ
  typeInstCache*: Table[ItemId, seq[PType]]
  procInstCache*: Table[ItemId, seq[PInstantiation]]

  # アタッチ演算子（デストラクタ、コピー等）
  attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]]
  loadedOps: array[TTypeAttachedOp, Table[string, PSym]]
  opsLog*: seq[LogEntry]

  # メソッド関連
  methodsPerGenericType*: Table[ItemId, seq[(int, PSym)]]
  memberProcsPerType*: Table[ItemId, seq[PSym]]
  methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]]
  dispatchers*: seq[PSym]
  methodsPerType*: Table[ItemId, seq[PSym]]

  # 依存関係
  deps*: IntSet
  importDeps*: Table[FileIndex, seq[FileIndex]]
  importStack*: seq[FileIndex]

  # システム・コンパイラプロシージャ
  systemModule*: PSym
  sysTypes*: array[TTypeKind, PType]
  compilerprocs*: TStrTable
  exposed*: TStrTable
  packageTypes*: TStrTable

  # マクロキャッシュAPI
  cacheSeqs*: Table[string, PNode]
  cacheCounters*: Table[string, BiggestInt]
  cacheTables*: Table[string, BTree[string, PNode]]

  # 設定・状態
  config*: ConfigRef
  cache*: IdentCache
  vm*: RootRef
  backend*: RootRef
  idgen*: IdGenerator
  operators*: Operators

  # nimsuggest関連
  suggestMode*: bool
  suggestSymbols*: SuggestSymbolDatabase
  suggestErrors*: Table[FileIndex, seq[Suggest]]
  usageSym*: PSym

  # NIF関連
  nifReplayActions*: Table[int32, seq[PNode]]
  cachedMods: IntSet
```

#### Iface型（モジュールインターフェース）
```nim
# compiler/modulegraphs.nim 30-38行目
Iface* = object
  module*: PSym           # モジュールシンボル
  converters*: seq[PSym]  # このモジュールのコンバータ
  patterns*: seq[PSym]    # パターン
  pureEnums*: seq[PSym]   # pure enum
  interf: TStrTable       # 公開インターフェース
  interfHidden: TStrTable # 非公開含む全シンボル
  uniqueName*: Rope       # 一意なモジュール名
```

#### PipelinePass列挙型
```nim
# compiler/modulegraphs.nim 44-55行目
PipelinePass* = enum
  NonePass
  SemPass           # セマンティック解析
  JSgenPass         # JavaScript生成
  CgenPass          # C生成
  NifgenPass        # NIF生成
  EvalPass          # 評価（VM）
  InterpreterPass   # インタプリタ
  GenDependPass     # 依存関係生成
  Docgen2TexPass    # LaTeXドキュメント
  Docgen2JsonPass   # JSONドキュメント
  Docgen2Pass       # HTMLドキュメント
```

#### Operators型（演算子シンボル）
```nim
# compiler/modulegraphs.nim 40-43行目
Operators* = object
  opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
  opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
```

#### TPass型（コンパイルパス）
```nim
# compiler/modulegraphs.nim 137-140行目
TPass* = tuple[
  open: TPassOpen,
  process: TPassProcess,
  close: TPassClose,
  isFrontend: bool
]
```


## 4. 処理フロー設計

### 4.1 モジュールグラフ初期化

```nim
# compiler/modulegraphs.nim 514-546行目
proc initModuleGraphFields(result: ModuleGraph) =
  # ID生成器（モジュール-1は特殊：グラフ自体のシンボル）
  result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)

  # テーブル初期化
  result.packageSyms = initStrTable()
  result.deps = initIntSet()
  result.importDeps = initTable[FileIndex, seq[FileIndex]]()
  result.ifaces = @[]
  result.importStack = @[]
  result.inclToMod = initTable[FileIndex, FileIndex]()
  result.owners = @[]
  result.suggestSymbols = initTable[FileIndex, SuggestFileSymbolDatabase]()
  result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
  result.methods = @[]
  result.compilerprocs = initStrTable()
  result.exposed = initStrTable()
  result.packageTypes = initStrTable()
  result.emptyNode = newNode(nkEmpty)

  # マクロキャッシュ初期化
  result.cacheSeqs = initTable[string, PNode]()
  result.cacheCounters = initTable[string, BiggestInt]()
  result.cacheTables = initTable[string, BTree[string, PNode]]()

  # 型キャッシュ
  result.canonTypes = initTable[SigHash, PType]()
  result.symBodyHashes = initTable[int, SigHash]()

  # 演算子初期化
  result.operators = initOperators(result)

proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
  result = ModuleGraph()
  result.config = config
  result.cache = cache
  initModuleGraphFields(result)
  ast.setupProgram(config, cache)
```

### 4.2 モジュール登録

```nim
# compiler/modulegraphs.nim 480-490行目
proc registerModule*(g: ModuleGraph; m: PSym) =
  assert m != nil
  assert m.kind == skModule

  # ifacesを拡張
  if m.position >= g.ifaces.len:
    setLen(g.ifaces, m.position + 1)

  # Ifaceを初期化（まだ登録されていない場合）
  if g.ifaces[m.position].module == nil:
    g.ifaces[m.position] = Iface(
      module: m,
      converters: @[],
      patterns: @[],
      uniqueName: rope(uniqueModuleName(g.config, m))
    )
    initStrTables(g, m)
```

### 4.3 依存関係管理

```nim
# compiler/modulegraphs.nim 571-591行目
proc dependsOn(a, b: int): int {.inline.} =
  (a shl 15) + b

proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
  assert m.position == m.info.fileIndex.int32
  if g.suggestMode:
    g.deps.incl m.position.dependsOn(dep.int)
    # 推移閉包はクエリ時に遅延計算

proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
  discard hasKeyOrPut(g.inclToMod, includeFile, module)

proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
  ## includeファイルの場合、親モジュールを返す
  if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len and
     g.ifaces[fileIdx.int32].module != nil:
    result = fileIdx
  else:
    result = g.inclToMod.getOrDefault(fileIdx)
```

### 4.4 アタッチ演算子管理

```nim
# compiler/modulegraphs.nim 271-304行目
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
  ## 型tに対するアタッチ演算子を取得。なければnil。
  if g.attachedOps[op].contains(t.itemId):
    result = g.attachedOps[op][t.itemId]
  elif g.config.cmd in {cmdNifC, cmdM}:
    # NIF読み込み済みフックのフォールバック
    let key = typeKey(t, g.config, loadTypeCallback, loadSymCallback)
    result = g.loadedOps[op].getOrDefault(key)
  else:
    result = nil

proc setAttachedOp*(g: ModuleGraph; module: int; t: PType;
                    op: TTypeAttachedOp; value: PSym) =
  ## アタッチ演算子を登録
  if not g.attachedOps[op].contains(t.itemId):
    let key = typeKey(t, g.config, loadTypeCallback, loadSymCallback)
    if key notin g.loadedOps[op]:
      # opsLogに記録（NIF出力用）
      let ownerModule = if t.sym != nil: t.sym.itemId.module.int else: module
      g.opsLog.add LogEntry(kind: HookEntry, op: op, module: ownerModule,
                            key: key, sym: value)
      g.loadedOps[op][key] = value
  g.attachedOps[op][t.itemId] = value
```

### 4.5 シンボル検索

```nim
# compiler/modulegraphs.nim 221-249行目
proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym;
                     name: PIdent): PSym =
  assert m.kind == skModule
  mi.modIndex = m.position
  mi.importHidden = optImportHidden in m.options
  result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden), name)

proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
  result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden))

iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
  let importHidden = optImportHidden in m.options
  for s in g.ifaces[m.position].interfSelect(importHidden).data:
    if s != nil:
      yield s

proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
  let importHidden = optImportHidden in m.options
  result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)

proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
  result = someSym(g, g.systemModule, name)
```

### 4.6 nimsuggest差分コンパイル

```nim
# compiler/modulegraphs.nim 602-649行目
proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
  let m = g.getModule fileIdx
  if m != nil:
    g.suggestSymbols.del(fileIdx)
    g.suggestErrors.del(fileIdx)
    incl m.flagsImpl, sfDirty

proc unmarkAllDirty*(g: ModuleGraph) =
  for i in 0i32..<g.ifaces.len.int32:
    let m = g.ifaces[i].module
    if m != nil:
      m.flagsImpl.excl sfDirty

proc isDirty*(g: ModuleGraph; m: PSym): bool =
  result = g.suggestMode and sfDirty in m.flags

proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
  # 推移閉包が無効なら再計算
  if g.invalidTransitiveClosure:
    g.invalidTransitiveClosure = false
    transitiveClosure(g.deps, g.ifaces.len)

  # このファイルに依存する全モジュールをdirtyに
  for i in 0i32..<g.ifaces.len.int32:
    if g.deps.contains(i.dependsOn(fileIdx.int)):
      g.markDirty(FileIndex(i))

proc needsCompilation*(g: ModuleGraph): bool =
  result = false
  for i in 0i32..<g.ifaces.len.int32:
    let m = g.ifaces[i].module
    if m != nil:
      if sfDirty in m.flags:
        return true
```


## 5. インターフェース設計

### 5.1 公開API

| API | 説明 |
|-----|------|
| `newModuleGraph` | モジュールグラフ作成 |
| `registerModule` | モジュール登録 |
| `getModule` | FileIndexからモジュール取得 |
| `addDep` | 依存関係追加 |
| `parentModule` | includeファイルの親モジュール取得 |
| `getAttachedOp` | アタッチ演算子取得 |
| `setAttachedOp` | アタッチ演算子設定 |
| `someSym` | モジュールからシンボル検索 |
| `allSyms` | モジュールの全シンボル列挙 |
| `systemModuleSym` | systemモジュールからシンボル検索 |
| `loadCompilerProc` | コンパイラプロシージャ読み込み |
| `markDirty` | モジュールをdirtyマーク（nimsuggest） |
| `needsCompilation` | 再コンパイル必要判定 |

### 5.2 マクロキャッシュAPI

マクロキャッシュはコンパイル時にマクロ間でデータを共有するための仕組み。

| API | 説明 |
|-----|------|
| `cacheSeqs` | シーケンスキャッシュ |
| `cacheCounters` | カウンタキャッシュ |
| `cacheTables` | テーブルキャッシュ |


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

### 6.1 推奨読解順序

1. **データ構造の理解**
   - `compiler/modulegraphs.nim` 30-38行目 - Iface型
   - `compiler/modulegraphs.nim` 57-127行目 - ModuleGraph型

2. **初期化処理**
   - `compiler/modulegraphs.nim` 514-547行目 - initModuleGraphFields、newModuleGraph

3. **モジュール登録**
   - `compiler/modulegraphs.nim` 480-490行目 - registerModule

4. **シンボル検索**
   - `compiler/modulegraphs.nim` 190-249行目 - interfSelect、someSym、allSyms

5. **依存関係管理**
   - `compiler/modulegraphs.nim` 571-600行目 - dependsOn、addDep、transitiveClosure

6. **アタッチ演算子**
   - `compiler/modulegraphs.nim` 271-307行目 - getAttachedOp、setAttachedOp

7. **nimsuggest統合**
   - `compiler/modulegraphs.nim` 602-649行目 - markDirty、needsCompilation

### 6.2 プログラム呼び出し階層図
```
newModuleGraph
  |
  +-- initModuleGraphFields
  |     |
  |     +-- initOperators
  |           |
  |           +-- createMagic (各演算子)
  |
  +-- ast.setupProgram

registerModule
  |
  +-- uniqueModuleName
  +-- initStrTables

getModule
  |
  +-- ifaces[fileIdx].module

addDep
  |
  +-- dependsOn (エンコード)
  +-- deps.incl

markClientsDirty
  |
  +-- transitiveClosure (Warshallアルゴリズム)
  +-- markDirty

getAttachedOp
  |
  +-- attachedOps[op].contains
  +-- typeKey (フォールバック用)
  +-- loadedOps[op].getOrDefault

setAttachedOp
  |
  +-- typeKey
  +-- opsLog.add
  +-- loadedOps[op][key] = value
  +-- attachedOps[op][t.itemId] = value
```

### 6.3 データフロー図
```
+----------------+
| ソースファイル |
+-------+--------+
        |
        v
+----------------+
| FileIndex      |
+-------+--------+
        |
        v
+----------------+     +----------------+
| registerModule |---->| ifaces[pos]    |
+-------+--------+     +-------+--------+
        |                      |
        v                      v
+----------------+     +----------------+
| PSym (skModule)|     | Iface          |
+-------+--------+     | - interf       |
        |              | - interfHidden |
        v              | - converters   |
+----------------+     +----------------+
| addDep         |
+-------+--------+
        |
        v
+----------------+
| deps (IntSet)  |
| (a shl 15) + b |
+----------------+
        |
        v
+----------------+
| transitiveClosure
| (Warshall)     |
+----------------+
```

### 6.4 関連ファイル一覧

| ファイル | 役割 | 行数目安 |
|----------|------|----------|
| compiler/modulegraphs.nim | モジュールグラフ本体 | 743行 |
| compiler/ast.nim | AST・シンボル定義 | - |
| compiler/options.nim | ConfigRef定義 | - |
| compiler/idents.nim | 識別子キャッシュ | - |
| compiler/passes.nim | コンパイルパス管理 | - |
| compiler/typekeys.nim | 型キー生成 | - |
| compiler/btrees.nim | BTree実装 | - |
| compiler/suggestsymdb.nim | nimsuggestシンボルDB | - |


## 7. 設計上の考慮点

### 7.1 依存関係のエンコード
依存関係は`(a shl 15) + b`でエンコードされ、IntSetに格納される。これにより2つの整数（最大32768モジュール）をひとつの整数として扱える。

### 7.2 推移閉包の遅延計算
nimsuggestモードでは、依存関係の推移閉包は`markClientsDirty`が呼ばれた時点で初めて計算される。これにより、通常のコンパイルではオーバーヘッドを回避できる。

### 7.3 インターフェーステーブルの二重管理
各モジュールは`interf`（公開シンボル）と`interfHidden`（全シンボル）の2つのテーブルを持つ。`optImportHidden`オプションにより、どちらを使用するか切り替わる。

### 7.4 NIF連携
cmdNifC/cmdMコマンドでは、`loadedOps`テーブルを使用してNIFファイルから読み込んだフックを管理する。型キー（typeKey）を使用することで、異なるItemIdを持つ同一構造の型に対して正しくフックを解決できる。

### 7.5 マクロキャッシュ
`cacheSeqs`、`cacheCounters`、`cacheTables`はマクロ間でデータを共有するための仕組み。インクリメンタルコンパイル（IC）対応済み。
