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

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

このコミットは、Go言語のコンパイラにおけるgoおよびdeferステートメントの引数に関する特定の挙動をテストするためのものです。具体的には、これらのステートメントの引数が括弧で囲まれた式である場合の挙動を確認し、コンパイラが正しくエラーを報告するかどうかを検証しています。

コミット

test: add some tests where go/defer arg starts with parenthesis

R=gri
CC=golang-dev
https://golang.org/cl/6890047

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

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

元コミット内容

commit b46d56ae7215ca1f56f4556f76408bab26a85669
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Dec 5 20:32:12 2012 -0800

    test: add some tests where go/defer arg starts with parenthesis
    
    R=gri
    CC=golang-dev
    https://golang.org/cl/6890047
---
 test/fixedbugs/issue4468.go | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)

diff --git a/test/fixedbugs/issue4468.go b/test/fixedbugs/issue4468.go
index c9d7699e26..ef0b46bcf6 100644
--- a/test/fixedbugs/issue4468.go
+++ b/test/fixedbugs/issue4468.go
@@ -8,7 +8,21 @@
 
 package p
 
+type T int
+
+func (t *T) F() T {
+\treturn *t
+}\n+\n+type S struct {\n+\tt T\n+}\n+\n func F() {\
 \tgo (F())\t// ERROR \"must be function call\"\
 \tdefer (F())\t// ERROR \"must be function call\"\
+\tvar s S\n+\t(&s.t).F()\n+\tgo (&s.t).F()\n+\tdefer (&s.t).F()\
 }\

変更の背景

このコミットは、Go言語のコンパイラがgoおよびdeferステートメントの引数として与えられた式をどのように扱うかに関するテストを追加しています。特に、引数が括弧で囲まれている場合に、コンパイラが期待通りに「must be function call」(関数呼び出しでなければならない)というエラーを報告するか、あるいは正しくコンパイルされるべきケースを区別できるかを検証しています。

Go言語では、godeferの後に続くのは関数呼び出しでなければなりません。しかし、Goのパーサーは、式が括弧で囲まれている場合でも、それが関数呼び出しであるかどうかを正しく識別する必要があります。このコミットは、このようなエッジケースに対するコンパイラの堅牢性を確保するためのものです。

元の問題は、Go issue 4468として報告されており、godeferの引数として括弧で囲まれた式が与えられた場合に、コンパイラが誤ったエラーを報告したり、あるいは本来エラーであるべきものを許容したりする可能性があったため、その修正と検証のためにテストが追加されました。

前提知識の解説

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

goステートメントは、Go言語におけるゴルーチン(goroutine)の起動に使用されます。ゴルーチンは軽量な並行実行単位であり、Goランタイムによって管理されます。goキーワードの後に続く関数呼び出しは、新しいゴルーチン内で非同期に実行されます。

例:

func myFunc() {
    // ...
}

func main() {
    go myFunc() // myFuncが新しいゴルーチンで実行される
}

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

deferステートメントは、そのステートメントを含む関数がリターンする直前に、指定された関数呼び出しを遅延実行するために使用されます。これは、リソースの解放(ファイルのクローズ、ロックの解除など)やエラーハンドリングのクリーンアップ処理によく利用されます。deferされた関数は、LIFO(後入れ先出し)の順序で実行されます。

例:

func readFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close() // 関数が終了する直前にファイルがクローズされる
    // ... ファイルの読み込み処理
}

Go言語における関数呼び出しと式の評価

Go言語では、関数呼び出しは特定の構文に従います。godeferの後に続くのは、関数名とその引数リストからなる「関数呼び出し」の形式でなければなりません。

例えば、F()は関数呼び出しですが、(F())のように括弧で囲まれた式は、Goのパーシングの文脈では単なる「式」として扱われることがあります。しかし、godeferの文脈では、その式が最終的に関数呼び出しに評価されるものでなければなりません。

このコミットでテストされているのは、コンパイラが(F())のような括弧で囲まれた式がgodeferの引数として不適切である(なぜなら、F()の結果は関数ではないため)と正しく判断し、「must be function call」というエラーを出すかどうかです。

一方で、(&s.t).F()のような式は、メソッド呼び出しであり、これは有効な関数呼び出しです。この場合、コンパイラはエラーを出すべきではありません。このコミットは、これらのケースを区別できることを確認しています。

ERROR "must be function call"

これはGoコンパイラが生成するエラーメッセージの一つで、godeferステートメントの後に続く式が関数呼び出しの形式になっていない場合に表示されます。例えば、go 1 + 2のようなコードは、1 + 2が関数呼び出しではないため、このエラーになります。

技術的詳細

このコミットは、Goコンパイラのパーサーと型チェッカーが、goおよびdeferキーワードに続く式の構文解析と意味解析をどのように行うかに関するものです。

Go言語の仕様では、goおよびdeferステートメントの後に続くのはFunctionCallでなければなりません。FunctionCallは、PrimaryExpr(プライマリ式)の後にArguments(引数)が続く形式です。PrimaryExprには、識別子、リテラル、関数呼び出し、メソッド呼び出し、型変換、括弧で囲まれた式などが含まれます。

問題となるのは、PrimaryExprが括弧で囲まれた式である場合です。例えば、(F())という式は、F()という関数呼び出しの結果を括弧で囲んだものです。F()が関数を返すのであれば、(F())()のようにさらに呼び出すことができますが、F()が非関数値を返す場合、(F())は非関数値の式となります。

このコミットで追加されたテストケースは、以下のシナリオを検証しています。

  1. go (F()) および defer (F()):

    • F()は関数ですが、その戻り値はfunc F() {}のようにvoid(Goでは())です。
    • F()の戻り値は関数ではないため、(F())という式全体は関数呼び出し可能な値ではありません。
    • したがって、godeferの引数としては不適切であり、コンパイラは「must be function call」というエラーを報告する必要があります。これは、コンパイラが括弧の中の式を評価し、その結果が関数呼び出し可能であるかをチェックしていることを示します。
  2. (&s.t).F():

    • sS型の変数で、St Tというフィールドを持ちます。Tint型です。
    • T型にはポインタレシーバを持つメソッドF()が定義されています。
    • &s.t*T型のポインタ式です。
    • (&s.t).F()は、ポインタレシーバを持つメソッドFの呼び出しです。これは有効な関数呼び出しです。
    • したがって、この式は単独で実行される場合も、godeferの引数として使用される場合も、コンパイラはエラーを報告すべきではありません。これは、コンパイラがメソッド呼び出しを正しく識別し、それがgodeferの引数として適切であると判断していることを示します。

このテストの追加は、Goコンパイラが複雑な式、特に括弧やポインタ、構造体フィールドアクセスを含むメソッド呼び出しの文脈において、goおよびdeferステートメントの引数として適切な関数呼び出しを正確に識別できることを保証するために重要です。これにより、コンパイラの堅牢性と、Go言語のセマンティクスへの厳密な準拠が向上します。

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

diff --git a/test/fixedbugs/issue4468.go b/test/fixedbugs/issue4468.go
index c9d7699e26..ef0b46bcf6 100644
--- a/test/fixedbugs/issue4468.go
+++ b/test/fixedbugs/issue4468.go
@@ -8,7 +8,21 @@
 
 package p
 
+type T int
+
+func (t *T) F() T {
+\treturn *t
+}\n+\n+type S struct {\
+\tt T\n+}\n+\n func F() {\
 \tgo (F())\t// ERROR \"must be function call\"\
 \tdefer (F())\t// ERROR \"must be function call\"\
+\tvar s S\n+\t(&s.t).F()\n+\tgo (&s.t).F()\n+\tdefer (&s.t).F()\
 }\

コアとなるコードの解説

このコミットは、test/fixedbugs/issue4468.goファイルに以下の変更を加えています。

  1. 新しい型の定義:

    • type T int: 整数型intを基底とする新しい型Tを定義しています。
    • func (t *T) F() T { return *t }: 型Tのポインタレシーバを持つメソッドFを定義しています。このメソッドはT型の値を返し、レシーバの指す値をデリファレンスして返します。
    • type S struct { t T }: 型Tのフィールドtを持つ構造体Sを定義しています。
  2. F()関数の変更: 既存のF()関数内に新しいテストケースが追加されています。

    • go (F()) // ERROR "must be function call":

      • これは、goステートメントの引数として、括弧で囲まれたF()の呼び出し結果を渡しています。
      • F()関数自体はvoid(Goでは())を返すため、その結果は関数ではありません。
      • したがって、コンパイラは「must be function call」というエラーを報告することが期待されます。この行のコメント// ERROR "must be function call"は、その期待されるエラーメッセージを示しています。
    • defer (F()) // ERROR "must be function call":

      • 上記と同様に、deferステートメントの引数として、括弧で囲まれたF()の呼び出し結果を渡しています。
      • これも同様に、F()の戻り値が関数ではないため、コンパイラは「must be function call」というエラーを報告することが期待されます。
    • var s S: 構造体Sの変数sを宣言しています。

    • (&s.t).F():

      • s.tSのフィールドt(型T)にアクセスします。
      • &s.ttのアドレス(型*T)を取得します。
      • (&s.t).F()は、*T型のポインタレシーバを持つメソッドF()を呼び出しています。これは有効なメソッド呼び出しであり、コンパイラはエラーを報告すべきではありません。この行は、コンパイラが有効なメソッド呼び出しを正しく認識できることをテストしています。
    • go (&s.t).F():

      • 有効なメソッド呼び出し(&s.t).F()を新しいゴルーチンで実行しています。これは正しくコンパイルされるべきです。
    • defer (&s.t).F():

      • 有効なメソッド呼び出し(&s.t).F()を関数終了時に遅延実行しています。これも正しくコンパイルされるべきです。

これらの追加されたテストケースは、Goコンパイラがgoおよびdeferステートメントの引数として与えられた式を、その構文(括弧の有無)や意味(関数呼び出し可能かどうか)に基づいて正確に検証できることを保証するために重要です。特に、括弧で囲まれた式が関数呼び出しではない場合にエラーを出すこと、そして有効なメソッド呼び出しを正しく認識してコンパイルすることの両方をテストしています。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語のコンパイラにおけるgoおよびdeferステートメントの引数に関する特定の挙動をテストするためのものです。具体的には、これらのステートメントの引数が括弧で囲まれた式である場合の挙動を確認し、コンパイラが正しくエラーを報告するかどうかを検証しています。

コミット

test: add some tests where go/defer arg starts with parenthesis

R=gri
CC=golang-dev
https://golang.org/cl/6890047

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

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

元コミット内容

commit b46d56ae7215ca1f56f4556f76408bab26a85669
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Dec 5 20:32:12 2012 -0800

    test: add some tests where go/defer arg starts with parenthesis
    
    R=gri
    CC=golang-dev
    https://golang.org/cl/6890047
---
 test/fixedbugs/issue4468.go | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)

diff --git a/test/fixedbugs/issue4468.go b/test/fixedbugs/issue4468.go
index c9d7699e26..ef0b46bcf6 100644
--- a/test/fixedbugs/issue4468.go
+++ b/test/fixedbugs/issue4468.go
@@ -8,7 +8,21 @@
 
 package p
 
+type T int
+
+func (t *T) F() T {
+\treturn *t
+}\n+\n+type S struct {\
+\tt T\n+}\n+\n func F() {\
 \tgo (F())\t// ERROR \"must be function call\"\
 \tdefer (F())\t// ERROR \"must be function call\"\
+\tvar s S\n+\t(&s.t).F()\n+\tgo (&s.t).F()\n+\tdefer (&s.t).F()\
 }\

変更の背景

このコミットは、Go言語のコンパイラがgoおよびdeferステートメントの引数として与えられた式をどのように扱うかに関するテストを追加しています。特に、引数が括弧で囲まれている場合に、コンパイラが期待通りに「must be function call」(関数呼び出しでなければならない)というエラーを報告するか、あるいは正しくコンパイルされるべきケースを区別できるかを検証しています。

Go言語では、godeferの後に続くのは関数呼び出しでなければなりません。しかし、Goのパーサーは、式が括弧で囲まれている場合でも、それが関数呼び出しであるかどうかを正しく識別する必要があります。このコミットは、このようなエッジケースに対するコンパイラの堅牢性を確保するためのものです。

元の問題は、Go issue 4468として報告されており、godeferの引数として括弧で囲まれた式が与えられた場合に、コンパイラが誤ったエラーを報告したり、あるいは本来エラーであるべきものを許容したりする可能性があったため、その修正と検証のためにテストが追加されました。

前提知識の解説

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

goステートメントは、Go言語におけるゴルーチン(goroutine)の起動に使用されます。ゴルーチンは軽量な並行実行単位であり、Goランタイムによって管理されます。goキーワードの後に続く関数呼び出しは、新しいゴルーチン内で非同期に実行されます。

例:

func myFunc() {
    // ...
}

func main() {
    go myFunc() // myFuncが新しいゴルーチンで実行される
}

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

deferステートメントは、そのステートメントを含む関数がリターンする直前に、指定された関数呼び出しを遅延実行するために使用されます。これは、リソースの解放(ファイルのクローズ、ロックの解除など)やエラーハンドリングのクリーンアップ処理によく利用されます。deferされた関数は、LIFO(後入れ先出し)の順序で実行されます。

例:

func readFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close() // 関数が終了する直前にファイルがクローズされる
    // ... ファイルの読み込み処理
}

Go言語における関数呼び出しと式の評価

Go言語では、関数呼び出しは特定の構文に従います。godeferの後に続くのは、関数名とその引数リストからなる「関数呼び出し」の形式でなければなりません。

例えば、F()は関数呼び出しですが、(F())のように括弧で囲まれた式は、Goのパーシングの文脈では単なる「式」として扱われることがあります。しかし、godeferの文脈では、その式が最終的に関数呼び出しに評価されるものでなければなりません。

このコミットでテストされているのは、コンパイラが(F())のような括弧で囲まれた式がgodeferの引数として不適切である(なぜなら、F()の結果は関数ではないため)と正しく判断し、「must be function call」というエラーを出すかどうかです。

一方で、(&s.t).F()のような式は、メソッド呼び出しであり、これは有効な関数呼び出しです。この場合、コンパイラはエラーを出すべきではありません。このコミットは、これらのケースを区別できることを確認しています。

ERROR "must be function call"

これはGoコンパイラが生成するエラーメッセージの一つで、godeferステートメントの後に続く式が関数呼び出しの形式になっていない場合に表示されます。例えば、go 1 + 2のようなコードは、1 + 2が関数呼び出しではないため、このエラーになります。

技術的詳細

このコミットは、Goコンパイラのパーサーと型チェッカーが、goおよびdeferキーワードに続く式の構文解析と意味解析をどのように行うかに関するものです。

Go言語の仕様では、goおよびdeferステートメントの後に続くのはFunctionCallでなければなりません。FunctionCallは、PrimaryExpr(プライマリ式)の後にArguments(引数)が続く形式です。PrimaryExprには、識別子、リテラル、関数呼び出し、メソッド呼び出し、型変換、括弧で囲まれた式などが含まれます。

問題となるのは、PrimaryExprが括弧で囲まれた式である場合です。例えば、(F())という式は、F()という関数呼び出しの結果を括弧で囲んだものです。F()が関数を返すのであれば、(F())()のようにさらに呼び出すことができますが、F()が非関数値を返す場合、(F())は非関数値の式となります。

このコミットで追加されたテストケースは、以下のシナリオを検証しています。

  1. go (F()) および defer (F()):

    • F()は関数ですが、その戻り値はfunc F() {}のようにvoid(Goでは())です。
    • F()の戻り値は関数ではないため、(F())という式全体は関数呼び出し可能な値ではありません。
    • したがって、godeferの引数としては不適切であり、コンパイラは「must be function call」というエラーを報告する必要があります。これは、コンパイラが括弧の中の式を評価し、その結果が関数呼び出し可能であるかをチェックしていることを示します。
  2. (&s.t).F():

    • sS型の変数で、St Tというフィールドを持ちます。Tint型です。
    • T型にはポインタレシーバを持つメソッドF()が定義されています。
    • &s.t*T型のポインタ式です。
    • (&s.t).F()は、ポインタレシーバを持つメソッドFの呼び出しです。これは有効な関数呼び出しです。
    • したがって、この式は単独で実行される場合も、godeferの引数として使用される場合も、コンパイラはエラーを報告すべきではありません。これは、コンパイラがメソッド呼び出しを正しく識別し、それがgodeferの引数として適切であると判断していることを示します。

このテストの追加は、Goコンパイラが複雑な式、特に括弧やポインタ、構造体フィールドアクセスを含むメソッド呼び出しの文脈において、goおよびdeferステートメントの引数として適切な関数呼び出しを正確に識別できることを保証するために重要です。これにより、コンパイラの堅牢性と、Go言語のセマンティクスへの厳密な準拠が向上します。

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

diff --git a/test/fixedbugs/issue4468.go b/test/fixedbugs/issue4468.go
index c9d7699e26..ef0b46bcf6 100644
--- a/test/fixedbugs/issue4468.go
+++ b/test/fixedbugs/issue4468.go
@@ -8,7 +8,21 @@
 
 package p
 
+type T int
+
+func (t *T) F() T {
+\treturn *t
+}\n+\n+type S struct {\
+\tt T\n+}\n+\n func F() {\
 \tgo (F())\t// ERROR \"must be function call\"\
 \tdefer (F())\t// ERROR \"must be function call\"\
+\tvar s S\n+\t(&s.t).F()\n+\tgo (&s.t).F()\n+\tdefer (&s.t).F()\
 }\

コアとなるコードの解説

このコミットは、test/fixedbugs/issue4468.goファイルに以下の変更を加えています。

  1. 新しい型の定義:

    • type T int: 整数型intを基底とする新しい型Tを定義しています。
    • func (t *T) F() T { return *t }: 型Tのポインタレシーバを持つメソッドFを定義しています。このメソッドはT型の値を返し、レシーバの指す値をデリファレンスして返します。
    • type S struct { t T }: 型Tのフィールドtを持つ構造体Sを定義しています。
  2. F()関数の変更: 既存のF()関数内に新しいテストケースが追加されています。

    • go (F()) // ERROR "must be function call":

      • これは、goステートメントの引数として、括弧で囲まれたF()の呼び出し結果を渡しています。
      • F()関数自体はvoid(Goでは())を返すため、その結果は関数ではありません。
      • したがって、コンパイラは「must be function call」というエラーを報告することが期待されます。この行のコメント// ERROR "must be function call"は、その期待されるエラーメッセージを示しています。
    • defer (F()) // ERROR "must be function call":

      • 上記と同様に、deferステートメントの引数として、括弧で囲まれたF()の呼び出し結果を渡しています。
      • これも同様に、F()の戻り値が関数ではないため、コンパイラは「must be function call」というエラーを報告することが期待されます。
    • var s S: 構造体Sの変数sを宣言しています。

    • (&s.t).F():

      • s.tSのフィールドt(型T)にアクセスします。
      • &s.ttのアドレス(型*T)を取得します。
      • (&s.t).F()は、*T型のポインタレシーバを持つメソッドF()を呼び出しています。これは有効なメソッド呼び出しであり、コンパイラはエラーを報告すべきではありません。この行は、コンパイラが有効なメソッド呼び出しを正しく認識できることをテストしています。
    • go (&s.t).F():

      • 有効なメソッド呼び出し(&s.t).F()を新しいゴルーチンで実行しています。これは正しくコンパイルされるべきです。
    • defer (&s.t).F():

      • 有効なメソッド呼び出し(&s.t).F()を関数終了時に遅延実行しています。これも正しくコンパイルされるべきです。

これらの追加されたテストケースは、Goコンパイラがgoおよびdeferステートメントの引数として与えられた式を、その構文(括弧の有無)や意味(関数呼び出し可能かどうか)に基づいて正確に検証できることを保証するために重要です。特に、括弧で囲まれた式が関数呼び出しではない場合にエラーを出すこと、そして有効なメソッド呼び出しを正しく認識してコンパイルすることの両方をテストしています。

関連リンク

参考にした情報源リンク