メソッドの4象限
「ジェネリックなメソッド」という言い方は、独立した2つの軸を混ぜている。軸は (1) レシーバの型がジェネリックか、(2) メソッド自身が型パラメータを持つか。この2軸で分けると4象限になる。
| メソッド自身は型パラメータ無し | メソッド自身が型パラメータ有り | |
|---|---|---|
| 普通の型 | A func (Ord) A() | C func (Ord) C[Q any](q Q) ← 1.27 で新規 |
| ジェネリック型 | B func (Gen[P]) B(p P) | D func (Gen[P]) D[Q any](p P, q Q) ← 1.27 で新規 |
比較用に E func E[Q any](q Q) (ジェネリック関数、1.18 から存在) を並べる。
Go 1.26 まで書けなかったのは C と D だけ。B は 1.18 からずっと書けていた。つまり Go 1.27 でツールが直面したのは「ジェネリクスが増えた」ではなく、「これまで存在しなかった2象限が新しく出現した」という事態である。
判定は go/types の Signature が持つ2つのアクセサで行う。
名 型 RecvTypeParams TypeParams
A func() - -
B func(p P) P -
C func[Q any](q Q) - Q
D func[Q any](p P, q Q) P Q
E func[Q any](q Q) - Q
Signature.TypeParams() != nil が「ジェネリクスメソッドか」の判定条件。RecvTypeParams() の方を見ると B まで拾ってしまう。さらに厄介なのは、C と E はシグネチャの見た目が完全に同じ (func[Q any](q Q)) ということで、両者を区別できるのは Recv() が nil かどうかだけである。
象限ごとの挙動差は types.MethodSet への参加可否や interface 充足の可否にも及ぶ。詳細は types.MethodSet (メソッドセット) と 静的解析から見たジェネリクスメソッド に譲る。実装面の型引数の扱いは GC shape stenciling と dictionary、この4象限という整理自体は ジェネリクスメソッド (Go 1.27) の前提になっている。
出典
Go Release Party 1.27 の発表資料リポジトリ static-analysis.md §1・§2.1・§4