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

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

このコミットは、gccgo のテストスイートで使用されている DejaGNU テストハーネスにおける正規表現のマッチングの問題を修正するものです。具体的には、エラーメッセージを捕捉するための ERROR ".*" という汎用的なパターンが、実際にはエラーメッセージ全体を飲み込んでしまい、テストが期待通りに失敗しない(またはエラーメッセージが正しく認識されない)という問題を解決するため、より具体的なエラーパターン "illegal|incompatible" に変更しています。

コミット

commit cc352e5c1c115ac6e88a30f6ebed76235b8aaf02
Author: Ian Lance Taylor <iant@golang.org>
Date:   Fri Dec 5 10:20:34 2008 -0800

    The DejaGNU testsuite harness used by the gccgo testsuite is
    broken if you give it something which matches everything--the
    .* swallows all the error messages.  Recognize some reasonable
    error text directly.
    
    R=rsc
    DELTA=2  (0 added, 0 deleted, 2 changed)
    OCL=20564
    CL=20595

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

https://github.com/golang/go/commit/cc352e5c1c115ac6e88a30f6ebed76235b8aaf02

元コミット内容

commit cc352e5c1c115ac6e88a30f6ebed76235b8aaf02
Author: Ian Lance Taylor <iant@golang.org>
Date:   Fri Dec 5 10:20:34 2008 -0800

    The DejaGNU testsuite harness used by the gccgo testsuite is
    broken if you give it something which matches everything--the
    .* swallows all the error messages.  Recognize some reasonable
    error text directly.
    
    R=rsc
    DELTA=2  (0 added, 0 deleted, 2 changed)
    OCL=20564
    CL=20595
---\n test/bugs/bug127.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/bugs/bug127.go b/test/bugs/bug127.go
index a67e85144d..b463d233ce 100644
--- a/test/bugs/bug127.go
+++ b/test/bugs/bug127.go
@@ -7,6 +7,6 @@
 package main
 func main() {
         var x int64 = 0;
-        println(x != nil);\t// ERROR \".*\"\n-        println(0 != nil);\t// ERROR \".*\"\n+        println(x != nil);\t// ERROR \"illegal|incompatible\"\n+        println(0 != nil);\t// ERROR \"illegal|incompatible\"\n }\n

変更の背景

このコミットの背景には、gccgo のテストプロセスにおける DejaGNU テストハーネスの特定の挙動があります。DejaGNU は、コンパイラやツールチェインのテストによく用いられるフレームワークであり、テストケースの出力(特にエラーメッセージ)を正規表現でマッチングすることで、期待される挙動を検証します。

問題は、テストコード内のコメントで指定されるエラーパターンに ".*" (任意の文字に0回以上マッチ) のような非常に汎用的な正規表現を使用した場合に発生しました。DejaGNU の内部処理において、この ".*" がエラーメッセージ全体を「飲み込んで」しまい、結果としてテストハーネスが期待する特定のエラーメッセージを正確に認識できなくなる、あるいはテストが正しく失敗したと判断できなくなるというバグがありました。

この挙動は、テストが本来検出するべきコンパイルエラーや実行時エラーを適切に検証できないことを意味します。例えば、x != nil のような Go 言語では不正な比較に対してコンパイラがエラーを出すべきですが、DejaGNU がそのエラーメッセージを正しく捕捉できないと、テストが誤って成功と判断されてしまう可能性があります。

この問題を解決するため、コミットでは ".*" のような汎用的なパターンではなく、実際に発生する可能性のあるエラーメッセージの一部を直接指定する "illegal|incompatible" のような、より具体的な正規表現を使用するように変更されました。これにより、DejaGNU は正確にエラーメッセージを識別し、テストの信頼性を向上させることができます。

前提知識の解説

DejaGNU (デジャグヌー)

DejaGNU は、GNU プロジェクトによって開発されたテストフレームワークであり、主にコンパイラ、ライブラリ、その他のツールチェインコンポーネントのテストに使用されます。Tcl (Tool Command Language) と Expect をベースにしており、テストスクリプトの記述と実行を容易にします。

DejaGNU の主要な機能は以下の通りです。

  • テストハーネス: テスト対象のプログラムを実行し、その出力を捕捉・解析するためのインフラを提供します。
  • 正規表現マッチング: テスト対象のプログラムが出力するメッセージ(特にエラーメッセージや警告)を、期待されるパターンと正規表現で比較することで、テストの合否を判定します。
  • クロスプラットフォーム対応: 異なるオペレーティングシステムやアーキテクチャ上でのテスト実行をサポートします。

コンパイラのテストにおいては、特定のコードがコンパイルエラーを発生させるべきか、あるいは特定の警告を出すべきか、といったシナリオを検証するのに非常に強力なツールです。テストファイル内に特別なコメント(例: // ERROR "...")を記述することで、DejaGNU はその行で指定された正規表現にマッチするエラーメッセージが出力されることを期待します。

gccgo

gccgo は、Go 言語のフロントエンドを GCC (GNU Compiler Collection) に統合したものです。通常の Go コンパイラ (gc) とは異なり、gccgo は GCC の既存の最適化バックエンドやコード生成インフラを利用します。これにより、Go プログラムを GCC がサポートする様々なアーキテクチャやプラットフォーム向けにコンパイルできるようになります。

gccgo は GCC の一部として開発されているため、そのテストスイートも GCC のテストインフラ、すなわち DejaGNU を利用しています。Go 言語の仕様に準拠しているか、特定の Go コードが gccgo で正しくコンパイル・実行されるか、あるいは期待されるエラーを生成するかなどを検証するために、DejaGNU ベースのテストが書かれています。

テストハーネス (Test Harness)

テストハーネスとは、ソフトウェアテストの実行を自動化し、テスト対象のコンポーネント(SUT: System Under Test)とテストケースの間のインタフェースを提供するフレームワークや環境のことです。テストハーネスは以下の役割を担います。

  • テストケースの実行: テストケースをロードし、SUT を呼び出します。
  • テストデータの準備: テスト実行に必要な入力データや環境を設定します。
  • 結果の収集と検証: SUT の出力や挙動を捕捉し、期待される結果と比較してテストの合否を判定します。
  • レポートの生成: テスト結果を記録し、レポートとして出力します。

DejaGNU は、コンパイラやツールチェインのテストに特化したテストハーネスの一種と言えます。

技術的詳細

このコミットが対処している技術的な問題は、正規表現の貪欲性(Greedy Quantifier)と DejaGNU のエラーマッチングの挙動の組み合わせに起因します。

正規表現において、*+ といった量指定子(quantifiers)はデフォルトで「貪欲(greedy)」です。これは、可能な限り多くの文字にマッチしようとすることを意味します。例えば、文字列 "abcde" に対して正規表現 a.*e を適用すると、.* は "bcde" 全体にマッチし、結果として "abcde" 全体がマッチします。

DejaGNU のテストハーネスでは、テスト対象のプログラムが出力するエラーメッセージを捕捉するために、テストファイル内の // ERROR "pattern" のようなコメントを使用します。DejaGNU は、この pattern を正規表現として解釈し、プログラムの標準エラー出力 (stderr) や標準出力 (stdout) から得られるメッセージと照合します。

問題は、pattern".*" を指定した場合に発生しました。本来、開発者は「この行で何らかのエラーメッセージが出力されることを期待する」という意味で ".*" を使ったと考えられます。しかし、DejaGNU の内部的なエラーメッセージ処理において、この ".*" が非常に貪欲に振る舞い、期待される特定のエラーメッセージだけでなく、その後に続く可能性のある他のエラーメッセージや、場合によっては全く関係のない出力までをも「飲み込んで」しまうことがありました。

これにより、DejaGNU は特定のコンパイルエラーメッセージを正確に識別できず、テストが誤って成功と判断されたり、エラーメッセージの検証が不完全になったりする可能性がありました。例えば、println(x != nil) のような不正なコードに対して、コンパイラが「nilint64 の比較は不正です」といったエラーメッセージを出力するはずですが、ERROR ".*" が指定されていると、DejaGNU がその具体的なエラーメッセージを「見失う」ことがあったのです。

このコミットでは、この問題を解決するために、より具体的な正規表現パターン "illegal|incompatible" を導入しました。

  • illegal: 「不正な」という意味で、Go 言語の型システムにおける不正な操作(例: 不正な型変換、不正な比較)を示すエラーメッセージの一部としてよく使われます。
  • incompatible: 「互換性のない」という意味で、型が互換性がないために操作ができない場合のエラーメッセージの一部として使われます。

これらのパターンは、x != nil0 != nil のような Go 言語の型システムにおける不正な比較に対して gccgo が出力するであろう具体的なエラーメッセージの一部にマッチするように設計されています。これにより、DejaGNU は汎用的な .* に惑わされることなく、期待されるエラーメッセージを正確に捕捉し、テストの信頼性を向上させることができます。

この変更は、DejaGNU の正規表現マッチングの挙動をより正確に制御し、テストの意図を明確にするための重要な修正です。

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

変更は test/bugs/bug127.go ファイルの以下の2行です。

--- a/test/bugs/bug127.go
+++ b/test/bugs/bug127.go
@@ -7,6 +7,6 @@
 package main
 func main() {
         var x int64 = 0;
-        println(x != nil);\t// ERROR \".*\"\n-        println(0 != nil);\t// ERROR \".*\"\n+        println(x != nil);\t// ERROR \"illegal|incompatible\"\n+        println(0 != nil);\t// ERROR \"illegal|incompatible\"\n }\n

具体的には、以下の行が変更されました。

  • println(x != nil);\t// ERROR \".*\"println(x != nil);\t// ERROR \"illegal|incompatible\"

  • println(0 != nil);\t// ERROR \".*\"println(0 != nil);\t// ERROR \"illegal|incompatible\"

コアとなるコードの解説

test/bugs/bug127.go は、Go 言語における型システムのエラーをテストするためのファイルです。

package main
func main() {
        var x int64 = 0;
        println(x != nil);      // ERROR "illegal|incompatible"
        println(0 != nil);      // ERROR "illegal|incompatible"
}

このコードスニペットの目的は、int64 型の変数 x やリテラル 0nil (Go 言語におけるゼロ値やインターフェースの未初期化状態を示す特別な値) と比較しようとすると、コンパイルエラーが発生することを確認することです。Go 言語では、異なる型(特に数値型と nil)を直接比較することは通常許可されていません。

各行の末尾にある // ERROR "..." コメントは、DejaGNU テストハーネスに対する指示です。

  • // ERROR: この行でコンパイルエラーが発生することを期待していることを DejaGNU に伝えます。
  • "illegal|incompatible": この正規表現は、DejaGNU がこの行で出力されるエラーメッセージにマッチすることを期待するパターンです。

変更前は ".*" という非常に汎用的なパターンが使われていました。これは「この行で何らかのエラーメッセージが出力されることを期待する」という意図だったと思われますが、前述の通り DejaGNU の内部的な正規表現マッチングの挙動により、この汎用パターンが問題を引き起こしていました。

変更後の "illegal|incompatible" は、gccgo がこのような不正な比較に対して出力するであろう具体的なエラーメッセージの一部(例: "illegal operand types", "incompatible types" など)にマッチするように選ばれています。これにより、テストはより堅牢になり、特定の型エラーが正しく検出されることを保証します。

このテストファイルは、gccgo コンパイラが Go 言語の型規則を正しく強制し、不正な操作に対して適切なエラーメッセージを生成できることを検証する役割を担っています。

関連リンク

参考にした情報源リンク