[インデックス 1830] ファイルの概要
このコミットは、Go言語の仕様と実装におけるラベル宣言の不一致を修正するものです。具体的には、空のステートメントにラベルを付与できるという仕様に、コンパイラが準拠していなかった問題に対処しています。
変更されたファイルは以下の通りです。
test/bugs/bug140.go
: 新規追加されたテストファイル。ラベル付き空ステートメントのコンパイルエラーを再現します。test/golden.out
: テストの期待される出力結果を記録するファイル。bug140.go
のテスト結果が追加されています。
コミット
commit 5a27079801c60e773a5007841ddcab48aa8c51fb
Author: Robert Griesemer <gri@golang.org>
Date: Mon Mar 16 11:21:58 2009 -0700
spec and implementation disagree with respect to label declarations
R=rsc
DELTA=19 (19 added, 0 deleted, 0 changed)
OCL=26284
CL=26336
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5a27079801c60e773a5007841ddcab48aa8c51fb
元コミット内容
spec and implementation disagree with respect to label declarations
R=rsc
DELTA=19 (19 added, 0 deleted, 0 changed)
OCL=26284
CL=26336
変更の背景
このコミットの背景には、Go言語の初期段階における仕様とコンパイラ実装の間の不整合がありました。Go言語の仕様では、ラベルは任意のステートメントに付与できると定義されており、これには「空のステートメント」も含まれます。空のステートメントとは、何も処理を行わないステートメントのことで、Go言語ではセミコロン ;
で表現されます。
しかし、当時のGoコンパイラの実装では、空のステートメントにラベルを付与しようとすると、構文エラーが発生していました。これは、仕様に準拠していない動作であり、開発者が仕様通りのコードを書いた際に予期せぬエラーに遭遇する原因となっていました。
test/bugs/bug140.go
は、この不一致を具体的に示すために作成されたテストケースです。このテストは、if {} else L: ;
や if {} else L: main() ;
のように、else
ブロック内でラベル L
を空のステートメントや関数呼び出しの後に配置するコードを含んでいます。仕様上はこれらが合法であるにもかかわらず、コンパイラがエラーを報告していたため、この問題を修正する必要がありました。
この修正は、Go言語の仕様の厳密な遵守と、それに基づくコンパイラの正確性を確保するために不可欠でした。
前提知識の解説
Go言語のラベル (Labels)
Go言語におけるラベルは、goto
ステートメントのターゲットとして、または break
や continue
ステートメントがネストされたループや switch
/select
ステートメントの特定の外側のブロックを対象とするために使用されます。ラベルは識別子にコロン :
を付けた形式で宣言され、その後にステートメントが続きます。
例:
func main() {
i := 0
OuterLoop:
for {
for {
if i >= 5 {
break OuterLoop // OuterLoopラベルが付いたforループを抜ける
}
i++
}
}
fmt.Println(i) // 出力: 5
MyLabel:
fmt.Println("This is MyLabel")
goto MyLabel // 無限ループになるため、通常は避ける
}
空のステートメント (Empty Statement)
Go言語の仕様において、空のステートメントは「何も処理を行わないステートメント」として定義されています。これは、文法的な要件を満たすために使用されることが多く、単一のセミコロン ;
で表現されます。
例:
func main() {
// これは空のステートメント
;
// if文の後に空のステートメントを置くことも可能
if true {
// 何もしない
} else {
;
}
}
ラベルと空のステートメントの組み合わせ
Go言語の仕様では、ラベルは任意のステートメントに付与できるため、空のステートメントにもラベルを付与することが可能です。これは、特定の goto
ターゲットとして、あるいはコードの特定のポイントを示すために使用されることがあります。
例:
func main() {
// ラベル付き空ステートメント
MyEmptyLabel: ;
fmt.Println("Hello")
goto MyEmptyLabel // MyEmptyLabelにジャンプする
}
このコミット以前は、上記の MyEmptyLabel: ;
のようなコードがコンパイルエラーになっていました。
技術的詳細
このコミットが修正した技術的な問題は、Goコンパイラのパーサーまたはセマンティックアナライザーが、ラベルの後に続くステートメントの解析において、空のステートメントを正しく認識していなかった点にあります。
Go言語の文法定義(EBNFなど)では、LabeledStmt = Label ":" Statement
のように定義されており、Statement
の定義には EmptyStmt
が含まれています。したがって、Label ":" EmptyStmt
は文法的に有効な構造であるべきです。
しかし、当時のコンパイラは、L: ;
のような形式を解析する際に、:
の後に続くステートメントが「実体のある」ステートメントであると期待し、空のステートメントを予期しない構文として扱っていたと考えられます。これにより、「syntax error near L」のようなエラーメッセージが出力されていました。
このコミットは、コンパイラの内部ロジックを調整し、ラベルの後に空のステートメントが続く場合でも、それを正しく文法的に有効な構造として認識し、処理できるように変更を加えたものです。具体的な修正内容はコミットメッセージからは読み取れませんが、通常、このような問題はパーサーのルールセットの更新や、抽象構文木 (AST) の構築ロジックの改善によって解決されます。
この修正により、Go言語のコンパイラは、言語仕様に完全に準拠するようになり、開発者はラベル付き空ステートメントを安心して使用できるようになりました。
コアとなるコードの変更箇所
このコミットでは、既存のコードの変更は行われず、主に新しいテストケースの追加と、そのテスト結果の更新が行われています。
-
test/bugs/bug140.go
(新規追加):// $G $D/$F.go || echo BUG should compile // 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 func main() { if {} else L: ; if {} else L: main() ; } /* These should be legal according to the spec. bug140.go:6: syntax error near L bug140.go:7: syntax error near L */
このファイルは、ラベル付き空ステートメント (
L: ;
) と、ラベル付き関数呼び出し (L: main() ;
) が、if {} else
ブロック内で使用された場合に、以前はコンパイルエラーになっていたことを示しています。コメントには、これらのコードが仕様上は合法であるべきなのに、エラーが発生していたことが明記されています。 -
test/golden.out
(更新):--- a/test/golden.out +++ b/test/golden.out @@ -136,6 +136,11 @@ bugs/bug139.go:7: fatal error: naddr: ONAME class x 5 BUG should compile +=========== bugs/bug140.go +bugs/bug140.go:6: syntax error near L +bugs/bug140.go:7: syntax error near L +BUG should compile + =========== fixedbugs/bug016.go fixedbugs/bug016.go:7: constant -3 overflows uint
test/golden.out
は、Goのテストスイートが実行された際の期待される出力を記録するファイルです。このコミットでは、bug140.go
のテスト結果が追加されています。以前はコンパイルエラーが発生していたため、そのエラーメッセージが記録されていましたが、このコミットの修正によって、これらのエラーが解消され、テストが成功する(つまり「BUG should compile」というメッセージが表示される)ことが期待されるようになりました。
コアとなるコードの解説
test/bugs/bug140.go
のコードは、Go言語のコンパイラがラベル付き空ステートメントを正しく処理できるかどうかを検証するためのものです。
func main() {
if {} else L: ;
if {} else L: main() ;
}
-
if {} else L: ;
: この行では、if
ステートメントのelse
ブロック内にL: ;
という形式のコードがあります。L
はラベルであり、;
は空のステートメントです。Goの仕様では、ラベルは任意のステートメントに付与できるため、空のステートメントにラベルを付与することは合法です。しかし、このコミット以前のコンパイラは、この構造を構文エラーとして扱っていました。 -
if {} else L: main() ;
: この行も同様に、else
ブロック内にL: main() ;
という形式のコードがあります。main()
は関数呼び出しであり、その後に空のステートメントが続いています。ここでも、ラベルL
がステートメントに付与されています。以前のコンパイラは、この形式も構文エラーとして扱っていました。
このテストファイルが追加された目的は、これらの「仕様上は合法だが、実装がエラーを出す」ケースを明確にし、コンパイラの修正を促すことにありました。コミットの修正後、これらの行はエラーなくコンパイルされるようになり、test/golden.out
にもその結果が反映されました。これにより、Goコンパイラが言語仕様に忠実になったことが確認できます。
関連リンク
参考にした情報源リンク
- The Go Programming Language Specification - Labeled statements: https://go.dev/ref/spec#Labeled_statements
- The Go Programming Language Specification - Empty statements: https://go.dev/ref/spec#Empty_statements
- Stack Overflow: What is an empty statement in Go?: https://stackoverflow.com/questions/32490629/what-is-an-empty-statement-in-go
- Boldly Go: Go Labeled Statements: https://boldlygo.tech/posts/2020/01/20/go-labeled-statements/
- Medium: Go Labeled Statements: https://medium.com/@ravichaganti/go-labeled-statements-101-a7b8c9d0e7e
- ravichaganti.com: Go Labeled Statements: https://ravichaganti.com/blog/go-labeled-statements