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

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

このコミットは、Goコンパイラが同じラベルを複数回定義しようとした際に発生する「ラベル再定義エラー」を適切に処理するように修正したものです。具体的には、コンパイラが同一スコープ内で同じ名前のラベルが複数回定義されることを検出し、エラーとして報告するように変更されました。これにより、Go言語の仕様に準拠し、より堅牢なコンパイルプロセスが実現されました。

コミット

Don't try to define the same label twice, as that produces a
label redefinition error.

R=gri
DELTA=6  (0 added, 0 deleted, 6 changed)
OCL=26357
CL=26372

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

https://github.com/golang/go/commit/1e39143966b602ce346e970b9ff85176a781475f

元コミット内容

Don't try to define the same label twice, as that produces a
label redefinition error.

変更の背景

この変更が行われた背景には、Go言語の初期段階におけるコンパイラのラベル処理の不完全さがありました。当時のコンパイラは、同じスコープ内で同じ名前のラベルが複数回定義された場合に、それを適切にエラーとして検出できていませんでした。これは、言語仕様に反するだけでなく、プログラマが意図しない動作を引き起こす可能性がありました。

具体的には、test/bugs/bug140.goというテストケースで示されているように、if {} else L: ;if {} else L: main() ; のように、同じラベルLmain関数内で2回定義されていました。このようなコードは、Go言語の仕様では不正であり、コンパイル時にエラーとなるべきです。しかし、このコミット以前のコンパイラは、この状況を適切に処理できず、予期せぬ動作や誤ったエラーメッセージを生成していました。

この問題を解決し、Go言語の仕様に厳密に準拠するために、コンパイラがラベルの再定義を検出し、明確なエラーメッセージを出すように修正する必要がありました。

前提知識の解説

ラベル (Labels)

プログラミング言語における「ラベル」とは、プログラム内の特定の文やブロックに名前を付けるための識別子です。主にgoto文のターゲットとして使用されたり、ループ文(forswitchselect)から抜け出すためのbreakcontinue文のターゲットとして使用されたりします。

Go言語におけるラベルの主な用途は以下の通りです。

  • goto: プログラムの実行フローを、指定されたラベルの位置にジャンプさせます。gotoの使用は、コードの可読性を損なう可能性があるため、一般的には推奨されません。
  • break文とcontinue: ネストされたループやswitch文、select文から、指定されたラベルを持つ外側の文を終了させたり、次のイテレーションに進んだりするために使用されます。これにより、通常のbreakcontinueでは到達できない外側のスコープを制御できます。

ラベルの定義とスコープ: ラベルは、そのラベルが適用される文の直前にコロン(:)を付けて定義されます。Go言語では、ラベルは関数スコープを持ちます。つまり、同じ関数内で同じ名前のラベルを複数回定義することはできません。これは、コンパイラがどのラベルを指しているのかを明確に識別できるようにするためです。

ラベル再定義エラー (Label Redefinition Error)

「ラベル再定義エラー」とは、同じスコープ内で同じ名前のラベルが複数回定義された場合に、コンパイラによって報告されるエラーです。これは、コンパイラがどのラベルを参照すべきか判断できなくなるため、曖昧さを排除するために発生します。

例えば、以下のようなコードはラベル再定義エラーを引き起こします。

func myFunc() {
    myLabel:
        // Some code
    myLabel: // Error: myLabel already defined
        // More code
}

このエラーは、プログラマが意図せず同じ名前のラベルを複数回使用してしまった場合や、異なる目的で同じ名前のラベルを使い回そうとした場合に発生します。コンパイラがこのエラーを検出することで、コードの論理的な一貫性が保たれ、予期せぬ動作を防ぐことができます。

技術的詳細

このコミットの技術的詳細は、Goコンパイラのセマンティック分析フェーズにおけるラベル管理の改善にあります。コンパイラは、ソースコードを解析する際に、シンボルテーブルと呼ばれるデータ構造を使用して、変数、関数、型、そしてラベルなどの識別子の情報を管理します。

ラベルが定義されると、コンパイラはそのラベルの名前と、それが指すコードの位置をシンボルテーブルに登録します。このコミット以前は、コンパイラがラベルを登録する際に、既に同じ名前のラベルがシンボルテーブルに存在するかどうかのチェックが不十分だったと考えられます。そのため、同じ名前のラベルが複数回登録されてしまい、コンパイルエラーとならなかったり、誤った動作を引き起こしたりしていました。

このコミットでは、コンパイラが新しいラベルを登録する前に、現在のスコープ(この場合は関数スコープ)内で同じ名前のラベルが既に定義されていないかを厳密にチェックするロジックが追加または強化されました。もし同じ名前のラベルが既に存在する場合、コンパイラは「ラベル再定義エラー」を生成し、コンパイルを停止します。

この修正は、Go言語の仕様に準拠するための重要なステップであり、コンパイラの堅牢性と信頼性を向上させました。これにより、プログラマはラベルの誤用による潜在的なバグを早期に発見できるようになりました。

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

このコミットにおけるコアとなるコードの変更は、主にテストファイルとその期待される出力ファイルに集中しています。これは、コンパイラ自体の変更が直接このコミットの差分には含まれていないためです。コンパイラの変更は、このテストケースが失敗し、その失敗を修正するために行われたものと推測されます。

変更されたファイルは以下の2つです。

  1. test/bugs/bug140.go
  2. test/golden.out

test/bugs/bug140.go の変更

--- a/test/bugs/bug140.go
+++ b/test/bugs/bug140.go
@@ -7,12 +7,12 @@
 package main

 func main() {
-	if {} else L: ;
-	if {} else L: main() ;
+	if {} else L1: ;
+	if {} else L2: main() ;
 }

 /*
 These should be legal according to the spec.
-bug140.go:6: syntax error near L
-bug140.go:7: syntax error near L
+bug140.go:6: syntax error near L1
+bug140.go:7: syntax error near L2
 */

このファイルでは、main関数内の2つのif文のelse節に続くラベルが変更されています。

  • 元のコードでは、両方のラベルが L でした。
  • 変更後、最初のラベルは L1 に、2番目のラベルは L2 に変更されました。

また、コメントブロック内の期待されるエラーメッセージも、新しいラベル名に合わせて更新されています。これは、このテストが「ラベル再定義エラー」を意図的に引き起こし、そのエラーメッセージが正しく表示されることを確認するためのものであることを示唆しています。

test/golden.out の変更

--- a/test/golden.out
+++ b/test/golden.out
@@ -137,8 +137,8 @@ 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
+bugs/bug140.go:6: syntax error near L1
+bugs/bug140.go:7: syntax error near L2
 BUG should compile

 =========== fixedbugs/bug016.go

test/golden.outは、Goコンパイラのテストスイートが生成する標準出力(またはエラー出力)の期待値が記述されたファイルです。このファイルでは、bug140.goに関連する行が変更されています。

  • 元の出力では、bug140.go:6: syntax error near Lbug140.go:7: syntax error near L が期待されていました。これは、コンパイラがLというラベルの再定義を検出したことを示しています。
  • 変更後、期待される出力は bug140.go:6: syntax error near L1bug140.go:7: syntax error near L2 になっています。

この変更は、bug140.goのラベル名がL1L2に修正されたことに伴い、コンパイラがそれらの新しいラベル名で構文エラーを報告することを期待していることを示しています。これは、コンパイラがラベルの再定義を正しく検出し、適切なエラーメッセージを生成するようになったことを確認するためのものです。

コアとなるコードの解説

このコミットの「コアとなるコードの変更箇所」で示されたtest/bugs/bug140.goの変更は、Goコンパイラのラベル再定義チェック機能が正しく動作することを確認するためのテストケースの修正です。

元のbug140.goでは、main関数内でLという同じラベルが2回定義されていました。

func main() {
	if {} else L: ;
	if {} else L: main() ;
}

Go言語の仕様では、同じ関数スコープ内で同じ名前のラベルを複数回定義することは許可されていません。したがって、このコードはコンパイルエラーとなるべきです。このコミット以前は、コンパイラがこの状況を適切にエラーとして扱えていなかったか、あるいはエラーメッセージが不明瞭だった可能性があります。

このコミットによって、コンパイラがラベルの再定義を正しく検出するようになったため、テストケースもそれに合わせて修正されました。具体的には、重複していたラベルLL1L2という異なる名前に変更しました。

func main() {
	if {} else L1: ;
	if {} else L2: main() ;
}

この変更により、bug140.goのコード自体はラベルの再定義エラーを引き起こさなくなりました。しかし、このテストの目的は、コンパイラが「ラベル再定義エラー」を正しく報告することを確認することです。したがって、test/golden.outの変更が重要になります。

test/golden.outでは、bug140.goがコンパイルされた際に期待されるエラーメッセージが更新されています。元のgolden.outでは、syntax error near Lというエラーが期待されていましたが、これはコンパイラがLの再定義を検出したことを示しています。

修正後のgolden.outでは、syntax error near L1syntax error near L2というエラーが期待されています。これは、bug140.goのコードがL1L2という異なるラベルを使用するように変更されたため、コンパイラがそれらのラベルに関連する構文エラー(この場合はif {} else L1: ;のような空の文に対するエラー)を報告することを期待していることを意味します。

つまり、このコミットは、コンパイラがラベルの再定義を正しく検出し、適切なエラーメッセージを生成するようになったことを示すために、テストケースとその期待される出力を更新したものです。コンパイラ内部のラベル管理ロジックが改善された結果として、これらのテストの変更が必要となりました。

関連リンク

  • Go言語の仕様 (The Go Programming Language Specification) - Labels:
  • Go言語のgoto文に関する議論 (Go's goto statement discussions, if any relevant historical context is found)
    • Go言語の初期の設計に関する議論は、GoのメーリングリストやIssueトラッカーに存在しますが、特定のコミットに関連する直接的な議論を見つけるのは困難な場合があります。

参考にした情報源リンク