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

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

このコミットは、Goコンパイラの一つであるgccgoが特定のコードパターンでコンパイルに失敗するバグを修正するために、そのバグを再現する新しいテストケースを追加するものです。具体的には、囲む関数内の変数名と構造体リテラルのフィールド名が同じ場合にgccgoが混乱するという問題に対処しています。

コミット

commit 29d27671e521a45247c31f694d8ec041510e8ed3
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Jul 16 15:31:42 2013 -0700

    test: add a compilation test that gccgo failed to compile
    
    R=golang-dev, remyoudompheng
    CC=golang-dev
    https://golang.org/cl/11379044
---
 test/fixedbugs/bug475.go | 22 ++++++++++++++++++++++\n 1 file changed, 22 insertions(+)\n
diff --git a/test/fixedbugs/bug475.go b/test/fixedbugs/bug475.go
new file mode 100644
index 0000000000..1bd6fa35ce
--- /dev/null
+++ b/test/fixedbugs/bug475.go
@@ -0,0 +1,22 @@
+// compile
+
+// Copyright 2013 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Variable in enclosing function with same name as field in struct
+// composite literal confused gccgo.
+
+package p
+
+type s1 struct {
+	f *s1
+}
+
+func F() {
+	var f *s1
+	_ = func() {
+		_ = s1{f: nil}
+	}
+	_ = f
+}

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

https://github.com/golang/go/commit/29d27671e521a45247c31f694d8ec041510e8ed3

元コミット内容

このコミットは、gccgoコンパイラがコンパイルに失敗した特定のコードパターンに対するコンパイルテストを追加するものです。具体的には、囲む関数内で宣言された変数と、構造体リテラル内で使用されるフィールドが同じ名前を持つ場合に、gccgoが混乱するというバグを修正するためのテストケースです。

変更の背景

Go言語には、公式のGoコンパイラであるgc(Go Compiler)とは別に、GCC(GNU Compiler Collection)をバックエンドとして利用するgccgoという実装が存在します。gccgoは、Go言語のコードをGCCの中間表現に変換し、GCCの最適化やコード生成の恩恵を受けることを目的としています。

このコミットが追加された背景には、gccgoが特定の有効なGoコードを正しくコンパイルできないというバグが存在したことがあります。コミットメッセージに「Variable in enclosing function with same name as field in struct composite literal confused gccgo.」とあるように、関数スコープ内のローカル変数名と、その関数内で使用される構造体リテラルのフィールド名が偶然一致した場合に、gccgoが名前解決の段階で誤った解釈をしてしまい、コンパイルエラーを引き起こしていました。

このようなコンパイラのバグは、Go言語の仕様に準拠したコードが正しく動作しないという重大な問題につながります。そのため、このバグを修正し、将来的な回帰を防ぐために、バグを再現するテストケースをtest/fixedbugsディレクトリに追加する必要がありました。test/fixedbugsディレクトリは、過去に発見され修正されたバグの回帰テストを格納するための場所です。

前提知識の解説

Go言語のスコープと名前解決

Go言語では、変数のスコープはブロック({}で囲まれた領域)によって定義されます。内側のブロックで宣言された変数は、外側のブロックで宣言された同名の変数をシャドウ(隠蔽)します。しかし、このケースではシャドウイングではなく、異なる種類のエンティティ(変数と構造体フィールド)が同じ名前を持つことによるコンパイラの混乱が問題でした。

構造体リテラル (Struct Composite Literal)

Go言語において、構造体リテラルは構造体の新しい値を初期化するための構文です。例えば、type MyStruct struct { Field1 int; Field2 string }という構造体がある場合、MyStruct{Field1: 10, Field2: "hello"}のようにして値を生成します。フィールド名を明示的に指定する形式(Field1: 10)は、フィールドの順序に依存せず、可読性が高いという特徴があります。

gccgoとgc (Go Compiler)

  • gc (Go Compiler): Go言語の公式かつ主要なコンパイラです。Go言語のソースコードを直接機械語にコンパイルします。Go言語の開発チームによって開発・メンテナンスされており、Go言語の最新の機能や最適化が最も早く取り入れられます。
  • gccgo: GCC(GNU Compiler Collection)の一部として実装されたGo言語のコンパイラです。Go言語のソースコードをGCCの中間表現(GIMPLEなど)に変換し、その後GCCのバックエンドを利用して様々なアーキテクチャ向けの機械語を生成します。gccgoは、GCCが提供する広範な最適化や、既存のC/C++ライブラリとの連携のしやすさといった利点を持つことがあります。しかし、gcと比較して、Go言語の最新機能への対応が遅れたり、特定のGo言語のセマンティクスを完全に再現する上で課題を抱えることがあります。このコミットで扱われているバグは、まさにgccgoがGo言語のセマンティクスを正しく解釈できなかった一例です。

コンパイルテスト

Go言語の標準ライブラリやコンパイラには、広範なテストスイートが含まれています。特にtest/fixedbugsディレクトリは、過去に発見され修正されたバグが将来的に再発しないことを保証するための回帰テストが格納されています。これらのテストは、特定のバグを再現する最小限のコードスニペットを含み、コンパイラがそのコードをエラーなくコンパイルできるか、または期待されるエラーを出すかを検証します。

技術的詳細

このバグは、gccgoのセマンティック解析フェーズ、特に名前解決(name resolution)の段階で発生したと考えられます。Go言語では、識別子(変数名、フィールド名など)が使用された際に、コンパイラはその識別子がどの宣言に対応するかを決定する必要があります。

問題のコードパターンは以下のようになります。

func F() {
	var f *s1 // (A) ローカル変数 'f'
	_ = func() {
		_ = s1{f: nil} // (B) 構造体リテラルのフィールド 'f'
	}()
	_ = f
}

ここで、F関数内にfという名前のローカル変数(A)が宣言されています。そして、その内部の匿名関数内でs1{f: nil}という構造体リテラルが使用されています。この構造体s1にはfという名前のフィールド(B)があります。

gccgoがこのコードを処理する際、匿名関数内のs1{f: nil}という部分で、fという識別子を解決しようとします。Go言語の仕様では、このfs1構造体のフィールドfを指すべきです。しかし、gccgoは、囲む関数Fのスコープにあるローカル変数fと、構造体s1のフィールドfの名前が同じであるため、名前解決の優先順位や内部的なシンボルテーブルの管理において混乱が生じた可能性があります。

考えられる原因としては:

  1. シンボルテーブルの衝突: gccgoの内部的なシンボルテーブルが、異なる種類のエンティティ(ローカル変数と構造体フィールド)に対して同じ名前を適切に区別できなかった。
  2. 名前解決の順序の誤り: 構造体リテラル内のフィールド名解決において、ローカルスコープの変数を誤って優先してしまった。通常、構造体リテラルのフィールド名:の形式では、そのフィールド名は構造体の定義内で解決されるべきです。
  3. 型チェックの不整合: s1{f: nil}fがローカル変数fと誤って解釈された場合、その型は*s1であり、nilを代入することは可能ですが、これは構造体フィールドへの代入としてではなく、ローカル変数への代入として扱われてしまい、結果的にコンパイルエラーや不正なコード生成につながった可能性があります。

このバグは、gccgoがGo言語のセマンティクス、特に名前解決の規則を完全に実装できていなかったことを示しています。テストケースの追加は、この特定のシナリオが将来のgccgoのバージョンで正しく処理されることを保証するための重要なステップです。

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

このコミットでは、test/fixedbugs/bug475.goという新しいファイルが追加されています。

// compile

// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Variable in enclosing function with same name as field in struct
// composite literal confused gccgo.

package p

type s1 struct {
	f *s1
}

func F() {
	var f *s1
	_ = func() {
		_ = s1{f: nil}
	}()
	_ = f
}

コアとなるコードの解説

追加されたbug475.goファイルは、gccgoがコンパイルに失敗した特定のコードパターンを再現するためのものです。

  1. // compile: このコメントは、Goのテストフレームワークに対する指示で、このファイルがコンパイル可能であるべきことを示します。もしコンパイルエラーが発生すれば、テストは失敗します。
  2. 著作権表示とライセンス: 標準的なGoソースファイルのヘッダーです。
  3. コメント: // Variable in enclosing function with same name as field in struct composite literal confused gccgo. このコメントが、このテストケースが何を目的としているかを明確に説明しています。すなわち、「囲む関数内の変数と、構造体リテラルのフィールドが同じ名前を持つ場合にgccgoが混乱した」というバグをテストするものです。
  4. package p: テスト用のパッケージ宣言です。
  5. type s1 struct { f *s1 }:
    • s1という名前の構造体を定義しています。
    • この構造体は、fという名前のフィールドを一つ持っています。
    • fフィールドの型は*s1であり、これはs1型へのポインタです。自己参照型の構造体であり、リンクリストやツリー構造などでよく見られるパターンです。
  6. func F() { ... }:
    • Fという名前の関数を定義しています。
    • var f *s1: この行がバグの核心です。F関数内でfという名前のローカル変数を宣言しています。この変数fの型は、構造体s1のフィールドfと同じ*s1です。
    • _ = func() { _ = s1{f: nil} }():
      • 匿名関数を定義し、即座に実行しています。
      • _ = s1{f: nil}: ここがgccgoが混乱した箇所です。s1構造体のリテラルを作成し、そのfフィールドにnilを代入しています。Go言語の仕様では、このfs1構造体のフィールドfを指すべきです。しかし、gccgoは、外側の関数Fで宣言されたローカル変数fと、この構造体リテラルのフィールドfの名前が同じであるために、名前解決の際に誤った解釈をしていました。
    • _ = f: ローカル変数fが使用されていることを示すためのダミーの代入です。これにより、fが未使用変数としてコンパイルエラーになるのを防ぎます。

このコードは、Go言語の仕様上は完全に合法であり、gcコンパイラでは問題なくコンパイルされます。しかし、gccgoはこの特定の名前の衝突(ローカル変数と構造体フィールド)を正しく解決できず、コンパイルエラーを引き起こしていました。このテストケースが追加されたことで、gccgoの将来のバージョンがこのバグを修正し、回帰しないことを保証できるようになります。

関連リンク

  • Go Code Review: https://golang.org/cl/11379044

参考にした情報源リンク

  • Go言語の公式ドキュメント (スコープ、構造体リテラルなど)
  • GCCGoに関する情報 (GCCのドキュメントなど)
  • Go言語のバグトラッカーやメーリングリスト (もしこのバグに関する議論が見つかれば)
  • Go言語のソースコードリポジトリ (特にtest/fixedbugsディレクトリの構造と目的)

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

このコミットは、Goコンパイラの一つであるgccgoが特定のコードパターンでコンパイルに失敗するバグを修正するために、そのバグを再現する新しいテストケースを追加するものです。具体的には、囲む関数内の変数名と構造体リテラルのフィールド名が同じ場合にgccgoが混乱するという問題に対処しています。

コミット

commit 29d27671e521a45247c31f694d8ec041510e8ed3
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Jul 16 15:31:42 2013 -0700

    test: add a compilation test that gccgo failed to compile
    
    R=golang-dev, remyoudompheng
    CC=golang-dev
    https://golang.org/cl/11379044
---
 test/fixedbugs/bug475.go | 22 ++++++++++++++++++++++\n 1 file changed, 22 insertions(+)\n
diff --git a/test/fixedbugs/bug475.go b/test/fixedbugs/bug475.go
new file mode 100644
index 0000000000..1bd6fa35ce
--- /dev/null
+++ b/test/fixedbugs/bug475.go
@@ -0,0 +1,22 @@
+// compile
+
+// Copyright 2013 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Variable in enclosing function with same name as field in struct
+// composite literal confused gccgo.
+
+package p
+
+type s1 struct {
+	f *s1
+}
+
+func F() {
+	var f *s1
+	_ = func() {
+		_ = s1{f: nil}
+	}()
+	_ = f
+}

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

https://github.com/golang/go/commit/29d27671e521a45247c31f694d8ec041510e8ed3

元コミット内容

このコミットは、gccgoコンパイラがコンパイルに失敗した特定のコードパターンに対するコンパイルテストを追加するものです。具体的には、囲む関数内で宣言された変数と、構造体リテラル内で使用されるフィールドが同じ名前を持つ場合に、gccgoが混乱するというバグを修正するためのテストケースです。

変更の背景

Go言語には、公式のGoコンパイラであるgc(Go Compiler)とは別に、GCC(GNU Compiler Collection)をバックエンドとして利用するgccgoという実装が存在します。gccgoは、Go言語のコードをGCCの中間表現に変換し、GCCの最適化やコード生成の恩恵を受けることを目的としています。

このコミットが追加された背景には、gccgoが特定の有効なGoコードを正しくコンパイルできないというバグが存在したことがあります。コミットメッセージに「Variable in enclosing function with same name as field in struct composite literal confused gccgo.」とあるように、関数スコープ内のローカル変数名と、その関数内で使用される構造体リテラルのフィールド名が偶然一致した場合に、gccgoが名前解決の段階で誤った解釈をしてしまい、コンパイルエラーを引き起こしていました。

このようなコンパイラのバグは、Go言語の仕様に準拠したコードが正しく動作しないという重大な問題につながります。そのため、このバグを修正し、将来的な回帰を防ぐために、バグを再現するテストケースをtest/fixedbugsディレクトリに追加する必要がありました。test/fixedbugsディレクトリは、過去に発見され修正されたバグの回帰テストを格納するための場所です。

前提知識の解説

Go言語のスコープと名前解決

Go言語では、変数のスコープはブロック({}で囲まれた領域)によって定義されます。内側のブロックで宣言された変数は、外側のブロックで宣言された同名の変数をシャドウ(隠蔽)します。しかし、このケースではシャドウイングではなく、異なる種類のエンティティ(変数と構造体フィールド)が同じ名前を持つことによるコンパイラの混乱が問題でした。

構造体リテラル (Struct Composite Literal)

Go言語において、構造体リテラルは構造体の新しい値を初期化するための構文です。例えば、type MyStruct struct { Field1 int; Field2 string }という構造体がある場合、MyStruct{Field1: 10, Field2: "hello"}のようにして値を生成します。フィールド名を明示的に指定する形式(Field1: 10)は、フィールドの順序に依存せず、可読性が高いという特徴があります。

gccgoとgc (Go Compiler)

  • gc (Go Compiler): Go言語の公式かつ主要なコンパイラです。Go言語のソースコードを直接機械語にコンパイルします。Go言語の開発チームによって開発・メンテナンスされており、Go言語の最新の機能や最適化が最も早く取り入れられます。
  • gccgo: GCC(GNU Compiler Collection)の一部として実装されたGo言語のコンパイラです。Go言語のソースコードをGCCの中間表現(GIMPLEなど)に変換し、その後GCCのバックエンドを利用して様々なアーキテクチャ向けの機械語を生成します。gccgoは、GCCが提供する広範な最適化や、既存のC/C++ライブラリとの連携のしやすさといった利点を持つことがあります。しかし、gcと比較して、Go言語の最新機能への対応が遅れたり、特定のGo言語のセマンティクスを完全に再現する上で課題を抱えることがあります。このコミットで扱われているバグは、まさにgccgoがGo言語のセマンティクスを正しく解釈できなかった一例です。

コンパイルテスト

Go言語の標準ライブラリやコンパイラには、広範なテストスイートが含まれています。特にtest/fixedbugsディレクトリは、過去に発見され修正されたバグが将来的に再発しないことを保証するための回帰テストが格納されています。これらのテストは、特定のバグを再現する最小限のコードスニペットを含み、コンパイラがそのコードをエラーなくコンパイルできるか、または期待されるエラーを出すかを検証します。

技術的詳細

このバグは、gccgoのセマンティック解析フェーズ、特に名前解決(name resolution)の段階で発生したと考えられます。Go言語では、識別子(変数名、フィールド名など)が使用された際に、コンパイラはその識別子がどの宣言に対応するかを決定する必要があります。

問題のコードパターンは以下のようになります。

func F() {
	var f *s1 // (A) ローカル変数 'f'
	_ = func() {
		_ = s1{f: nil} // (B) 構造体リテラルのフィールド 'f'
	}()
	_ = f
}

ここで、F関数内にfという名前のローカル変数(A)が宣言されています。そして、その内部の匿名関数内でs1{f: nil}という構造体リテラルが使用されています。この構造体s1にはfという名前のフィールド(B)があります。

gccgoがこのコードを処理する際、匿名関数内のs1{f: nil}という部分で、fという識別子を解決しようとします。Go言語の仕様では、このfs1構造体のフィールドfを指すべきです。しかし、gccgoは、囲む関数Fのスコープにあるローカル変数fと、構造体s1のフィールドfの名前が同じであるため、名前解決の優先順位や内部的なシンボルテーブルの管理において混乱が生じた可能性があります。

考えられる原因としては:

  1. シンボルテーブルの衝突: gccgoの内部的なシンボルテーブルが、異なる種類のエンティティ(ローカル変数と構造体フィールド)に対して同じ名前を適切に区別できなかった。
  2. 名前解決の順序の誤り: 構造体リテラル内のフィールド名解決において、ローカルスコープの変数を誤って優先してしまった。通常、構造体リテラルのフィールド名:の形式では、そのフィールド名は構造体の定義内で解決されるべきです。
  3. 型チェックの不整合: s1{f: nil}fがローカル変数fと誤って解釈された場合、その型は*s1であり、nilを代入することは可能ですが、これは構造体フィールドへの代入としてではなく、ローカル変数への代入として扱われてしまい、結果的にコンパイルエラーや不正なコード生成につながった可能性があります。

このバグは、gccgoがGo言語のセマンティクス、特に名前解決の規則を完全に実装できていなかったことを示しています。類似のバグとして、Goの公式GitHubリポジトリのIssue #7590「gccgo: confusion between field name and variable name leads to incorrect initialization loop error」でも、グローバル変数と構造体フィールドの名前が衝突した場合にgccgoが誤ったエラーを出す問題が報告されています。これらの問題は、gccgoがGo言語のスコープ規則と名前解決のセマンティクスを正確に処理する上での課題を示しています。テストケースの追加は、この特定のシナリオが将来のgccgoのバージョンで正しく処理されることを保証するための重要なステップです。

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

このコミットでは、test/fixedbugs/bug475.goという新しいファイルが追加されています。

// compile

// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Variable in enclosing function with same name as field in struct
// composite literal confused gccgo.

package p

type s1 struct {
	f *s1
}

func F() {
	var f *s1
	_ = func() {
		_ = s1{f: nil}
	}()
	_ = f
}

コアとなるコードの解説

追加されたbug475.goファイルは、gccgoがコンパイルに失敗した特定のコードパターンを再現するためのものです。

  1. // compile: このコメントは、Goのテストフレームワークに対する指示で、このファイルがコンパイル可能であるべきことを示します。もしコンパイルエラーが発生すれば、テストは失敗します。
  2. 著作権表示とライセンス: 標準的なGoソースファイルのヘッダーです。
  3. コメント: // Variable in enclosing function with same name as field in struct composite literal confused gccgo. このコメントが、このテストケースが何を目的としているかを明確に説明しています。すなわち、「囲む関数内の変数と、構造体リテラルのフィールドが同じ名前を持つ場合にgccgoが混乱した」というバグをテストするものです。
  4. package p: テスト用のパッケージ宣言です。
  5. type s1 struct { f *s1 }:
    • s1という名前の構造体を定義しています。
    • この構造体は、fという名前のフィールドを一つ持っています。
    • fフィールドの型は*s1であり、これはs1型へのポインタです。自己参照型の構造体であり、リンクリストやツリー構造などでよく見られるパターンです。
  6. func F() { ... }:
    • Fという名前の関数を定義しています。
    • var f *s1: この行がバグの核心です。F関数内でfという名前のローカル変数を宣言しています。この変数fの型は、構造体s1のフィールドfと同じ*s1です。
    • _ = func() { _ = s1{f: nil} }():
      • 匿名関数を定義し、即座に実行しています。
      • _ = s1{f: nil}: ここがgccgoが混乱した箇所です。s1構造体のリテラルを作成し、そのfフィールドにnilを代入しています。Go言語の仕様では、このfs1構造体のフィールドfを指すべきです。しかし、gccgoは、外側の関数Fで宣言されたローカル変数fと、この構造体リテラルのフィールドfの名前が同じであるために、名前解決の際に誤った解釈をしていました。
    • _ = f: ローカル変数fが使用されていることを示すためのダミーの代入です。これにより、fが未使用変数としてコンパイルエラーになるのを防ぎます。

このコードは、Go言語の仕様上は完全に合法であり、gcコンパイラでは問題なくコンパイルされます。しかし、gccgoはこの特定の名前の衝突(ローカル変数と構造体フィールド)を正しく解決できず、コンパイルエラーを引き起こしていました。このテストケースが追加されたことで、gccgoの将来のバージョンがこのバグを修正し、回帰しないことを保証できるようになります。

関連リンク

  • Go Code Review: https://golang.org/cl/11379044

参考にした情報源リンク

  • Go言語の公式ドキュメント (スコープ、構造体リテラルなど)
  • GCCGoに関する情報 (GCCのドキュメントなど)
  • Go言語のソースコードリポジトリ (特にtest/fixedbugsディレクトリの構造と目的)
  • GitHub Issue #7590: gccgo: confusion between field name and variable name leads to incorrect initialization loop error: https://github.com/golang/go/issues/7590