[インデックス 1080] ファイルの概要
このコミットは、Go言語のselect
ステートメント内で使用されるbreak
ステートメントが、そのselect
ステートメントを囲むループから正しく脱出することを確認するためのテストケースを追加します。
コミット
commit ce15158502dad699a4781908a2fb0bed1dc8b61b
Author: Ian Lance Taylor <iant@golang.org>
Date: Thu Nov 6 15:24:10 2008 -0800
Test that a break statement inside a select statement breaks
out of the enclosing loop.
R=ken
DELTA=20 (20 added, 0 deleted, 0 changed)
OCL=18686
CL=18714
---
test/bugs/bug119.go | 20 ++++++++++++++++++++
test/golden.out | 4 ++++
2 files changed, 24 insertions(+)
diff --git a/test/bugs/bug119.go b/test/bugs/bug119.go
new file mode 100644
index 0000000000..956f1b95d4
--- /dev/null
+++ b/test/bugs/bug119.go
@@ -0,0 +1,20 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG: should not fail
+//
+// 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() {
+ a := new(chan bool);
+ for {
+ select {
+ case <- a:
+ panic();
+ default:
+ break;
+ }
+ panic();
+ }
+}
diff --git a/test/golden.out b/test/golden.out
index 640267d301..77fe8dc881 100644
--- a/test/golden.out
+++ b/test/golden.out
@@ -153,6 +153,10 @@ BUG: should compile
=========== bugs/bug118.go
BUG should compile
+=========== bugs/bug119.go
+
+panic on line 82 PC=xxx
+BUG should not panic
=========== fixedbugs/bug016.go
fixedbugs/bug016.go:7: overflow converting constant to uint
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ce15158502dad699a4781908a2fb0bed1dc8b61b
元コミット内容
このコミットは、Go言語のselect
ステートメント内でbreak
がどのように動作するかを検証するための新しいテストケースを追加します。具体的には、select
内のbreak
がselect
ステートメント自体ではなく、それを囲むfor
ループを終了させることを確認します。
変更の背景
Go言語は2008年当時、まだ開発の初期段階にありました。言語のセマンティクス、特に制御フローに関する挙動は、厳密に定義され、テストされる必要がありました。select
ステートメントはGoの並行処理の重要な要素であり、break
のような基本的な制御フローキーワードとの相互作用は、開発者が期待する直感的な挙動と一致していることを保証することが不可欠でした。
このコミットは、select
ステートメント内のbreak
が、switch
ステートメントやfor
ループ内のbreak
と同様に、最も内側の囲むfor
、switch
、またはselect
ステートメントを終了させるというGoの言語仕様の原則をテストするために追加されました。この特定のケースでは、select
がループ内にネストされているため、break
がループを終了させることを確認することが目的です。これにより、言語の挙動が一貫しており、予測可能であることが保証されます。
前提知識の解説
Go言語のfor
ループ
Go言語のfor
ループは、C言語やJavaのそれとは異なり、より柔軟です。Goのfor
は、while
ループや無限ループ(for {}
)としても機能します。
- 無限ループ:
for {}
は条件なしで無限に実行されます。このループから脱出するには、break
、return
、またはpanic
などの制御フローキーワードが必要です。
Go言語のselect
ステートメント
select
ステートメントは、Go言語における並行処理の強力なプリミティブです。複数の通信操作(チャネルの送受信)を待機し、そのうちのいずれかが準備できたときに実行します。
select
は、複数のcase
句を持ち、それぞれのcase
はチャネル操作(<-ch
またはch<-
)を含みます。- 複数の
case
が同時に準備できた場合、select
はランダムに1つを選択して実行します。 - どの
case
も準備できていない場合、default
句があればそれが実行されます。 default
句がなく、どのcase
も準備できていない場合、select
はチャネル操作のいずれかが準備できるまでブロックします。
Go言語のbreak
ステートメント
break
ステートメントは、for
、switch
、またはselect
ステートメントの実行を終了するために使用されます。
break
は、最も内側の囲むfor
、switch
、またはselect
ステートメントを終了させます。- ラベル付き
break
を使用すると、特定の外側のループやswitch
/select
ステートメントを終了させることができますが、このコミットのテストケースでは使用されていません。
panic
関数
panic
はGo言語の組み込み関数で、通常のプログラム実行フローを中断し、パニック状態を開始します。これは通常、回復不可能なエラーを示すために使用されます。パニックが発生すると、現在の関数の実行が停止し、遅延関数が実行され、呼び出しスタックをさかのぼってパニックが伝播します。
技術的詳細
このコミットで追加されたbug119.go
テストファイルは、for
無限ループ内にselect
ステートメントをネストしたシナリオを検証します。
テストコードの構造は以下の通りです。
package main
func main() {
a := new(chan bool); // チャネル 'a' を作成。このチャネルには何も送信されない。
for { // 無限ループ
select {
case <- a: // チャネル 'a' からの受信を試みる。'a'は閉じられていないため、このケースは永遠に準備できない。
panic(); // このpanicは実行されないはず
default: // どのcaseも準備できていないため、defaultが実行される
break; // このbreakが、selectではなく外側のforループを終了させることを期待
}
panic(); // このpanicは、breakがforループを終了させれば実行されないはず
}
}
a := new(chan bool);
:バッファなしのチャネルa
を宣言し、初期化します。このチャネルには、テストの実行中にデータが送信されることはありません。for {}
:無限ループです。select {}
:このselect
ステートメントには2つのcase
があります。case <- a:
:チャネルa
からの受信を試みます。a
には何も送信されないため、このcase
は準備されることがありません。default:
:case <- a
が準備されないため、select
はすぐにdefault
句を実行します。
break;
:default
句内で実行されます。Goの言語仕様により、break
は最も内側の囲むfor
、switch
、またはselect
ステートメントを終了させます。この場合、select
ステートメントはfor
ループ内にネストされているため、break
はfor
ループを終了させるべきです。panic();
(select
の直後):このpanic
ステートメントは、break
が正しくfor
ループを終了した場合、到達されるべきではありません。もしbreak
がselect
ステートメントのみを終了させ、for
ループが継続した場合、このpanic
が実行されてテストは失敗します。
test/golden.out
ファイルには、このテストがpanic
しないことが期待される旨が追記されています。これは、break
が正しくループを終了し、select
ステートメントの直後のpanic()
に到達しないことを意味します。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、test/bugs/bug119.go
という新しいテストファイルの追加です。
--- /dev/null
+++ test/bugs/bug119.go
@@ -0,0 +1,20 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG: should not fail
+//
+// 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() {
+ a := new(chan bool);
+ for {
+ select {
+ case <- a:
+ panic();
+ default:
+ break;
+ }
+ panic();
+ }
+}
また、test/golden.out
ファイルに、bug119.go
のテスト結果に関する期待値が追加されています。
--- a/test/golden.out
+++ b/test/golden.out
@@ -153,6 +153,10 @@ BUG: should compile
=========== bugs/bug118.go
BUG should compile
+=========== bugs/bug119.go
+
+panic on line 82 PC=xxx
+BUG should not panic
=========== fixedbugs/bug016.go
fixedbugs/bug016.go:7: overflow converting constant to uint
panic on line 82 PC=xxx
という記述は、もしパニックが発生した場合の一般的な出力形式を示しており、その後にBUG should not panic
と続くことで、このテストがパニックを起こさないことが正しい挙動であると明示しています。
コアとなるコードの解説
bug119.go
のテストコードは、select
ステートメント内のbreak
が、そのselect
ステートメントを囲むfor
ループを終了させるというGo言語のセマンティクスを検証します。
a := new(chan bool);
:テスト用のチャネルa
を初期化します。このチャネルは、テスト中にデータを送受信するために使用されません。for {}
:無限ループを開始します。このループは、break
ステートメントによってのみ終了することを意図しています。select {}
:このselect
ステートメントは、チャネルa
からの受信を試みるcase
と、default
句を含みます。case <- a:
:チャネルa
には何も送信されないため、このcase
は準備されません。default:
:case
が準備されないため、select
はすぐにdefault
句を実行します。break;
:default
句内で実行されます。Goの言語仕様では、break
は最も内側のfor
、switch
、またはselect
ステートメントを終了させます。この文脈では、select
ステートメントがfor
ループ内にネストされているため、break
はfor
ループを終了させるべきです。
panic();
(select
の直後):このpanic
ステートメントは、break
が正しくfor
ループを終了した場合、到達されるべきではありません。もしbreak
がselect
ステートメントのみを終了させ、for
ループが継続した場合、このpanic
が実行され、テストは失敗します。
test/golden.out
の更新は、このテストが成功した場合(つまり、panic
が発生しない場合)の期待される出力を定義しています。これにより、Goコンパイラとランタイムがbreak
ステートメントのセマンティクスを正しく実装していることが確認されます。
このテストは、Go言語の初期段階において、言語の挙動が明確で予測可能であることを保証するための、基本的ながらも重要なステップでした。
関連リンク
- Go言語の
select
ステートメントに関する公式ドキュメント(Go言語仕様):https://go.dev/ref/spec#Select_statements - Go言語の
break
ステートメントに関する公式ドキュメント(Go言語仕様):https://go.dev/ref/spec#Break_statements - Go言語の
for
ステートメントに関する公式ドキュメント(Go言語仕様):https://go.dev/ref/spec#For_statements
参考にした情報源リンク
- Go言語の公式ドキュメントおよび言語仕様
- Go言語のソースコードリポジトリ(GitHub)
- Go言語の初期開発に関する議論やメーリングリストのアーカイブ(一般公開されている場合)
- Go言語のテストフレームワークと
golden.out
ファイルの慣習に関する一般的な知識