[インデックス 1259] ファイルの概要
このコミットは、Go言語のコンパイラにおけるバグ修正に関するものです。具体的には、定数(const
)が同じスコープ内で複数回宣言された場合に、コンパイラがエラーを適切に報告しないという問題に対処しています。この修正により、Go言語の設計思想である「識別子の再宣言の禁止」が定数に対しても厳密に適用されるようになります。
コミット
commit b0192ea1a592f0f7069fc35c4279d2516739f046
Author: Robert Griesemer <gri@golang.org>
Date: Mon Dec 1 17:27:57 2008 -0800
bug: constants should not be redeclarable
R=rsc
DELTA=9 (9 added, 0 deleted, 0 changed)
OCL=20187
CL=20187
---
test/bugs/bug126.go | 10 ++++++++++
test/golden.out | 3 +++
2 files changed, 13 insertions(+)
diff --git a/test/bugs/bug126.go b/test/bugs/bug126.go
new file mode 100644
index 0000000000..2481e1dd3f
--- /dev/null
+++ b/test/bugs/bug126.go
@@ -0,0 +1,10 @@
+// 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
+
+const none = 0 // same const identifier declared twice should not be accepted
+const none = 1 // ERROR "redeclared"
diff --git a/test/golden.out b/test/golden.out
index 957aa86e7f..fe10981df5 100644
--- a/test/golden.out
+++ b/test/golden.out
@@ -173,6 +173,9 @@ BUG: errchk: command succeeded unexpectedly: 6g bugs/bug124.go
=========== bugs/bug125.go
BUG: errchk: command succeeded unexpectedly: 6g bugs/bug125.go
+=========== bugs/bug126.go
+BUG: errchk: command succeeded unexpectedly: 6g bugs/bug126.go
+
=========== fixedbugs/bug016.go
fixedbugs/bug016.go:7: overflow converting constant to uint
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b0192ea1a592f0f7069fc35c4279d2516739f046
元コミット内容
bug: constants should not be redeclarable
R=rsc
DELTA=9 (9 added, 0 deleted, 0 changed)
OCL=20187
CL=20187
変更の背景
Go言語は、その設計当初から、コードの明確性と予測可能性を重視しています。この原則の一つに、同じスコープ内で同じ識別子を複数回宣言することを禁止するというものがあります。これは、変数、関数、型など、あらゆる識別子に適用されるべき基本的なルールです。
しかし、このコミットが作成された時点では、Goコンパイラ(当時の6g
など)が定数(const
キーワードで宣言されるもの)の再宣言を適切に検出せず、エラーとして扱わないというバグが存在していました。これにより、開発者が意図せず同じ名前の定数を複数回定義してしまう可能性があり、コードの挙動が不明瞭になったり、予期せぬバグを引き起こしたりするリスクがありました。
このコミットは、このコンパイラの不整合を修正し、定数の再宣言も他の識別子と同様にコンパイルエラーとして扱われるようにすることで、Go言語の設計原則を徹底し、より堅牢なコード記述を促進することを目的としています。test/bugs/bug126.go
という新しいテストケースが追加され、このバグが修正されたことを検証する仕組みが導入されています。
前提知識の解説
Go言語の定数(Constants)
Go言語における定数は、プログラムの実行中に値が変更されない不変の値を定義するために使用されます。const
キーワードを用いて宣言され、数値、真偽値、文字列などの基本的な型を持つことができます。定数はコンパイル時に評価され、その値はプログラムのバイナリに埋め込まれます。
例:
const Pi = 3.14159
const Greeting = "Hello, World!"
定数の主な特徴は以下の通りです。
- 不変性: 一度宣言されると、その値は変更できません。
- コンパイル時評価: 定数の値はコンパイル時に決定されます。
- 型推論: 明示的な型指定がない場合、Goコンパイラは初期値から型を推論します。
- 識別子のスコープ: 定数も変数と同様にスコープを持ち、そのスコープ内で一意である必要があります。
識別子の再宣言の禁止
多くのプログラミング言語、特に静的型付け言語では、同じスコープ内で同じ名前の識別子を複数回宣言することを禁止しています。これは、コードの可読性を保ち、意図しない名前の衝突や曖昧さを避けるためです。例えば、Go言語では以下のようなコードはコンパイルエラーになります。
package main
func main() {
var x int = 10
var x string = "hello" // エラー: x が再宣言されています
}
この原則は、変数だけでなく、関数名、型名、そして本来であれば定数名にも適用されるべきものです。このコミット以前は、定数に対してこのチェックが漏れていたことがバグの原因でした。
Go言語のテストフレームワークとerrchk
Go言語には、標準で強力なテストフレームワークが組み込まれています。go test
コマンドを使用してテストを実行し、コードの正確性を検証します。
Goのコンパイラ開発においては、特定のコードがコンパイルエラーになるべきかどうかをテストするために、特別なディレクティブが使用されることがあります。このコミットで追加されたtest/bugs/bug126.go
ファイルの冒頭にある// errchk $G $D/$F.go
は、そのようなディレクティブの一つです。
// errchk
: この行がコメントとして記述されている場合、Goのテストシステムは、続くコマンドがエラーを生成することを期待します。$G
: Goコンパイラへのパスを表す変数です。$D/$F.go
: 現在のテストファイルのディレクトリとファイル名を表す変数です。
したがって、// errchk $G $D/$F.go
は、「このファイルをGoコンパイラでコンパイルした際に、コンパイルエラーが発生することを期待する」という意味になります。もしコンパイルが成功してしまった場合、それはテストの失敗(バグの存在)を意味します。
test/golden.out
ファイルは、Goコンパイラのテストスイートにおいて、特定のテストケースが期待通りにエラーを発生させない(つまり、バグが存在する)場合に、その状況を記録するための「ゴールデンファイル」として機能します。このファイルにエントリが追加されることは、そのテストケースが現在バグを示しており、修正が必要であることを示唆します。
技術的詳細
このコミットの技術的詳細は、Goコンパイラの内部における識別子解決とシンボルテーブル管理の改善にあります。Goコンパイラは、ソースコードを解析する際に、各スコープで宣言された識別子(変数、関数、型、定数など)を管理するためのシンボルテーブルを構築します。新しい識別子が宣言されるたびに、コンパイラはその識別子が現在のスコープで既に存在しないかを確認します。
このバグは、定数宣言を処理する際に、この「既に存在するかどうかのチェック」が適切に行われていなかったことに起因します。具体的には、const
キーワードによる宣言が、既存の識別子との衝突を検出するロジックを迂回していたか、あるいは定数に特化したチェックが不足していたと考えられます。
コミットメッセージのDELTA=9
という変更行数から、この修正がコンパイラのコードベース全体にわたる大規模な変更ではなく、定数宣言を処理する特定の箇所に、既存の識別子チェックロジックを適用する、あるいは同様のチェックを追加する比較的ピンポイントな修正であったことが推測されます。
修正後のコンパイラは、const none = 0
の後にconst none = 1
という宣言を検出すると、シンボルテーブル内でnone
という識別子が既に定数として登録されていることを認識し、再宣言エラーを生成するようになります。これにより、Go言語の型システムと識別子管理の一貫性が保たれます。
test/bugs/bug126.go
の追加は、この修正が正しく機能することを確認するためのものです。このテストファイルは、意図的に定数を再宣言し、コンパイラが"redeclared"
というエラーメッセージを出力することを期待します。test/golden.out
へのエントリ追加は、このテストが修正前は期待通りにエラーを出力せず、バグが存在していたことを記録しています。修正が適用された後、このエントリはgolden.out
から削除されるか、テストが成功したことを示す別の形式に更新されることが期待されます。
コアとなるコードの変更箇所
このコミットで直接変更されたコードは、test/bugs/bug126.go
とtest/golden.out
の2ファイルです。
-
test/bugs/bug126.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 const none = 0 // same const identifier declared twice should not be accepted const none = 1 // ERROR "redeclared"
このファイルは、定数の再宣言がコンパイルエラーとなるべきであることを検証するための新しいテストケースです。
// errchk
ディレクティブは、このファイルのコンパイルがエラーを生成することを期待していることを示します。特に、const none = 1
の行には// ERROR "redeclared"
というコメントが付されており、コンパイラが「redeclared」という文字列を含むエラーメッセージを出力することを期待していることが明示されています。 -
test/golden.out
(変更):--- a/test/golden.out +++ b/test/golden.out @@ -173,6 +173,9 @@ BUG: errchk: command succeeded unexpectedly: 6g bugs/bug124.go =========== bugs/bug125.go BUG: errchk: command succeeded unexpectedly: 6g bugs/bug125.go +=========== bugs/bug126.go +BUG: errchk: command succeeded unexpectedly: 6g bugs/bug126.go + =========== fixedbugs/bug016.go fixedbugs/bug016.go:7: overflow converting constant to uint
test/golden.out
ファイルに、test/bugs/bug126.go
に関する新しいエントリが追加されています。BUG: errchk: command succeeded unexpectedly: 6g bugs/bug126.go
という行は、このコミットが適用される前は、6g
コンパイラがbug126.go
をコンパイルした際に、errchk
コマンドが期待通りにエラーを検出できなかった(つまり、コンパイルが成功してしまった)ことを記録しています。これは、定数の再宣言バグが当時存在していたことの証拠となります。このエントリは、バグが修正され、bug126.go
が期待通りにコンパイルエラーを生成するようになった時点で、golden.out
から削除されるか、更新されることが期待されます。
コアとなるコードの解説
このコミット自体は、Goコンパイラのソースコード(例えば、src/cmd/gc
やsrc/go/types
など、当時のコンパイラ実装に関連するディレクトリ)に対する直接的な変更の差分を示していません。しかし、コミットメッセージと変更されたファイルから、コンパイラの内部で以下の論理的な変更が行われたと推測できます。
Goコンパイラのフロントエンドは、ソースコードを抽象構文木(AST)にパースし、その後、型チェックとセマンティック解析を行います。この段階で、識別子の宣言と使用が検証されます。
- シンボルテーブルの更新: コンパイラは、各スコープ(パッケージスコープ、関数スコープなど)に対応するシンボルテーブルを維持しています。識別子(変数、関数、型、定数など)が宣言されるたびに、その識別子名と関連情報(型、値、宣言された場所など)が現在のスコープのシンボルテーブルに登録されます。
- 再宣言チェックの強化: このコミットの修正は、定数宣言を処理するコードパスにおいて、シンボルテーブルに新しい定数を登録する前に、同じ名前の識別子が既に現在のスコープに存在しないかを厳密にチェックするロジックが追加または強化されたことを意味します。
- もし
const none = 0
が処理された後、none
がシンボルテーブルに登録されます。 - 次に
const none = 1
が処理される際、コンパイラはnone
が既にシンボルテーブルに存在することを発見します。 - このとき、コンパイラは「再宣言」のエラーを生成し、コンパイルプロセスを停止します。
- もし
この修正は、Go言語のコンパイラが、言語仕様で定められた識別子の一意性ルールを、定数に対しても完全に適用するようにした重要なステップです。これにより、Goコードの堅牢性と信頼性が向上しました。
関連リンク
- Go言語の公式ウェブサイト: https://go.dev/
- Go言語の仕様: https://go.dev/ref/spec (特に「Declarations and scope」のセクション)
- Go言語の定数に関するドキュメント: https://go.dev/tour/basics/15 (Go TourのConstantsセクション)
参考にした情報源リンク
- Go言語の公式Gitリポジトリ: https://github.com/golang/go
- Go言語の初期のコミット履歴とテストスイートの構造に関する一般的な知識。
- Go言語のコンパイラ設計に関する一般的な情報源(例: Goのソースコード自体、関連する論文やブログ記事)。
- Go言語の
errchk
ディレクティブに関する情報(Goのテストシステムに関するドキュメントやソースコードコメント)。 - Go言語における識別子のスコープと宣言ルールに関する一般的なプログラミング知識。