[インデックス 15699] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)におけるcomplex
組み込み関数の型チェックロジックを修正し、引数の型が厳密に一致しない場合にコンパイルエラーを発生させるように変更したものです。具体的には、Go言語の仕様で定められている「complex
関数の2つの引数は同じ浮動小数点型でなければならない」という要件をより厳密に強制するために、型比較の方法を改善しています。
コミット
commit 401e0fea3ad120a495f7c8770cfbf1278c00c16e
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Mon Mar 11 22:55:14 2013 +0100
cmd/gc: reject complex calls with mismatched argument types.
The specification says "the two arguments must be of the same
floating-point type."
R=rsc, gri
CC=golang-dev
https://golang.org/cl/7671045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/401e0fea3ad120a495f7c8770cfbf1278c00c16e
元コミット内容
cmd/gc: reject complex calls with mismatched argument types.
(cmd/gc
: complex
呼び出しにおける引数の型不一致を拒否する。)
The specification says "the two arguments must be of the same floating-point type."
(仕様では「2つの引数は同じ浮動小数点型でなければならない」と規定されている。)
変更の背景
Go言語には、実部と虚部から複素数を作成するための組み込み関数complex
が存在します。この関数は、complex(real, imag)
のように使用され、real
とimag
は同じ浮動小数点型(float32
またはfloat64
)である必要があります。しかし、このコミット以前のGoコンパイラでは、この型チェックが不十分でした。
具体的には、float32
とそれから派生したユーザー定義型(例: type MyFloat32 float32
)をcomplex
関数の引数として渡した場合、コンパイラはこれらを同じ「基本型」(float32
)として扱ってしまい、型が厳密に一致しないことを見落とす可能性がありました。Goの型システムでは、基底型が同じであっても、異なる型として宣言されたものは異なる型として扱われます。このコミットは、この仕様の厳密な解釈と実装の乖離を修正し、より堅牢な型安全性を提供することを目的としています。
前提知識の解説
Go言語の型システム
Go言語は静的型付け言語であり、変数は宣言時に型を持ち、その型に合った値のみを格納できます。Goの型システムは、以下のような特徴を持ちます。
- 基本型 (Basic Types):
int
,float32
,string
,bool
など、言語に組み込まれている型です。 - 複合型 (Composite Types): 配列、スライス、マップ、構造体、インターフェースなど、基本型を組み合わせて作られる型です。
- ユーザー定義型 (User-defined Types):
type NewType UnderlyingType
の形式で、既存の型を基にして新しい型を定義できます。例えば、type MyFloat32 float32
と定義した場合、MyFloat32
はfloat32
とは異なる新しい型として扱われます。ただし、基底型が同じであれば、型変換(キャスト)によって相互に変換可能です。
complex
組み込み関数
complex
関数は、Go言語の組み込み関数の一つで、2つの浮動小数点数から複素数を作成します。
- シグネチャ:
func complex(real, imag FloatType) ComplexType
- 引数:
real
とimag
は同じ浮動小数点型(float32
またはfloat64
)でなければなりません。 - 戻り値: 引数が
float32
の場合、complex64
を返します。引数がfloat64
の場合、complex128
を返します。
Go言語の仕様では、complex
関数の引数について「The two arguments must be of the same floating-point type.」(2つの引数は同じ浮動小数点型でなければならない。)と明記されています。
Goコンパイラの型チェック
Goコンパイラは、ソースコードを機械語に変換する過程で、プログラムの型がGo言語の仕様に準拠しているかを確認します。この型チェックの段階で、型の不一致や不正な操作が検出されると、コンパイルエラーが発生します。
Node
構造体: Goコンパイラの内部では、AST(抽象構文木)の各ノードがNode
構造体で表現されます。このNode
構造体には、そのノードが表す式の型情報を持つtype
フィールドが含まれています。Type
構造体:Type
構造体は、Go言語の型システムにおける型を表す内部表現です。この構造体には、型の種類(基本型、ポインタ型、構造体型など)を示すetype
(element type)フィールドや、型の詳細な情報が含まれます。etype
:etype
は、型の「種類」を示す列挙型のようなものです。例えば、float32
とMyFloat32
は異なる型ですが、どちらも基底型がfloat32
であるため、etype
は同じ値(例えばTFLOAT32
)を持つ可能性があります。eqtype
関数:eqtype
関数は、2つのType
構造体が「等しい型」であるかを厳密に比較する関数です。これは、単にetype
が同じであるかを見るだけでなく、型が持つすべての属性(例えば、ユーザー定義型であるか、ポインタの深さ、構造体のフィールドなど)を考慮して比較します。
技術的詳細
このコミットの核心は、complex
組み込み関数の引数に対する型チェックの厳密化です。以前のコンパイラでは、src/cmd/gc/typecheck.c
内のcomplex
関数の型チェックにおいて、引数の型がl->type->etype != r->type->etype
という条件で比較されていました。これは、引数の「基本型」が異なる場合にエラーを出すものでした。
しかし、このアプローチでは、以下のようなケースで問題が発生します。
type MyFloat32 float32
var f32 float32
var mf32 MyFloat32
// 以前のコンパイラではエラーにならなかった可能性
_ = complex(f32, mf32)
上記の例では、f32
とmf32
は異なる型ですが、どちらも基底型はfloat32
です。そのため、etype
は同じ値を持つ可能性があり、l->type->etype != r->type->etype
という比較では、この型不一致を検出できませんでした。Goの仕様では、これらは「同じ浮動小数点型」ではないため、エラーとなるべきです。
このコミットでは、この問題を解決するために、型比較を!eqtype(l->type, r->type)
に変更しました。eqtype
関数は、2つの型が完全に同一であるかをチェックします。これには、基底型だけでなく、型がユーザー定義型であるかどうかも含め、型のすべての属性が考慮されます。これにより、float32
とMyFloat32
のような、基底型は同じだが異なるユーザー定義型である引数がcomplex
関数に渡された場合に、コンパイラが正しく型不一致を検出できるようになります。
エラーメッセージも、より正確に「mismatched types」(型不一致)を示すように変更されました。
コアとなるコードの変更箇所
変更は主に以下の2つのファイルで行われています。
-
src/cmd/gc/typecheck.c
:complex
関数の型チェックロジックが変更されました。--- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -1182,9 +1182,9 @@ reswitch: defaultlit2(&l, &r, 0);\n \t\tn->left = l;\n \t\tn->right = r;\n-\t\tif(l->type->etype != r->type->etype) {\n+\t\tif(!eqtype(l->type, r->type)) {\n \t\tbadcmplx:\n-\t\t\tyyerror(\"invalid operation: %N (complex of types %T, %T)\", n, l->type, r->type);\n+\t\t\tyyerror(\"invalid operation: %N (mismatched types %T and %T)\", n, l->type, r->type);\n \t\t\tgoto error;\n \t\t}\n \t\tswitch(l->type->etype) {
l->type->etype != r->type->etype
が!eqtype(l->type, r->type)
に変更されました。- エラーメッセージが
"invalid operation: %N (complex of types %T, %T)"
から"invalid operation: %N (mismatched types %T and %T)"
に変更されました。
-
test/cmplx.go
: 新しいテストケースが追加され、この変更によって正しくエラーが検出されることを検証しています。package main type ( Float32 float32 Float64 float64 Complex64 complex64 Complex128 complex128 ) var ( f32 float32 f64 float64 F32 Float32 // ユーザー定義型 F64 Float64 // ユーザー定義型 c64 complex64 c128 complex128 C64 Complex64 // ユーザー定義型 C128 Complex128 // ユーザー定義型 ) func main() { _ = complex128(0) // ok _ = complex(f32, f64) // ERROR "complex" (既存のテスト) _ = complex(f64, f32) // ERROR "complex" (既存のテスト) // 新しく追加されたテストケース _ = complex(f32, F32) // ERROR "complex" - float32 と MyFloat32 の不一致 _ = complex(F32, f32) // ERROR "complex" - MyFloat32 と float32 の不一致 _ = complex(f64, F64) // ERROR "complex" - float64 と MyFloat64 の不一致 _ = complex(F64, f64) // ERROR "complex" - MyFloat64 と float64 の不一致 c128 = complex(f32, f32) // ERROR "cannot use" (既存のテスト) c64 = complex(f64, f64) // ERROR "cannot use" (既存のテスト) c64 = complex(1.0, 2.0) // ok, constant is untyped c128 = complex(1.0, 2.0) C64 = complex(1.0, 2.0) C128 = complex(1.0, 2.0) C64 = complex(f32, f32) // ERROR "cannot use" - ユーザー定義型への代入 C128 = complex(f64, f64) // ERROR "cannot use" - ユーザー定義型への代入 }
コアとなるコードの解説
src/cmd/gc/typecheck.c
における変更は、Goコンパイラの型チェックの精度を大幅に向上させます。
-
l->type->etype != r->type->etype
から!eqtype(l->type, r->type)
への変更:- 旧ロジック (
etype
比較):etype
は型の「基本カテゴリ」を示すため、float32
とtype MyFloat32 float32
のようなユーザー定義型は、どちらも基底型がfloat32
であるため、同じetype
を持つ可能性がありました。これにより、コンパイラはこれらを同じ型として誤認し、仕様に反してcomplex(float32, MyFloat32)
のような呼び出しを許可してしまう可能性がありました。 - 新ロジック (
eqtype
比較):eqtype
関数は、2つのType
構造体が完全に同一であるかを厳密に比較します。これには、型の基底型だけでなく、それがユーザー定義型であるか、ポインタの深さ、構造体のフィールド構成など、型のすべての属性が考慮されます。したがって、float32
とMyFloat32
はeqtype
関数によって異なる型であると正しく識別され、complex
関数に渡された場合にエラーが報告されるようになります。これは、Go言語の型システムの厳密なルールをコンパイラが正しく適用するために不可欠な変更です。
- 旧ロジック (
-
エラーメッセージの変更:
- 旧エラーメッセージ:
"invalid operation: %N (complex of types %T, %T)"
- 新エラーメッセージ:
"invalid operation: %N (mismatched types %T and %T)"
この変更は、エラーの原因が単に「complex型」であることではなく、「引数の型が一致しない」ことにあることをより明確にユーザーに伝えるためのものです。これにより、開発者は問題の根本原因をより迅速に特定し、修正することができます。
- 旧エラーメッセージ:
test/cmplx.go
に追加されたテストケースは、この変更が意図通りに機能することを確認するためのものです。特に、complex(f32, F32)
のような、基底型は同じだがユーザー定義型と基本型が混在するケースが正しくエラーとして扱われることを検証しています。
このコミットは、Go言語の型安全性を高め、言語仕様の厳密な遵守を保証する上で重要な改善です。
関連リンク
- Go CL 7671045: https://golang.org/cl/7671045
参考にした情報源リンク
- Go言語の仕様 (The Go Programming Language Specification): https://go.dev/ref/spec
- Go言語の組み込み関数 (Built-in functions): https://go.dev/ref/spec#Built-in_functions
- Go言語の型 (Types): https://go.dev/ref/spec#Types
- Goコンパイラのソースコード (Go compiler source code): https://github.com/golang/go/tree/master/src/cmd/compile
- Goコンパイラの型システムに関する議論やドキュメント (Go compiler type system discussions/documentation) - 必要に応じてWeb検索で参照。