[インデックス 14495] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)における型チェックの挙動、特にブーリアン型の「理想性(idealness)」に関するバグ修正と、それに関連するテストケースの追加を行っています。
コミット
commit 5188c0b59cfb9f904ece4ee7e8f69d56ab15fbbd
Author: Daniel Morsing <daniel.morsing@gmail.com>
Date: Mon Nov 26 22:23:13 2012 +0100
cmd/gc: Make sure bools lose idealness when used with logical operators.
Bools from comparisons can be assigned to all bool types, but this idealness would propagate through logical operators when the result should have been lowered to a non-ideal form.
Fixes #3924.
R=golang-dev, remyoudompheng, r, rsc, mtj
CC=golang-dev
https://golang.org/cl/6855061
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5188c0b59cfb9f904ece4ee7e8f69d56ab15fbbd
元コミット内容
cmd/gc: Make sure bools lose idealness when used with logical operators.
比較演算子から得られるブーリアン値は、すべてのブーリアン型に代入可能ですが、この「理想性」が論理演算子を介して伝播してしまい、結果が非理想的な形式に変換されるべき場合にそうならないという問題がありました。
このコミットは #3924 を修正します。
変更の背景
Go言語のコンパイラには、特定の定数や比較演算の結果に対して「理想型(ideal type)」という概念が存在します。これは、コンパイル時に型がまだ確定しておらず、より柔軟な型推論や型変換を可能にするための内部的な表現です。例えば、true
や false
といったブーリアンリテラルや、x < y
のような比較演算の結果は、最初は特定の具象型(bool
)ではなく、「理想的なブーリアン型(ideal boolean type)」として扱われます。これにより、var b bool = x < y
のように、直接 bool
型の変数に代入したり、type mybool bool; var mb mybool = x < y
のように、基底型が bool
であるユーザー定義型に代入したりすることが可能になります。
しかし、このコミットが修正しようとしている問題は、この「理想性」が論理演算子(&&
や ||
)を介して不適切に伝播してしまうことでした。具体的には、x < y && z > w
のような式において、x < y
と z > w
の結果はそれぞれ理想的なブーリアン型として扱われますが、それらを論理演算子で結合した結果も、本来であれば具象的な bool
型に「格下げ(lower)」されるべきなのに、理想的なブーリアン型のままで扱われてしまうというバグがありました。
この挙動は、ユーザー定義のブーリアン型(例: type mybool bool
)への代入時に問題を引き起こしました。理想的なブーリアン型は、任意のブーリアン型に代入可能ですが、論理演算の結果は、その「理想性」を失い、通常の bool
型として扱われるべきです。もし理想性が維持されたままだと、コンパイラが誤ってユーザー定義型への代入を許可してしまう可能性がありました。
この問題は Issue 3924 として報告されており、このコミットはその修正を目的としています。
前提知識の解説
Go言語の型システムにおける「理想型(Ideal Type)」
Go言語の型システムには、数値リテラルやブーリアンリテラル、比較演算の結果など、特定の式に対して「型なし(untyped)」または「理想型(ideal type)」という概念が存在します。これは、コンパイルの初期段階でこれらの値に厳密な具象型を割り当てず、文脈に応じて柔軟に型推論や型変換を行えるようにするためのGoコンパイラの内部的な仕組みです。
- 型なし定数(Untyped Constants):
100
や3.14
、"hello"
、true
などは、最初は特定の型を持たない「型なし定数」として扱われます。これにより、var i int = 100
やvar f float64 = 100
のように、異なる数値型に同じリテラルを代入できます。 - 理想的なブーリアン(Ideal Boolean):
true
,false
といったブーリアンリテラルや、x < y
のような比較演算の結果は、Goコンパイラの内部では「理想的なブーリアン型(idealbool
)」として表現されます。これは、通常のbool
型だけでなく、type MyBool bool
のようにbool
を基底型とするユーザー定義型にも直接代入できる柔軟性を提供します。
この「理想性」は、式がより複雑になるにつれて失われ、最終的には具象型(例: bool
)に「格下げ」されるのが通常の挙動です。例えば、true && false
のような論理演算の結果は、最終的には通常の bool
型として扱われるべきです。
cmd/gc
cmd/gc
は、Go言語の公式コンパイラです。Goのソースコードを機械語に変換する役割を担っています。このコンパイラは、型チェック、最適化、コード生成など、コンパイルプロセスの様々な段階を処理します。このコミットで変更されている src/cmd/gc/typecheck.c
は、コンパイラの型チェックフェーズを担当する部分のC言語ソースファイルです。
論理演算子 (&&
, ||
)
Go言語における論理演算子 &&
(AND) と ||
(OR) は、ブーリアン値を結合するために使用されます。これらの演算子は、短絡評価(short-circuit evaluation)を行います。つまり、A && B
の場合、A
が false
なら B
は評価されません。A || B
の場合、A
が true
なら B
は評価されません。
TBOOL
と idealbool
(コンパイラ内部型)
Goコンパイラの内部では、様々な型が独自の列挙型や構造体で表現されます。
TBOOL
: これは、Go言語の組み込み型であるbool
を表すコンパイラ内部の具象型です。idealbool
: これは、前述の「理想型」の概念に基づき、型がまだ確定していないブーリアン値を表すコンパイラ内部の特殊な型です。
技術的詳細
このコミットの技術的詳細は、Goコンパイラの型チェックロジック、特に src/cmd/gc/typecheck.c
内の typecheck
関数(または関連する型チェック処理)に焦点を当てています。
問題は、比較演算子(例: <
, >
, ==
など)の結果として生成される idealbool
が、論理演算子(&&
, ||
)と組み合わされた際に、その「理想性」を適切に失わないことにありました。
Goコンパイラは、式の型を決定する際に、その式のオペランドの型と演算子の種類を考慮します。比較演算の結果は idealbool
としてマークされますが、論理演算子の結果は、たとえオペランドが idealbool
であっても、最終的には具象的な bool
型(TBOOL
)に「格下げ」されるべきです。これは、論理演算の結果がもはや「型なし」の柔軟性を持つべきではないためです。
修正前は、typecheck.c
内の型チェックロジックが、論理演算子の結果が idealbool
である場合に、それを TBOOL
に変換する処理を欠いていました。これにより、例えば var _ mybool = x < y && x < y
のようなコードが、本来であれば型不一致エラー(cannot use
)となるべきなのに、コンパイルが通ってしまう可能性がありました。
このコミットは、typecheck.c
の reswitch
ラベル内の処理に、以下のロジックを追加することでこの問題を解決しています。
} else if(t == idealbool)
t = types[TBOOL];
このコードスニペットは、ある式の型 t
が idealbool
であり、かつその式が比較演算子以外の演算子(この文脈では論理演算子)の結果である場合に、その型を明示的に TBOOL
に変更しています。これにより、論理演算の結果が常に具象的な bool
型として扱われるようになり、不適切な型推論や代入が防止されます。
test/fixedbugs/issue3924.go
は、この修正が正しく機能することを確認するための新しいテストケースです。このテストは、mybool
というユーザー定義型を定義し、x < y && x < y
や x < y || x < y
のような論理演算の結果を mybool
型の変数に代入しようとします。修正が適用されていれば、これらの代入は「cannot use
」エラーを引き起こすはずです。これは、論理演算の結果が idealbool
ではなく bool
型に格下げされ、bool
型から mybool
型への暗黙的な変換が許可されないためです。
コアとなるコードの変更箇所
src/cmd/gc/typecheck.c
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -614,7 +614,10 @@ reswitch:
n->left = l;
n->right = r;
}
- }
+ // non-comparison operators on ideal bools should make them lose their ideal-ness
+ } else if(t == idealbool)
+ t = types[TBOOL];
+
if(et == TSTRING) {
if(iscmp[n->op]) {
n->etype = n->op;
test/fixedbugs/issue3924.go
--- /dev/null
+++ b/test/fixedbugs/issue3924.go
@@ -0,0 +1,13 @@
+// errorcheck
+
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package foo
+
+type mybool bool
+
+var x, y = 1, 2
+var _ mybool = x < y && x < y // ERROR "cannot use"
+var _ mybool = x < y || x < y // ERROR "cannot use"
コアとなるコードの解説
src/cmd/gc/typecheck.c
の変更
この変更は、typecheck.c
内の reswitch
ラベルの直後、特定の型チェックロジックのフローに挿入されています。
else if(t == idealbool)
: これは、現在処理している式の型t
がidealbool
であるかどうかをチェックしています。このコードブロックが実行される文脈は、比較演算子以外の演算子(この場合は論理演算子)が適用された結果の型を処理している場合です。t = types[TBOOL];
: もし型がidealbool
であれば、その型をGoの組み込みbool
型に対応するコンパイラ内部型TBOOL
に明示的に変更します。
この変更により、論理演算子によって結合された idealbool
の結果は、その「理想性」を失い、通常の bool
型として扱われるようになります。これにより、コンパイラはより厳密な型チェックを行い、bool
型からユーザー定義の mybool
型への暗黙的な代入を禁止するようになります。
test/fixedbugs/issue3924.go
の追加
このファイルは、Goのテストフレームワークにおける errorcheck
テストの一種です。errorcheck
テストは、特定のコードがコンパイル時に期待されるエラーを生成するかどうかを検証するために使用されます。
// errorcheck
: このコメントは、このファイルがerrorcheck
テストであることをコンパイラに指示します。type mybool bool
:bool
を基底型とする新しいユーザー定義型mybool
を定義しています。var x, y = 1, 2
: 比較演算に使用する整数変数x
とy
を宣言し、初期化しています。var _ mybool = x < y && x < y // ERROR "cannot use"
:x < y
はidealbool
を生成します。x < y && x < y
は、論理AND演算子によって結合された式です。- 修正前は、この式の結果が
idealbool
のままであったため、mybool
型への代入が許可されてしまう可能性がありました。 - 修正後は、論理AND演算の結果は
TBOOL
に格下げされるため、bool
型からmybool
型への直接代入は許可されず、「cannot use
」エラーが期待されます。
var _ mybool = x < y || x < y // ERROR "cannot use"
:- 同様に、論理OR演算子を使用した場合も、修正後は「
cannot use
」エラーが期待されます。
- 同様に、論理OR演算子を使用した場合も、修正後は「
このテストケースは、コンパイラの型チェックが、論理演算子の結果に対して「理想性」を適切に剥奪し、具象的な bool
型として扱うようになったことを検証しています。
関連リンク
- Go Issue 3924: https://github.com/golang/go/issues/3924
- Go CL 6855061: https://golang.org/cl/6855061
参考にした情報源リンク
- Go言語の型システムに関する公式ドキュメントやブログ記事 (Goの型推論、型なし定数など)
- Goコンパイラのソースコード (
src/cmd/gc/
) - Goのテストフレームワークに関するドキュメント (
errorcheck
テストについて) - Go言語の仕様書 (論理演算子、型変換規則など)
- Go Issue 3924 の議論スレッド