ChatGPT - KSP コンパイラのオーバーロード部分のフィードバック

zipありがとうございます。中身(Domain/Compilation/SymbolsShared.IO.Symbols.Yaml)をざっと読んだうえでの見解です。

あなたの用途(コンパイラ起動時に全部ロード→ Name 起点で候補を取って意味解析で絞る/仕様更新はGitでYAML更新)だと、やはり DB化は不要寄りです。現状の設計で十分“辞書”として成立しています。そのうえで、コード側で「ここは直すと事故が減る」という点がいくつか見つかりました。


1) DB化しないなら、今いちばん効く改善ポイント

A. OverloadedSymbolTable の「親スコープ検索」が効いていない(バグっぽい)

OverloadedSymbolTable.TryGet / TrySearchByName / TrySearchIndexByName / TryGetOverloadIndexByName が、ローカルテーブルに name が無い場合に parent を見に行きません

例:TryGet(name, overload, ...) は最初に table.TryGetValue(name, out overloads) が false だと、そのまま false を返して終わります(Parent に行かない)。

スコープチェーン(Parent)を想定しているなら、これは実害が出ます。 SymbolTable<T> の方は親を辿る実装になっているので、Overloaded側も同様に直すのが自然です。

対応案

  • name が無かったときも enableSearchParent && Parent != null なら parent を再帰/ループで探す

B. “オーバーロードキー”に ArgumentSymbolList を使う設計が、等価性・ハッシュ的に危ない

OverloadedSymbolTable は内部で

Dictionary<SymbolName, Dictionary<TOverload, TSymbol>>

を持っていて、CommandSymbolTable だと TOverload = CommandArgumentSymbolList ですよね。

ところが今の CommandArgumentSymbolList.Equals は中の要素 CommandArgumentSymbol に対して thisArg.Equals(otherArg) を呼びますが、ArgumentSymbol/VariableSymbolEquals/GetHashCode をオーバーライドしていないので 参照一致になります。

さらに重要なのが、CommandArgumentSymbolList 自体が GetHashCode をオーバーライドしていない点です。 .NETの Dictionary<TKey,...>EqualsGetHashCode の整合が必須なので、将来「構造的に同じ引数列」を別インスタンスでキーにした瞬間に挙動が壊れやすいです(見つからない/重複が入る等)。

現状、あなたが「TrySearchByName で候補全部取って自前で照合」運用なら致命傷は回避できていますが、**テーブル構造が“将来壊れやすい”**状態です。

おすすめの方向性(どれか1つでOK)

  1. キーを “署名文字列” にする

    • 例:"I,S,R[]" みたいな正規化済みシグネチャ文字列を作り、それを TOverload にする

    • これが一番シンプルで堅牢(Equals/Hashが自然に安定)

  2. CommandArgumentSymbolList/ArgumentSymbol構造的 Equals/GetHashCode を実装する

    • DataTypeUITypeNamesOtherTypeNames 等で同値判定
  3. そもそも Dictionary<TOverload, TSymbol> をやめて List<TSymbol> をぶら下げる

    • =「Name → overloadの集合」で十分ならこれが最も安全

    • 既にあなたの解決ロジックがそれに近いので相性良いです


C. 引数の重複チェックが “参照” になっていて、同名引数を弾けない

ArgumentSymbolList.Add()CommandSymbol.AddArgument()Contains が参照一致なので、同じ名前の引数が別インスタンスなら追加できてしまう状態です。

対応案

  • arguments.Any(a => a.Name == item.Name) で弾く(最低限)

  • もしくは「位置で管理(position必須)」なら position を持たせて弾く


D. AggregateSymbolTable.Merge が “元のSymbolインスタンス” をそのまま target に突っ込んで、TableIndex を上書きする

OverloadedSymbolTable.AddAsOverload/SymbolTable.Addsymbol.TableIndex = Next() をします。 Merge(source, target)source が持っている symbol をそのまま target に入れるので、source側の symbol.TableIndex も書き換わる(同一参照なので)ことになります。

テーブルインデックスが「そのテーブル内での番号」なら、これは意図しない副作用になりがちです。

対応案

  • Merge 時は Symbol をコピーしてから追加(Clone/Copyコンストラクタ)

  • あるいは「TableIndexはテーブル側に持ち、Symbolをimmutable寄りにする」(大改修なので後回しでOK)


2) YAML入出力側は概ね良いが、差分安定性を少し上げられる

YamlSymbolExporterOrderBy(x => x.Name.Value) だけなので、同名オーバーロードの順序が入力列に依存しやすいです。Git差分が毎回揺れる原因になります。

改善案

  • ThenBy(署名文字列)(上で言った“署名文字列”をここでも再利用)

  • さらに ThenBy(Id) まで入れると完全に安定します


3) まとめ:あなたの現状に対する結論

  • DB化は今のところ不要(YAML + 起動時ロード + in-memory索引が最適)

  • 代わりに、上の **A(親検索)・B(オーバーロードキー)・D(Merge副作用)**は、将来の不具合に直結しやすいので優先度高めです

  • 特に「オーバーロードキー=署名文字列」へ寄せると、実装も運用も一気に安定します(辞書のキーに適した形になる)


必要なら、次のどちらかの方向で「具体的な修正パッチ案(コード例)」まで書けます。

  1. 最小変更で安全性を上げる(親検索修正+Exporter安定化+重複チェック)

  2. オーバーロードキーを署名文字列に寄せる(推し。バグ耐性が大きく上がる)

どっち寄りで進めたいですか?(聞きつつ、迷うなら 2 を推します)