[インデックス 12557] ファイルの概要
このコミットは、Go言語のAPI検証ツールである cmd/api
の改善に関するものです。具体的には、Windows環境での動作の安定化と、gccgo
コンパイラで生成されたファイルに対する互換性の向上を目的としています。主な変更点は、string
および []byte
への型変換が cmd/api
ツールによって正しく認識されるように修正されたことです。
コミット
commit e31fa68a43d71efb73cf0745ab0f911400a40a5e
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Sun Mar 11 17:55:15 2012 -0700
cmd/api: work on Windows again, and make gccgo files work a bit more
handle string and []byte conversions.
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/5754082
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e31fa68a43d71efb73cf0745ab0f911400a40a5e
元コミット内容
cmd/api: work on Windows again, and make gccgo files work a bit more
handle string and []byte conversions.
このコミットは、cmd/api
ツールがWindows上で再び動作するようにし、gccgo
でコンパイルされたファイルがより適切に処理されるようにするものです。具体的には、string
および []byte
への型変換の扱いを改善しています。
変更の背景
Go言語の cmd/api
ツールは、Goの標準ライブラリやその他のパッケージの公開APIサーフェスを検証するために使用されます。これは、APIの意図しない変更を防ぎ、互換性を維持するために重要なツールです。
このコミットが作成された2012年3月時点では、Go言語のツールチェインはまだ発展途上にありました。特に、Goの抽象構文木(AST)には、型情報が十分に埋め込まれていないという制約がありました。このため、cmd/api
のようなツールは、コードを解析する際に型変換のような特定の構文要素を正しく識別するのに苦労していました。
具体的な問題として、以下の点が挙げられます。
- Windows環境での問題: 以前の変更により、
cmd/api
ツールがWindows環境で正しく動作しなくなっていた可能性があります。これは、パスの扱い、ファイルシステムの特性、または特定のシステムコールが原因である可能性がありますが、コミットメッセージからは直接的な原因は読み取れません。しかし、このコミットによってWindowsでの動作が回復したことが示唆されています。 gccgo
との互換性問題:gccgo
は、GCC(GNU Compiler Collection)をバックエンドとするGo言語の代替コンパイラです。標準のgc
コンパイラとは異なる実装であるため、cmd/api
ツールがgccgo
でコンパイルされた、またはgccgo
のコードスタイルに沿ったGoファイルを解析する際に、特定の構文(特に型変換)を誤って解釈していた可能性があります。string
および[]byte
変換の誤認識: Go言語では、string(expr)
や[]byte(expr)
のように、組み込みの型変換構文が存在します。これらは関数呼び出しではなく、特定の型への明示的な変換です。しかし、cmd/api
ツールは、ASTからこれらの構文を解析する際に、これらを「未知の関数呼び出し」として誤って認識し、エラーを発生させていました。これは、当時のGo ASTが型情報を欠いていたため、ツールが構文要素のセマンティクスを正確に判断できなかったことに起因します。
これらの問題を解決し、cmd/api
ツールの堅牢性と互換性を向上させることが、このコミットの背景にあります。
前提知識の解説
Go言語の cmd/api
ツール
cmd/api
は、Go言語の標準ライブラリやその他のGoパッケージの公開APIサーフェスを抽出・検証するためのコマンドラインツールです。このツールは、Goのソースコードを解析し、エクスポートされた型、関数、変数、メソッドなどの情報を収集します。収集されたAPI情報は、通常、golden.txt
のようなファイルに保存され、将来の変更がAPI互換性を損なわないかを確認するための基準として使用されます。
Go言語の抽象構文木 (AST)
Go言語のコンパイラやツールは、ソースコードを直接扱うのではなく、まずソースコードを抽象構文木(AST)に変換します。ASTは、プログラムの構造を木構造で表現したものです。Goには go/ast
パッケージがあり、GoプログラムのASTをプログラム的に操作できます。
このコミットの時点(2012年)では、go/ast
パッケージが提供するASTには、型情報(例えば、ある変数の具体的な型や、関数呼び出しの戻り値の型など)が直接埋め込まれていませんでした。これは、ASTが主に構文構造を表現するためのものであり、型チェックやセマンティック解析はASTの構築後のフェーズで行われるためです。この制約により、cmd/api
のようなツールは、型変換のような構文要素のセマンティクスを正確に判断するために、追加のロジックや推論が必要でした。
string
および []byte
への型変換
Go言語には、異なる型間で値を変換するための組み込みのメカニズムがあります。string(x)
は x
を文字列型に変換し、[]byte(x)
は x
をバイトスライス型に変換します。これらは関数呼び出しのように見えますが、実際にはGo言語の仕様で定義された特別な型変換構文です。例えば、string([]byte{'a', 'b', 'c'})
はバイトスライスを文字列に変換します。
gccgo
コンパイラ
gccgo
は、Go言語のプログラムをコンパイルするための代替コンパイラです。これは、GCC(GNU Compiler Collection)のフロントエンドとして実装されており、GCCの最適化機能やターゲットプラットフォームのサポートを利用できます。Go言語の公式コンパイラは gc
と呼ばれ、Googleによって開発されています。gccgo
は gc
とは独立して開発されており、両者には細かな挙動の違いや、特定の構文の解釈の違いが存在する可能性があります。
技術的詳細
このコミットの核心は、src/cmd/api/goapi.go
ファイル内の varValueType
関数に対する変更です。この関数は、GoのASTノードを走査し、変数の値の型を決定しようとします。
変更前のコードでは、ast.CallExpr
(関数呼び出しや型変換を表すASTノード)に遭遇した際、それが既知の関数でない場合に一律にエラーを返していました。
// 変更前 (抜粋)
// maybe a function call; maybe a conversion. Need to lookup type.
return "", fmt.Errorf("not a known function %q", w.nodeString(v.Fun))
このロジックでは、string("foo")
や []byte("foo")
のような型変換も「未知の関数」として扱われ、エラーとなっていました。
このコミットでは、この部分に以下の switch
ステートメントが追加されました。
// 変更後 (抜粋)
// TODO(bradfitz): this is a hack, but arguably most of this tool is,
// until the Go AST has type information.
nodeStr := w.nodeString(v.Fun)
switch nodeStr {
case "string", "[]byte":
return nodeStr, nil
}
return "", fmt.Errorf("not a known function %q", nodeStr)
この変更により、v.Fun
(呼び出しの対象となる式、この場合は型変換のターゲット型名)が文字列として "string"
または "[]byte"
である場合、それを特別なケースとして扱い、エラーを発生させずにその型名を返すようになりました。
開発者自身がコメントで「これはハックである」と述べているように、これは当時のGo ASTが型情報を提供していなかったことに対する暫定的な回避策です。本来であれば、ASTノードから直接そのセマンティックな型情報を取得できるべきですが、それが不可能であったため、構文要素の文字列表現に基づいて型変換を識別するという、やや脆弱な方法が取られました。
また、テストデータ (src/cmd/api/testdata/src/pkg/p1/p1.go
と src/cmd/api/testdata/src/pkg/p1/golden.txt
) が更新され、string
および []byte
への型変換を含む変数が追加されました。これにより、cmd/api
ツールがこれらの変換を正しく処理できるようになったことが検証されます。
p1.go
にStrConv = string("foo")
とByteConv = []byte("foo")
が追加。golden.txt
にpkg p1, var ByteConv []byte
とpkg p1, var StrConv string
が追加され、cmd/api
がこれらの変数を正しく認識し、その型を抽出できるようになったことを示しています。
コアとなるコードの変更箇所
src/cmd/api/goapi.go
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -579,7 +579,14 @@ func (w *Walker) varValueType(vi interface{}) (string, error) {
}
}
// maybe a function call; maybe a conversion. Need to lookup type.
- return "", fmt.Errorf("not a known function %q", w.nodeString(v.Fun))
+ // TODO(bradfitz): this is a hack, but arguably most of this tool is,
+ // until the Go AST has type information.
+ nodeStr := w.nodeString(v.Fun)
+ switch nodeStr {
+ case "string", "[]byte":
+ return nodeStr, nil
+ }
+ return "", fmt.Errorf("not a known function %q", nodeStr)
default:
return "", fmt.Errorf("unknown const value type %T", vi)
}
src/cmd/api/testdata/src/pkg/p1/golden.txt
--- a/src/cmd/api/testdata/src/pkg/p1/golden.txt
+++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt
@@ -58,14 +58,16 @@ pkg p1, type T struct
pkg p1, type TPtrExported struct
pkg p1, type TPtrExported struct, embedded *Embedded
pkg p1, type TPtrUnexported struct
+pkg p1, var ByteConv []byte
pkg p1, var ChecksumError error
pkg p1, var SIPtr *SI
pkg p1, var SIPtr2 *SI
pkg p1, var SIVal SI
+pkg p1, var StrConv string
pkg p1, var V string
-pkg p1, var VError Error
pkg p1, var V1 uint64
pkg p1, var V2 p2.Twoer
+pkg p1, var VError Error
pkg p1, var X I
pkg p1, var X int64
pkg p1, var Y int
src/cmd/api/testdata/src/pkg/p1/p1.go
--- a/src/cmd/api/testdata/src/pkg/p1/p1.go
+++ b/src/cmd/api/testdata/src/pkg/p1/p1.go
@@ -27,6 +27,12 @@ var (
V2 = ptwo.G()\n
)
+// Variables with conversions:
+var (
+ StrConv = string("foo")
+ ByteConv = []byte("foo")
+)
+
var ChecksumError = ptwo.NewError("gzip checksum error")
const B = 2
コアとなるコードの解説
src/cmd/api/goapi.go
の変更
varValueType
関数は、GoのASTを走査し、変数の初期値の型を特定しようとします。変更された箇所は、ast.CallExpr
(関数呼び出しや型変換を表すASTノード)を処理する部分です。
nodeStr := w.nodeString(v.Fun)
:v.Fun
はast.CallExpr
の中で呼び出される関数や型変換の対象となる式を表します。ここでは、そのASTノードを文字列に変換しています。例えば、string("foo")
の場合は"string"
、[]byte("foo")
の場合は"[]byte"
となります。switch nodeStr { case "string", "[]byte": return nodeStr, nil }
: ここがこのコミットの主要な変更点です。もしnodeStr
が"string"
または"[]byte"
であれば、それはGoの組み込み型変換であると判断し、エラーを返さずにその型名("string"
または"[]byte"
) を返します。これにより、cmd/api
ツールはこれらの型変換を正しく認識し、処理を続行できるようになります。return "", fmt.Errorf("not a known function %q", nodeStr)
: 上記のswitch
ケースに該当しない場合、つまり"string"
や"[]byte"
以外の未知の関数呼び出しであった場合は、以前と同様にエラーを返します。
この変更は、当時のGo ASTの制約(型情報が不足していること)を回避するための「ハック」であり、構文の文字列表現に依存しているため、理想的な解決策ではありません。しかし、これにより cmd/api
ツールが string
および []byte
への型変換を含むGoファイルを正しく解析できるようになり、Windows環境や gccgo
での互換性問題が解消されました。
テストデータの変更
src/cmd/api/testdata/src/pkg/p1/p1.go
には、新しいテスト変数 StrConv
と ByteConv
が追加されました。これらはそれぞれ string("foo")
と []byte("foo")
という型変換を含む初期値を持っています。
src/cmd/api/testdata/src/pkg/p1/golden.txt
は、cmd/api
ツールが p1.go
を解析した際に期待される出力(APIサーフェス定義)を記述したファイルです。このファイルに pkg p1, var ByteConv []byte
と pkg p1, var StrConv string
が追加されたことは、cmd/api
ツールがこれらの新しい変数を正しく検出し、その型を認識できるようになったことを示しています。これにより、コードの変更が意図した通りに機能していることが検証されます。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- Go言語の型変換に関する仕様: https://go.dev/ref/spec#Conversions
- Go言語のASTパッケージ (
go/ast
): https://pkg.go.dev/go/ast gccgo
プロジェクトページ (GCCの一部): https://gcc.gnu.org/onlinedocs/gccgo/
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Gerrit Code Review (Goプロジェクトのコードレビューシステム): https://go-review.googlesource.com/
- Go言語のIssue Tracker: https://go.dev/issue/
- Go言語のメーリングリスト (golang-dev): https://groups.google.com/g/golang-dev
- Go言語の歴史に関する情報 (当時のASTの制約など): Go言語の初期の設計に関するブログ記事やカンファレンストーク、またはGo言語の書籍(例: "The Go Programming Language" by Alan A. A. Donovan and Brian W. Kernighan)の歴史的背景に関する章。