コンパイラの//lineディレクティブにおける相対ファイル名の解決を、go/scannerと同じくディレクティブを含むファイルのディレクトリ基準に変更
Compiler
概要
コンパイラは、//lineおよび/*line*/ディレクティブ中の相対ファイル名を、そのディレクティブを含むファイルのディレクトリを基準に解決するようになった。これはgo/scannerの既存の挙動に合わせたものである。絶対パスのファイル名は従来どおり影響を受けない。
導入経緯
従来、cmd/compileは//lineディレクティブ中の相対ファイル名を、他の相対パスと同様にコンパイラ自身の作業ディレクトリ(os.Getwd)基準で解釈していた。これに対しgo/scannerは、スキャン対象ファイルのディレクトリを基準に解釈しており、両者の挙動は長らく食い違っていた(なおgo/scannerは2018年3月のCL 100235でコンパイラ側に合わせる変更が一度入ったが、同年8月のCL 127658で元に戻されている)。
この食い違いにより、yacc生成コードやcgoなど//lineディレクティブを出力するツールが、ビルド時の作業ディレクトリによってエラーメッセージ中のファイルパスが実在しないものになる、という問題が発生していた。#70478は、コンパイラの相対ファイル名解決をgo/scannerの挙動に統一することを提案した。
議論のハイライト
- 非互換性への懸念とGODEBUG: 2018年の巻き戻し経緯があったことから、後方互換とはいえない変更である以上、無効化のための
GODEBUG設定が必要ではないかという意見が出た。これに対し提案者側は、影響が及ぶのは主にビルド失敗時のエラーメッセージであり(定義上、互換性保証の対象外)、2018年の変更(source tool向けの機能後退)とは性質が異なると説明した。 - トレースバック・プロファイルへの影響: エラーメッセージだけでなく、パニック時のトレースバックやプロファイル出力のファイルパスにも影響しうるとの指摘があった。これに対しては、そうした出力の具体的なファイル/行番号に依存する既存テストは少ないだろうとの見解が示された。
- ビルドシステムでの
-trimpathとの相互作用: BazelやBuckのように-trimpathでパスを操作するビルドシステムでは、生成する//lineディレクティブの相対パスの組み立て方を見直す必要があるとの報告があった。この対応として、cmd/cgoに//lineディレクティブの出力先を明示的に制御する-line-trimpathフラグを追加するCL 772700が別途用意された。 - Proposal Review Groupの判断: レビューグループは「
foo.goという名前のファイルの123行目にある//line foo.go:123は実質no-opであるべき」という基準で提案の方向性自体は正しいと判断しつつ、実際にどれだけ既存コードに影響するかを見極めるため、まず実装して様子を見る方針を取った(その後いったんholdとなっている)。 - 最終的な扱い: 実装(CL 706795ほか)がマージされ、Google社内コードベースやBuckでの検証でも大きな問題が見つからなかったことから、単に
go/scannerの挙動に合わせるバグ修正とみなされ、proposalプロセスからは除外された。
移行時の注意
相対ファイル名を持つ//lineディレクティブを自前で生成しているツール(cgo、yaccベースのコード生成ツールなど)を使っている場合、生成されるエラーメッセージやトレースバックのファイルパスが、従来のコンパイラ作業ディレクトリ基準ではなく、ディレクティブを含むファイルのディレクトリ基準で解決されるようになる点に注意が必要である。特に-trimpathでパスを書き換えるビルドシステム(Bazel/Buckなど)では、//lineディレクティブに書き込む相対パスの組み立て方を見直す必要が生じる場合がある。絶対パスの//lineディレクティブを使っている場合は影響を受けない。
実装解説
該当処理はcmd/compile/internal/syntaxパッケージのparser.updateBaseにある。//line filename:line[:col]を解析した後、filenameが空でも絶対パスでもない場合に、filepath.Dir(p.file.Filename())をベースディレクトリとしてfilepath.Joinで結合するようになっており、これがディレクティブを含むファイル自身のディレクトリを基準にする実装である。
対応するgo/scanner側の実装はScanner.updateLineInfoにあり、同様に相対ファイル名をs.dir(スキャン対象ファイルのディレクトリ)とfilepath.Joinする実装になっている。今回の変更は、このgo/scanner側の実装パターンをcmd/compile側にも適用したものである。