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

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

このコミットは、Go言語の仕様書 doc/go_spec.html を更新し、式の評価順序に関する記述を明確にしています。具体的には、1つのファイルが変更され、11行が追加され、3行が削除されています。

コミット

commit f05a91e18b4a3e1e962e1013cbf6ab16e120d190
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Aug 9 11:50:16 2012 -0700

    spec: clarify evaluation order
    
    Fixes #3852.
    
    R=r, rsc, iant, ken
    CC=golang-dev
    https://golang.org/cl/6441102

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

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

元コミット内容

spec: clarify evaluation order
Fixes #3852.
R=r, rsc, iant, ken
CC=golang-dev
https://golang.org/cl/6441102

変更の背景

このコミットは、Go言語のIssue #3852「spec: evaluation order in assignments return statements」を解決するために行われました。このIssueは、Go言語の仕様において、代入文やreturn文における評価順序が不明確であったことに起因しています。特に、gcコンパイラとgccgoコンパイラの間で異なる挙動を示す可能性があり、これは仕様の曖昧さに起因するものでした。

プログラミング言語の仕様は、その言語の挙動を一貫して定義するものであり、異なるコンパイラ実装間での互換性を保証するために非常に重要です。評価順序の不明確さは、開発者がコードの挙動を予測することを困難にし、移植性の問題を引き起こす可能性があります。このコミットは、このような曖昧さを解消し、Go言語の挙動をより明確に定義することを目的としています。

前提知識の解説

評価順序 (Order of Evaluation)

プログラミング言語における「評価順序」とは、式や文に含まれる各要素(オペランド、関数呼び出し、演算子など)がどのような順番で評価されるかを定めた規則のことです。評価順序は、プログラムの最終的な結果に直接影響を与えるため、言語仕様において明確に定義されている必要があります。

Go言語では、いくつかの評価順序に関する規則があります。

  • レキシカルな左から右への評価: 関数呼び出し、メソッド呼び出し、および通信操作(チャネル操作)は、レキシカルな(コードに記述された)左から右の順序で評価されます。これは、これらの操作が副作用を持つ可能性があるため、その副作用の発生順序を予測可能にするために重要です。

  • オペランドの評価順序の非指定: しかし、Go言語の仕様では、式のオペランド代入文、またはreturn文におけるオペランドの評価順序は、一般的に指定されていません。これは、コンパイラが最適化のために評価順序を自由に選択できる余地を与えるためです。例えば、x + yという式において、xyのどちらが先に評価されるかは保証されません。

このコミットの背景にある問題は、特に後者の「オペランドの評価順序の非指定」に関するものでした。既存の仕様では、この点が十分に明確でなかったため、コンパイラの実装によって挙動が異なる可能性があったのです。

副作用 (Side Effects)

関数や式の評価が、その戻り値以外にプログラムの状態を変更する効果を「副作用」と呼びます。例えば、変数の値を変更する、I/O操作を行う、グローバルな状態を変更するなどが副作用にあたります。評価順序が不明確な場合、副作用を持つ操作がいつ実行されるか予測できなくなり、プログラムの挙動が非決定論的になる可能性があります。

技術的詳細

このコミットは、Go言語の公式仕様書である doc/go_spec.html を修正し、式の評価順序に関する記述をより厳密にしています。

変更の核心は、以下の点にあります。

  1. 評価順序の適用範囲の明確化: 以前の記述では、「代入や式の要素を評価する際、すべての関数呼び出し、メソッド呼び出し、通信操作はレキシカルな左から右の順序で評価される」とされていました。 今回の変更では、この規則が適用される対象を「式のオペランド、代入、またはreturn文を評価する際」と明確にしています。これにより、関数呼び出しなどが含まれる文脈がより広範にカバーされることが示されます。

  2. オペランド評価順序の非指定の強調: 最も重要な変更点は、以下の記述の追加です。 「しかし、式のオペランド、代入、またはreturn文のオペランドの評価順序は、指定されていません。」 この一文により、関数呼び出しや通信操作自体は左から右に評価されるものの、それらが含まれるより大きな式や文のオペランド全体の評価順序は保証されないことが明確にされました。

  3. 具体的なコード例の追加: この変更の意図を明確にするために、具体的なコード例が追加されました。

    a := 1
    f := func() int { a = 2; return 3 }
    x := []int{a, f()}  // x may be [1, 3] or [2, 3]: evaluation order between a and f() is not specified
    

    この例は、[]int{a, f()} という複合リテラルにおける af() の評価順序が指定されていないことを示しています。f()a の値を変更する副作用を持つため、af() の前に評価されるか、後に評価されるかによって、x の最初の要素が 1 になるか 2 になるかが変わります。この例は、Go言語のコンパイラがこれらのオペランドを任意の順序で評価できることを明確に示しています。

この修正により、Go言語のコンパイラ実装者は、式のオペランドの評価順序に関してより大きな自由度を持つことになります。同時に、Go言語のプログラマは、このような非指定の評価順序に依存しないコードを書くことの重要性を認識するよう促されます。

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

変更は doc/go_spec.html ファイルに対して行われました。

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
 <!--{
 	"Title": "The Go Programming Language Specification",
-	"Subtitle": "Version of July 11, 2012",
+	"Subtitle": "Version of August 9, 2012",
 	"Path": "/ref/spec"
 }-->
 
@@ -3694,8 +3694,10 @@ overflow etc. errors being caught.
 <h3 id="Order_of_evaluation">Order of evaluation</h3>
 
 <p>
-When evaluating the elements of an assignment or expression,
-all function calls, method calls and
+When evaluating the <a href="#Operands">operands</a> of an expression,
+<a href="#Assignments">assignment</a>, or
+<a href="#Return_statements">return statement</a>,
+all function calls, method calls, and
 communication operations are evaluated in lexical left-to-right
 order.
 </p>
@@ -3715,6 +3717,12 @@ and indexing of <code>x</code> and the evaluation
 of <code>y</code> is not specified.\n</p>\n \n+<pre>\n+a := 1\n+f := func() int { a = 2; return 3 }\n+x := []int{a, f()}  // x may be [1, 3] or [2, 3]: evaluation order between a and f() is not specified\n+</pre>\n+\n <p>\n Floating-point operations within a single expression are evaluated according to\n the associativity of the operators.  Explicit parentheses affect the evaluation\n```

主な変更点は以下の通りです。

1.  **仕様書のバージョン日付の更新**:
    `"Subtitle": "Version of July 11, 2012"` から `"Subtitle": "Version of August 9, 2012"` へ変更。

2.  **評価順序の記述の修正**:
    旧: `When evaluating the elements of an assignment or expression, all function calls, method calls and communication operations are evaluated in lexical left-to-right order.`
    新: `When evaluating the <a href="#Operands">operands</a> of an expression, <a href="#Assignments">assignment</a>, or <a href="#Return_statements">return statement</a>, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.`
    これにより、評価順序の規則が適用される文脈がより明確になりました。

3.  **オペランド評価順序の非指定に関する記述の追加**:
    既存の段落の後に、以下の重要な文が追加されました。
    `However, the order of evaluation of the operands of an expression, assignment, or return statement is not specified.`
    これは、関数呼び出しなどが左から右に評価される一方で、それらが含まれるより大きな式のオペランド全体の評価順序は保証されないことを明示しています。

4.  **具体的なコード例の追加**:
    上記の非指定の評価順序を説明するためのコード例が `<pre>` タグで囲まれて追加されました。
    ```go
    a := 1
    f := func() int { a = 2; return 3 }
    x := []int{a, f()}  // x may be [1, 3] or [2, 3]: evaluation order between a and f() is not specified
    ```

## コアとなるコードの解説

追加されたコード例は、Go言語における評価順序の非指定の挙動を具体的に示しています。

```go
a := 1
f := func() int { a = 2; return 3 }
x := []int{a, f()}  // x may be [1, 3] or [2, 3]: evaluation order between a and f() is not specified

この例では、以下の要素が含まれています。

  • a := 1: 整数変数 a1 で初期化します。
  • f := func() int { a = 2; return 3 }: 匿名関数 f を定義します。この関数は、実行されると変数 a の値を 2 に変更し、3 を返します。これは副作用を持つ関数です。
  • x := []int{a, f()}: 整数スライス x を初期化します。このスライスは2つの要素を持ち、1つ目は変数 a の値、2つ目は関数 f() の戻り値です。

コメント // x may be [1, 3] or [2, 3]: evaluation order between a and f() is not specified が示すように、[]int{a, f()} という複合リテラルにおいて、af() のどちらが先に評価されるかはGo言語の仕様によって保証されていません。

  • ケース1: a が先に評価される場合

    1. a の値 (1) が評価されます。
    2. f() が呼び出され、a2 に変更され、3 が返されます。
    3. 結果として x[1, 3] となります。
  • ケース2: f() が先に評価される場合

    1. f() が呼び出され、a2 に変更され、3 が返されます。
    2. a の値 (2) が評価されます。
    3. 結果として x[2, 3] となります。

この例は、Go言語のコンパイラがこれらのオペランドを任意の順序で評価できることを明確に示しており、プログラマはこのような非決定論的な挙動に依存するコードを書くべきではないという警告を含んでいます。これは、コンパイラがコードを最適化する際に、評価順序を柔軟に変更できる余地を与えるための重要な仕様です。

関連リンク

参考にした情報源リンク

  • Go Issue #3852: spec: evaluation order in assignments return statements - GitHub: https://github.com/golang/go/issues/3852
  • Go Programming Language Specification (August 9, 2012版): このコミットによって更新されたGo言語の仕様書。