メインコンテンツへスキップ
#79427likely_accept

decouple export data formats used by compiler and x/tools

ステータス変更: active likely_accept

要約

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

概要

Goコンパイラが使う型情報の形式(export data)と、x/tools や gopls などのツールが使う形式を完全に切り離す提案です。コンパイラは独自形式を自由に変えられるようになり、ツールは公開されたツール向け形式だけを読めば済むようになります。

ステータス変更

active → likely_accept
2026年9月30日の Proposal Review では、Go 1.28 の時点でコンパイラ形式とツールの互換性を意図的に断ち切ってよいと判断されました。理由は次の2点です。

  • 影響を受ける人は go list -export の Export フィールドに切り替えれば済みます。この方法は Go 1.27 でもそれ以降でも動きます。
  • 互換性を切るのを先送りしても、移行期間が延びるだけです。問題があるという早い段階のシグナルは得られません。
    古いコンパイラ(Go 1.27 以前)が出力した export data を読む機能は、x/tools の通常のサポート期間に従って2リリース分残します。go-command ワーキンググループ(@matloob)からも反対はありませんでした。

技術的背景

現状の問題点

export data は、パッケージの型情報(概念的にはシリアライズした types.Package)を収めたバイナリ形式です。現在は2つの形式があります。

  • unified (u) 形式: コンパイラが読み書きする
  • indexed (i) 形式: x/tools が読み書きする
    ところが go list -export はコンパイラを実行して export data を作るため、x/tools 側も u 形式を読めなければなりません。その結果、言語仕様が変わるたびにコンパイラと x/tools をリポジトリをまたいで同時に直す必要があり、両者が強く結び付いていました。

提案された解決策

  • コンパイラの形式(今後は p 形式)は、同じバージョンのツールチェーン内だけで使う非公開の実装詳細にします。どのコミットでも自由に変更でき、外部から読むためのサポートされた手段は提供しません。
  • go list -export はコンパイラの代わりに新しいツール cmd/export を実行し、i 形式を出力します。
    • cmd/export は go/ast、go/types と vendor した x/tools を使います。unitchecker から解析部分を取り除いたような構成です。
    • 関数本体の型チェックを省けるため、コンパイラより高速になると見込まれています。
  • go/importer は vendor した x/tools のデコーダを使い、i 形式を読みます。
  • go vet は今後 compile のステップを経由しません。unitchecker が型情報とファクト(解析結果)を .vetx ファイルに自分で書き出します(CL 824004、CL 823832)。
  • gcexportdata.NewReader は非推奨になります。用途がコンパイラの .a アーカイブから読み出すことだけだったためです。

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

  • 言語機能を追加するとき、コンパイラ側の export data 形式をツール側に縛られずに変更できます。
  • gopls や静的解析ツールは、安定した i 形式だけを相手にすればよくなります。
  • vet などのビルドグラフが単純になり、クリティカルパスも短くなります。
    一方で、コンパイラを直接呼び出してその出力を読んでいたツールは移行が必要です。代表例は Bazel rules_go の nogo、go build -o x.a で作ったファイルを読むツール、GOPATH モードの Target を使うツール、独自の packages ドライバなどです。

コード例

// Before: コンパイラが生成した .a ファイルを直接読む(Go 1.28 以降は非サポート)
// $ go build -o runtime.a runtime
f, _ := os.Open("runtime.a")
r, _ := gcexportdata.NewReader(f) // 非推奨化される
pkg, _ := gcexportdata.Read(r, fset, imports, "runtime")
// After: go list -export が返すファイル(cmd/export が出力する i 形式)を読む
// $ go list -export -f '{{.Export}}' runtime
f, _ := os.Open(exportPath)
pkg, _ := gcexportdata.Read(bufio.NewReader(f), fset, imports, "runtime")

After 側の読み込み方法の細部は、実装に合わせて変わる可能性があります。

議論のハイライト

  • x/tools の後方互換性について: 「x/tools の形式の後方互換性を保証して、ツールが壊れないようにできないか」という意見が出ました。しかし新しい言語機能を書き出せない古い形式では正しい処理ができず、バージョン不一致エラーを出すしかないため、一般的には不可能だと説明されました。
  • 影響範囲の整理(@mrkfrmn): 影響範囲を見積もりました。go/importer は 2275 件、gcexportdata は 282 件のパッケージから import されています。そのうえで、凍結版の u 形式リーダーを残す段階的な案も提示しました。
  • 3形式の整理(@adonovan): i(ツール用)、u(Go 1.27 以前のコンパイラ出力、当面は読み込みのみ維持)、p(Go 1.28 以降のコンパイラ専用、安定性の保証なし)に整理されました。
  • 移行の進め方(@aclements): 開発サイクルの早い段階で互換性を意図的に壊すべきとしました。逃げ道は opt-in ではなく opt-out で用意し、問題報告を早く集める方針です。
  • キャッシュの扱い(@cherrymui): 「ツールが自分で export data を書く」方向性に賛同しました。そのうえでキャッシュをどう管理するかが課題だと指摘しています(-exporttool フラグ案)。
  • Bazel の移行: Bazel の nogo への影響が報告されました。CL 824004 と同じ方法で移行するよう案内されています。この方法は現行のツールチェーンでもすぐに使えます。

関連リンク