[インデックス 1534] ファイルの概要
このコミットは、Go言語のコンパイラが構造体(struct
)内で重複するフィールド名を適切に診断しないというバグを修正するためのテストケースを追加するものです。具体的には、x, x int
のように同じ名前のフィールドが複数宣言された場合に、コンパイル時エラーとして検出されるべきであるにもかかわらず、コンパイルが成功してしまう問題を顕在化させるためのテストが導入されました。
コミット
commit 41644d713870000f4dc9e0ba7ae144ccaaa654b4
Author: Robert Griesemer <gri@golang.org>
Date: Wed Jan 21 14:11:54 2009 -0800
- duplicate struct field not diagnosed
R=rsc
DELTA=16 (16 added, 0 deleted, 0 changed)
OCL=23224
CL=23229
---\n test/bugs/bug132.go | 17 +++++++++++++++++
test/golden.out | 3 +++
2 files changed, 20 insertions(+)
diff --git a/test/bugs/bug132.go b/test/bugs/bug132.go
new file mode 100644
index 0000000000..958db9dc45
--- /dev/null
+++ b/test/bugs/bug132.go
@@ -0,0 +1,17 @@
+// errchk $G $D/$F.go
+\n+// 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.\n
+package main
+\n+type T struct {
+\tx, x int // this should be a compile-time error
+}\n+\n+/*
+Accessing obj.x for obj of type T will lead to an error so this cannot
+be used in a program, but I would argue that this should be a compile-
+tume error at the declaration point.
+*/
diff --git a/test/golden.out b/test/golden.out
index ac8c788b07..889b421818 100644
--- a/test/golden.out
+++ b/test/golden.out
@@ -146,6 +146,9 @@ BUG: should run
=========== bugs/bug131.go
BUG: should not compile
\n+=========== bugs/bug132.go
+BUG: compilation succeeds incorrectly
+\n =========== fixedbugs/bug016.go
fixedbugs/bug016.go:7: overflow converting constant to uint
\n
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/41644d713870000f4dc9e0ba7ae144ccaaa654b4
元コミット内容
- duplicate struct field not diagnosed
R=rsc
DELTA=16 (16 added, 0 deleted, 0 changed)
OCL=23224
CL=23229
変更の背景
Go言語の初期開発段階において、コンパイラには構造体(struct
)のフィールド定義に関する特定の不具合が存在していました。具体的には、同じ名前のフィールドが構造体内で複数回宣言された場合、コンパイラがこれをエラーとして認識せず、不正なコードがコンパイルされてしまうという問題です。このようなコードは、フィールドへのアクセス時に曖昧さを生じさせ、予期せぬ動作や実行時エラーの原因となる可能性があります。
このコミットは、このコンパイラのバグを修正する直接的なコード変更ではなく、そのバグを明確に浮き彫りにするための新しいテストケースを導入するものです。テストケースが追加されることで、バグが修正されるまでそのテストは失敗し続け、開発者に問題の存在を通知し、修正を促す役割を果たします。これは、テスト駆動開発(TDD)のアプローチに近く、まずバグを再現するテストを書き、そのテストが失敗することを確認してから、バグを修正するという開発サイクルの一部です。
前提知識の解説
Go言語の構造体(Struct)
Go言語における構造体(struct
)は、異なるデータ型のフィールドをひとまとめにした複合データ型です。C言語の構造体やJava/C++のクラスのデータメンバーに似ています。構造体を使用することで、関連するデータを論理的にグループ化し、コードの可読性と保守性を向上させることができます。
例:
type Person struct {
Name string
Age int
}
このPerson
構造体は、Name
(文字列型)とAge
(整数型)という2つのフィールドを持ちます。各フィールドは一意の名前を持つことが期待されます。
コンパイル時エラーと実行時エラー
- コンパイル時エラー (Compile-time Error): プログラムがコンパイラによって機械語に変換される(コンパイルされる)段階で検出されるエラーです。文法間違い、型不一致、未定義の変数使用など、コードの構造や記述に問題がある場合に発生します。コンパイル時エラーが発生すると、プログラムは実行可能な形式に変換されず、実行できません。
- 実行時エラー (Runtime Error): コンパイルは成功し、プログラムが実行された後に発生するエラーです。ゼロ除算、ヌルポインタ参照、メモリ不足など、プログラムのロジックや外部環境との相互作用に問題がある場合に発生します。実行時エラーが発生すると、プログラムは異常終了することがあります。
構造体における重複フィールドは、プログラムの論理的な整合性に関わる問題であり、コンパイル時に検出されるべき「文法的な誤り」と見なされます。
Go言語のコンパイラ
Go言語のコンパイラは、Goのソースコードを機械語に変換するプログラムです。コンパイラの主な役割は以下の通りです。
- 構文解析: ソースコードがGo言語の文法規則に従っているかを確認します。
- 意味解析: コードの意味的な正当性を検証します(例: 変数の型が正しいか、関数呼び出しの引数が一致するかなど)。この段階で、重複する構造体フィールドのような論理的な矛盾が検出されるべきです。
- コード生成: 解析されたコードをターゲットのCPUアーキテクチャで実行可能な機械語に変換します。
テスト駆動開発(TDD)と回帰テスト
- テスト駆動開発 (Test-Driven Development - TDD): ソフトウェア開発手法の一つで、「テストを先に書く」ことを特徴とします。まず、実装したい機能や修正したいバグを明確にするテストコードを書き、そのテストが失敗することを確認します。次に、そのテストが成功するように最小限のコードを実装し、最後にコードをリファクタリングします。
- 回帰テスト (Regression Test): ソフトウェアの変更(新機能の追加、バグ修正など)が、既存の機能に悪影響を与えていないことを確認するためのテストです。このコミットで追加されたようなバグを再現するテストは、将来的に同じバグが再発しないことを保証するための回帰テストとして機能します。
技術的詳細
Go言語の仕様では、構造体のフィールド名は一意である必要があります。同じ名前のフィールドが複数存在することは、そのフィールドにアクセスする際にどのフィールドを指しているのかが曖昧になるため、言語設計上許容されません。例えば、obj.x
と記述した場合、x
が複数存在するとコンパイラはどちらのx
を参照すべきか判断できません。
このコミット以前のGoコンパイラは、この重複フィールドのチェックが不十分であったため、type T struct { x, x int }
のような不正な構造体定義をエラーとせずに受け入れてしまっていました。これはコンパイラのバグであり、本来であればコンパイル時にエラーを報告し、開発者に修正を促すべき挙動です。
このコミットは、test/bugs/bug132.go
という新しいテストファイルを追加することで、このコンパイラのバグを具体的に示しています。このテストファイルは、意図的に重複フィールドを持つ構造体を定義しており、そのファイルがコンパイル時にエラーを出すべきであることをerrchk
ディレクティブで指定しています。test/golden.out
の更新は、このテストが「コンパイルに失敗すべき」という期待をGoのテストスイートに登録するものです。
このテストが導入された時点では、コンパイラはまだバグを抱えているため、bug132.go
はコンパイルに成功してしまい、テストは失敗します(期待されるエラーが出ないため)。この失敗が、コンパイラの修正が必要であることを明確に示します。コンパイラが修正され、重複フィールドが正しくエラーとして診断されるようになれば、このテストは成功するようになります。
コアとなるコードの変更箇所
このコミットでは、以下の2つのファイルが変更されています。
-
test/bugs/bug132.go
(新規追加): このファイルは、重複する構造体フィールドのバグをテストするために新しく作成されたGoのソースファイルです。// errchk $G $D/$F.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. package main type T struct { x, x int // this should be a compile-time error } /* Accessing obj.x for obj of type T will lead to an error so this cannot be used in a program, but I would argue that this should be a compile- tume error at the declaration point. */
特に注目すべきは、
type T struct { x, x int }
という行です。ここでx
というフィールドが2回宣言されています。また、ファイルの先頭にある// errchk $G $D/$F.go
というコメントは、このファイルがコンパイル時にエラーを出すべきであることをGoのテストフレームワークに指示するものです。 -
test/golden.out
(変更): このファイルは、Goのテストスイートにおける期待される出力("golden" output)を記録するファイルです。test/bugs/bug132.go
が追加されたことに伴い、そのテストに関する期待される結果が追記されました。--- a/test/golden.out +++ b/test/golden.out @@ -146,6 +146,9 @@ BUG: should run =========== bugs/bug131.go BUG: should not compile +=========== bugs/bug132.go +BUG: compilation succeeds incorrectly +\n =========== fixedbugs/bug016.go fixedbugs/bug016.go:7: overflow converting constant to uint
追加された
=========== bugs/bug132.go
とBUG: compilation succeeds incorrectly
の行は、bug132.go
のテストが、コンパイルが誤って成功してしまうというバグを報告していることを示しています。
コアとなるコードの解説
test/bugs/bug132.go
このファイルは、Goコンパイラのバグを再現し、その修正を検証するためのテストケースです。
// errchk $G $D/$F.go
: これはGoのテストシステムで使用される特別なディレクティブです。errchk
: このファイルがコンパイル時にエラーを生成することを期待していることを示します。$G
: Goコンパイラへのパスを表す変数です。$D/$F.go
: 現在のテストファイルのパスを表す変数です。 この行全体で、「Goコンパイラがこのファイルをコンパイルした際にエラーを出すべきである」というテストの期待値を定義しています。
type T struct { x, x int }
: この構造体定義が、このテストの核心です。x
というフィールドが重複して宣言されており、これがコンパイラによってエラーとして検出されるべき不正なコードです。- コメントブロック:
このコメントは、重複フィールドを持つ構造体は、たとえコンパイルが通ってしまっても、/* Accessing obj.x for obj of type T will lead to an error so this cannot be used in a program, but I would argue that this should be a compile- tume error at the declaration point. */
obj.x
のようなアクセス時に問題を引き起こすことを指摘しています。そして、このような問題は、宣言の時点でコンパイル時エラーとして扱われるべきであるという、このテストの意図を明確に述べています。
test/golden.out
golden.out
ファイルは、Goのテストスイートが実行された際に、特定のテストファイルがどのような結果(エラーメッセージ、成功/失敗のステータスなど)を出すべきかを記録するリファレンスファイルです。
=========== bugs/bug132.go
: これは、bug132.go
というテストファイルに関するセクションの開始を示します。BUG: compilation succeeds incorrectly
: この行は、bug132.go
のテストが報告する「バグ」の内容を簡潔に記述しています。つまり、このテストファイルは本来コンパイルに失敗すべきなのに、誤って成功してしまうというコンパイラの挙動がバグであると指摘しています。
このgolden.out
への変更は、Goのテストフレームワークに対して、bug132.go
がコンパイル時にエラーを出すべきであるという期待を登録し、もしエラーが出ずにコンパイルが成功した場合は、それをバグとして報告するように設定するものです。これにより、コンパイラのバグが修正されるまで、このテストは継続的に失敗し、問題の存在を開発者に通知し続けます。
関連リンク
- Go言語の仕様(Struct typesに関する記述): https://go.dev/ref/spec#Struct_types (Go言語の公式仕様書で、構造体の定義と制約について記述されています。フィールド名の一意性に関する暗黙的または明示的なルールが示されているはずです。)
- Go言語のテストに関するドキュメント(
errchk
ディレクティブなど): Goの公式ドキュメントやテストフレームワークの内部資料に詳しい情報がある可能性がありますが、一般公開されている特定のURLを見つけるのは難しい場合があります。
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(特に
cmd/compile
ディレクトリ内のコードやテストスイート) - Go言語のコンパイラ設計に関する一般的な知識
- テスト駆動開発(TDD)の原則に関する一般的な知識