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

[インデックス 15345] ファイルの概要

このコミットは、Go言語のコンパイラツールチェーンの一部であるgo/typesパッケージ内のgcimporterの挙動に関するものです。具体的には、エクスポートされるデータの結果型が常に括弧で囲まれるように変更し、gcimporterのコードを簡素化することを目的としています。これにより、Goの型システムにおける関数シグネチャのパース処理がより一貫性を持つようになります。

コミット

commit 8473b4487c26f85fa31088da739507d3b218dc29
Author: Robert Griesemer <gri@golang.org>
Date:   Wed Feb 20 17:37:13 2013 -0800

    go/types: export data result types are always parenthesized
    
    Minor simplification of gcimporter, removed TODO.
    
    R=adonovan
    CC=golang-dev
    https://golang.org/cl/7363044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/8473b4487c26f85fa31088da739507d3b218dc29

元コミット内容

このコミットの元の内容は、「go/types: エクスポートされるデータの結果型は常に括弧で囲まれる」というものです。これは、gcimporterのわずかな簡素化と、既存のTODOコメントの削除を伴います。

変更の背景

Go言語では、関数の戻り値の型を定義する際に、複数の戻り値や名前付き戻り値の場合には括弧()で囲む必要があります。例えば、func foo() (int, error)func bar() (result int) のように記述します。一方、単一の無名な戻り値の場合、括弧は省略可能です(例: func baz() int)。

gcimporterは、Goコンパイラ(gc)が生成するオブジェクトファイルから型情報(エクスポートデータ)を読み込む役割を担っています。この型情報には、関数のシグネチャも含まれます。以前のgcimporterの実装では、単一の無名な戻り値の型をパースする際に、括弧がないケースと括弧があるケースを区別して処理する必要がありました。

コミットメッセージにある「TODO(gri) does this ever happen?」というコメントは、単一の無名な戻り値が括弧で囲まれてエクスポートされるケースが実際に発生するのかどうか、あるいはその処理が本当に必要かどうかの疑問を示唆しています。このコミットは、エクスポートされるデータにおいては、結果型が常に括弧で囲まれるという前提を導入することで、gcimporterのパースロジックを簡素化しようとしています。これにより、gcimporterは戻り値の型が常に括弧で始まるものとして処理できるようになり、コードの複雑性が軽減されます。

前提知識の解説

Go言語の型システムと関数シグネチャ

Goは静的型付け言語であり、変数の型はコンパイル時に決定されます。関数のシグネチャは、その関数が受け取る引数と返す戻り値の型を定義します。

  • 単一の無名な戻り値: func add(a, b int) int のように、戻り値が1つで名前がない場合、型は括弧なしで記述できます。
  • 複数の戻り値: func divide(a, b int) (int, error) のように、複数の戻り値を返す場合、それらの型は必ず括弧で囲み、カンマで区切ります。
  • 名前付き戻り値: func getStatus() (success bool)func calculate(x, y int) (sum int, product int) のように、戻り値に名前を付ける場合、単一であっても複数であっても、必ず括弧で囲みます。名前付き戻り値は、関数の先頭で宣言された変数として扱われ、return文で明示的に値を指定せずに返す「naked return」が可能です。

このコミットの文脈では、gcimporterがGoのソースコードではなく、コンパイラが生成したエクスポートデータを扱うため、Go言語の構文規則がどのようにエクスポートデータに反映されるかが重要になります。

go/typesパッケージ

go/typesパッケージは、Goの標準ライブラリの一部であり、Goプログラムの型チェックを行うための機能を提供します。これは、Goのコンパイラや、Goのコードを分析・操作するツール(リンター、IDEなど)にとって非常に重要なパッケージです。go/typesは、識別子の解決、式の型の推論、型関連のエラー報告などを行います。

gcimporter

gcimporterは、go/typesパッケージの一部として、Goコンパイラ(gc)が生成するオブジェクトファイル(.aファイルなど)から型情報を読み込む役割を担っています。Goのパッケージがコンパイルされると、そのパッケージがエクスポートする型や宣言に関するメタデータがオブジェクトファイルに埋め込まれます。gcimporterは、このメタデータをパースし、go/typesパッケージが理解できる内部表現に変換することで、他のパッケージがその型情報をインポートして利用できるようにします。

つまり、gcimporterはGoのコンパイルプロセスにおいて、異なるパッケージ間の型情報の連携を可能にする低レベルなコンポーネントです。開発者が直接このパッケージを操作することは通常ありませんが、Goコンパイラの内部動作を理解する上で重要です。

技術的詳細

このコミットの技術的な核心は、gcimporterが関数の結果型をパースするロジックの簡素化にあります。

変更前のコードでは、parseSignature関数内で結果型をパースする際に、p.tok(現在のトークン)がscanner.Ident[, *, <, @のいずれかである場合に、単一の無名な結果型として処理するswitch文のケースがありました。このケースは、結果型が括弧で囲まれていない場合に対応していました。そして、(トークンである場合に、括弧で囲まれた(名前付きまたは複数の)結果型として処理する別のケースがありました。

// 変更前
switch p.tok {
case scanner.Ident, '[', '*', '<', '@':
    // TODO(gri) does this ever happen?
    // single, unnamed result
    results = []*Var{{Type: p.parseType()}}
case '(':
    // named or multiple result(s)
    var variadic bool
    results, variadic = p.parseParameters()
    if variadic {
        // ...
    }
}

このswitch文の最初のケース(scanner.Identなど)は、単一の無名な戻り値が括弧なしで記述されるGoの構文に対応していました。しかし、コミットメッセージが示唆するように、gcimporterが処理する「エクスポートデータ」においては、結果型が常に括弧で囲まれているという前提が成り立つ場合、このswitch文の最初のケースは不要になります。

このコミットは、その前提が正しいことを確認し、gcimporterがパースするエクスポートデータでは、関数の結果型が常に括弧で囲まれていると仮定することで、コードを簡素化しています。これにより、switch文を削除し、単にp.tok == '('であるかどうかをチェックするif文に置き換えることが可能になりました。

// 変更後
if p.tok == '(' {
    var variadic bool
    results, variadic = p.parseParameters()
    if variadic {
        // ...
    }
}

この変更により、gcimporterは、エクスポートされた関数の結果型をパースする際に、常に括弧で囲まれた形式を期待するようになります。これは、Goコンパイラがエクスポートデータを生成する際の内部的な規約が、単一の無名な戻り値であっても常に括弧で囲むように変更されたか、あるいは元々そのように設計されていたが、gcimporterのコードが過剰に汎用的に記述されていたことを示唆しています。

結果として、コードの行数が減り、パースロジックがより直接的になり、TODOコメントも削除されました。これは、Goコンパイラと関連ツールチェーンの内部的な整合性と効率性を向上させるための、小さなしかし重要な改善です。

コアとなるコードの変更箇所

--- a/src/pkg/go/types/gcimporter.go
+++ b/src/pkg/go/types/gcimporter.go
@@ -548,13 +548,7 @@ func (p *gcParser) parseSignature() *Signature {
 
 	// optional result type
 	var results []*Var
-	switch p.tok {
-	case scanner.Ident, '[', '*', '<', '@':
-		// TODO(gri) does this ever happen?
-		// single, unnamed result
-		results = []*Var{{Type: p.parseType()}}\n-	case '(':
-		// named or multiple result(s)
+	if p.tok == '(' {
 		var variadic bool
 		results, variadic = p.parseParameters()
 		if variadic {

コアとなるコードの解説

変更はsrc/pkg/go/types/gcimporter.goファイルのparseSignature関数内で行われています。

  • 削除されたコード:

    	switch p.tok {
    	case scanner.Ident, '[', '*', '<', '@':
    		// TODO(gri) does this ever happen?
    		// single, unnamed result
    		results = []*Var{{Type: p.parseType()}}\n-	case '(':
    		// named or multiple result(s)
    

    このswitch文は、関数の戻り値の型をパースする際の分岐ロジックでした。 scanner.Ident, '[', '*', '<', '@' のケースは、単一の無名な戻り値(例: func() intint 部分)をパースするためのものでした。この部分には「TODO(gri) does this ever happen?」というコメントがあり、このコードパスが実際に必要かどうか疑問視されていました。 '(' のケースは、括弧で囲まれた戻り値(例: func() (int, error)func() (result int))をパースするためのものでした。

  • 追加されたコード:

    	if p.tok == '(' {
    

    削除されたswitch文全体が、このシンプルなif文に置き換えられました。これは、gcimporterが処理するエクスポートデータにおいては、関数の戻り値の型が常に括弧で囲まれている(つまり、p.tokが常に'('である)という新しい前提に基づいています。

この変更により、gcimporterは、戻り値の型が括弧で囲まれていないケースを考慮する必要がなくなり、パースロジックが大幅に簡素化されました。これは、Goコンパイラが生成するエクスポートデータのフォーマットに関する内部的な規約が明確化または変更された結果であると考えられます。

関連リンク

(注: コミットメッセージに記載されている https://golang.org/cl/7363044 は、現在のGoのChange Listシステムでは見つかりませんでした。これは非常に古い変更であるか、非公開の変更である可能性があります。)

参考にした情報源リンク