GC shape stenciling と dictionary
Go のジェネリクスはコンパイル時に型ごとの実体を生成する方式だが、型ごとに丸ごとコードを複製するわけではない。ポインタなど GC shape (GC が扱う上での形) が同じ型はコードを共有し、型ごとの違いは実行時に渡される dictionary (型情報テーブル) で埋める。これが GC shape stenciling で、Go 1.18 のジェネリクス導入時からある実装方式である。
Go 1.27 のジェネリクスメソッドは、この既存の仕組みをそのまま使っている。新しい実行時機構は入っていない。dictionary の構築については、コンパイラのコミットメッセージ (cmd/compile/internal/noder: encode dictionaries for generic methods, 2026-03-31) が簡潔に説明している。
Generic methods have 2 sources of explicit type arguments — the receiver and the method itself. We simply concatenate these lists when constructing the dictionary.
つまりメソッド自身の型パラメータと、レシーバの型パラメータという2つの出所を単純に連結してひとつの dictionary を作っている。非インターフェースレシーバへのメソッド呼び出しは静的に解決できるため、コンパイラは内部で generic function の呼び出しに書き換えて扱っている。
なお現状では最適化を1つ取り逃している (cmd/compile/internal/noder: omit wrapper functions for generic methods, 2026-04-03)。
This has the slight downside of not using a statically computed wrapper for generic methods and instead dynamically computing the dictionary pointer. Thus, we miss out on a performance optimization for generic methods for now. Code that does not use generic methods is unaffected.
静的な wrapper を出さず、dictionary ポインタを実行時に計算しているため、その分の最適化が効いていない。ただし generic method を使わないコードには影響しない。
この「レシーバ由来とメソッド自身の型引数を連結する」という構造は、静的解析からもそのまま観測できる。ssa.Function.TypeParams() はレシーバとメソッド自身の型パラメータを連結して保持しており、(Gen[int]).D[string] のようなメソッドは TypeArgs=[int string] になる (実測は 静的解析から見たジェネリクスメソッド 参照)。コンパイラの dictionary と静的解析ツールが見るシグネチャが、同じ「連結」という設計をなぞっている形になる。
こうした構造は「実行時に型を作れない」という Go の制約が前提になっており、その制約が generic method がインターフェースを満たさない設計判断にもつながっている (Go FAQ が却下した4案)。象限ごとの詳しい対応は メソッドの4象限 を参照。
出典
- Go Release Party 1.27 の発表資料リポジトリ
memo.md§1 - Go Release Party 1.27 の発表資料リポジトリ
static-analysis.md§2.6・§3.3