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

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

このコミットは、Goコンパイラ(cmd/gc)における、埋め込みフィールドを持つ無名構造体のインライン化に関するバグを修正するものです。具体的には、Go言語のパーサー生成ツールであるBisonのバージョンアップと、それに伴う文法定義ファイルの修正が含まれています。

コミット

commit 49da9a8e44fecbf9b3287472b6554419840b03b5
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Mon Aug 5 22:09:53 2013 +0200

    cmd/gc: fix inlining of unnamed structs with embedded fields.
    
    Update #5910.
    
    R=golang-dev, daniel.morsing, rsc
    CC=golang-dev
    https://golang.org/cl/11373044

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

https://github.com/golang/go/commit/49da9a8e44fecbf9b3287472b6554419840b03b5

元コミット内容

cmd/gc: fix inlining of unnamed structs with embedded fields.

Update #5910.

R=golang-dev, daniel.morsing, rsc
CC=golang-dev
https://golang.org/cl/11373044

変更の背景

このコミットは、Go言語のコンパイラ(cmd/gc)が、埋め込みフィールドを持つ無名構造体をインライン化する際に発生していたバグ(Issue 5910)を修正するために行われました。

Go言語では、コードのパフォーマンスを向上させるために、コンパイラが関数をインライン化する最適化を行います。インライン化とは、関数呼び出しのオーバーヘッドを削減するために、呼び出される関数のコードを呼び出し元のコードに直接埋め込むプロセスです。

しかし、特定の条件下、特に無名構造体(名前を持たない構造体)が埋め込みフィールド(他の構造体をフィールドとして持つこと)を持つ場合に、コンパイラのパーサーがこれらの型を正しく処理できない問題がありました。これにより、インライン化されたコードが不正になったり、コンパイルエラーが発生したりする可能性がありました。

この問題は、Goコンパイラのフロントエンドが使用しているパーサー生成ツールであるGNU Bisonのバージョンが古かったこと(Bison 2.3)と、それに伴う文法定義ファイル(go.y)の特定のルールが、新しいGo言語のセマンティクスや複雑な型構造を適切に扱えていなかったことに起因すると考えられます。

前提知識の解説

  • cmd/gc: Go言語の公式コンパイラです。Goのソースコードを機械語に変換する役割を担います。gcは「Go compiler」の略です。
  • インライン化 (Inlining): コンパイラ最適化の一種で、関数呼び出しのオーバーヘッドを削減するために、呼び出される関数の本体を呼び出し元の位置に直接展開することです。これにより、実行時のパフォーマンスが向上する可能性があります。
  • 無名構造体 (Unnamed Structs): Go言語では、名前を付けずに構造体を定義できます。これらは通常、一時的なデータ構造や、特定の関数内でのみ使用される型として利用されます。例: var s struct { X int; Y string }
  • 埋め込みフィールド (Embedded Fields): Go言語の構造体は、他の型(構造体やインターフェース)をフィールドとして埋め込むことができます。これにより、埋め込まれた型のメソッドやフィールドが、埋め込み元の構造体のトップレベルで利用できるようになります。これは、継承ではなく「コンポジション(合成)」を促進するGoの設計思想の一部です。例:
    type Point struct {
        X, Y int
    }
    type Circle struct {
        Point // Point構造体を埋め込み
        Radius int
    }
    
  • Yacc/Bison: Yacc (Yet Another Compiler Compiler) は、文法定義からパーサー(構文解析器)を生成するツールです。BisonはGNUプロジェクトによるYaccのフリーな実装です。Goコンパイラのフロントエンドは、Go言語の文法を定義した.yファイル(go.y)をBisonで処理し、C言語のパーサーコード(y.tab.cy.tab.h)を生成しています。
  • パーサー (Parser): プログラミング言語のソースコードを読み込み、その構文が言語の文法規則に準拠しているかを検証し、抽象構文木(AST)などの内部表現を構築するコンパイラのコンポーネントです。

技術的詳細

このコミットの技術的詳細は、主にGoコンパイラのパーサー部分に焦点を当てています。

  1. Bisonのバージョンアップ: src/cmd/gc/y.tab.cの変更を見ると、Bisonのバージョンが2.3から2.5に更新されています。これは、Bison自体が生成するパーサーコードの内部構造や最適化、エラーハンドリングロジックが変更されたことを意味します。Bisonのバージョンアップは、パーサーの堅牢性や性能向上に寄与し、古いバージョンでは対応できなかった特定の構文解析の課題を解決する可能性があります。特に、エラーメッセージの生成ロジック(yysyntax_error関数)やスタック管理のメカニズム(YYSTACK_RELOCATEマクロ)に大きな変更が見られます。

  2. go.yの文法ルール修正: src/cmd/gc/go.yファイルはGo言語の文法を定義するYacc/Bisonの入力ファイルです。このファイル内のstructdcl(構造体宣言)に関するルールが修正されています。 元のコード:

    if(l != nil && l->next == nil && l->n == nil) {
        // ? symbol, during import
    

    修正後のコード:

    if(l == nil) {
        // ? symbol, during import (list1(N) == nil)
    

    この変更は、structdclルール内でリストlが空であるかどうかのチェックをより直接的に行っています。元の条件は、リストがnilではないが、その次の要素もノードもnilであるという、より複雑な状態をチェックしていました。この複雑な条件が、無名構造体や埋め込みフィールドの特定のケース、特にインポート時にシンボルを解決する際に、パーサーが誤ったパスをたどる原因となっていた可能性があります。l == nilというシンプルなチェックにすることで、パーサーがより正確に空のリストを認識し、適切な処理フローに進むことができるようになります。これは、無名構造体や埋め込みフィールドの型情報が、パーサーの内部表現でどのように扱われるかに影響を与え、インライン化の際に正しく型が解決されるようにするための重要な修正です。

  3. テストケースの追加: test/fixedbugs/issue5910.dir/a.gotest/fixedbugs/issue5910.dir/main.gotest/fixedbugs/issue5910.goといったテストファイルが追加されています。これらのテストは、埋め込みフィールドを持つ無名構造体のインライン化が正しく機能することを確認するために書かれています。これにより、将来的に同様の回帰バグが発生しないように保証されます。

これらの変更は、Goコンパイラがより複雑な型構造、特にインライン化のコンテキストで、無名構造体と埋め込みフィールドを正確に解析し、処理できるようにするために不可欠でした。

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

このコミットのコアとなるコードの変更は、主に以下の2つのファイルにあります。

  1. src/cmd/gc/go.y: Go言語の文法定義ファイル。

    --- a/src/cmd/gc/go.y
    +++ b/src/cmd/gc/go.y
    @@ -1541,8 +1541,8 @@ structdcl:
    
     		Node *n;
     		l = $1;
    -		if(l != nil && l->next == nil && l->n == nil) {
    -			// ? symbol, during import
    +		if(l == nil) {
    +			// ? symbol, during import (list1(N) == nil)
     			n = $2;
     			if(n->op == OIND)
     				n = n->left;
    
  2. src/cmd/gc/y.tab.c: go.yからBisonによって生成されたC言語のパーサー実装ファイル。このファイルはBisonのバージョンアップに伴い、広範囲にわたる変更があります。主な変更はBisonの内部構造の更新によるもので、手動での変更ではありません。

コアとなるコードの解説

src/cmd/gc/go.yにおける変更は、Goコンパイラのパーサーが構造体宣言(structdcl)を処理する際のロジックを修正しています。

  • structdclルール: このルールは、Go言語の構造体型がどのように宣言されるかを定義しています。これには、通常の名前付き構造体だけでなく、無名構造体や埋め込みフィールドも含まれます。
  • 変更された条件式:
    • 変更前: if(l != nil && l->next == nil && l->n == nil) この条件は、「lnilではなく、かつlの次の要素もnilで、かつlのノードもnilである」という、非常に特定の状態をチェックしていました。コメントには「? symbol, during import」とあり、これはインポート処理中にシンボルを解決する際の特殊なケースを扱っていたことを示唆しています。この複雑な条件が、無名構造体や埋め込みフィールドのような、より複雑な型構造を持つ場合に、パーサーが意図しない動作をする原因となっていた可能性があります。特に、インライン化の際にこれらの型がどのように表現され、解析されるかに影響を与えていたと考えられます。
    • 変更後: if(l == nil) この条件は、「lnilである」という、よりシンプルで直接的なチェックです。コメントも「? symbol, during import (list1(N) == nil)」と変更されており、list1(N)nilを返すケース、つまりリストが空であるケースを明示的に扱っていることがわかります。この修正により、パーサーは空のリストをより正確に識別し、それに応じて適切な構文解析パスを選択できるようになります。これにより、埋め込みフィールドを持つ無名構造体の型情報が、インライン化のコンテキストで正しく解釈されるようになり、バグが解消されました。

src/cmd/gc/y.tab.cの変更は、Bisonのバージョンが2.3から2.5に更新されたことによる自動生成コードの変更です。これには、パーサーの内部データ構造、スタック管理、エラー報告メカニズムなどの改善が含まれます。このBisonのバージョンアップ自体が、パーサーの堅牢性を高め、特定の構文解析のコーナーケースをより適切に処理できるようになることで、間接的にバグの修正に貢献しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (インライン化、構造体、埋め込みフィールドに関する一般的な情報)
  • GNU Bisonのドキュメント (Bisonの機能とバージョン間の変更に関する一般的な情報)
  • Goコンパイラのソースコード (特にcmd/gcディレクトリ内のファイル構造と役割)