[インデックス 15430] ファイルの概要
本コミットは、Go言語のコンパイラ(cmd/gc
)におけるswitch
ステートメントの挙動に関する修正です。具体的には、インターフェース型に対するswitch
文において、同じ定数値を持つが型が異なるcase
ラベルが正しく扱われるように改善されています。これにより、以前はコンパイルエラーとなっていた特定のコードパターンが許容されるようになります。
コミット
commit 71b3b460738decbce5f1797492eaa59b06d69687
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Tue Feb 26 00:45:43 2013 +0100
cmd/gc: accept cases with same value but different types in switch.
Fixes #4781.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7365056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/71b3b460738decbce5f1797492eaa59b06d69687
元コミット内容
cmd/gc: accept cases with same value but different types in switch.
Fixes #4781.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7365056
変更の背景
この変更は、Go言語のIssue 4781「cmd/gc: switch on interface with constant cases differing by type
」を修正するために行われました。
Go言語のswitch
ステートメントは非常に強力で、特に型スイッチ(type switch)やインターフェース値に対するスイッチは、動的な型チェックと処理の分岐に利用されます。しかし、このコミット以前のGoコンパイラ(cmd/gc
)には、インターフェース値に対するswitch
文において、case
ラベルが同じ定数値を持つが、その定数の「型」が異なる場合に、コンパイルエラーとなるバグが存在していました。
例えば、float64(1.0)
という値を持つインターフェース変数に対してswitch
を行う際、case 1:
(int型)とcase F(1.0):
(Fはfloat64のエイリアス型)のように、値としては同じ1
や1.0
を指すものの、型が異なるcase
が並ぶと、コンパイラがこれを重複と誤認し、不正なコードとして扱ってしまう問題がありました。これは、switch
文のcase
ラベルの比較ロジックが、値だけでなく型の違いも適切に考慮していなかったためと考えられます。
このバグは、Go言語の柔軟な型システム、特にインターフェースと型エイリアスを組み合わせた際に、開発者が期待する挙動とコンパイラの実際の挙動との間に乖離を生じさせていました。この修正により、より直感的で柔軟なswitch
文の記述が可能になり、Go言語の表現力が向上しました。
前提知識の解説
Go言語のswitch
ステートメント
Go言語のswitch
ステートメントは、他の言語のそれと似ていますが、いくつかの特徴があります。
- 式スイッチ (Expression Switch):
switch
キーワードの後に式を記述し、その式の値とcase
ラベルの値を比較します。 - 型スイッチ (Type Switch):
switch x.(type)
という形式で記述し、インターフェース変数x
の動的な型に基づいて処理を分岐させます。 - フォールスルーなし: デフォルトでは、
case
がマッチしても次のcase
にはフォールスルーしません。明示的にfallthrough
キーワードを使用した場合のみフォールスルーします。 - 複数の
case
値: 1つのcase
ラベルに複数の値をカンマ区切りで指定できます(例:case 1, 2, 3:
)。 case
の評価順序:case
は記述された順に評価されます。最初にマッチしたcase
ブロックが実行されます。
インターフェースと動的型
Go言語のインターフェースは、メソッドの集合を定義する型です。インターフェース型の変数は、そのインターフェースが要求するすべてのメソッドを実装する任意の具象型の値を保持できます。
インターフェース変数が具象型の値を保持している場合、そのインターフェース変数は「動的な型(dynamic type)」と「動的な値(dynamic value)」を持ちます。動的な型は、インターフェース変数に格納されている具象値の実際の型を指します。
定数と型
Go言語では、数値定数(例: 1
、1.0
、"hello"
)は、その使用される文脈によって型が推論されます。例えば、1
はint
型として扱われることが多いですが、float64
型の変数に代入されればfloat64
型として扱われます。また、type T int
のように型エイリアスを定義した場合、T(1)
のように明示的に型変換を行うことで、基底の型は同じでも異なる型として扱うことができます。
このコミットの背景にある問題は、switch
文のcase
比較ロジックが、このような定数の型推論や明示的な型変換によって生じる「値は同じだが型が異なる」ケースを適切に区別できていなかった点にあります。
cmd/gc
(Goコンパイラ)
cmd/gc
は、Go言語の公式コンパイラです。Goのソースコードを機械語に変換する役割を担っています。switch
ステートメントの処理は、コンパイル時にcmd/gc
によって行われます。特に、case
ラベルの評価順序や重複チェック、最適化などはコンパイラの重要な機能の一部です。
このコミットで変更されたsrc/cmd/gc/swt.c
は、switch
ステートメントの処理ロジックを実装しているC言語のソースファイルです。Goコンパイラの一部はC言語で書かれています。
技術的詳細
このコミットの技術的な核心は、cmd/gc
内のswitch
文のcase
比較ロジック、特にexprcmp
関数とmkcaselist
関数の変更にあります。
exprcmp
関数の変更
exprcmp
関数は、switch
文のcase
ラベルをソートするために使用される比較関数です。case
ラベルが重複していないか、あるいは特定の順序で処理されるべきかを判断するために、case
の値を比較します。
変更前のexprcmp
は、case
の定数型(ctype
)が異なる場合に、すぐにソート順を返していました。これは、異なる型の定数は異なるcase
として扱われるべきという前提に基づいています。しかし、インターフェースに対するswitch
の場合、同じ値を持つ異なる型(例: int(1)
とfloat64(1.0)
)が問題を引き起こしていました。
変更後のexprcmp
は、以下のロジックを追加しています。
-
型によるソート(インターフェーススイッチの場合):
// sort by type (for switches on interface) ct = n1->val.ctype; if(ct != n2->val.ctype) return ct - n2->val.ctype;
この部分は変更前と変わらず、定数型が異なる場合はその差を返します。これは、異なる定数型を持つ
case
を区別するための基本的なソート順を提供します。 -
具象型の比較:
if(!eqtype(n1->type, n2->type)) { if(n1->type->vargen > n2->type->vargen) return +1; else return -1; }
ここが重要な変更点です。
ct != n2->val.ctype
がfalse
(つまり、定数型が同じ)であっても、n1->type
とn2->type
(これはcase
ラベルの具象型を指す)がeqtype
(型が等しいか)でfalse
を返す場合、つまり定数型は同じだが、具象型が異なる場合に、vargen
(型生成バージョン、型のユニークな識別子のようなもの)に基づいてソート順を決定します。 これにより、例えば1
(int)とT(1)
(intのエイリアスT)のように、値は同じだが型が異なるcase
を、コンパイラが異なるエントリとして認識し、適切にソート・処理できるようになります。以前は、このようなケースで重複エラーが発生していました。
mkcaselist
関数の変更
mkcaselist
関数は、switch
文のcase
リストを構築する際に呼び出されます。この関数内で、各case
ノードに対して追加の処理が行われます。
変更点:
case Strue:
case Sfalse:
c->type = Texprvar;
c->hash = typehash(n->left->type); // 追加された行
switch(consttype(n->left)) {
// ...
}
c->hash = typehash(n->left->type);
という行が追加されています。
これは、case
ラベルの具象型(n->left->type
)のハッシュ値を計算し、Case
構造体のhash
フィールドに格納するものです。このハッシュ値は、case
の比較や重複チェックの際に、値だけでなく型も考慮に入れるための補助的な情報として利用されます。特に、インターフェースに対するswitch
で、同じ値だが異なる型を持つcase
を区別するために役立ちます。
総合的な影響
これらの変更により、Goコンパイラはswitch
文のcase
ラベルを比較する際に、定数値だけでなく、その定数の具象型も考慮に入れるようになりました。これにより、Issue 4781で報告されたような、インターフェースに対するswitch
で同じ値だが異なる型を持つcase
がコンパイルエラーとなる問題が解決されました。コンパイラはこれらのcase
を正しく区別し、それぞれ独立したcase
として扱えるようになります。
コアとなるコードの変更箇所
src/cmd/gc/swt.c
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -117,12 +117,15 @@ exprcmp(Case *c1, Case *c2)
n1 = c1->node->left;
n2 = c2->node->left;
+ // sort by type (for switches on interface)
ct = n1->val.ctype;
- if(ct != n2->val.ctype) {
- // invalid program, but return a sort
- // order so that we can give a better
- // error later.
+ if(ct != n2->val.ctype)
return ct - n2->val.ctype;
+ if(!eqtype(n1->type, n2->type)) {
+ if(n1->type->vargen > n2->type->vargen)
+ return +1;
+ else
+ return -1;
}
// sort by constant value
@@ -379,6 +382,7 @@ mkcaselist(Node *sw, int arg)
case Strue:
case Sfalse:
c->type = Texprvar;
+ c->hash = typehash(n->left->type);
switch(consttype(n->left)) {
case CTFLT:
case CTINT:
test/switch.go
--- a/test/switch.go
+++ b/test/switch.go
@@ -305,6 +305,35 @@ func main() {
assert(false, "i should be true")
}
+ // switch on interface with constant cases differing by type.
+ // was rejected by compiler: see issue 4781
+ type T int
+ type B bool
+ type F float64
+ type S string
+ switch i := interface{}(float64(1.0)); i {
+ case nil:
+ assert(false, "i should be float64(1.0)")
+ case (*int)(nil):
+ assert(false, "i should be float64(1.0)")
+ case 1:
+ assert(false, "i should be float64(1.0)")
+ case T(1):
+ assert(false, "i should be float64(1.0)")
+ case F(1.0):
+ assert(false, "i should be float64(1.0)")
+ case 1.0:
+ assert(true, "true")
+ case "hello":
+ assert(false, "i should be float64(1.0)")
+ case S("hello"):
+ assert(false, "i should be float64(1.0)")
+ case true, B(false):
+ assert(false, "i should be float64(1.0)")
+ case false, B(true):
+ assert(false, "i should be float64(1.0)")
+ }
+
// switch on array.
switch ar := [3]int{1, 2, 3}; ar {
case [3]int{1, 2, 3}:
コアとなるコードの解説
src/cmd/gc/swt.c
の変更
-
exprcmp
関数:if(ct != n2->val.ctype)
のブロックから、以前のコメントアウトされたエラーハンドリング部分が削除され、より簡潔になっています。- 最も重要な追加は、
if(!eqtype(n1->type, n2->type))
ブロックです。これは、case
ラベルの定数型(ctype
)が同じであっても、その背後にある具象型(n1->type
,n2->type
)が異なる場合に、vargen
(型生成バージョン)に基づいてソート順を決定するようにしています。これにより、int(1)
とT(1)
のように、値は同じだが型が異なるcase
を区別できるようになりました。vargen
はGoコンパイラ内部で型を一意に識別するためのIDのようなもので、これによって型の比較を正確に行います。
-
mkcaselist
関数:c->hash = typehash(n->left->type);
という行が追加されました。これは、case
ラベルの具象型のハッシュ値を計算し、Case
構造体に格納することで、case
の比較処理において型の情報も効率的に利用できるようにするためのものです。これにより、exprcmp
関数での型比較がより堅牢になります。
test/switch.go
の変更
このファイルには、新しいテストケースが追加されています。これは、Issue 4781で報告された問題が修正されたことを検証するためのものです。
// switch on interface with constant cases differing by type.
// was rejected by compiler: see issue 4781
type T int
type B bool
type F float64
type S string
switch i := interface{}(float64(1.0)); i {
case nil:
assert(false, "i should be float64(1.0)")
case (*int)(nil):
assert(false, "i should be float64(1.0)")
case 1: // int型
assert(false, "i should be float64(1.0)")
case T(1): // T型 (intのエイリアス)
assert(false, "i should be float64(1.0)")
case F(1.0): // F型 (float64のエイリアス)
assert(false, "i should be float64(1.0)")
case 1.0: // float64型
assert(true, "true") // ここがマッチするはず
case "hello":
assert(false, "i should be float64(1.0)")
case S("hello"): // S型 (stringのエイリアス)
assert(false, "i should be float64(1.0)")
case true, B(false): // bool型とB型 (boolのエイリアス)
assert(false, "i should be float64(1.0)")
case false, B(true): // bool型とB型 (boolのエイリアス)
assert(false, "i should be float64(1.0)")
}
このテストケースでは、float64(1.0)
という値を持つインターフェース変数i
に対してswitch
を行っています。注目すべきは、case 1:
(int型)、case T(1):
(T型)、case F(1.0):
(F型)、そしてcase 1.0:
(float64型)のように、値としては1
または1.0
を指すものの、型が異なる複数のcase
が記述されている点です。
修正前は、これらのcase
がコンパイラによって重複と誤認され、コンパイルエラーとなっていました。しかし、このコミットの修正により、コンパイラはこれらのcase
を正しく区別し、i
の動的な値がfloat64(1.0)
であるため、case 1.0:
が正しくマッチし、assert(true, "true")
が実行されるようになります。他のcase
はマッチしないため、assert(false, ...)
は実行されません。
このテストケースは、コンパイラがインターフェースに対するswitch
文で、値が同じでも型が異なるcase
を適切に処理できるようになったことを明確に示しています。
関連リンク
- Go Issue 4781:
cmd/gc: switch on interface with constant cases differing by type
- https://github.com/golang/go/issues/4781 - Go Code Review 7365056:
cmd/gc: accept cases with same value but different types in switch.
- https://golang.org/cl/7365056
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(
src/cmd/gc/swt.c
) - Go言語のIssueトラッカー
- Go言語のCode Reviewシステム
[インデックス 15430] ファイルの概要
本コミットは、Go言語のコンパイラ(cmd/gc
)におけるswitch
ステートメントの挙動に関する修正です。具体的には、インターフェース型に対するswitch
文において、同じ定数値を持つが型が異なるcase
ラベルが正しく扱われるように改善されています。これにより、以前はコンパイルエラーとなっていた特定のコードパターンが許容されるようになります。
コミット
commit 71b3b460738decbce5f1797492eaa59b06d69687
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Tue Feb 26 00:45:43 2013 +0100
cmd/gc: accept cases with same value but different types in switch.
Fixes #4781.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7365056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/71b3b460738decbce5f1797492eaa59b06d69687
元コミット内容
cmd/gc: accept cases with same value but different types in switch.
Fixes #4781.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7365056
変更の背景
この変更は、Go言語のIssue 4781「cmd/gc: switch on interface with constant cases differing by type
」を修正するために行われました。
Go言語のswitch
ステートメントは非常に強力で、特に型スイッチ(type switch)やインターフェース値に対するスイッチは、動的な型チェックと処理の分岐に利用されます。しかし、このコミット以前のGoコンパイラ(cmd/gc
)には、インターフェース値に対するswitch
文において、case
ラベルが同じ定数値を持つが、その定数の「型」が異なる場合に、コンパイルエラーとなるバグが存在していました。
例えば、float64(1.0)
という値を持つインターフェース変数に対してswitch
を行う際、case 1:
(int型)とcase F(1.0):
(Fはfloat64のエイリアス型)のように、値としては同じ1
や1.0
を指すものの、型が異なるcase
が並ぶと、コンパイラがこれを重複と誤認し、不正なコードとして扱ってしまう問題がありました。これは、switch
文のcase
ラベルの比較ロジックが、値だけでなく型の違いも適切に考慮していなかったためと考えられます。
このバグは、Go言語の柔軟な型システム、特にインターフェースと型エイリアスを組み合わせた際に、開発者が期待する挙動とコンパイラの実際の挙動との間に乖離を生じさせていました。この修正により、より直感的で柔軟なswitch
文の記述が可能になり、Go言語の表現力が向上しました。
前提知識の解説
Go言語のswitch
ステートメント
Go言語のswitch
ステートメントは、他の言語のそれと似ていますが、いくつかの特徴があります。
- 式スイッチ (Expression Switch):
switch
キーワードの後に式を記述し、その式の値とcase
ラベルの値を比較します。 - 型スイッチ (Type Switch):
switch x.(type)
という形式で記述し、インターフェース変数x
の動的な型に基づいて処理を分岐させます。 - フォールスルーなし: デフォルトでは、
case
がマッチしても次のcase
にはフォールスルーしません。明示的にfallthrough
キーワードを使用した場合のみフォールスルーします。 - 複数の
case
値: 1つのcase
ラベルに複数の値をカンマ区切りで指定できます(例:case 1, 2, 3:
)。 case
の評価順序:case
は記述された順に評価されます。最初にマッチしたcase
ブロックが実行されます。
インターフェースと動的型
Go言語のインターフェースは、メソッドの集合を定義する型です。インターフェース型の変数は、そのインターフェースが要求するすべてのメソッドを実装する任意の具象型の値を保持できます。
インターフェース変数が具象型の値を保持している場合、そのインターフェース変数は「動的な型(dynamic type)」と「動的な値(dynamic value)」を持ちます。動的な型は、インターフェース変数に格納されている具象値の実際の型を指します。
定数と型
Go言語では、数値定数(例: 1
、1.0
、"hello"
)は、その使用される文脈によって型が推論されます。例えば、1
はint
型として扱われることが多いですが、float64
型の変数に代入されればfloat64
型として扱われます。また、type T int
のように型エイリアスを定義した場合、T(1)
のように明示的に型変換を行うことで、基底の型は同じでも異なる型として扱うことができます。
このコミットの背景にある問題は、switch
文のcase
比較ロジックが、このような定数の型推論や明示的な型変換によって生じる「値は同じだが型が異なる」ケースを適切に区別できていなかった点にあります。
cmd/gc
(Goコンパイラ)
cmd/gc
は、Go言語の公式コンパイラです。Goのソースコードを機械語に変換する役割を担っています。switch
ステートメントの処理は、コンパイル時にcmd/gc
によって行われます。特に、case
ラベルの評価順序や重複チェック、最適化などはコンパイラの重要な機能の一部です。
このコミットで変更されたsrc/cmd/gc/swt.c
は、switch
ステートメントの処理ロジックを実装しているC言語のソースファイルです。Goコンパイラの一部はC言語で書かれています。
技術的詳細
このコミットの技術的な核心は、cmd/gc
内のswitch
文のcase
比較ロジック、特にexprcmp
関数とmkcaselist
関数の変更にあります。
exprcmp
関数の変更
exprcmp
関数は、switch
文のcase
ラベルをソートするために使用される比較関数です。case
ラベルが重複していないか、あるいは特定の順序で処理されるべきかを判断するために、case
の値を比較します。
変更前のexprcmp
は、case
の定数型(ctype
)が異なる場合に、すぐにソート順を返していました。これは、異なる型の定数は異なるcase
として扱われるべきという前提に基づいています。しかし、インターフェースに対するswitch
の場合、同じ値を持つ異なる型(例: int(1)
とfloat64(1.0)
)が問題を引き起こしていました。
変更後のexprcmp
は、以下のロジックを追加しています。
-
型によるソート(インターフェーススイッチの場合):
// sort by type (for switches on interface) ct = n1->val.ctype; if(ct != n2->val.ctype) return ct - n2->val.ctype;
この部分は変更前と変わらず、定数型が異なる場合はその差を返します。これは、異なる定数型を持つ
case
を区別するための基本的なソート順を提供します。 -
具象型の比較:
if(!eqtype(n1->type, n2->type)) { if(n1->type->vargen > n2->type->vargen) return +1; else return -1; }
ここが重要な変更点です。
ct != n2->val.ctype
がfalse
(つまり、定数型が同じ)であっても、n1->type
とn2->type
(これはcase
ラベルの具象型を指す)がeqtype
(型が等しいか)でfalse
を返す場合、つまり定数型は同じだが、具象型が異なる場合に、vargen
(型生成バージョン、型のユニークな識別子のようなもの)に基づいてソート順を決定します。 これにより、例えば1
(int)とT(1)
(intのエイリアスT)のように、値は同じだが型が異なるcase
を、コンパイラが異なるエントリとして認識し、適切にソート・処理できるようになります。以前は、このようなケースで重複エラーが発生していました。
mkcaselist
関数の変更
mkcaselist
関数は、switch
文のcase
リストを構築する際に呼び出されます。この関数内で、各case
ノードに対して追加の処理が行われます。
変更点:
case Strue:
case Sfalse:
c->type = Texprvar;
c->hash = typehash(n->left->type); // 追加された行
switch(consttype(n->left)) {
// ...
}
c->hash = typehash(n->left->type);
という行が追加されています。
これは、case
ラベルの具象型(n->left->type
)のハッシュ値を計算し、Case
構造体のhash
フィールドに格納するものです。このハッシュ値は、case
の比較や重複チェックの際に、値だけでなく型も考慮に入れるための補助的な情報として利用されます。特に、インターフェースに対するswitch
で、同じ値だが異なる型を持つcase
を区別するために役立ちます。
総合的な影響
これらの変更により、Goコンパイラはswitch
文のcase
ラベルを比較する際に、定数値だけでなく、その定数の具象型も考慮に入れるようになりました。これにより、Issue 4781で報告されたような、インターフェースに対するswitch
で同じ値だが異なる型を持つcase
がコンパイルエラーとなる問題が解決されました。コンパイラはこれらのcase
を正しく区別し、それぞれ独立したcase
として扱えるようになります。
コアとなるコードの変更箇所
src/cmd/gc/swt.c
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -117,12 +117,15 @@ exprcmp(Case *c1, Case *c2)
n1 = c1->node->left;
n2 = c2->node->left;
+ // sort by type (for switches on interface)
ct = n1->val.ctype;
- if(ct != n2->val.ctype) {
- // invalid program, but return a sort
- // order so that we can give a better
- // error later.
+ if(ct != n2->val.ctype)
return ct - n2->val.ctype;
+ if(!eqtype(n1->type, n2->type)) {
+ if(n1->type->vargen > n2->type->vargen)
+ return +1;
+ else
+ return -1;
}
// sort by constant value
@@ -379,6 +382,7 @@ mkcaselist(Node *sw, int arg)
case Strue:
case Sfalse:
c->type = Texprvar;
+ c->hash = typehash(n->left->type);
switch(consttype(n->left)) {
case CTFLT:
case CTINT:
test/switch.go
--- a/test/switch.go
+++ b/test/switch.go
@@ -305,6 +305,35 @@ func main() {
assert(false, "i should be true")
}
+ // switch on interface with constant cases differing by type.
+ // was rejected by compiler: see issue 4781
+ type T int
+ type B bool
+ type F float64
+ type S string
+ switch i := interface{}(float64(1.0)); i {
+ case nil:
+ assert(false, "i should be float64(1.0)")
+ case (*int)(nil):
+ assert(false, "i should be float64(1.0)")
+ case 1:
+ assert(false, "i should be float64(1.0)")
+ case T(1):
+ assert(false, "i should be float64(1.0)")
+ case F(1.0):
+ assert(false, "i should be float64(1.0)")
+ case 1.0:
+ assert(true, "true")
+ case "hello":
+ assert(false, "i should be float64(1.0)")
+ case S("hello"):
+ assert(false, "i should be float64(1.0)")
+ case true, B(false):
+ assert(false, "i should be float64(1.0)")
+ case false, B(true):
+ assert(false, "i should be float64(1.0)")
+ }
+
// switch on array.
switch ar := [3]int{1, 2, 3}; ar {
case [3]int{1, 2, 3}:
コアとなるコードの解説
src/cmd/gc/swt.c
の変更
-
exprcmp
関数:if(ct != n2->val.ctype)
のブロックから、以前のコメントアウトされたエラーハンドリング部分が削除され、より簡潔になっています。- 最も重要な追加は、
if(!eqtype(n1->type, n2->type))
ブロックです。これは、case
ラベルの定数型(ctype
)が同じであっても、その背後にある具象型(n1->type
,n2->type
)が異なる場合に、vargen
(型生成バージョン)に基づいてソート順を決定するようにしています。これにより、int(1)
とT(1)
のように、値は同じだが型が異なるcase
を区別できるようになりました。vargen
はGoコンパイラ内部で型を一意に識別するためのIDのようなもので、これによって型の比較を正確に行います。
-
mkcaselist
関数:c->hash = typehash(n->left->type);
という行が追加されました。これは、case
ラベルの具象型のハッシュ値を計算し、Case
構造体に格納することで、case
の比較処理において型の情報も効率的に利用できるようにするためのものです。これにより、exprcmp
関数での型比較がより堅牢になります。
test/switch.go
の変更
このファイルには、新しいテストケースが追加されています。これは、Issue 4781で報告された問題が修正されたことを検証するためのものです。
// switch on interface with constant cases differing by type.
// was rejected by compiler: see issue 4781
type T int
type B bool
type F float64
type S string
switch i := interface{}(float64(1.0)); i {
case nil:
assert(false, "i should be float64(1.0)")
case (*int)(nil):
assert(false, "i should be float64(1.0)")
case 1: // int型
assert(false, "i should be float64(1.0)")
case T(1): // T型 (intのエイリアス)
assert(false, "i should be float64(1.0)")
case F(1.0): // F型 (float64のエイリアス)
assert(false, "i should be float64(1.0)")
case 1.0: // float64型
assert(true, "true") // ここがマッチするはず
case "hello":
assert(false, "i should be float64(1.0)")
case S("hello"): // S型 (stringのエイリアス)
assert(false, "i should be float64(1.0)")
case true, B(false): // bool型とB型 (boolのエイリアス)
assert(false, "i should be float64(1.0)")
case false, B(true): // bool型とB型 (boolのエイリアス)
assert(false, "i should be float64(1.0)")
}
このテストケースでは、float64(1.0)
という値を持つインターフェース変数i
に対してswitch
を行っています。注目すべきは、case 1:
(int型)、case T(1):
(T型)、case F(1.0):
(F型)、そしてcase 1.0:
(float64型)のように、値としては1
または1.0
を指すものの、型が異なる複数のcase
が記述されている点です。
修正前は、これらのcase
がコンパイラによって重複と誤認され、コンパイルエラーとなっていました。しかし、このコミットの修正により、コンパイラはこれらのcase
を正しく区別し、i
の動的な値がfloat64(1.0)
であるため、case 1.0:
が正しくマッチし、assert(true, "true")
が実行されるようになります。他のcase
はマッチしないため、assert(false, ...)
は実行されません。
このテストケースは、コンパイラがインターフェースに対するswitch
文で、値が同じでも型が異なるcase
を適切に処理できるようになったことを明確に示しています。
関連リンク
- Go Issue 4781:
cmd/gc: switch on interface with constant cases differing by type
- https://github.com/golang/go/issues/4781 - Go Code Review 7365056:
cmd/gc: accept cases with same value but different types in switch.
- https://golang.org/cl/7365056
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(
src/cmd/gc/swt.c
) - Go言語のIssueトラッカー
- Go言語のCode Reviewシステム