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

[インデックス 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内のbreakselectステートメント自体ではなく、それを囲むforループを終了させることを確認します。

変更の背景

Go言語は2008年当時、まだ開発の初期段階にありました。言語のセマンティクス、特に制御フローに関する挙動は、厳密に定義され、テストされる必要がありました。selectステートメントはGoの並行処理の重要な要素であり、breakのような基本的な制御フローキーワードとの相互作用は、開発者が期待する直感的な挙動と一致していることを保証することが不可欠でした。

このコミットは、selectステートメント内のbreakが、switchステートメントやforループ内のbreakと同様に、最も内側の囲むforswitch、またはselectステートメントを終了させるというGoの言語仕様の原則をテストするために追加されました。この特定のケースでは、selectがループ内にネストされているため、breakがループを終了させることを確認することが目的です。これにより、言語の挙動が一貫しており、予測可能であることが保証されます。

前提知識の解説

Go言語のforループ

Go言語のforループは、C言語やJavaのそれとは異なり、より柔軟です。Goのforは、whileループや無限ループ(for {})としても機能します。

  • 無限ループ: for {} は条件なしで無限に実行されます。このループから脱出するには、breakreturn、またはpanicなどの制御フローキーワードが必要です。

Go言語のselectステートメント

selectステートメントは、Go言語における並行処理の強力なプリミティブです。複数の通信操作(チャネルの送受信)を待機し、そのうちのいずれかが準備できたときに実行します。

  • selectは、複数のcase句を持ち、それぞれのcaseはチャネル操作(<-chまたはch<-)を含みます。
  • 複数のcaseが同時に準備できた場合、selectはランダムに1つを選択して実行します。
  • どのcaseも準備できていない場合、default句があればそれが実行されます。
  • default句がなく、どのcaseも準備できていない場合、selectはチャネル操作のいずれかが準備できるまでブロックします。

Go言語のbreakステートメント

breakステートメントは、forswitch、またはselectステートメントの実行を終了するために使用されます。

  • breakは、最も内側の囲むforswitch、または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は最も内側の囲むforswitch、またはselectステートメントを終了させます。この場合、selectステートメントはforループ内にネストされているため、breakforループを終了させるべきです。
  • panic();selectの直後):このpanicステートメントは、breakが正しくforループを終了した場合、到達されるべきではありません。もしbreakselectステートメントのみを終了させ、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言語のセマンティクスを検証します。

  1. a := new(chan bool);:テスト用のチャネルaを初期化します。このチャネルは、テスト中にデータを送受信するために使用されません。
  2. for {}:無限ループを開始します。このループは、breakステートメントによってのみ終了することを意図しています。
  3. select {}:このselectステートメントは、チャネルaからの受信を試みるcaseと、default句を含みます。
    • case <- a::チャネルaには何も送信されないため、このcaseは準備されません。
    • default:caseが準備されないため、selectはすぐにdefault句を実行します。
    • break;default句内で実行されます。Goの言語仕様では、breakは最も内側のforswitch、またはselectステートメントを終了させます。この文脈では、selectステートメントがforループ内にネストされているため、breakforループを終了させるべきです。
  4. panic();selectの直後):このpanicステートメントは、breakが正しくforループを終了した場合、到達されるべきではありません。もしbreakselectステートメントのみを終了させ、forループが継続した場合、このpanicが実行され、テストは失敗します。

test/golden.outの更新は、このテストが成功した場合(つまり、panicが発生しない場合)の期待される出力を定義しています。これにより、Goコンパイラとランタイムがbreakステートメントのセマンティクスを正しく実装していることが確認されます。

このテストは、Go言語の初期段階において、言語の挙動が明確で予測可能であることを保証するための、基本的ながらも重要なステップでした。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよび言語仕様
  • Go言語のソースコードリポジトリ(GitHub)
  • Go言語の初期開発に関する議論やメーリングリストのアーカイブ(一般公開されている場合)
  • Go言語のテストフレームワークとgolden.outファイルの慣習に関する一般的な知識