タラバガニー設計局stalins.clubNOTE/notes/method-quadrants

メソッドの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/typesSignature が持つ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

▸ ノート一覧に戻る