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

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

このコミットは、Go言語のテストスイート内の test/declbad.go ファイルにおける型定義の誤りを修正するものです。具体的には、短縮変数宣言の再宣言に関するテストケースが、意図しない型不一致エラーを引き起こしていた問題を解決しています。これにより、テストが本来意図する「変数の再宣言」または「新しい変数が宣言されていない」というエラーのみを正確に検出できるようになります。

コミット

commit 9844e4cd7cc52ecbe7d49307b6546a118de5bea7
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Sun Oct 7 21:52:57 2012 +0200

    test: correct type in declbad.go
    
    The test is not about type mismatches and it causes
    an extra error to appear.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6614062

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

https://github.com/golang/go/commit/9844e4cd7cc52ecbe7d49307b6546a118de5bea7

元コミット内容

test: correct type in declbad.go

このテストは型不一致に関するものではなく、余分なエラーが発生していました。

変更の背景

Go言語のコンパイラテストでは、特定のコードが期待されるエラーを生成するかどうかを検証するために、// ERROR "..." のようなコメントが使用されます。test/declbad.go は、Goの短縮変数宣言 (:=) における変数の再宣言ルール、特に「新しい変数が宣言されていない場合はエラーとなる」という挙動をテストするためのファイルです。

このコミットが行われる前、declbad.go 内のあるテストケースでは、i := func() { という行がありました。この行は、i という変数を再宣言しようとする際に、その型が以前に宣言された i の型(おそらく f1 の型)と暗黙的に異なるために、意図しない「型不一致」のエラーも同時に発生させていました。

テストの目的は「変数の再宣言ルール」の検証であり、型不一致の検証ではありませんでした。そのため、テストが本来検出したいエラー(再宣言に関するもの)に加えて、無関係な型不一致エラーが発生することで、テストの意図が不明瞭になり、コンパイラの出力が複雑になるという問題がありました。このコミットは、この「余分なエラー」を取り除き、テストの焦点を明確にすることを目的としています。

前提知識の解説

Go言語の短縮変数宣言 (:=)

Go言語では、var キーワードを使わずに、name := expression の形式で変数を宣言し、初期化することができます。これを短縮変数宣言と呼びます。 短縮変数宣言には以下の重要なルールがあります。

  1. 新しい変数の宣言: := の左辺には、少なくとも1つの新しい変数が含まれていなければなりません。
  2. スコープ: 変数は現在のブロック内で宣言されます。
  3. 再宣言: 同じブロック内で既に宣言されている変数名が := の左辺に含まれていても、その変数に新しい値が代入されるだけで、新しい変数が少なくとも1つ宣言されていればエラーにはなりません。しかし、左辺の全ての変数が既に宣言済みであり、かつ新しい変数が一つも含まれていない場合、コンパイルエラーとなります。このエラーは通常、「no new variables on left side of :=」または「i redeclared in this block」といったメッセージで示されます。

Go言語の関数リテラルと型推論

Go言語では、関数をその場で定義し、変数に代入したり、引数として渡したりすることができます。これを関数リテラルと呼びます。 例: func() { fmt.Println("Hello") } 関数リテラルの型は、その引数と戻り値の型によって決まります。例えば、引数なしで整数を返す関数は func() int 型です。 Goのコンパイラは、文脈から変数の型を推論する能力(型推論)を持っています。しかし、型が明示されていない関数リテラルは、その使用方法によって型が決定されます。

Go言語のコンパイラテストにおける // ERROR "..." コメント

Go言語のソースコードリポジトリには、コンパイラの挙動をテストするための特別なテストファイルが含まれています。これらのファイルでは、特定の行がコンパイルエラーになることを期待する場合、その行の末尾に // ERROR "expected error message" のようなコメントを記述します。コンパイラテストツールは、このコメントに指定された正規表現が、実際にその行で発生したエラーメッセージに含まれているかどうかを検証します。 このメカニズムにより、コンパイラが正しいエラーを正しい場所で報告するかどうかを確認できます。

技術的詳細

このコミットが修正しているのは、test/declbad.go 内の以下のコードブロックです。

	{
		// multiline no new variables
		i := f1
		i := func() { // ERROR "redeclared|no new|incompatible"
		}
		_ = i
	}

ここで問題となっていたのは、2行目の i := func() { の部分です。 1行目で i := f1 と宣言されており、f1 の具体的な型は不明ですが、このテストの文脈から、おそらく f1 は何らかの関数型、または少なくとも func() とは異なる型を持つ変数であったと推測されます。

もし f1 が例えば func() int 型であった場合、1行目の i := f1 により ifunc() int 型となります。 その直後の i := func() { は、引数も戻り値もない関数リテラルであり、その型は func() です。 この場合、func() int 型の i に対して func() 型の値を代入しようとすることになり、これは型不一致のエラーを引き起こします。

テストのコメント // ERROR "redeclared|no new|incompatible" は、この行で「再宣言された」「新しい変数がない」「互換性がない」のいずれかのエラーが発生することを期待しています。しかし、テストの本来の意図は「新しい変数が宣言されていない場合の再宣言エラー」を検証することでした。型不一致のエラーは、このテストケースの主要な目的ではありませんでした。

このコミットは、i := func() {i := func() int { return 0 } に変更することで、この問題を解決しています。 これにより、2行目の関数リテラルの型が明示的に func() int となります。もし f1func() int 型であれば、1行目と2行目の i の型は一致します。この場合、2行目は「既存の変数 i を同じ型で再宣言しようとしているが、新しい変数が他にない」という状況を作り出し、Goの短縮変数宣言のルールに則って「redeclared」または「no new variables」のエラーが正確に発生するようになります。これにより、テストが本来意図するエラーのみを検出し、「余分な型不一致エラー」が排除されます。

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

--- a/test/declbad.go
+++ b/test/declbad.go
@@ -41,7 +41,8 @@ func main() {
 	{\n \t\t// multiline no new variables\n \t\ti := f1\n-\t\ti := func() { // ERROR \"redeclared|no new|incompatible\"\n+\t\t\ti := func() int { // ERROR \"redeclared|no new|incompatible\"\n+\t\t\t\treturn 0\n \t\t}\n \t\t_ = i\n \t}\

コアとなるコードの解説

変更された行は以下の2行です。

  1. - i := func() { // ERROR "redeclared|no new|incompatible"

    • 変更前のコードです。i を引数なし、戻り値なしの関数リテラルで再宣言しようとしていました。この関数リテラルの型は func() です。
    • もし f1 の型が func() int など、func() と異なる型であった場合、この行は短縮変数宣言の「新しい変数が無い」エラーに加えて、型不一致のエラーも引き起こしていました。
  2. + i := func() int { // ERROR "redeclared|no new|incompatible" + return 0

    • 変更後のコードです。i を引数なし、int 型の戻り値を持つ関数リテラルで再宣言しようとしています。この関数リテラルの型は func() int です。
    • return 0 が追加され、関数リテラルが int を返すように明示されました。
    • この変更により、i の型が f1 の型(おそらく func() int)と一致する可能性が高まり、型不一致による「余分なエラー」が発生しなくなります。結果として、このテストケースは短縮変数宣言の「新しい変数が無い」ことによる再宣言エラーのみを正確に検証できるようになります。

この修正は、テストの意図を明確にし、コンパイラテストの信頼性を向上させるためのものです。

関連リンク

参考にした情報源リンク