ChatGPT - KSP コンパイラのオーバーロード部分のフィードバック
zipありがとうございます。中身(Domain/Compilation/Symbols と Shared.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/VariableSymbol が Equals/GetHashCode をオーバーライドしていないので 参照一致になります。
さらに重要なのが、CommandArgumentSymbolList 自体が GetHashCode をオーバーライドしていない点です。
.NETの Dictionary<TKey,...> は Equals と GetHashCode の整合が必須なので、将来「構造的に同じ引数列」を別インスタンスでキーにした瞬間に挙動が壊れやすいです(見つからない/重複が入る等)。
現状、あなたが「TrySearchByName で候補全部取って自前で照合」運用なら致命傷は回避できていますが、**テーブル構造が“将来壊れやすい”**状態です。
おすすめの方向性(どれか1つでOK)
-
キーを “署名文字列” にする
-
例:
"I,S,R[]"みたいな正規化済みシグネチャ文字列を作り、それをTOverloadにする -
これが一番シンプルで堅牢(Equals/Hashが自然に安定)
-
-
CommandArgumentSymbolList/ArgumentSymbolに 構造的 Equals/GetHashCode を実装するDataTypeとUITypeNamesとOtherTypeNames等で同値判定
-
そもそも
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.Add は symbol.TableIndex = Next() をします。
Merge(source, target) は source が持っている symbol をそのまま target に入れるので、source側の symbol.TableIndex も書き換わる(同一参照なので)ことになります。
テーブルインデックスが「そのテーブル内での番号」なら、これは意図しない副作用になりがちです。
対応案
-
Merge時は Symbol をコピーしてから追加(Clone/Copyコンストラクタ) -
あるいは「TableIndexはテーブル側に持ち、Symbolをimmutable寄りにする」(大改修なので後回しでOK)
2) YAML入出力側は概ね良いが、差分安定性を少し上げられる
YamlSymbolExporter が OrderBy(x => x.Name.Value) だけなので、同名オーバーロードの順序が入力列に依存しやすいです。Git差分が毎回揺れる原因になります。
改善案
-
ThenBy(署名文字列)(上で言った“署名文字列”をここでも再利用) -
さらに
ThenBy(Id)まで入れると完全に安定します
3) まとめ:あなたの現状に対する結論
-
DB化は今のところ不要(YAML + 起動時ロード + in-memory索引が最適)
-
代わりに、上の **A(親検索)・B(オーバーロードキー)・D(Merge副作用)**は、将来の不具合に直結しやすいので優先度高めです
-
特に「オーバーロードキー=署名文字列」へ寄せると、実装も運用も一気に安定します(辞書のキーに適した形になる)
必要なら、次のどちらかの方向で「具体的な修正パッチ案(コード例)」まで書けます。
-
最小変更で安全性を上げる(親検索修正+Exporter安定化+重複チェック)
-
オーバーロードキーを署名文字列に寄せる(推し。バグ耐性が大きく上がる)
どっち寄りで進めたいですか?(聞きつつ、迷うなら 2 を推します)