go/types: add String methods to TypeParamList, TypeList, and Instance
要約
概要
go/types パッケージの TypeParamList、TypeList、Instance という3つの型に String メソッドを追加するproposalです。現在これらの型はデバッグ時に log.Println などで出力するとメモリアドレスの16進数表現(ポインタ値)として表示されてしまうため、デバッグの利便性が著しく低下しています。
ステータス変更
(新規) → likely_accept
2026年5月13日のProposal Review Meeting(@aclements, @adonovan, @bradfitz, @cherrymui, @griesemer, @ianlancetaylor, @neild, @rolandshoemaker が出席)で審議され、likely accept に分類されました。これらの String メソッドは内部スライスの各要素の既存 String メソッドを呼び出すだけのシンプルな実装で済み、設計上の複雑さが低く実用的な価値が高いと判断されたためと考えられます。@aclements は「スライスが隠蔽されているために今日は見やすくフォーマットされないが、これらは要素の String メソッドを呼ぶだけ」とコメントしており、実装・設計の双方で異論がない状態です。
技術的背景
現状の問題点
TypeParamList と TypeList はジェネリクスのサポートのために導入された型で、型パラメータリストと型引数リストをそれぞれ保持します。Instance は型・関数のインスタンス化情報(TypeArgs *TypeList と Type)を格納します。
これらの型はいずれも fmt.Stringer インターフェース(String() string メソッド)を実装していないため、fmt パッケージや log パッケージで出力する際にポインタの16進数表現が表示されてしまいます。
// 現状の問題: TypeParamList をそのまま出力するとポインタアドレスが表示される
var tpl *types.TypeParamList = ...
log.Println(tpl) // 出力例: &{[0xc000123456]}
fmt.Println(tpl) // 出力例: &{[0xc000123456]}
なお、関連するIssue #50760 では TypeList.String が一度 go-review.googlesource.com/c/go/+/346552 で実装されたものの(CL 346552)、正式なproposalを経ていなかったため CL 381634 で一旦削除された経緯があります。
提案された解決策
TypeParamList、TypeList、Instance のそれぞれに String() string メソッドを追加します。実装は各型が内部に保持するスライスの要素(*TypeParam や Type)の既存の String メソッドを呼び出して整形するシンプルなものになると考えられます。
これによって何ができるようになるか
型チェッカー・コンパイラツール・静的解析ツールを開発する際のデバッグ体験が大幅に改善されます。
コード例
// Before: String メソッドなしの場合(ポインタアドレスが表示される)
func debugGeneric(info *types.Info) {
for id, inst := range info.Instances {
// inst.TypeArgs は *TypeList だが、String() がないためアドレスが表示される
log.Printf("identifier=%s, TypeArgs=%v", id.Name, inst.TypeArgs)
// 出力: identifier=F, TypeArgs=&{[0xc000a1b2c3 0xc000d4e5f6]}
}
}
// After: String メソッド追加後(読みやすい文字列表現が表示される)
func debugGeneric(info *types.Info) {
for id, inst := range info.Instances {
log.Printf("identifier=%s, TypeArgs=%v", id.Name, inst.TypeArgs)
// 出力例: identifier=F, TypeArgs=[int, string]
log.Printf("instance=%v", inst)
// 出力例: instance={TypeArgs:[int, string] Type:F[int, string]}
}
}
実践的なユースケース
- 静的解析ツールの開発:
go/analysisベースのツール開発中に、型パラメータや型引数の内容をlog.Printfで素早く確認できる。 - コンパイラ・型チェッカーのデバッグ: ジェネリック関数・型のインスタンス化情報をトレースする際に可読性のある出力が得られる。
- IDEやコード解析基盤の開発:
go/typesを利用するツール(gopls 等)の内部デバッグで、型情報を人間が読める形式で出力できる。
議論のハイライト
- 非対称性の解消:
TypeListとTypeParamListは対称的な設計を持つ類似型だが、前者には一時的にStringが実装されたことがあり(後に提案プロセスを経ずに追加されたため削除)、この非対称性を正式なproposalで解消することが本質的な目的のひとつ。 - 設計の単純さ: @aclements の指摘通り、実装は既存の要素
Stringメソッドを呼ぶだけで済むため、設計上の論点がほとんどない。 - 過去のCLとの経緯: CL 346552 で
TypeList.Stringが実装されたが、Issue #47916(generics対応のgo/types変更まとめ)のproposalに含まれていなかったため CL 381634 で削除された。今回はそれを正式提案として再提案している。 - 即時のデバッグ価値: この変更はAPIの追加であり後方互換性を破らないため、承認コストが低い。
fmt.Stringer実装により、既存のデバッグコードが即座に恩恵を受けられる。 Instanceへの拡張: 提案にはリスト型だけでなくInstance構造体も含まれており、インスタンス化情報全体を一括して可読表示できるよう配慮されている。