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

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

このコミットは、Go言語の仕様書(doc/go_spec.html)から、シフト演算子における「型なし定数(untyped constant)」の特別な型推論ルールを削除するものです。このルールは、Goコンパイラ(gc)、gccgogo/typesといった主要なGoツールチェインの実装間で一貫して守られておらず、特にgo/typesがこのルールを実装していなかったにもかかわらず、標準ライブラリ全体を問題なくコンパイル・型チェックできていたため、仕様から削除されました。これにより、Go言語のシフト演算子の型推論がよりシンプルかつ一貫性のあるものになります。

コミット

commit 58e21ddaf9498462acb5c552c48e0c52073e1db3
Author: Robert Griesemer <gri@golang.org>
Date:   Fri Mar 15 13:55:50 2013 -0700

    spec: remove special int rule for shifts
    
    The rule is not concistently followed by gc.
    It appears that gccgo is ignoring it. go/types
    does not implement this rule. However, both
    gccgo and now go/types can compile/type-check
    the entire std library (and thus all the shift
    expressions occuring in it) w/o errors. For
    more details see the discussion in issue 4883.
    
    Fixes #4880.
    Fixes #4881.
    Fixes #4883.
    
    R=rsc, r, iant, ken, ken, mtj, rogpeppe
    CC=golang-dev
    https://golang.org/cl/7707043

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

https://github.com/golang/go/commit/58e21ddaf9498462acb5c552c48e0c52073e1db3

元コミット内容

spec: remove special int rule for shifts

The rule is not concistently followed by gc.
It appears that gccgo is ignoring it. go/types
does not implement this rule. However, both
gccgo and now go/types can compile/type-check
the entire std library (and thus all the shift
expressions occuring in it) w/o errors. For
more details see the discussion in issue 4883.

Fixes #4880.
Fixes #4881.
Fixes #4883.

R=rsc, r, iant, ken, ken, mtj, rogpeppe
CC=golang-dev
https://golang.org/cl/7707043

変更の背景

この変更の背景には、Go言語のシフト演算子における型推論のルールが、実際のコンパイラ実装(gcgccgo)や型チェッカー(go/types)と乖離していたという問題があります。

Go言語では、数値リテラルなどの「型なし定数」が、その使用される文脈によって型が推論されます。シフト演算子の場合、左オペランドが型なし定数である場合、その定数の型はシフト式がその左オペランド単独で置き換えられた場合の型になると規定されていました。さらに、もし文脈から型が決定できない場合(例えば、型なし定数との比較におけるシフト式の場合など)、その型はintになるとする特別なルールが存在していました。

しかし、この「文脈から型が決定できない場合にintになる」という特別なルールが、Go言語の主要なコンパイラであるgcによって一貫して守られていませんでした。また、gccgo(GCCベースのGoコンパイラ)もこのルールを無視しているように見えました。さらに重要なのは、go/types(Goの型システムを実装したライブラリ)がこのルールを実装していなかったにもかかわらず、Goの標準ライブラリ全体をエラーなくコンパイル・型チェックできていたという事実です。

この状況は、仕様と実装の間に不整合があることを示しており、開発者にとって混乱の原因となる可能性がありました。特に、go/typesがこのルールなしで標準ライブラリを処理できるということは、この特別なルールが実際には不要であるか、あるいはその適用範囲が非常に限定的であることを示唆していました。

これらの問題は、Issue 4883で議論され、その結果として、この特別なintルールを仕様から削除することが決定されました。これにより、Go言語の型システムがよりシンプルになり、異なるツールチェイン間での一貫性が向上することが期待されました。

前提知識の解説

Go言語のシフト演算子 (<<, >>)

Go言語におけるシフト演算子 (<< は左シフト、>> は右シフト) は、ビット単位の操作を行います。

  • 左シフト (<<): オペランドのビットを左に指定された数だけ移動させます。これは通常、2のべき乗を掛けることと同じ効果を持ちます。 例: 1 << 31 * 2^3 = 8 となります。
  • 右シフト (>>): オペランドのビットを右に指定された数だけ移動させます。これは通常、2のべき乗で割ることと同じ効果を持ちます。 例: 8 >> 38 / 2^3 = 1 となります。

シフト演算子の右オペランド(シフト量)は、符号なし整数型であるか、符号なし整数型に変換可能な型なし定数である必要があります。

型なし定数 (Untyped Constants)

Go言語には、型が明示的に指定されていない数値リテラルやブールリテラルなどの「型なし定数」という概念があります。例えば、13.14trueなどは型なし定数です。

型なし定数は、その値が使用される文脈(代入先の変数型、演算の相手の型など)に基づいて型が推論されます。この柔軟性により、Goのコードはより簡潔に記述できます。

例:

var i int = 1 // 1 は int 型に推論される
var f float64 = 3.14 // 3.14 は float64 型に推論される

型推論 (Type Inference)

Go言語の型推論は、変数の宣言時や式の中で、コンパイラが自動的に型を決定する機能です。これにより、開発者は冗長な型宣言を省略できます。

シフト演算子における型推論の特別なルールは、左オペランドが型なし定数である場合に適用されていました。このルールは、シフト式の型が、その左オペランド単独の型によって決定されるというものでした。そして、もし文脈から型が決定できない場合にint型に推論されるという、今回のコミットで削除された部分がありました。

gc, gccgo, go/types

  • gc: Go言語の公式コンパイラであり、Goツールチェインの主要な部分です。Goソースコードを機械語にコンパイルします。
  • gccgo: GCC (GNU Compiler Collection) をバックエンドとして使用するGoコンパイラです。gcとは異なる実装ですが、Go言語の仕様に準拠することを目指しています。
  • go/types: Go言語の型システムを実装したライブラリです。主にGoのソースコードを解析し、型情報を抽出・検証するために使用されます。IDEやリンターなどのツールで利用されます。

これらのツールがGo言語の仕様に準拠していることが、Goエコシステムの一貫性と健全性を保つ上で非常に重要です。今回のコミットは、これらのツール間での仕様解釈の不一致を解消し、一貫性を高めることを目的としています。

技術的詳細

このコミットは、Go言語の仕様書(doc/go_spec.html)におけるシフト演算子の型推論に関する特定の記述を修正しています。具体的には、以下のルールが削除されました。

「もしシフト式がその左オペランド単独で置き換えられた場合の型が、文脈から決定できない場合(例えば、シフト式が型なし定数との比較におけるオペランドである場合など)、その型はintである。」

このルールは、シフト演算子の左オペランドが型なし定数である場合に適用されるものでした。Go言語では、型なし定数はその使用される文脈によって型が推論されますが、この特別なルールは、特定の曖昧な状況下で型なし定数が強制的にint型に推論されることを意味していました。

コミットメッセージが示すように、このルールは以下の点で問題がありました。

  1. gcとの不整合: Goの公式コンパイラであるgcが、このルールを一貫して守っていませんでした。これは、仕様と実装の間に乖離があることを意味します。
  2. gccgoの無視: gccgoもこのルールを無視しているように見えました。異なるコンパイラが同じ仕様に対して異なる振る舞いをすることは、Goプログラムの移植性や予測可能性を損なう可能性があります。
  3. go/typesの非実装と成功: go/typesライブラリは、この特別なintルールを実装していませんでした。にもかかわらず、go/typesはGoの標準ライブラリ全体に含まれるすべてのシフト式をエラーなくコンパイル・型チェックできていました。これは、このルールが実際には不要であるか、あるいはその適用範囲が非常に限定的であり、実用上問題を引き起こさないことを強く示唆しています。

これらの理由から、この特別なintルールはGo言語の仕様から削除されることになりました。これにより、シフト演算子における型推論のロジックが簡素化され、より予測可能で一貫性のある振る舞いが期待されます。

具体的には、シフト演算子の左オペランドが型なし定数である場合、その定数の型は、シフト式がその左オペランド単独で置き換えられた場合の型として推論される、という基本的なルールは残ります。しかし、文脈から型が決定できない場合に強制的にintになるという例外的なケースがなくなるため、より一般的な型推論の原則に従うことになります。

この変更は、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 March 12, 2013",
+	"Subtitle": "Version of March 15, 2013",
 	"Path": "/ref/spec"
 }-->
 
@@ -2905,9 +2905,7 @@ The right operand in a shift expression must have unsigned integer type
 or be an untyped constant that can be converted to unsigned integer type.\n If the left operand of a non-constant shift expression is an untyped constant,\n the type of the constant is what it would be if the shift expression were\n-replaced by its left operand alone; the type is <code>int</code> if it cannot\n-be determined from the context (for instance, if the shift expression is an\n-operand in a comparison against an untyped constant).\n+replaced by its left operand alone.\n </p>\n \n <pre>\n@@ -2916,10 +2914,12 @@ var i = 1&lt;&lt;s           // 1 has type int\n var j int32 = 1&lt;&lt;s     // 1 has type int32; j == 0\n var k = uint64(1&lt;&lt;s)   // 1 has type uint64; k == 1&lt;&lt;33\n var m int = 1.0&lt;&lt;s     // 1.0 has type int\n-var n = 1.0&lt;&lt;s != 0    // 1.0 has type int; n == false if ints are 32bits in size\n+var n = 1.0&lt;&lt;s != i    // 1.0 has type int; n == false if ints are 32bits in size\n var o = 1&lt;&lt;s == 2&lt;&lt;s   // 1 and 2 have type int; o == true if ints are 32bits in size\n var p = 1&lt;&lt;s == 1&lt;&lt;33  // illegal if ints are 32bits in size: 1 has type int, but 1&lt;&lt;33 overflows int\n var u = 1.0&lt;&lt;s         // illegal: 1.0 has type float64, cannot shift\n+var u1 = 1.0&lt;&lt;s != 0   // illegal: 1.0 has type float64, cannot shift\n+var u2 = 1&lt;&lt;s != 1.0   // illegal: 1 has type float64, cannot shift\n var v float32 = 1&lt;&lt;s   // illegal: 1 has type float32, cannot shift\n var w int64 = 1.0&lt;&lt;33  // 1.0&lt;&lt;33 is a constant shift expression\n </pre>\n```

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

このコミットの主要な変更点は、Go言語の仕様書(`doc/go_spec.html`)におけるシフト演算子の型推論に関する記述の修正です。

1.  **仕様バージョンの更新**:
    ```diff
    -	"Subtitle": "Version of March 12, 2013",
    +	"Subtitle": "Version of March 15, 2013",
    ```
    これは、仕様書の更新日をコミット日に合わせて変更したものです。

2.  **シフト演算子の型推論ルールの削除**:
    ```diff
    -replaced by its left operand alone; the type is <code>int</code> if it cannot
    -be determined from the context (for instance, if the shift expression is an
    -operand in a comparison against an untyped constant).
    +replaced by its left operand alone.
    ```
    この部分が、今回のコミットの核心です。以前の仕様では、非定数シフト式の左オペランドが型なし定数である場合、その定数の型は、シフト式がその左オペランド単独で置き換えられた場合の型になるとされていました。さらに、もし文脈から型が決定できない場合(例: シフト式が型なし定数との比較のオペランドである場合)、その型は`int`であるという特別なルールがありました。

    このコミットでは、この「文脈から型が決定できない場合に`int`になる」という部分が削除されました。これにより、型なし定数の型推論は、より一般的なGoの型推論ルールに従うことになり、特定の文脈で強制的に`int`になるという例外がなくなります。

3.  **例の修正と追加**:
    ```diff
    -var n = 1.0&lt;&lt;s != 0    // 1.0 has type int; n == false if ints are 32bits in size
    +var n = 1.0&lt;&lt;s != i    // 1.0 has type int; n == false if ints are 32bits in size
    ```
    `var n`の例が修正されました。以前は`!= 0`との比較でしたが、`!= i`(`i`は`int`型の変数)との比較に変更されています。これにより、`1.0<<s`の型が`int`に推論されるという説明は維持されますが、比較対象がより具体的な変数になったことで、例の意図が明確になります。

    ```diff
    var u = 1.0&lt;&lt;s         // illegal: 1.0 has type float64, cannot shift
    +var u1 = 1.0&lt;&lt;s != 0   // illegal: 1.0 has type float64, cannot shift
    +var u2 = 1&lt;&lt;s != 1.0   // illegal: 1 has type float64, cannot shift
    ```
    `u`の例に加えて、`u1`と`u2`という新しい例が追加されました。これらは、浮動小数点型の値(`1.0`)をシフト演算子の左オペランドとして使用しようとしたり、シフト演算の結果を浮動小数点型と比較しようとしたりするケースが「不正(illegal)」であることを示しています。これは、シフト演算が整数型にのみ適用されるというGoの基本的なルールを強調するものです。

    これらの例の追加は、特別な`int`ルールが削除された後も、シフト演算子の型推論と型チェックがどのように機能するかをより明確にするために行われました。特に、型なし浮動小数点定数がシフト演算の左オペランドになる場合、その定数は浮動小数点型に推論され、シフト演算は許可されないという点が強調されています。

全体として、このコミットはGo言語の仕様をより正確で一貫性のあるものにし、実装との乖離を解消することを目的としています。

## 関連リンク

*   **Go言語の変更リスト**: [https://golang.org/cl/7707043](https://golang.org/cl/7707043)
*   **関連するGo Issue**:
    *   Issue 4880: [https://github.com/golang/go/issues/4880](https://github.com/golang/go/issues/4880)
    *   Issue 4881: [https://github.com/golang/go/issues/4881](https://github.com/golang/go/issues/4881)
    *   Issue 4883: [https://github.com/golang/go/issues/4883](https://github.com/golang/go/issues/4883)

## 参考にした情報源リンク

*   Go言語のシフト演算子に関するStack Overflowの議論:
    *   [https://stackoverflow.com/questions/2906070/what-are-the-shift-operators-in-go](https://stackoverflow.com/questions/2906070/what-are-the-shift-operators-in-go)
    *   [https://stackoverflow.com/questions/10474000/go-language-shift-operator](https://stackoverflow.com/questions/10474000/go-language-shift-operator)
*   Go言語のシフト演算子の型チェックに関するGitHub Issue (より新しい議論):
    *   [https://github.com/golang/go/issues/66071](https://github.com/golang/go/issues/66071) (cmd/compile: inconsistent shift operator type check)
*   Go言語のエラーハンドリングに関する提案 (文脈で「shifting」という言葉が使われている例):
    *   [https://github.com/golang/go/issues/52175](https://github.com/golang/go/issues/52175) (proposal: `try...or` for right-shifting error handling)
*   Go言語の仕様書 (現在のバージョン): [https://go.dev/ref/spec](https://go.dev/ref/spec)
    *   特に「Shift operators」のセクションを参照。
*   Go言語の型なし定数に関する情報: [https://go.dev/blog/constants](https://go.dev/blog/constants) (Go Blog: Constants)