メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

新機能

go/types のTypeParamList・TypeList・InstanceにString()メソッドを追加し、デバッグ出力の可読性を改善する

go/types

この項目の注釈は AI により生成されており、誤りを含む場合があります。
使用例のコンパイル検証: 検証済み

概要

go/types パッケージの TypeParamListTypeListInstance の3型に String() string メソッドを追加する。いずれも fmt.Stringer を実装していなかったため、これまで log.Printlnfmt.Printf("%v", ...) に渡すと内部の非公開フィールド(要素スライス)がリフレクション越しに整形できず、ポインタのhex表記で出力されていた。今回の追加により、それぞれの型が保持する要素の既存 String() を利用して読みやすい文字列表現を返すようになる。

導入経緯

Issue #79287 は、TypeParamListTypeListInstancelog.Println に渡すとhex表記になってしまい、ジェネリクス関連のデバッグが不便だという指摘から始まった。レビューでは「これらは要素の String メソッドを呼ぶだけで済むが、スライスが(非公開フィールドとして)隠れているため今日はきれいにフォーマットされない」との整理がなされ、大きな設計上の論点はないまま話が進んだ。

議論タイムライン

  • 2026-05-13: Proposal Review Meetingで審議され、実装が既存要素の String() を呼び出すだけの低リスクな変更であることから「likely accept」と判定された。
  • 2026-05-21: 1週間反論が上がらず、正式に「accepted」となった。

議論のハイライト

  • TypeList には過去に一度 String() が実装されたことがあったが(CL 346552)、正式なproposalを経ていなかったため後に削除された(Issue #50760)。今回はその再提案を兼ねている。
  • TypeListTypeParamList は対称的な設計を持つべきで、片方にだけ String() があるのは不自然という点が共有されていた。
  • 実装は CL 780581 として go/typescmd/compile/internal/types2 の両方に提出された。

使用例

Before

package main

import (
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
)

func main() {
	const src = `package p

type Pair[K comparable, V any] struct {
	Key   K
	Value V
}
`
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "p.go", src, 0)
	if err != nil {
		panic(err)
	}
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
	if err != nil {
		panic(err)
	}
	pair := pkg.Scope().Lookup("Pair").Type().(*types.Named)
	tparams := pair.TypeParams() // *types.TypeParamList
	fmt.Println(tparams)         // &{[0xc0000... 0xc0000...]} のようなhex表記になる
}

After

package main

import (
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
)

func main() {
	const src = `package p

type Pair[K comparable, V any] struct {
	Key   K
	Value V
}
`
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "p.go", src, 0)
	if err != nil {
		panic(err)
	}
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
	if err != nil {
		panic(err)
	}
	pair := pkg.Scope().Lookup("Pair").Type().(*types.Named)
	tparams := pair.TypeParams() // *types.TypeParamList
	fmt.Println(tparams)         // [K, V] のように読みやすく出力される
}

実装解説

実装は生成元の src/cmd/compile/internal/types2/typelists.go を元に go generatesrc/go/types/typelists.go が生成される構成になっている(コンパイラ内部の型チェッカーである types2 と、公開APIである go/types が同一ロジックを共有する)。

  • TypeParamList.StringTypeList.String は、各要素に対して WriteType(&buf, elem, nil) を呼び出しながら [, ] で連結するだけの実装になっている。TypeParamList の要素は *TypeParam であり、この経路では制約(constraint)は出力されず、宣言された型パラメータ名のみが書き出される。
  • Instance.Stringfmt.Sprintf("%s%s", inst.TypeArgs, inst.Type) という1行で、TypeArgs(*TypeList)の文字列表現とインスタンス化後の型の文字列表現を単純に連結しているだけである。

関連リンク