タラバガニー設計局stalins.clubNOTE/notes/monomorphization-checker

monomorphization checker

Go のコンパイラは、ジェネリクスをコンパイル時に型ごと実体化する (monomorphization / GC shape stenciling) 方式を採る。この実体化がコンパイル中に無限ループしないよう、意図的に入れている検査が monomorphization checker であり、instantiation cycle エラーの出どころになっている。

griesemer が #80109 (2026-06-23, CLOSED/NOT_PLANNED) で最小再現に落として説明している。

type A[T any] struct{}
func f[T any](A[T], A[A[T]]) {}  // OK (比較用)
func (A[T]) m(A[A[T]])       {}  // instantiation cycle

同じシグネチャでも、パッケージ関数の f は通り、メソッドの m だけが落ちる。griesemer の回答は「これは interface 充足の再検査ではなく monomorphization checker 由来」。

なぜメソッドだけが引っかかるのか。ある型を instantiate するとき、そのメソッドはeager にすべて instantiate する必要がある。普通のメソッドはインターフェース経由の動的ディスパッチの的になりうるため、型を作った時点で呼ばれるかどうかに関わらず全部作っておかなければならない。一方、関数は呼ばれた場所からしか実体化されないので、この連鎖が起きない。A[int] を作る → メソッド m も作る → m の引数は A[A[int]] → それも作る → その m の引数は A[A[A[int]]] … と終わらない連鎖に落ちるのはメソッドだけ、という理屈である。

重要なのは、これが Go 1.27 で新しく入った制限ではなく、Go 1.18 からある制限だという点。ジェネリクスメソッドが無かった 1.18〜1.26 でも、ジェネリック型の普通のメソッドで同じ現象が起きる。実際に go1.26 + go.modgo 1.18 という組み合わせで同じ instantiation cycle エラーが出ることが実測で確認されている。

この制限を「ジェネリクスメソッドはインターフェースを満たさないのだから理屈上は外せるのでは」という議論もあったが、#80172 で 2026-08-12 に「当面変えない」と決着した。その経緯と具体的な症状は 関数だと通りメソッドだと通らないシグネチャ、回避策として提案されたダミー型パラメータの手法は ダミー型パラメータによる instantiation cycle 回避 に詳しい。実装方式そのものは GC shape stenciling と dictionary を参照。

出典

  • Go Release Party 1.27 の発表資料リポジトリ memo.md §1、issues.md §2
  • #80109
  • #80172

▸ ノート一覧に戻る