[インデックス 13198] ファイルの概要
このコミットは、Go言語の実験的な型システムパッケージ exp/types
における、ドット付き識別子(dotted identifiers)の読み込みに関する問題を修正するものです。具体的には、gcimporter
(Goコンパイラが生成するバイナリ形式の型情報をインポートするコンポーネント)が、特定の形式の識別子を正しく解析できないバグに対応しています。
コミット
commit bd7c626348f3013ef307f9e3ae7c51708e2579eb
Author: Robert Griesemer <gri@golang.org>
Date: Tue May 29 13:15:13 2012 -0700
exp/types: properly read dotted identifiers
Fixes #3682.
R=rsc
CC=golang-dev
https://golang.org/cl/6256067
---
src/pkg/exp/types/gcimporter.go | 2 +-\
src/pkg/exp/types/gcimporter_test.go | 7 -------
src/pkg/exp/types/testdata/exports.go | 5 +++++
3 files changed, 6 insertions(+), 8 deletions(-)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bd7c626348f3013ef307f9e3ae7c51708e2579eb
元コミット内容
exp/types: properly read dotted identifiers
このコミットは、Go言語の実験的な型システムパッケージ exp/types
において、ドット付き識別子(例: pkg.Name
)を適切に読み込むように修正します。
Fixes #3682.
この修正は、GoのIssueトラッカーにおけるIssue 3682を解決します。
R=rsc
CC=golang-dev
https://golang.org/cl/6256067
この変更は、rsc
(Russ Cox) によってレビューされ、golang-dev
メーリングリストにCCされています。関連するGoの変更リスト(Change List)は 6256067
です。
変更の背景
このコミットの背景には、Go言語の型システムが、コンパイル済みのパッケージから型情報を正確にインポートする際の課題がありました。特に、Goの内部的な表現において、エクスポートされた識別子(例えば、パッケージの外部から参照可能な変数、関数、型など)が、ドット(.
)を含む形式で表現されることがあります。これは、パッケージ名と識別子名を結合した「ドット付き識別子」として知られています。
元の実装では、gcimporter
がこれらのドット付き識別子を解析する際に、特定の文字(具体的には ·
(U+00B7))を識別子の一部として認識できない問題がありました。この問題は、crypto/md5
のような標準ライブラリパッケージの型情報をインポートしようとした際に顕在化しました。crypto/md5
には const init1 = 0x...
のような定数宣言が含まれており、gcimporter
がこれを関数として誤認識してしまうというバグ(Issue 3682)が発生していました。
この誤認識は、gcimporter
がGoコンパイラによって生成されたバイナリ形式の型情報(GC export data)を読み込む際に、識別子を構成する文字セットが不完全であったために起こりました。結果として、型情報のインポートが失敗したり、誤った型情報が構築されたりする可能性がありました。
前提知識の解説
Go言語の型システム (exp/types
)
exp/types
は、Go言語のコンパイラやツールがGoのソースコードの型チェックを行うために使用する、実験的な型システムパッケージです。これは、Goのプログラムの抽象構文木(AST)を解析し、各識別子の型を決定し、型エラーを検出する役割を担います。このパッケージは、Goのコンパイラが内部的に使用するだけでなく、Goのコード分析ツールやIDEなどでも利用されます。
gcimporter
gcimporter
は、exp/types
パッケージの一部であり、Goコンパイラが生成するバイナリ形式の型情報(GC export data)を読み込むためのコンポーネントです。Goのコンパイラは、パッケージをコンパイルする際に、そのパッケージがエクスポートする型、関数、変数などの情報をバイナリ形式で出力します。他のパッケージがそのパッケージをインポートする際には、gcimporter
がこのバイナリ情報を読み込み、メモリ上にGoの型システムが理解できる形式で再構築します。これにより、インポートされたパッケージの型情報を参照して、型チェックやコード補完などを行うことができます。
ドット付き識別子 (Dotted Identifiers)
Go言語では、パッケージの外部からエクスポートされた識別子を参照する際に、パッケージ名.識別子名
の形式を使用します。例えば、fmt.Println
の Println
は fmt
パッケージの関数です。Goのコンパイラが生成する内部的な型情報では、これらのエクスポートされた識別子を表現するために、特殊な文字(例えば ·
(U+00B7))を使用してパッケージ名と識別子名を結合することがあります。これは、Goのソースコード上では見えない内部的な表現であり、gcimporter
がこれを正しく解析する必要があります。
scanner.Scan()
と scanner.TokenText()
Goの text/scanner
パッケージは、Goのソースコードやその他のテキストをトークンに分割するためのスキャナーを提供します。
scanner.Scan()
: 入力ストリームから次のトークンを読み込み、そのトークンの種類(scanner.Ident
、scanner.Int
、scanner.String
など)を返します。scanner.TokenText()
:scanner.Scan()
によって読み込まれた最新のトークンのテキスト表現を返します。
このコミットでは、gcimporter
が内部的に使用するスキャナーが、ドット付き識別子を構成する特殊な文字を正しくトークンとして認識できるように、scanner.Scan()
の結果を処理するロジックが変更されています。
技術的詳細
このコミットの技術的な核心は、gcimporter
がGoコンパイラによって生成されたバイナリ形式の型情報(GC export data)を解析する際に、ドット付き識別子を正しく処理できるようにすることです。
Goの内部では、エクスポートされた識別子、特にパッケージの外部から参照される識別子(例: fmt.Println
の Println
)は、バイナリ形式の型情報において、パッケージパスと識別子名を結合した特殊な形式で表現されることがあります。この結合には、Goの内部で「ドット」として機能する特殊な文字 ·
(U+00B7, Middle Dot) が使用されることがあります。
元の gcimporter.go
の next()
メソッドでは、スキャナーが読み込んだトークンの種類を switch
文で判定し、scanner.Ident
(識別子)、scanner.Int
(整数リテラル)、scanner.String
(文字列リテラル) の場合に p.lit
にトークンのテキストを格納していました。しかし、この switch
文には、ドット付き識別子を構成する ·
文字がトークンとして現れた場合の処理が欠けていました。
その結果、gcimporter
は ·
を含む識別子を正しく認識できず、例えば crypto/md5
パッケージの init1
定数(内部的には crypto/md5·init1
のように表現される可能性があった)を、誤って関数として解釈してしまうというバグが発生していました。これは、gcimporter
が期待するトークンのシーケンスと、実際に読み込んだトークンのシーケンスが一致しないために、解析ロジックが誤ったパスに進んでしまったためと考えられます。
このコミットでは、gcimporter.go
の next()
メソッドの switch
文に case '·':
を追加することで、スキャナーが ·
文字を読み込んだ場合にも、そのトークンのテキストを p.lit
に正しく格納するように修正しています。これにより、gcimporter
はドット付き識別子を構成するすべての要素を正しく認識し、完全な識別子として処理できるようになります。
また、gcimporter_test.go
から、このバグを一時的にスキップしていたテストコードが削除されています。これは、修正によってバグが解消されたため、もはやスキップする必要がなくなったことを示しています。
さらに、testdata/exports.go
には、Issue 3682を再現し、修正を検証するための新しいテストケースが追加されています。具体的には、const init1 = 0
と func init() {}
という宣言が追加されており、gcimporter
がこれらの宣言を正しく解析できることを確認します。特に init1
のような名前は、Goの内部で特殊な扱いを受けることがあるため、このテストケースは重要です。
コアとなるコードの変更箇所
src/pkg/exp/types/gcimporter.go
--- a/src/pkg/exp/types/gcimporter.go
+++ b/src/pkg/exp/types/gcimporter.go
@@ -182,7 +182,7 @@ func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*
func (p *gcParser) next() {
p.tok = p.scanner.Scan()
switch p.tok {
- case scanner.Ident, scanner.Int, scanner.String:
+ case scanner.Ident, scanner.Int, scanner.String, '·':
p.lit = p.scanner.TokenText()
default:
p.lit = ""
src/pkg/exp/types/gcimporter_test.go
--- a/src/pkg/exp/types/gcimporter_test.go
+++ b/src/pkg/exp/types/gcimporter_test.go
@@ -92,13 +92,6 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
}
func TestGcImport(t *testing.T) {
- // Dies trying to read crypto/md5, which contains
- // const init1 = 0x...\
- // The importer believes init1 should be a function for some reason.
- // golang.org/issue/3682.
- t.Logf("broken; skipping")
- return
-
// On cross-compile builds, the path will not exist.
// Need to use GOHOSTOS, which is not available.
if _, err := os.Stat(gcPath); err != nil {
src/pkg/exp/types/testdata/exports.go
--- a/src/pkg/exp/types/testdata/exports.go
+++ b/src/pkg/exp/types/testdata/exports.go
@@ -11,6 +11,11 @@ import (
"go/ast"
)
+// Issue 3682: Correctly read dotted identifiers from export data.
+const init1 = 0
+
+func init() {}
+
const (
C0 int = 0
C1 = 3.14159265
コアとなるコードの解説
src/pkg/exp/types/gcimporter.go
の変更
この変更は、gcParser
構造体の next()
メソッド内で行われています。next()
メソッドは、gcimporter
がバイナリ形式の型情報を解析する際に、次のトークンを読み込む役割を担っています。
元のコードでは、p.scanner.Scan()
で読み込んだトークン p.tok
が scanner.Ident
(識別子)、scanner.Int
(整数リテラル)、scanner.String
(文字列リテラル) のいずれかである場合に、そのトークンのテキストを p.lit
に格納していました。
修正後のコードでは、この switch
文に '·'
(U+00B7, Middle Dot) が追加されています。これは、Goの内部的な表現において、パッケージ名と識別子名を結合するために使用される特殊な文字です。この変更により、gcimporter
は ·
文字を識別子の一部として正しく認識し、そのテキストを p.lit
に格納できるようになります。これにより、crypto/md5·init1
のようなドット付き識別子全体を正しく解析し、init1
が定数であることを適切に判断できるようになります。
src/pkg/exp/types/gcimporter_test.go
の変更
このファイルでは、TestGcImport
関数から、Issue 3682に関連する一時的なスキップロジックが削除されています。
元のコードでは、crypto/md5
のインポートが失敗する問題(init1
が関数として誤認識される)があったため、t.Logf("broken; skipping")
と return
を使用して、このテストケースを一時的にスキップしていました。
今回の修正によって、gcimporter
がドット付き識別子を正しく処理できるようになり、このバグが解消されたため、スキップロジックは不要になりました。これにより、TestGcImport
は完全に実行され、gcimporter
の機能が正しく動作することを検証できるようになります。
src/pkg/exp/types/testdata/exports.go
の変更
このファイルは、gcimporter
のテストデータとして使用されるGoのソースコードです。
追加されたコードは以下の通りです。
// Issue 3682: Correctly read dotted identifiers from export data.
const init1 = 0
func init() {}
// Issue 3682: Correctly read dotted identifiers from export data.
: このコメントは、このコードがIssue 3682の修正を検証するためのものであることを明確に示しています。const init1 = 0
:init1
という名前の定数を宣言しています。この名前は、Goの内部で特殊な意味を持つinit
関数と関連付けられることがあり、gcimporter
がこれを正しく定数として認識できるかどうかのテストケースとして重要です。func init() {}
: Goのinit
関数を宣言しています。これは、パッケージの初期化時に自動的に実行される特殊な関数です。gcimporter
がinit
関数を正しく識別できるかどうかも、型情報の正確なインポートにとって重要です。
これらの追加により、gcimporter
が init1
のような名前を持つ定数や、init
関数のような特殊な関数を、バイナリ形式の型情報から正しく読み取れることを検証できるようになります。
関連リンク
- Go Issue 3682: https://github.com/golang/go/issues/3682 (このコミットが修正した具体的なIssue)
- Go Change List 6256067: https://golang.org/cl/6256067 (このコミットに対応するGoの変更リスト)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Go Issue Tracker
- Go Code Review (Gerrit)
text/scanner
パッケージのドキュメント- Go言語の内部的な型表現に関する情報 (Goコンパイラの設計ドキュメントなど)
- Go言語の
init
関数に関する情報