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

Go Proposal Weekly Digest

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

#79603likely_accept

go/types: NewType{Param,}List

新規提案

要約

AIによる要約であり、誤りを含む場合があります。

概要

go/types パッケージに NewTypeParamList および NewTypeList の2つのコンストラクタ関数を追加するproposalです。これにより、型パラメータリスト(TypeParamList)と型リスト(TypeList)をプログラムから安全に生成できるようになります。

ステータス変更

(なし)likely accept
Proposal Review GroupのAustin Clements(@aclements)が2026年5月28日にlikely acceptと判断しました。APIが非常にシンプルで、明確な動機(unsafeパッケージへの依存排除)があること、また@adonovanらコアチームメンバーがNewTypeListの追加にも同意したことが背景にあります。

技術的背景

現状の問題点

go/typesパッケージは型パラメータリスト(TypeParamList)と型リスト(TypeList)を公開していますが、これらを外部から構築するコンストラクタが存在しません。既存のTypeParamListを扱うツール側では、以下のような問題が発生しています。

  1. コンパイラ内部(noder): レシーバと関数の型パラメータを結合する際に、いったん[]*TypeParamスライスに変換しなければならない。
  2. go/ssa(x/tools): Function.TypeParams*TypeParamListを返すAPIを維持するために、unsafe.Pointerを用いた危険なキャストが必要になっている。
// 現在のワークアラウンド(go/ssaでの実装例)
func consTypeParamLists(l, r *types.TypeParamList) *types.TypeParamList {
    tpars := make([]*types.TypeParam, l.Len()+r.Len())
    for i := range l.Len() {
        tpars[i] = l.At(i)
    }
    for i := range r.Len() {
        tpars[i+l.Len()] = r.At(i)
    }
    // unsafe.Pointerによる強制キャスト(危険)
    return (*types.TypeParamList)(unsafe.Pointer(&tpars))
}

提案された解決策

以下の2つのエクスポートされたコンストラクタ関数をgo/typesに追加します。

func NewTypeParamList(tparams ...*TypeParam) *TypeParamList
func NewTypeList(types ...*Type) *TypeList

実装は非常にシンプルで、内部フィールドに渡された引数をそのまま格納します。NewTypeListの追加は@adonovan(Googler)が提案し、対称性と一貫性の観点からproposalに組み込まれました。

これによって何ができるようになるか

ツール作者やコンパイラ実装者がTypeParamListおよびTypeListを安全・明示的に生成できるようになります。

コード例

// Before: unsafe.Pointerを使った強制キャスト(危険)
func mergeTypeParams(l, r *types.TypeParamList) *types.TypeParamList {
    tpars := make([]*types.TypeParam, l.Len()+r.Len())
    for i := range l.Len() {
        tpars[i] = l.At(i)
    }
    for i := range r.Len() {
        tpars[i+l.Len()] = r.At(i)
    }
    return (*types.TypeParamList)(unsafe.Pointer(&tpars)) // 内部レイアウトに依存
}
// After: 安全なコンストラクタを使った書き方
func mergeTypeParams(l, r *types.TypeParamList) *types.TypeParamList {
    tpars := make([]*types.TypeParam, l.Len()+r.Len())
    for i := range l.Len() {
        tpars[i] = l.At(i)
    }
    for i := range r.Len() {
        tpars[i+l.Len()] = r.At(i)
    }
    return types.NewTypeParamList(tpars...) // 安全・明示的
}

議論のハイライト

  • NewTypeListの追加: 元のproposalはNewTypeParamListのみを対象としていたが、@adonovanがNewTypeListも追加すべきと提案し、API設計の対称性から採用された。
  • 動機の明確さ: unsafe.Pointerを使った実装がx/toolsのSSAパッケージで実際に行われており、標準ライブラリとしてAPIを提供すべき根拠が明確だった。
  • Genericメソッド提案(#77273)との関連: Goにgenericメソッドが将来追加された場合、レシーバの型パラメータと関数の型パラメータを統合する操作はさらに頻繁になるため、本APIの必要性は増す。
  • APIのシンプルさ: コンストラクタの実装は内部フィールドへの代入のみで、複雑なバリデーションや副作用がなく、リスクが非常に低い。
  • TypeParamListの不変性: 既存のbindTParamsは型パラメータにインデックスを割り当てる副作用があるが、NewTypeParamListはそのような副作用を持たない設計であることが期待される。

関連リンク