[インデックス 15807] ファイルの概要
このコミットでは、Goコンパイラのcmd/gc
パッケージ内のconst.c
とtypecheck.c
、およびテストファイルtest/shift1.go
が変更されています。主な変更は、complex()
ビルトイン関数の型推論に関するバグ修正と、それに関連するテストケースの追加です。
コミット
commit 861aa4698ada865ab402b47e6c201da8f4e567b3
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Sat Mar 16 00:37:28 2013 +0100
cmd/gc: missing type inference for untyped complex() calls.
Fixes #5014.
R=golang-dev, r, rsc, daniel.morsing
CC=golang-dev
https://golang.org/cl/7664043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/861aa4698ada865ab402b47e6c201da8f4e567b3
元コミット内容
cmd/gc: missing type inference for untyped complex() calls.
Fixes #5014.
R=golang-dev, r, rsc, daniel.morsing
CC=golang-dev
https://golang.org/cl/7664043
変更の背景
このコミットは、Goコンパイラ(cmd/gc
)における、型付けされていないcomplex()
ビルトイン関数の呼び出しに対する型推論の欠陥を修正することを目的としています。具体的には、complex(real, imag)
のように引数が型付けされていない定数である場合に、コンパイラが適切な型を推論できず、誤った型エラーや予期せぬ挙動を引き起こす可能性がありました。
Go言語では、数値定数(整数、浮動小数点数、複素数)は、明示的に型が指定されない限り「型付けされていない(untyped)」状態として扱われます。これにより、より柔軟な数値演算が可能になりますが、特定のコンテキスト(例えば、ビルトイン関数の引数)では、コンパイラが最終的な型を決定するための適切な推論ロジックが必要となります。
この問題は、GoのIssue #5014として報告されており、このコミットはその問題を解決するためのものです。
前提知識の解説
Go言語の型システムとUntyped定数
Go言語の型システムは静的型付けでありながら、ある程度の柔軟性を持っています。特に数値定数に関しては、「型付けされていない定数(untyped constants)」という概念があります。
- 型付けされていない定数:
10
,3.14
,1i
のような数値リテラルは、デフォルトでは特定の型を持ちません。これらは「理想的な型(ideal type)」を持つと表現され、その値が収まる任意の型に変換可能です。例えば、var x float64 = 10
のように、10
は自動的にfloat64
に変換されます。 - 型推論: Goコンパイラは、変数の宣言や関数の引数など、コンテキストから型を推論します。型付けされていない定数は、この型推論のプロセスにおいて、周囲のコンテキストに基づいて具体的な型が決定されます。
complex()
, real()
, imag()
ビルトイン関数
Go言語には、複素数型を扱うための以下のビルトイン関数があります。
complex(realPart, imagPart)
: 実部と虚部から複素数を作成します。引数は同じ浮動小数点型である必要があり、結果は引数の型に対応する複素数型(complex64
またはcomplex128
)になります。real(complexNum)
: 複素数の実部を返します。imag(complexNum)
: 複素数の虚部を返します。
これらの関数に型付けされていない定数を渡した場合、コンパイラは適切な複素数型を推論する必要があります。
Goコンパイラ(cmd/gc
)の役割
cmd/gc
はGo言語の公式コンパイラです。コンパイルプロセスは複数のフェーズに分かれており、このコミットが関連するのは主に以下の部分です。
const.c
: 定数(リテラル)の処理と型変換を担当します。型付けされていない定数が特定の型に変換される際のロジックが含まれます。typecheck.c
: 型チェックと型推論の主要なロジックが含まれます。式の型を決定し、型の一貫性を検証します。
TIDEAL
型
Goコンパイラの内部では、型付けされていない定数はTIDEAL
という特別な型として表現されます。これは、その定数がまだ具体的なGoの型にバインドされていないことを示します。コンパイルの過程で、TIDEAL
型の値は最終的にint
, float64
, complex128
などの具体的な型に変換されます。
技術的詳細
このコミットの核心は、complex()
ビルトイン関数の引数が型付けされていない定数(TIDEAL
型)である場合に、コンパイラがどのように適切な複素数型(complex64
またはcomplex128
)を推論し、実部と虚部を対応する浮動小数点型(float32
またはfloat64
)に変換するかという点にあります。
convlit1
関数の変更
src/cmd/gc/const.c
内のconvlit1
関数は、リテラル(定数)を特定の型に変換する役割を担っています。このコミットでは、OCONFLEX
(complex()
ビルトイン関数の内部表現)の場合の処理が追加されました。
変更前は、complex()
の引数がTIDEAL
型の場合、適切な型推論が行われず、デフォルトでcomplex128
に変換されるか、あるいは型エラーが発生する可能性がありました。
変更後、convlit1
は以下のロジックでOCONFLEX
ノードを処理します。
- ターゲット型の確認:
complex()
の結果が変換されるべき型t
を調べます。 TIDEAL
型の場合: もしcomplex()
の結果がまだTIDEAL
型である場合(つまり、まだ具体的な型が決定されていない場合)、以下の推論を行います。TCOMPLEX128
への変換: ターゲット型がTCOMPLEX128
であるか、または他の非複素数型である場合(この場合はcomplex128
にフォールバックし、後で型チェッカーがエラーを報告する)、complex128
型をn->type
に設定します。そして、実部と虚部(n->left
とn->right
)をそれぞれfloat64
型に変換するようconvlit
を呼び出します。TCOMPLEX64
への変換: ターゲット型がTCOMPLEX64
である場合、complex64
型をn->type
に設定します。そして、実部と虚部をそれぞれfloat32
型に変換するようconvlit
を呼び出します。
OREAL
とOIMAG
のidealkind
:idealkind
関数は、理想的な型を持つノードの種類を返します。OREAL
(real()
ビルトイン関数の内部表現)とOIMAG
(imag()
ビルトイン関数の内部表現)に対して、CTFLT
(浮動小数点定数)を返すように変更されました。これは、real()
やimag()
の結果が浮動小数点数であることをコンパイラに正しく伝えるためです。同様に、OCONFLEX
に対してはCTCPLX
(複素数定数)を返すように変更されました。
typecheck
関数の変更
src/cmd/gc/typecheck.c
内のtypecheck
関数は、式の型をチェックし、必要に応じて型変換を行う主要な場所です。このコミットでは、OCONFLEX
ノードの型チェックロジックが改善されました。
変更前は、complex()
の引数の型が一致しない場合に、一般的なbadcmplx
ラベルにジャンプして「mismatched types」エラーを報告していました。しかし、これは引数が浮動小数点型であるべきというより具体的なエラーメッセージではありませんでした。
変更後、complex()
の引数l
とr
がTIDEAL
型である場合に、defaultlit2
(型付けされていないリテラルをデフォルトの型に変換する関数)を呼び出した後、再度TIDEAL
型でないことを確認するチェックが追加されました。これにより、引数がまだ型付けされていない状態であれば、エラー処理に進むことができます。
さらに、引数の型がTIDEAL
型でない場合(つまり、すでに具体的な型が決定されている場合)のswitch
文において、default
ケースでのエラーメッセージがより具体的になりました。以前は「mismatched types」エラーにフォールバックしていましたが、新しいメッセージは「arguments have type %T, expected floating-point」(引数は%T型ですが、浮動小数点型が期待されます)と、complex()
の引数が浮動小数点型であるべきという制約を明確に示します。
これらの変更により、complex()
の引数が型付けされていない定数である場合に、コンパイラがより正確な型推論を行い、必要に応じて適切な型エラーを報告できるようになりました。
コアとなるコードの変更箇所
src/cmd/gc/const.c
--- a/src/cmd/gc/const.c
+++ b/src/cmd/gc/const.c
@@ -119,6 +119,27 @@ convlit1(Node **np, Type *t, int explicit)
}
n->type = t;
return;
+ case OCOMPLEX:
+ if(n->type->etype == TIDEAL) {
+ switch(t->etype) {
+ default:
+ // If trying to convert to non-complex type,
+ // leave as complex128 and let typechecker complain.
+ t = types[TCOMPLEX128];
+ //fallthrough
+ case TCOMPLEX128:
+ n->type = t;
+ convlit(&n->left, types[TFLOAT64]);
+ convlit(&n->right, types[TFLOAT64]);
+ break;
+ case TCOMPLEX64:
+ n->type = t;
+ convlit(&n->left, types[TFLOAT32]);
+ convlit(&n->right, types[TFLOAT32]);
+ break;
+ }
+ }
+ return;
}
// avoided repeated calculations, errors
@@ -1068,6 +1089,11 @@ idealkind(Node *n)
return k1;
else
return k2;
+ case OREAL:
+ case OIMAG:
+ return CTFLT;
+ case OCOMPLEX:
+ return CTCPLX;
case OADDSTR:
return CTSTR;
case OANDAND:
src/cmd/gc/typecheck.c
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -1180,16 +1180,18 @@ reswitch:
if(l->type == T || r->type == T)
goto error;
defaultlit2(&l, &r, 0);
+ if(l->type == T || r->type == T)
+ goto error;
n->left = l;
n->right = r;
if(!eqtype(l->type, r->type)) {
-\t\tbadcmplx:
yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type);
goto error;
}
switch(l->type->etype) {
default:
-\t\t\tgoto badcmplx;
+ yyerror("invalid operation: %N (arguments have type %T, expected floating-point)", n, l->type, r->type);
+ goto error;
case TIDEAL:
t = types[TIDEAL];
break;
test/shift1.go
--- a/test/shift1.go
+++ b/test/shift1.go
@@ -42,4 +42,16 @@ var (
a3 = 1.0<<s + 0 // ERROR "invalid operation|shift of non-integer operand"
// issue 4937
b3 = 1<<s + 1 + 1.0 // ERROR "invalid operation|shift of non-integer operand"
+ // issue 5014
+ c3 = complex(1<<s, 0) // ERROR "shift of type float64"
+ d3 int = complex(1<<s, 3) // ERROR "cannot use.*as type int" "shift of type float64"
+ e3 = real(1 << s) // ERROR "invalid"
+ f3 = imag(1 << s) // ERROR "invalid"
+)
+
+var (
+ a4 float64
+ b4 int
+ c4 = complex(1<<s, a4) // ERROR "shift of type float64"
+ d4 = complex(1<<s, b4) // ERROR "invalid"
)
コアとなるコードの解説
src/cmd/gc/const.c
の変更点
-
convlit1
関数内のOCONFLEX
ケースの追加:complex()
ビルトイン関数の内部表現であるOCONFLEX
ノードが、まだTIDEAL
型(型付けされていない状態)である場合に、そのノードの型推論と引数の型変換を行います。- ターゲットの型が
TCOMPLEX128
(または他の非複素数型で、complex128
にフォールバックする場合)であれば、complex128
型をノードに設定し、実部と虚部をfloat64
に変換します。 - ターゲットの型が
TCOMPLEX64
であれば、complex64
型をノードに設定し、実部と虚部をfloat32
に変換します。 - これにより、
complex(1, 2)
のような型付けされていない定数引数を持つcomplex()
呼び出しが、周囲のコンテキスト(例えば、代入先の変数型)に基づいて適切にcomplex128
またはcomplex64
に型推論され、その引数も対応する浮動小数点型に変換されるようになります。
-
idealkind
関数内のOREAL
,OIMAG
,OCONFLEX
ケースの追加:real()
とimag()
ビルトイン関数の内部表現であるOREAL
とOIMAG
が、理想的な型としてCTFLT
(浮動小数点定数)を返すように変更されました。これは、これらの関数の結果が常に浮動小数点数であることをコンパイラに明示します。complex()
ビルトイン関数の内部表現であるOCONFLEX
が、理想的な型としてCTCPLX
(複素数定数)を返すように変更されました。これは、complex()
の結果が常に複素数であることをコンパイラに明示します。- これらの変更は、型推論の精度を高め、コンパイラがこれらのビルトイン関数の戻り値の型をより正確に理解できるようにします。
src/cmd/gc/typecheck.c
の変更点
OCONFLEX
の型チェックロジックの改善:complex()
の引数l
とr
がdefaultlit2
(型付けされていないリテラルをデフォルトの型に変換する関数)を呼び出した後もTIDEAL
型である場合、エラー処理に進むための追加のチェックが導入されました。これにより、型推論が失敗した場合に適切にエラーを報告できます。- 引数の型が一致しない場合のエラーメッセージがより具体的になりました。以前の一般的な「mismatched types」エラーから、「arguments have type %T, expected floating-point」(引数は%T型ですが、浮動小数点型が期待されます)というメッセージに変更され、
complex()
の引数には浮動小数点型が期待されるという制約を明確に示します。 - これにより、開発者は
complex()
の引数に関する型エラーが発生した場合に、より分かりやすい診断メッセージを受け取ることができます。
test/shift1.go
の変更点
- Issue #5014に関連するテストケースの追加:
complex(1<<s, 0)
: シフト演算の結果が浮動小数点型になるため、complex()
の引数として不適切であることを示すエラーが期待されます。complex(1<<s, 3)
: 同様に、シフト演算の結果と整数リテラルがcomplex()
の引数として不適切であり、かつint
型への代入も不適切であることを示すエラーが期待されます。real(1 << s)
とimag(1 << s)
:real()
とimag()
の引数としてシフト演算の結果が不適切であることを示すエラーが期待されます。complex(1<<s, a4)
(a4
はfloat64
) とcomplex(1<<s, b4)
(b4
はint
): シフト演算の結果と変数との組み合わせで、complex()
の引数として不適切であることを示すエラーが期待されます。- これらのテストケースは、
complex()
,real()
,imag()
ビルトイン関数が、型付けされていない定数やシフト演算の結果のような特定のコンテキストでどのように型推論され、どのような場合にエラーとなるべきかを検証します。
これらの変更により、Goコンパイラはcomplex()
ビルトイン関数の型推論をより堅牢に行い、開発者に対してより正確で分かりやすい型エラーメッセージを提供するようになりました。
関連リンク
- Go Issue #5014: このコミットが修正した問題のトラッキング。ただし、古いIssueのため、現在のGoのIssueトラッカーでは直接見つからない可能性があります。
- Go言語仕様: https://go.dev/ref/spec (特に「Constants」と「Built-in functions」のセクション)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Goコンパイラのソースコード (
src/cmd/gc/
) - Go言語の型システムに関する一般的な知識