Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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 のようなツールは、コードを解析する際に型変換のような特定の構文要素を正しく識別するのに苦労していました。

具体的な問題として、以下の点が挙げられます。

  1. Windows環境での問題: 以前の変更により、cmd/api ツールがWindows環境で正しく動作しなくなっていた可能性があります。これは、パスの扱い、ファイルシステムの特性、または特定のシステムコールが原因である可能性がありますが、コミットメッセージからは直接的な原因は読み取れません。しかし、このコミットによってWindowsでの動作が回復したことが示唆されています。
  2. gccgo との互換性問題: gccgo は、GCC(GNU Compiler Collection)をバックエンドとするGo言語の代替コンパイラです。標準の gc コンパイラとは異なる実装であるため、cmd/api ツールが gccgo でコンパイルされた、または gccgo のコードスタイルに沿ったGoファイルを解析する際に、特定の構文(特に型変換)を誤って解釈していた可能性があります。
  3. 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によって開発されています。gccgogc とは独立して開発されており、両者には細かな挙動の違いや、特定の構文の解釈の違いが存在する可能性があります。

技術的詳細

このコミットの核心は、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.gosrc/cmd/api/testdata/src/pkg/p1/golden.txt) が更新され、string および []byte への型変換を含む変数が追加されました。これにより、cmd/api ツールがこれらの変換を正しく処理できるようになったことが検証されます。

  • p1.goStrConv = string("foo")ByteConv = []byte("foo") が追加。
  • golden.txtpkg p1, var ByteConv []bytepkg 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ノード)を処理する部分です。

  1. nodeStr := w.nodeString(v.Fun): v.Funast.CallExpr の中で呼び出される関数や型変換の対象となる式を表します。ここでは、そのASTノードを文字列に変換しています。例えば、string("foo") の場合は "string"[]byte("foo") の場合は "[]byte" となります。
  2. switch nodeStr { case "string", "[]byte": return nodeStr, nil }: ここがこのコミットの主要な変更点です。もし nodeStr"string" または "[]byte" であれば、それはGoの組み込み型変換であると判断し、エラーを返さずにその型名("string" または "[]byte") を返します。これにより、cmd/api ツールはこれらの型変換を正しく認識し、処理を続行できるようになります。
  3. 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 には、新しいテスト変数 StrConvByteConv が追加されました。これらはそれぞれ string("foo")[]byte("foo") という型変換を含む初期値を持っています。

src/cmd/api/testdata/src/pkg/p1/golden.txt は、cmd/api ツールが p1.go を解析した際に期待される出力(APIサーフェス定義)を記述したファイルです。このファイルに pkg p1, var ByteConv []bytepkg p1, var StrConv string が追加されたことは、cmd/api ツールがこれらの新しい変数を正しく検出し、その型を認識できるようになったことを示しています。これにより、コードの変更が意図した通りに機能していることが検証されます。

関連リンク

参考にした情報源リンク