[インデックス 15883] ファイルの概要
このコミットは、Go言語のシフト演算子に関するテストケースを拡充し、より体系的な検証を行うことを目的としています。特に、型なし定数(untyped constants)とシフト演算の相互作用、およびそれらが比較演算やその他の非算術演算にどのように影響するかを詳細にテストしています。
コミット
commit f8ff6893a579184d1ba62b0e178fe7ddfd695a1b
Author: Robert Griesemer <gri@golang.org>
Date: Thu Mar 21 16:56:59 2013 -0700
test: more systematic shift tests
To be submitted once gc agrees.
R=rsc, iant, remyoudompheng
CC=golang-dev
https://golang.org/cl/7861045
---
test/shift1.go | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 187 insertions(+), 4 deletions(-)
diff --git a/test/shift1.go b/test/shift1.go
index 46867a9334..71f7861c61 100644
--- a/test/shift1.go
+++ b/test/shift1.go
@@ -49,9 +49,192 @@ var (
f3 = imag(1 << s) // ERROR "invalid"
)
+// from the spec
+func _() {
+ var (
+ s uint = 33
+ i = 1 << s // 1 has type int
+ j int32 = 1 << s // 1 has type int32; j == 0
+ k = uint64(1 << s) // 1 has type uint64; k == 1<<33
+ m int = 1.0 << s // 1.0 has type int
+ n = 1.0<<s != i // 1.0 has type int; n == false if ints are 32bits in size
+ o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size
+ // next test only fails on 32bit systems
+ // p = 1<<s == 1<<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
+ u = 1.0 << s // ERROR "float64"
+ u1 = 1.0<<s != 0 // ERROR "float64"
+ u2 = 1<<s != 1.0 // ERROR "float64"
+ v float32 = 1 << s // ERROR "float32"
+ w int64 = 1.0 << 33 // 1.0<<33 is a constant shift expression
+ )
+}
+
+// shifts in comparisons w/ untyped operands
+var (
+- a4 float64
+- b4 int
+- c4 = complex(1<<s, a4) // ERROR "shift of type float64"
+- d4 = complex(1<<s, b4) // ERROR "invalid"
++ _ = 1<<s == 1
++ _ = 1<<s == 1. // ERROR "shift of type float64"
++ _ = 1.<<s == 1 // ERROR "shift of type float64"
++ _ = 1.<<s == 1. // ERROR "shift of type float64"
++
++ _ = 1<<s+1 == 1
++ _ = 1<<s+1 == 1. // ERROR "shift of type float64"
++ _ = 1<<s+1. == 1 // ERROR "shift of type float64"
++ _ = 1<<s+1. == 1. // ERROR "shift of type float64"
++ _ = 1.<<s+1 == 1 // ERROR "shift of type float64"
++ _ = 1.<<s+1 == 1. // ERROR "shift of type float64"
++ _ = 1.<<s+1. == 1 // ERROR "shift of type float64"
++ _ = 1.<<s+1. == 1. // ERROR "shift of type float64"
++
++ _ = 1<<s == 1<<s
++ _ = 1<<s == 1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s == 1<<s // ERROR "shift of type float64"
++ _ = 1.<<s == 1.<<s // ERROR "shift of type float64"
++
++ _ = 1<<s+1<<s == 1
++ _ = 1<<s+1<<s == 1. // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1 // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1. // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1 // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1. // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1 // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1. // ERROR "shift of type float64"
++
++ _ = 1<<s+1<<s == 1<<s+1<<s
++ _ = 1<<s+1<<s == 1<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1<<s+1<<s == 1.<<s+1<<s // ERROR "shift of type float64"
++ _ = 1<<s+1<<s == 1.<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1<<s+1<<s // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1.<<s+1<<s // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1.<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1<<s+1<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1.<<s+1<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1.<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1<<s+1<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1.<<s+1<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1.<<s+1.<<s // ERROR "shift of type float64"
+ )
++
+// shifts in comparisons w/ typed operands
+var (\n+\tx int\n+\t_ = 1<<s == x\n+\t_ = 1.<<s == x\n+\t_ = 1.1<<s == x // ERROR \"1.1 truncated\"\n+\n+\t_ = 1<<s+x == 1\n+\t_ = 1<<s+x == 1.\n+\t_ = 1<<s+x == 1.1 // ERROR \"1.1 truncated\"\n+\t_ = 1.<<s+x == 1\n+\t_ = 1.<<s+x == 1.\n+\t_ = 1.<<s+x == 1.1 // ERROR \"1.1 truncated\"\n+\t_ = 1.1<<s+x == 1 // ERROR \"1.1 truncated\"\n+\t_ = 1.1<<s+x == 1. // ERROR \"1.1 truncated\"\n+\t_ = 1.1<<s+x == 1.1 // ERROR \"1.1 truncated\"\n+\n+\t_ = 1<<s == x<<s\n+\t_ = 1.<<s == x<<s\n+\t_ = 1.1<<s == x<<s // ERROR \"1.1 truncated\"\n+)\n+\n+// shifts as operands in non-arithmetic operations and as arguments\n+func _() {\n+\tvar s uint\n+\tvar a []int\n+\t_ = a[1<<s]\n+\t_ = a[1.]\n+\t// For now, the spec disallows these. We may revisit past Go 1.1.\n+\t_ = a[1.<<s] // ERROR \"shift of type float64\"\n+\t_ = a[1.1<<s] // ERROR \"shift of type float64\"\n+\n+\t_ = make([]int, 1)\n+\t_ = make([]int, 1.)\n+\t_ = make([]int, 1.<<s)\n+\t_ = make([]int, 1.1<<s) // ERROR \"1.1 truncated\"\n+\n+\t_ = float32(1)\n+\t_ = float32(1 << s) // ERROR \"shift of type float32\"\n+\t_ = float32(1.)\n+\t_ = float32(1. << s) // ERROR \"shift of type float32\"\n+\t_ = float32(1.1 << s) // ERROR \"shift of type float32\"\n+\n+\t_ = append(a, 1<<s)\n+\t_ = append(a, 1.<<s)\n+\t_ = append(a, 1.1<<s) // ERROR \"1.1 truncated\"\n+\n+\tvar b []float32\n+\t_ = append(b, 1<<s) // ERROR \"type float32\"\n+\t_ = append(b, 1.<<s) // ERROR \"type float32\"\n+\t_ = append(b, 1.1<<s) // ERROR \"type float32\"\n+\n+\t_ = complex(1.<<s, 0) // ERROR \"shift of type float64\"\n+\t_ = complex(1.1<<s, 0) // ERROR \"shift of type float64\"\n+\t_ = complex(0, 1.<<s) // ERROR \"shift of type float64\"\n+\t_ = complex(0, 1.1<<s) // ERROR \"shift of type float64\"\n+\n+\tvar a4 float64\n+\tvar b4 int\n+\t_ = complex(1<<s, a4) // ERROR \"shift of type float64\"\n+\t_ = complex(1<<s, b4) // ERROR \"invalid\"\n+\n+\tvar m1 map[int]string\n+\tdelete(m1, 1<<s)\n+\tdelete(m1, 1.<<s)\n+\tdelete(m1, 1.1<<s) // ERROR \"1.1 truncated|shift of type float64\"\n+\n+\tvar m2 map[float32]string\n+\tdelete(m2, 1<<s) // ERROR \"invalid|cannot use 1 << s as type float32\"\n+\tdelete(m2, 1.<<s) // ERROR \"invalid|cannot use 1 << s as type float32\"\n+\tdelete(m2, 1.1<<s) // ERROR \"invalid|cannot use 1.1 << s as type float32\"\n+}\n+\n+// shifts of shifts\n+func _() {\n+\tvar s uint\n+\t_ = 1 << (1 << s)\n+\t_ = 1 << (1. << s)\n+\t_ = 1 << (1.1 << s) // ERROR \"1.1 truncated\"\n+\t_ = 1. << (1 << s) // ERROR \"shift of type float64\"\n+\t_ = 1. << (1. << s) // ERROR \"shift of type float64\"\n+\t_ = 1.1 << (1.1 << s) // ERROR \"invalid|1.1 truncated\"\n+\n+\t_ = (1 << s) << (1 << s)\n+\t_ = (1 << s) << (1. << s)\n+\t_ = (1 << s) << (1.1 << s) // ERROR \"1.1 truncated\"\n+\t_ = (1. << s) << (1 << s) // ERROR \"shift of type float64\"\n+\t_ = (1. << s) << (1. << s) // ERROR \"shift of type float64\"\n+\t_ = (1.1 << s) << (1.1 << s) // ERROR \"invalid|1.1 truncated\"\n+\n+\tvar x int\n+\tx = 1 << (1 << s)\n+\tx = 1 << (1. << s)\n+\tx = 1 << (1.1 << s) // ERROR \"1.1 truncated\"\n+\tx = 1. << (1 << s)\n+\tx = 1. << (1. << s)\n+\tx = 1.1 << (1.1 << s) // ERROR \"1.1 truncated\"\n+\n+\tx = (1 << s) << (1 << s)\n+\tx = (1 << s) << (1. << s)\n+\tx = (1 << s) << (1.1 << s) // ERROR \"1.1 truncated\"\n+\tx = (1. << s) << (1 << s)\n+\tx = (1. << s) << (1. << s)\n+\tx = (1.1 << s) << (1.1 << s) // ERROR \"1.1 truncated\"\n+\n+\tvar y float32\n+\ty = 1 << (1 << s) // ERROR \"type float32\"\n+\ty = 1 << (1. << s) // ERROR \"type float32\"\n+\ty = 1 << (1.1 << s) // ERROR \"invalid|1.1 truncated|float32\"\n+\ty = 1. << (1 << s) // ERROR \"type float32\"\n+\ty = 1. << (1. << s) // ERROR \"type float32\"\n+\ty = 1.1 << (1.1 << s) // ERROR \"invalid|1.1 truncated|float32\"\n+\n+\tvar z complex128\n+\tz = (1 << s) << (1 << s) // ERROR \"type complex128\"\n+\tz = (1 << s) << (1. << s) // ERROR \"type complex128\"\n+\tz = (1 << s) << (1.1 << s) // ERROR \"invalid|1.1 truncated|complex128\"\n+\tz = (1. << s) << (1 << s) // ERROR \"type complex128\"\n+\tz = (1. << s) << (1. << s) // ERROR \"type complex128\"\n+\tz = (1.1 << s) << (1.1 << s) // ERROR \"invalid|1.1 truncated|complex128\"\n+}\n```
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/f8ff6893a579184d1ba62b0e178fe7ddfd695a1b](https://github.com/golang/go/commit/f8ff6893a579184d1ba62b0e178fe7ddfd695a1b)
## 元コミット内容
`test: more systematic shift tests`
このコミットは、Go言語のシフト演算に関するテストをより体系的に拡充するものです。Goコンパイラ(gc)の合意が得られ次第、提出される予定でした。
## 変更の背景
Go言語は、その厳格な型システムと、型なし定数(untyped constants)の扱いにおいて独特のルールを持っています。特にシフト演算は、オペランドの型推論や、シフト量によるオーバーフローの挙動が言語仕様で細かく定義されています。
このコミットが作成された2013年3月は、Go 1.1のリリースに向けて言語仕様の安定化とコンパイラの堅牢性向上が進められていた時期と推測されます。シフト演算は、ビット操作や数値計算において頻繁に用いられるため、その挙動が仕様通りに、かつ予測可能であることを保証するための網羅的なテストが不可欠でした。
既存のテストではカバーしきれていなかった、以下のようなエッジケースや複雑な組み合わせを検証するために、このコミットが導入されました。
* 型なし浮動小数点数とシフト演算の組み合わせ
* 比較演算子とシフト演算の組み合わせ
* 非算術演算(`make`、`append`、`complex`、`delete`など)におけるシフト演算の結果
* シフト演算の結果をさらにシフトする「シフトのシフト」
これらのテストを追加することで、コンパイラがGo言語のシフト演算に関する仕様を正確に実装しているかを確認し、将来的なバグの混入を防ぐ狙いがあったと考えられます。
## 前提知識の解説
このコミットを理解するためには、Go言語における以下の概念を理解しておく必要があります。
### 1. シフト演算子 (`<<`, `>>`)
Go言語には、左シフト (`<<`) と右シフト (`>>`) の2つのシフト演算子があります。
* **左シフト (`x << y`)**: `x` のビットを `y` ビットだけ左にシフトします。これは `x * 2^y` と同等です。
* **右シフト (`x >> y`)**: `x` のビットを `y` ビットだけ右にシフトします。これは `x / 2^y` と同等です。符号付き整数に対する右シフトは算術シフト(符号ビットを保持)です。
シフト演算のオペランドには以下の制約があります。
* **左オペランド (シフト対象)**: 整数型または型なし定数である必要があります。
* **右オペランド (シフト量)**: 符号なし整数型(`uint`, `uint8`, `uint16`, `uint32`, `uint64`, `uintptr`)または型なし定数である必要があります。シフト量は負であってはなりません。
### 2. 型なし定数 (Untyped Constants)
Go言語の定数は、明示的な型を持たない「型なし定数」として宣言できます。例えば、`1` や `3.14` はデフォルトでは型なしの整数定数や浮動小数点数定数です。型なし定数は、その値が表現できる任意の型に変換できます。
```go
const c = 1 // c は型なし整数定数
var i int = c // c は int 型に変換される
var f float64 = c // c は float64 型に変換される
型なし定数が演算のオペランドとして使用される場合、Goコンパイラは文脈に基づいてその定数に型を推論します。この型推論のルールは、特にシフト演算において重要になります。
3. シフト演算における型推論とオーバーフロー
Go言語のシフト演算では、左オペランドが型なし定数の場合、その結果の型はシフト量によって決定されます。
- シフト量が定数の場合: シフト結果は型なし定数になります。この場合、結果がオーバーフローしない限り、任意の精度で計算されます。
- シフト量が非定数(変数)の場合: シフト結果は、左オペランドの型なし定数が
int
型に変換されたものとして扱われます。つまり、1 << s
のような式では、1
はまずint
型に変換され、その後にシフト演算が行われます。この際、int
型の最大値を超えるシフトはオーバーフローとなり、コンパイルエラーまたは未定義の動作を引き起こす可能性があります。
この挙動は、特に32ビットシステムと64ビットシステムで int
のサイズが異なる場合に、異なる結果やエラーを引き起こす可能性があるため、注意が必要です。
4. 浮動小数点数とシフト演算
Go言語の仕様では、浮動小数点数に対するシフト演算は許可されていません。したがって、1.0 << s
のような式は、1.0
が型なし浮動小数点数であるため、コンパイルエラーとなります。ただし、型なし浮動小数点数が整数に変換可能な場合は、その限りではありません。例えば、1.0
は整数 1
としても扱えるため、文脈によっては整数に型推論されることがあります。このコミットのテストケースは、この微妙な挙動を検証しています。
技術的詳細
このコミットは、test/shift1.go
ファイルに大量の新しいテストケースを追加することで、Go言語のシフト演算に関するコンパイラの挙動を詳細に検証しています。追加されたテストケースは、主に以下のカテゴリに分類できます。
1. 仕様からの例の検証 (func _()
)
Go言語の仕様書に記載されているシフト演算の例を再現し、その挙動が期待通りであることを確認しています。特に、1 << s
のような式で、s
が uint = 33
の場合(32ビットシステムでは int
のビット幅を超えるシフト)の型推論と結果に焦点を当てています。
i = 1 << s
:1
はint
型に推論され、i
はint
型。j int32 = 1 << s
:1
はint32
型に推論され、j
は0
(オーバーフローによる)。k = uint64(1 << s)
:1
はuint64
型に推論され、k
は1 << 33
。m int = 1.0 << s
:1.0
はint
型に推論され、m
はint
型。u = 1.0 << s
:1.0
がfloat64
型に推論されるため、ERROR "float64"
となることを期待。これは浮動小数点数に対するシフトが許可されていないため。
2. 型なしオペランドとの比較におけるシフト (shifts in comparisons w/ untyped operands
)
シフト演算の結果が、型なし定数や他のシフト演算の結果と比較される際の型推論とエラー挙動を検証しています。特に、浮動小数点数リテラル(例: 1.
や 1.0
)が絡む場合に、シフト演算が浮動小数点数型に推論されてエラーになるケースを多数含んでいます。
1<<s == 1.
:1.
が浮動小数点数であるため、1<<s
も浮動小数点数に推論され、ERROR "shift of type float64"
となる。1.<<s == 1
:1.
が浮動小数点数であるため、ERROR "shift of type float64"
となる。1<<s+1.<<s == 1
: 複数のシフト演算と浮動小数点数リテラルが混在する複雑な式での型推論を検証。
3. 型付きオペランドとの比較におけるシフト (shifts in comparisons w/ typed operands
)
シフト演算の結果が、型付き変数と比較される際の挙動を検証しています。
1.1<<s == x
:1.1
が浮動小数点数であり、シフト演算の結果が整数型x
と比較されるため、ERROR "1.1 truncated"
となる。これは、浮動小数点数が整数に変換される際に小数点以下が切り捨てられることを示唆しています。
4. 非算術演算のオペランドとしてのシフト (shifts as operands in non-arithmetic operations and as arguments
)
make
、append
、complex
、delete
などの組み込み関数や、配列のインデックス、型変換の引数としてシフト演算の結果が使用される場合の挙動を検証しています。ここでも、浮動小数点数を含むシフト演算がエラーとなるケースが多数含まれています。
a[1.<<s]
: 配列のインデックスに浮動小数点数を含むシフト演算の結果を使用しようとするため、ERROR "shift of type float64"
。float32(1 << s)
: シフト演算の結果をfloat32
に型変換しようとするが、シフト演算がfloat32
型に推論されるため、ERROR "shift of type float32"
。delete(m2, 1<<s)
:map[float32]string
のキーにint
型のシフト演算の結果を使用しようとするため、ERROR "invalid|cannot use 1 << s as type float32"
。
5. シフトのシフト (shifts of shifts
)
シフト演算の結果をさらにシフトする、ネストされたシフト演算の挙動を検証しています。これもまた、型推論とエラー挙動の複雑な組み合わせをテストしています。
1 << (1. << s)
: 内側のシフト演算(1. << s)
が浮動小数点数に推論されるため、外側のシフト演算の右オペランドとして無効となり、ERROR "shift of type float64"
。y = 1 << (1.1 << s)
: 浮動小数点数を含むシフトのシフトの結果をfloat32
型の変数に代入しようとする際の型不一致エラーを検証。
これらのテストケースは、Goコンパイラが言語仕様に厳密に従って型推論を行い、不正な型変換やオーバーフローを適切に検出できることを保証するために設計されています。ERROR
コメントは、その行がコンパイルエラーになることを期待していることを示しており、コンパイラのテストフレームワークによって自動的に検証されます。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、test/shift1.go
ファイルへの大規模な追加です。
--- a/test/shift1.go
+++ b/test/shift1.go
@@ -49,9 +49,192 @@ var (
f3 = imag(1 << s) // ERROR "invalid"
)
+// from the spec
+func _() {
+ var (
+ s uint = 33
+ i = 1 << s // 1 has type int
+ j int32 = 1 << s // 1 has type int32; j == 0
+ k = uint64(1 << s) // 1 has type uint64; k == 1<<33
+ m int = 1.0 << s // 1.0 has type int
+ n = 1.0<<s != i // 1.0 has type int; n == false if ints are 32bits in size
+ o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size
+ // next test only fails on 32bit systems
+ // p = 1<<s == 1<<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
+ u = 1.0 << s // ERROR "float64"
+ u1 = 1.0<<s != 0 // ERROR "float64"
+ u2 = 1<<s != 1.0 // ERROR "float64"
+ v float32 = 1 << s // ERROR "float32"
+ w int64 = 1.0 << 33 // 1.0<<33 is a constant shift expression
+ )
+}
+
+// shifts in comparisons w/ untyped operands
+var (
+- a4 float64
+- b4 int
+- c4 = complex(1<<s, a4) // ERROR "shift of type float64"
+- d4 = complex(1<<s, b4) // ERROR "invalid"
++ _ = 1<<s == 1
++ _ = 1<<s == 1. // ERROR "shift of type float64"
++ _ = 1.<<s == 1 // ERROR "shift of type float64"
++ _ = 1.<<s == 1. // ERROR "shift of type float64"
++
++ _ = 1<<s+1 == 1
++ _ = 1<<s+1 == 1. // ERROR "shift of type float64"
++ _ = 1<<s+1. == 1 // ERROR "shift of type float64"
++ _ = 1<<s+1. == 1. // ERROR "shift of type float64"
++ _ = 1.<<s+1 == 1 // ERROR "shift of type float64"
++ _ = 1.<<s+1 == 1. // ERROR "shift of type float64"
++ _ = 1.<<s+1. == 1 // ERROR "shift of type float64"
++ _ = 1.<<s+1. == 1. // ERROR "shift of type float64"
++
++ _ = 1<<s == 1<<s
++ _ = 1<<s == 1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s == 1<<s // ERROR "shift of type float64"
++ _ = 1.<<s == 1.<<s // ERROR "shift of type float64"
++
++ _ = 1<<s+1<<s == 1
++ _ = 1<<s+1<<s == 1. // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1 // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1. // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1 // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1. // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1 // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1. // ERROR "shift of type float64"
++
++ _ = 1<<s+1<<s == 1<<s+1<<s
++ _ = 1<<s+1<<s == 1<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1<<s+1<<s == 1.<<s+1<<s // ERROR "shift of type float64"
++ _ = 1<<s+1<<s == 1.<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1<<s+1<<s // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1.<<s+1<<s // ERROR "shift of type float64"
++ _ = 1<<s+1.<<s == 1.<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1<<s+1<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1.<<s+1<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1<<s == 1.<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1<<s+1<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1<<s+1.<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1.<<s+1<<s // ERROR "shift of type float64"
++ _ = 1.<<s+1.<<s == 1.<<s+1.<<s // ERROR "shift of type float64"
+ )
++
+// shifts in comparisons w/ typed operands
+var (\n+\tx int\n+\t_ = 1<<s == x\n+\t_ = 1.<<s == x\n+\t_ = 1.1<<s == x // ERROR \"1.1 truncated\"\n+\n+\t_ = 1<<s+x == 1\n+\t_ = 1<<s+x == 1.\n+\t_ = 1<<s+x == 1.1 // ERROR \"1.1 truncated\"\n+\t_ = 1.<<s+x == 1\n+\t_ = 1.<<s+x == 1.\n+\t_ = 1.<<s+x == 1.1 // ERROR \"1.1 truncated\"\n+\t_ = 1.1<<s+x == 1 // ERROR \"1.1 truncated\"\n+\t_ = 1.1<<s+x == 1. // ERROR \"1.1 truncated\"\n+\t_ = 1.1<<s+x == 1.1 // ERROR \"1.1 truncated\"\n+\n+\t_ = 1<<s == x<<s\n+\t_ = 1.<<s == x<<s\n+\t_ = 1.1<<s == x<<s // ERROR \"1.1 truncated\"\n+)\n+\n+// shifts as operands in non-arithmetic operations and as arguments\n+func _() {\n+\tvar s uint\n+\tvar a []int\n+\t_ = a[1<<s]\n+\t_ = a[1.]\n+\t// For now, the spec disallows these. We may revisit past Go 1.1.\n+\t_ = a[1.<<s] // ERROR \"shift of type float64\"\n+\t_ = a[1.1<<s] // ERROR \"shift of type float64\"\n+\n+\t_ = make([]int, 1)\n+\t_ = make([]int, 1.)\n+\t_ = make([]int, 1.<<s)\n+\t_ = make([]int, 1.1<<s) // ERROR \"1.1 truncated\"\n+\n+\t_ = float32(1)\n+\t_ = float32(1 << s) // ERROR \"shift of type float32\"\n+\t_ = float32(1.)\n+\t_ = float32(1. << s) // ERROR \"shift of type float32\"\n+\t_ = float32(1.1 << s) // ERROR \"shift of type float32\"\n+\n+\t_ = append(a, 1<<s)\n+\t_ = append(a, 1.<<s)\n+\t_ = append(a, 1.1<<s) // ERROR \"1.1 truncated\"\n+\n+\tvar b []float32\n+\t_ = append(b, 1<<s) // ERROR \"type float32\"\n+\t_ = append(b, 1.<<s) // ERROR \"type float32\"\n+\t_ = append(b, 1.1<<s) // ERROR \"type float32\"\n+\n+\t_ = complex(1.<<s, 0) // ERROR \"shift of type float64\"\n+\t_ = complex(1.1<<s, 0) // ERROR \"shift of type float64\"\n+\t_ = complex(0, 1.<<s) // ERROR \"shift of type float64\"\n+\t_ = complex(0, 1.1<<s) // ERROR \"shift of type float64\"\n+\n+\tvar a4 float64\n+\tvar b4 int\n+\t_ = complex(1<<s, a4) // ERROR \"shift of type float64\"\n+\t_ = complex(1<<s, b4) // ERROR \"invalid\"\n+\n+\tvar m1 map[int]string\n+\tdelete(m1, 1<<s)\n+\tdelete(m1, 1.<<s)\n+\tdelete(m1, 1.1<<s) // ERROR \"1.1 truncated|shift of type float64\"\n+\n+\tvar m2 map[float32]string\n+\tdelete(m2, 1<<s) // ERROR \"invalid|cannot use 1 << s as type float32\"\n+\tdelete(m2, 1.<<s) // ERROR \"invalid|cannot use 1 << s as type float32\"\n+\tdelete(m2, 1.1<<s) // ERROR \"invalid|cannot use 1.1 << s as type float32\"\n+}\n+\n+// shifts of shifts\n+func _() {\n+\tvar s uint\n+\t_ = 1 << (1 << s)\n+\t_ = 1 << (1. << s)\n+\t_ = 1 << (1.1 << s) // ERROR \"1.1 truncated\"\n+\t_ = 1. << (1 << s) // ERROR \"shift of type float64\"\n+\t_ = 1. << (1. << s) // ERROR \"shift of type float64\"\n+\t_ = 1.1 << (1.1 << s) // ERROR \"invalid|1.1 truncated\"\n+\n+\t_ = (1 << s) << (1 << s)\n+\t_ = (1 << s) << (1. << s)\n+\t_ = (1 << s) << (1.1 << s) // ERROR \"1.1 truncated\"\n+\t_ = (1. << s) << (1 << s) // ERROR \"shift of type float64\"\n+\t_ = (1. << s) << (1. << s) // ERROR \"shift of type float64\"\n+\t_ = (1.1 << s) << (1.1 << s) // ERROR \"invalid|1.1 truncated\"\n+\n+\tvar x int\n+\tx = 1 << (1 << s)\n+\tx = 1 << (1. << s)\n+\tx = 1 << (1.1 << s) // ERROR \"1.1 truncated\"\n+\tx = 1. << (1 << s)\n+\tx = 1. << (1. << s)\n+\tx = 1.1 << (1.1 << s) // ERROR \"1.1 truncated\"\n+\n+\tx = (1 << s) << (1 << s)\n+\tx = (1 << s) << (1. << s)\n+\tx = (1 << s) << (1.1 << s) // ERROR \"1.1 truncated\"\n+\tx = (1. << s) << (1 << s)\n+\tx = (1. << s) << (1. << s)\n+\tx = (1.1 << s) << (1.1 << s) // ERROR \"1.1 truncated\"\n+\n+\tvar y float32\n+\ty = 1 << (1 << s) // ERROR \"type float32\"\n+\ty = 1 << (1. << s) // ERROR \"type float32\"\n+\ty = 1 << (1.1 << s) // ERROR \"invalid|1.1 truncated|float32\"\n+\ty = 1. << (1 << s) // ERROR \"type float32\"\n+\ty = 1. << (1. << s) // ERROR \"type float32\"\n+\ty = 1.1 << (1.1 << s) // ERROR \"invalid|1.1 truncated|float32\"\n+\n+\tvar z complex128\n+\tz = (1 << s) << (1 << s) // ERROR \"type complex128\"\n+\tz = (1 << s) << (1. << s) // ERROR \"type complex128\"\n+\tz = (1 << s) << (1.1 << s) // ERROR \"invalid|1.1 truncated|complex128\"\n+\tz = (1. << s) << (1 << s) // ERROR \"type complex128\"\n+\tz = (1. << s) << (1. << s) // ERROR \"type complex128\"\n+\tz = (1.1 << s) << (1.1 << s) // ERROR \"invalid|1.1 truncated|complex128\"\n+}\n```
## コアとなるコードの解説
追加されたコードは、Go言語のコンパイラがシフト演算をどのように処理するかを検証するための、非常に詳細なテストケースの集合体です。各テストケースは、特定のシナリオにおけるシフト演算の挙動を検証し、期待されるコンパイルエラー(`ERROR`コメントで示される)が発生するかどうかを確認します。
主なポイントは以下の通りです。
* **型なし定数の型推論**: `1 << s` のような式で、`1` のような型なし定数が、周囲の文脈(代入先の型、比較対象の型など)に基づいてどのように型推論されるかをテストしています。特に、シフト量が変数である場合に、左オペランドが `int` 型に推論されるルールが厳密に検証されています。
* **浮動小数点数とシフト演算**: Go言語では浮動小数点数に対するシフト演算は許可されていません。`1.0 << s` や `1. << s` のように、浮動小数点数リテラルを含むシフト演算が、適切に `ERROR "shift of type float64"` や `ERROR "shift of type float32"` となることを確認しています。これは、型なし浮動小数点数定数が整数に変換できない文脈でシフト演算が適用された場合に発生します。
* **オーバーフローと切り捨て**: `1.1 << s` のように、小数点以下を持つ浮動小数点数定数をシフト演算の左オペランドとして使用した場合に、`ERROR "1.1 truncated"` となることをテストしています。これは、浮動小数点数が整数に変換される際に小数点以下が切り捨てられることを示しており、意図しないデータ損失を防ぐためのコンパイラのチェックです。
* **複雑な式における型推論**: 比較演算子 (`==`, `!=`) や算術演算子 (`+`) とシフト演算が組み合わされた複雑な式において、Goコンパイラがどのように型を推論し、エラーを検出するかを網羅的にテストしています。
* **非算術演算での使用**: `make`、`append`、`complex`、`delete` といった組み込み関数の引数や、配列のインデックスとしてシフト演算の結果が使用される場合の型チェックを強化しています。これにより、Go言語の型システムが、これらの文脈においてもシフト演算の結果の型を正しく検証できることを保証します。
* **ネストされたシフト演算**: `1 << (1 << s)` のように、シフト演算の右オペランドが別のシフト演算の結果である場合の挙動もテストしています。これにより、複雑な式における型推論の正確性をさらに高めています。
これらのテストは、Go言語のコンパイラが言語仕様に厳密に準拠し、開発者が意図しない型変換やオーバーフローによって引き起こされる潜在的なバグから保護されることを保証するための重要な役割を果たしています。
## 関連リンク
* [https://golang.org/cl/7861045](https://golang.org/cl/7861045) - このコミットに対応するGerrit Code Reviewのリンク。Goプロジェクトでは、GitHubへのコミット前にGerritでコードレビューが行われます。
## 参考にした情報源リンク
* [The Go Programming Language Specification - Shift operators](https://go.dev/ref/spec#Shift_operators) - Go言語のシフト演算子に関する公式仕様。
* [The Go Programming Language Specification - Constants](https://go.dev/ref/spec#Constants) - Go言語の定数、特に型なし定数に関する公式仕様。
* [Go 1.1 Release Notes](https://go.dev/doc/go1.1) - Go 1.1リリースに関する情報。このコミットがGo 1.1のリリースサイクル中に作成された可能性を示唆します。
* [Go Language Blog - Go 1.1 is released](https://go.dev/blog/go1.1) - Go 1.1リリースに関するブログ記事。
* [Go Language Blog - Untyped constants in Go](https://go.dev/blog/untyped-constants) - Go言語の型なし定数に関する詳細な解説。