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

[インデックス 15807] ファイルの概要

このコミットでは、Goコンパイラのcmd/gcパッケージ内のconst.ctypecheck.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関数は、リテラル(定数)を特定の型に変換する役割を担っています。このコミットでは、OCONFLEXcomplex()ビルトイン関数の内部表現)の場合の処理が追加されました。

変更前は、complex()の引数がTIDEAL型の場合、適切な型推論が行われず、デフォルトでcomplex128に変換されるか、あるいは型エラーが発生する可能性がありました。

変更後、convlit1は以下のロジックでOCONFLEXノードを処理します。

  1. ターゲット型の確認: complex()の結果が変換されるべき型tを調べます。
  2. TIDEAL型の場合: もしcomplex()の結果がまだTIDEAL型である場合(つまり、まだ具体的な型が決定されていない場合)、以下の推論を行います。
    • TCOMPLEX128への変換: ターゲット型がTCOMPLEX128であるか、または他の非複素数型である場合(この場合はcomplex128にフォールバックし、後で型チェッカーがエラーを報告する)、complex128型をn->typeに設定します。そして、実部と虚部(n->leftn->right)をそれぞれfloat64型に変換するようconvlitを呼び出します。
    • TCOMPLEX64への変換: ターゲット型がTCOMPLEX64である場合、complex64型をn->typeに設定します。そして、実部と虚部をそれぞれfloat32型に変換するようconvlitを呼び出します。
  3. OREALOIMAGidealkind: idealkind関数は、理想的な型を持つノードの種類を返します。OREALreal()ビルトイン関数の内部表現)とOIMAGimag()ビルトイン関数の内部表現)に対して、CTFLT(浮動小数点定数)を返すように変更されました。これは、real()imag()の結果が浮動小数点数であることをコンパイラに正しく伝えるためです。同様に、OCONFLEXに対してはCTCPLX(複素数定数)を返すように変更されました。

typecheck関数の変更

src/cmd/gc/typecheck.c内のtypecheck関数は、式の型をチェックし、必要に応じて型変換を行う主要な場所です。このコミットでは、OCONFLEXノードの型チェックロジックが改善されました。

変更前は、complex()の引数の型が一致しない場合に、一般的なbadcmplxラベルにジャンプして「mismatched types」エラーを報告していました。しかし、これは引数が浮動小数点型であるべきというより具体的なエラーメッセージではありませんでした。

変更後、complex()の引数lrTIDEAL型である場合に、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()ビルトイン関数の内部表現であるOREALOIMAGが、理想的な型としてCTFLT(浮動小数点定数)を返すように変更されました。これは、これらの関数の結果が常に浮動小数点数であることをコンパイラに明示します。
    • complex()ビルトイン関数の内部表現であるOCONFLEXが、理想的な型としてCTCPLX(複素数定数)を返すように変更されました。これは、complex()の結果が常に複素数であることをコンパイラに明示します。
    • これらの変更は、型推論の精度を高め、コンパイラがこれらのビルトイン関数の戻り値の型をより正確に理解できるようにします。

src/cmd/gc/typecheck.cの変更点

  • OCONFLEXの型チェックロジックの改善:
    • complex()の引数lrdefaultlit2(型付けされていないリテラルをデフォルトの型に変換する関数)を呼び出した後も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) (a4float64) と complex(1<<s, b4) (b4int): シフト演算の結果と変数との組み合わせで、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言語の型システムに関する一般的な知識