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

[インデックス 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のエイリアス型)のように、値としては同じ11.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言語では、数値定数(例: 11.0"hello")は、その使用される文脈によって型が推論されます。例えば、1int型として扱われることが多いですが、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は、以下のロジックを追加しています。

  1. 型によるソート(インターフェーススイッチの場合):

    // sort by type (for switches on interface)
    ct = n1->val.ctype;
    if(ct != n2->val.ctype)
        return ct - n2->val.ctype;
    

    この部分は変更前と変わらず、定数型が異なる場合はその差を返します。これは、異なる定数型を持つcaseを区別するための基本的なソート順を提供します。

  2. 具象型の比較:

    if(!eqtype(n1->type, n2->type)) {
        if(n1->type->vargen > n2->type->vargen)
            return +1;
        else
            return -1;
    }
    

    ここが重要な変更点です。ct != n2->val.ctypefalse(つまり、定数型が同じ)であっても、n1->typen2->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の変更

  1. 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のようなもので、これによって型の比較を正確に行います。
  2. 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言語の公式ドキュメント
  • 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のエイリアス型)のように、値としては同じ11.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言語では、数値定数(例: 11.0"hello")は、その使用される文脈によって型が推論されます。例えば、1int型として扱われることが多いですが、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は、以下のロジックを追加しています。

  1. 型によるソート(インターフェーススイッチの場合):

    // sort by type (for switches on interface)
    ct = n1->val.ctype;
    if(ct != n2->val.ctype)
        return ct - n2->val.ctype;
    

    この部分は変更前と変わらず、定数型が異なる場合はその差を返します。これは、異なる定数型を持つcaseを区別するための基本的なソート順を提供します。

  2. 具象型の比較:

    if(!eqtype(n1->type, n2->type)) {
        if(n1->type->vargen > n2->type->vargen)
            return +1;
        else
            return -1;
    }
    

    ここが重要な変更点です。ct != n2->val.ctypefalse(つまり、定数型が同じ)であっても、n1->typen2->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の変更

  1. 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のようなもので、これによって型の比較を正確に行います。
  2. 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言語の公式ドキュメント
  • Go言語のソースコード(src/cmd/gc/swt.c
  • Go言語のIssueトラッカー
  • Go言語のCode Reviewシステム