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

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

このコミットは、Go言語の仕様書 (doc/go_spec.html) に、型付き定数 (typed constants) の挙動、特に数値の範囲外エラーと単項ビット反転演算子 (^) の適用に関する説明を追加するものです。

コミット

commit 21d03496e7b6e7c32a0b6f5a76abab0c9e9c086b
Author: Rob Pike <r@golang.org>
Date:   Tue Mar 24 19:16:42 2009 -0700

    add some words (written by rsc) about the state of typed constants.
    
    DELTA=31  (31 added, 0 deleted, 0 changed)
    OCL=26709
    CL=26716

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

https://github.com/golang/go/commit/21d03496e7b6e7c32a0b6f5a76abab0c9e9c086b

元コミット内容

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -2950,6 +2950,37 @@ floating point variable, while <code>-1e12</code> can be assigned to a
 but not <code>uint64</code> or <code>string</code>.
 </p>
 \n
+<p>
+If a typed constant expression evaluates to a value that is not
+representable by that type, the compiler reports an error.\n
+</p>
+\n
+<pre>
+uint8(-1)         // error, out of range
+uint8(100) * 100  // error, out of range
+</pre>
+\n
+<p>
+The size of the mask used by the unary bitwise complement
+operator in a typed constant expression is equal to the size of the
+expression\'s type.  In an ideal constant expression, the bitwise
+complement operator inverts all the bits, producing a negative value.\n
+</p>
+\n
+<pre>
+^1          // ideal constant, equal to -2
+uint8(^1)   // error, same as uint8(-2), out of range
+^uint8(1)   // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
+int8(^1)    // same as int8(-2)
+^int8(1)    // error, same as 0xFF ^ int8(1) = int8(0xFE), out of range
+</pre>
+\n
+<p>
+TODO: perhaps ^ should be disallowed on non-uints instead of assuming twos complement.\n
+Also it may be possible to make typed constants more like variables, at the cost of fewer\n
+overflow etc. errors being caught.\n
+</p>
+\n
 <hr/>
 \n
 <h2>Statements</h2>

変更の背景

このコミットは、Go言語の初期段階において、定数、特に型付き定数 (typed constants) の挙動に関する仕様を明確化するために行われました。Go言語の定数システムは、他の多くの言語とは異なり、「型なし定数 (untyped constants)」または「理想定数 (ideal constants)」という概念を持っています。これにより、定数はより柔軟に型推論され、必要に応じて特定の型に変換されます。しかし、この柔軟性が、特定の操作、特にビット演算や型変換において、開発者にとって直感的でない挙動を引き起こす可能性がありました。

このコミットは、以下の点を明確にすることを目的としています。

  1. 型付き定数の範囲外エラー: 型付き定数式がその型の表現可能な範囲を超える値になった場合に、コンパイラがエラーを報告すること。
  2. 単項ビット反転演算子 (^) の挙動: 型なし定数と型付き定数で ^ 演算子がどのように異なる結果をもたらすか。特に、型なし定数ではビット反転が負の値を生み出すこと、そして型付き定数ではその型のビット幅に応じたマスクが適用されること。

これらの説明を追加することで、Go言語の定数システムの理解を深め、予期せぬバグを防ぐことを意図しています。

前提知識の解説

Go言語の定数

Go言語には、他の多くのプログラミング言語とは異なる独自の定数システムがあります。

  • 型なし定数 (Untyped Constants) / 理想定数 (Ideal Constants): Goの定数は、デフォルトでは「型なし」です。これは、定数が特定のGoの型(int, float64, stringなど)に関連付けられていないことを意味します。型なし定数は、その値が表現できる限り、任意の数値型として振る舞うことができます。例えば、const x = 100x は型なし定数であり、int, int32, float64 など、様々な数値型に代入できます。コンパイラは、代入や演算の文脈に基づいて、適切な型を推論します。これにより、数値のオーバーフローエラーを早期に検出できるという利点があります。

  • 型付き定数 (Typed Constants): 定数を明示的に型付けすることも可能です。例えば、const x int8 = 100xint8 型の型付き定数です。型付き定数は、その宣言された型に厳密に従います。

単項ビット反転演算子 (^)

Go言語における単項ビット反転演算子 (^) は、オペランドのすべてのビットを反転させます。

  • 整数型に対する挙動: 整数型の場合、^xx のビットを反転させます。符号なし整数 (unsigned integers) の場合、これは単純なビット反転です。符号付き整数 (signed integers) の場合、Goは2の補数表現を使用するため、ビット反転は負の値を生み出す可能性があります。

2の補数表現

コンピュータが負の数を表現する一般的な方法です。最上位ビット (MSB) が符号ビットとして機能し、0は正、1は負を示します。負の数は、対応する正の数のビットを反転させ(1の補数)、それに1を加えることで得られます。

技術的詳細

このコミットで追加された内容は、Go言語の定数システムの重要な側面を説明しています。

  1. 型付き定数の範囲チェック: Goコンパイラは、型付き定数式がその型の表現可能な範囲を超えた場合にエラーを報告します。これは、コンパイル時に型安全性を確保するための重要な機能です。 例:

    uint8(-1)         // エラー: uint8型は負の数を表現できない
    uint8(100) * 100  // エラー: 100 * 100 = 10000 は uint8 (0-255) の範囲を超える
    

    これは、型なし定数が柔軟な型推論を行うのに対し、型付き定数は厳密な型チェックを受けることを示しています。

  2. 単項ビット反転演算子 (^) の挙動:

    • 理想定数 (型なし定数) の場合: ^1 のような理想定数に対するビット反転は、その値のすべてのビットを反転させます。Goの理想定数は、必要に応じて任意のビット幅を持つことができるため、この操作は概念的に無限のビット幅で行われます。結果として、^1-2 となります。これは、2の補数表現において 1 (0...01) のビットをすべて反転させると ...1110 となり、これが -2 を表すためです。

      ^1          // 理想定数、-2 に等しい
      
    • 型付き定数 (Typed Constants) の場合: 型付き定数に対する ^ 演算子は、その定数の型が持つビット幅に応じたマスクを使用してビット反転を行います。これは、その型の最大値(すべてのビットが1)とのXOR演算に相当します。 例:

      ^uint8(1)   // 型付き uint8 定数。0xFF ^ uint8(1) = uint8(0xFE) に等しい
      

      uint8 は8ビットの符号なし整数なので、100000001 です。これをビット反転すると 11111110 となり、これは 0xFE (254) に相当します。

      また、型付き定数に理想定数のビット反転結果を適用しようとすると、範囲外エラーが発生する可能性があります。

      uint8(^1)   // エラー: uint8(-2) と同じで、範囲外
      

      これは、^1 が理想定数として -2 に評価され、その -2uint8 型に変換しようとすると、uint8 が負の数を表現できないためエラーとなることを示しています。

      符号付き整数型の場合も同様です。

      ^int8(1)    // エラー: 0xFF ^ int8(1) = int8(0xFE) と同じで、範囲外
      

      int8 は8ビットの符号付き整数です。100000001 です。これをビット反転すると 11111110 となり、これは int8 の範囲で -2 を表します。しかし、Goの仕様では、型付き定数に対する ^ 演算は、その型のビット幅に応じたマスク(つまり、その型のすべてのビットが1である値)とのXORとして定義されています。int80xFF11111111 です。11111111 ^ 00000001 = 11111110 となり、これは -2 です。この例では、int8(^1)^int8(1) の両方が -2 に評価されるため、int8 の範囲内であり、エラーにはなりません。しかし、元のコミットのコメントでは ^int8(1) がエラーとされています。これは、初期のGoの仕様がまだ固まっていない時期の記述であり、後のGoの仕様では ^int8(1)-2 となりエラーにはなりません。このコミットの時点では、0xFFint8 の範囲外であるという解釈があったのかもしれません。

  3. TODOコメント: コミットには以下の TODO コメントが含まれています。

    TODO: perhaps ^ should be disallowed on non-uints instead of assuming twos complement.
    Also it may be possible to make typed constants more like variables, at the cost of fewer
    overflow etc. errors being caught.
    

    これは、^ 演算子が符号なし整数 (uints) 以外に適用される場合に、2の補数を仮定するのではなく、禁止すべきではないかという議論を示唆しています。また、型付き定数を変数のように振る舞わせることで、オーバーフローなどのエラー検出が少なくなる可能性についても言及しています。これは、Go言語の定数システムの設計における初期の検討事項やトレードオフを示しており、最終的なGoの仕様では、^ 演算子は符号付き整数にも適用可能であり、2の補数表現に基づいて動作します。

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

変更は doc/go_spec.html ファイルに対して行われました。これはGo言語の公式仕様書の一部です。

具体的には、floating point variable, while -1e12 can be assigned to a but not uint64 or string. の段落の後に、以下の内容が追加されています。

  • 型付き定数式の範囲外エラーに関する説明と例。
  • 単項ビット反転演算子 (^) の理想定数と型付き定数における挙動の説明と例。
  • 将来的な検討事項を示す TODO コメント。

コアとなるコードの解説

追加されたHTMLスニペットは、Go言語の定数に関する仕様を文書化しています。

  • <p>If a typed constant expression evaluates to a value that is not representable by that type, the compiler reports an error.</p> これは、型付き定数の厳密な型チェックの原則を述べています。

  • <pre>uint8(-1) // error, out of range\nuint8(100) * 100 // error, out of range</pre> 具体的なコード例で、型付き定数における範囲外エラーを示しています。

  • <p>The size of the mask used by the unary bitwise complement operator in a typed constant expression is equal to the size of the expression\'s type. In an ideal constant expression, the bitwise complement operator inverts all the bits, producing a negative value.</p> ^ 演算子の挙動の核心を説明しています。型付き定数では型のビット幅に応じたマスクが使われ、理想定数ではすべてのビットが反転し負の値になることを明確にしています。

  • <pre>^1 // ideal constant, equal to -2\nuint8(^1) // error, same as uint8(-2), out of range\n^uint8(1) // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)\nint8(^1) // same as int8(-2)\n^int8(1) // error, same as 0xFF ^ int8(1) = int8(0xFE), out of range</pre> 様々なケースでの ^ 演算子の結果を具体例で示しています。特に、理想定数と型付き定数での挙動の違い、そして型変換時の範囲外エラーの発生を示しています。

  • <p>TODO: perhaps ^ should be disallowed on non-uints instead of assuming twos complement.\nAlso it may be possible to make typed constants more like variables, at the cost of fewer\noverflow etc. errors being caught.</p> Go言語の設計における初期の議論や将来的な改善の可能性を示唆するメモです。

これらの追加は、Go言語の定数システムの複雑な側面を開発者が理解し、正しく利用するための重要なドキュメントです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • コミットメッセージと差分
  • Go言語の定数に関する一般的な知識
  • 2の補数表現に関する一般的な知識