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

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

このコミットは、Go言語における型(type)の順方向宣言(forward declarations)に関するテストを追加するものです。具体的には、Go言語が型宣言の順序に依存しない設計であることを検証するためのテストケースと、意図的にコンパイルエラーを引き起こすケースを追加しています。これにより、Goコンパイラが型の参照解決を正しく行っているか、特に相互参照する型や未宣言の型に対する挙動を確認しています。

コミット

commit 709e3a3fbde355cf557e8fde4d977131f0982fcd
Author: Robert Griesemer <gri@golang.org>
Date:   Fri Jun 6 17:23:22 2008 -0700

    - tests for type forward declarations
    
    SVN=121560
---
 test/bugs/bug041.go | 15 +++++++++++++++
 test/bugs/bug042.go | 20 ++++++++++++++++++++\n test/golden.out     |  7 +++++++
 3 files changed, 42 insertions(+)

diff --git a/test/bugs/bug041.go b/test/bugs/bug041.go
new file mode 100644
index 0000000000..1a6e0dde80
--- /dev/null
+++ b/test/bugs/bug041.go
@@ -0,0 +1,15 @@
+// Copyright 2009 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.
+
+// $G $D/$F.go && echo BUG: compilation succeeds incorrectly
+
+package main
+
+type S struct {
+  p *T  // BUG T never declared
+}
+
+func main() {
+  var s S;
+}
diff --git a/test/bugs/bug042.go b/test/bugs/bug042.go
new file mode 100644
index 0000000000..37a2bfd693
--- /dev/null
+++ b/test/bugs/bug042.go
@@ -0,0 +1,20 @@
+// Copyright 2009 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.
+
+// $G $D/$F.go || echo BUG: compilation should succeed
+
+package main
+
+type T  // BUG forward declaration should be accepted
+type S struct {
+  p *T
+}
+
+type T struct {
+  p *S
+}
+
+func main() {
+  var s S;
+}
diff --git a/test/golden.out b/test/golden.out
index 4ea3036e73..ca0323b756 100644
--- a/test/golden.out
+++ b/test/golden.out
@@ -262,6 +262,13 @@ BUG: compilation succeeds incorrectly
 =========== bugs/bug040.go
 BUG: compilation succeeds incorrectly
 
+=========== bugs/bug041.go
+BUG: compilation succeeds incorrectly
+\n+=========== bugs/bug042.go
+bugs/bug042.go:6: syntax error
+BUG: compilation should succeed
+\n =========== fixedbugs/bug000.go
 
 =========== fixedbugs/bug005.go

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

https://github.com/golang/go/commit/709e3a3fbde355cf557e8fde4d977131f0982fcd

元コミット内容

- tests for type forward declarations
    
    SVN=121560

変更の背景

このコミットは、Go言語の初期開発段階(2008年6月)に行われたもので、Go言語の設計思想である「順方向宣言(forward declarations)の不要性」を検証するために追加されました。C++などの言語では、型や関数の使用前にその宣言が必要となる「順方向宣言」の概念が存在します。これは、コンパイラがコードを上から順に解析する際に、未定義のシンボルに遭遇しないようにするためのものです。しかし、これによりヘッダーファイルと実装ファイルの分離や、相互参照する型の扱いの複雑さといった問題が生じることがあります。

Go言語は、これらの問題を解決するために、宣言の順序に依存しない設計を採用しました。パッケージ内のトップレベルの識別子(定数、型、変数、関数)のスコープは、そのパッケージ全体に及ぶため、宣言の順序が可視性に影響を与えることはありません。これにより、明示的な順方向宣言の構文を必要とせずに、実質的に「自動的な」順方向宣言が提供されます。

このコミットで追加されたテストは、この設計が意図通りに機能しているか、特に以下のようなケースでコンパイラが正しく振る舞うかを確認することを目的としています。

  1. 未宣言の型への参照: 存在しない型を参照した場合に、コンパイラが正しくエラーを報告するか。
  2. 相互参照する型: 互いに参照し合う型(例: ABを参照し、BAを参照する)が、宣言順序に関わらず正しくコンパイルできるか。

これらのテストは、Go言語のコンパイラが、宣言順序に依存しない型解決ロジックを適切に実装していることを保証するための重要なステップでした。

前提知識の解説

順方向宣言 (Forward Declaration)

プログラミング言語における「順方向宣言」とは、変数、関数、クラス、構造体などの識別子を、その定義(実装)よりも前に宣言することです。これは主に、コンパイラがコードを上から下へと一度に処理する「ワンパスコンパイル」を行う言語で必要とされます。

例(C++の場合):

// 関数 `bar` の順方向宣言
void bar();

void foo() {
    bar(); // `bar` はここで使用されるが、定義は後にある
}

// 関数 `bar` の定義
void bar() {
    // ...
}

// クラス `B` の順方向宣言
class B;

class A {
public:
    B* b_ptr; // `B` はここで使用されるが、定義は後にある
};

// クラス `B` の定義
class B {
public:
    A* a_ptr; // `A` はここで使用されるが、定義は前にある
};

順方向宣言の目的は、コンパイラが識別子の型やシグネチャを知ることで、その識別子を使用するコードを正しく型チェックし、コンパイルできるようにすることです。特に、相互に参照し合う型(相互再帰型)を定義する際には、どちらかの型がもう一方の型よりも先に完全に定義されることができないため、順方向宣言が不可欠となります。

Go言語における宣言とスコープ

Go言語は、C++のような明示的な順方向宣言を必要としません。これは、Goのコンパイラがパッケージ内のすべての宣言を一度に処理し、シンボル解決を行うためです。Goでは、トップレベル(パッケージレベル)で宣言された識別子(変数、定数、型、関数)は、そのパッケージ内のどこからでも参照可能です。宣言の物理的な順序は、その識別子の可視性には影響しません。

この設計は、以下の利点をもたらします。

  • コードの読みやすさ: 宣言順序を気にする必要がなくなり、コードの記述がより自然になります。
  • 相互参照の容易さ: 相互に参照し合う型や関数を、特別な構文なしに定義できます。
  • コンパイル速度の向上: ヘッダーファイルのような重複した宣言が不要になり、コンパイルプロセスが簡素化されます。

Go言語のこの特性は、特に大規模なプロジェクトにおいて、コードの管理と保守を容易にする重要な要素となっています。

技術的詳細

Go言語のコンパイラは、ソースファイルを解析する際に、まずパッケージ内のすべてのトップレベル宣言を収集し、シンボルテーブルを構築します。この段階で、各識別子の名前と型情報が登録されます。その後、実際のコード生成や型チェックのフェーズで、シンボルテーブルを参照して識別子の解決を行います。この二段階のプロセスにより、宣言の物理的な順序に依存せずに、識別子を解決することが可能になります。

このコミットで追加されたテストケースは、このGo言語の設計原則を具体的に検証しています。

test/bugs/bug041.go の意図

このテストファイルは、Go言語が未宣言の型への参照を正しくエラーとして検出するかどうかを検証します。

package main

type S struct {
  p *T  // BUG T never declared
}

func main() {
  var s S;
}

ここで、type S struct { p *T } の中で T という型が参照されていますが、T はどこにも宣言されていません。Go言語の設計では、このような未定義の型への参照はコンパイルエラーとなるべきです。テストのコメント $G $D/$F.go && echo BUG: compilation succeeds incorrectly は、「このファイルがコンパイルに成功したらバグである」ことを示しています。つまり、コンパイラが T が未宣言であることを検出し、エラーを出すことを期待しています。

test/bugs/bug042.go の意図

このテストファイルは、Go言語が相互参照する型を、明示的な順方向宣言なしに正しく処理できるかどうかを検証します。

package main

type T  // BUG forward declaration should be accepted
type S struct {
  p *T
}

type T struct {
  p *S
}

func main() {
  var s S;
}

このコードでは、S 型が T 型へのポインタを持ち、T 型が S 型へのポインタを持つという相互参照の関係があります。C++のような言語では、このような場合、どちらかの型を順方向宣言する必要があります。しかし、Go言語では、パッケージ内のトップレベル宣言は順序に依存しないため、このコードはコンパイルに成功するべきです。

テストのコメント $G $D/$F.go || echo BUG: compilation should succeed は、「このファイルがコンパイルに失敗したらバグである」ことを示しています。また、type T // BUG forward declaration should be accepted というコメントは、この行がGo言語の設計において「順方向宣言」として機能することを意図しているが、実際にはGoではそのような明示的な構文は不要であり、コンパイラが自動的に解決すべきであることを示唆しています。

しかし、コミットの test/golden.out を見ると、bugs/bug042.go:6: syntax error とあり、BUG: compilation should succeed とも書かれています。これは、このコミット時点では type T という構文がGo言語の文法として正しくなく、コンパイルエラーになっていることを示しています。本来はコンパイルが成功すべきであるという意図がコメントに込められていますが、当時のコンパイラの実装ではまだそのレベルに達していなかったか、あるいは type T という記述自体がGoの文法として不適切であった可能性を示唆しています。Go言語の最終的な仕様では、type T のような単独の型宣言は存在せず、型は常にその定義を伴います。相互参照する型は、定義の順序に関わらずコンパイラが解決します。

test/golden.out の変更

test/golden.out は、Go言語のテストスイートにおける期待される出力(コンパイルエラーメッセージなど)を記録するファイルです。このコミットでは、bug041.gobug042.go のテスト結果が追加されています。

  • =========== bugs/bug041.go の下には BUG: compilation succeeds incorrectly とあり、これは bug041.go がコンパイルエラーになることを期待していることを示しています。
  • =========== bugs/bug042.go の下には bugs/bug042.go:6: syntax errorBUG: compilation should succeed があります。これは、bug042.go が構文エラーでコンパイルに失敗しているが、本来は成功すべきであるという当時の開発者の意図を示しています。

これらの変更は、Go言語の型システムが進化していく過程で、コンパイラの挙動を検証し、期待される動作と実際の動作のギャップを特定するために非常に重要でした。

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

このコミットでは、既存のGo言語のソースコード自体に変更は加えられていません。代わりに、Goコンパイラの挙動をテストするための新しいテストファイルが追加されています。

  1. test/bugs/bug041.go: 新規追加されたテストファイル。未宣言の型 T を参照する構造体 S を定義し、コンパイルエラーが発生することを期待する。
  2. test/bugs/bug042.go: 新規追加されたテストファイル。相互参照する型 ST を定義し、明示的な順方向宣言なしにコンパイルが成功することを期待する(ただし、コミット時点では構文エラーで失敗)。
  3. test/golden.out: 既存のテスト結果ファイルに、上記2つの新しいテストケースの期待される出力(または当時の実際の出力と期待される動作のギャップ)が追記された。

コアとなるコードの解説

test/bugs/bug041.go

// Copyright 2009 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.

// $G $D/$F.go && echo BUG: compilation succeeds incorrectly

package main

type S struct {
  p *T  // BUG T never declared
}

func main() {
  var s S;
}

このファイルは、Go言語のコンパイラが未定義の型への参照を正しく検出できるかをテストします。type S struct { p *T } の行で、T という型が使用されていますが、このファイル内には T の宣言がありません。Go言語の仕様では、このような場合、コンパイラは「未定義の型」エラーを報告するべきです。テストのコメント $G $D/$F.go && echo BUG: compilation succeeds incorrectly は、もしコンパイルが成功してしまったら、それはコンパイラのバグであることを示しています。これは、コンパイラが未定義のシンボルを適切に処理していることを確認するための基本的なテストです。

test/bugs/bug042.go

// Copyright 2009 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.

// $G $D/$F.go || echo BUG: compilation should succeed

package main

type T  // BUG forward declaration should be accepted
type S struct {
  p *T
}

type T struct {
  p *S
}

func main() {
  var s S;
}

このファイルは、Go言語が相互参照する型(ST)を、明示的な順方向宣言なしに処理できるかをテストします。Go言語の設計目標の一つは、C++のような言語で必要とされる明示的な順方向宣言を不要にすることです。そのため、ST を参照し、TS を参照するような場合でも、宣言の順序に関わらずコンパイルが成功するべきです。

しかし、コミット時点の test/golden.out には bugs/bug042.go:6: syntax error と記録されており、type T という行が構文エラーを引き起こしていることがわかります。これは、当時のGo言語の文法では type T のような単独の型宣言が許されていなかったことを示唆しています。Go言語の最終的な仕様では、型は常にその定義を伴います。このテストの意図は、相互参照する型が問題なくコンパイルできることの検証ですが、当時の実装ではまだその構文がサポートされていなかったか、あるいはテストコード自体がGoの文法に合致していなかった可能性があります。それでも、BUG: compilation should succeed というコメントは、開発者がこのシナリオが最終的に成功することを期待していたことを明確に示しています。

これらのテストは、Go言語の型システムとコンパイラの堅牢性を確保し、言語設計の意図が正しく実装されていることを検証するための重要な役割を果たしました。

関連リンク

参考にした情報源リンク

  • go.dev (Go言語公式サイトのドキュメントやFAQ)
  • practical-go-lessons.com (Go言語の学習リソース)
  • medium.com (Go言語に関する記事)
  • wikibooks.org (Go言語に関する情報)
  • reddit.com (Go言語に関する議論)
  • wikipedia.org (Go言語の歴史に関する情報)
  • ycombinator.com (Go言語に関する議論)