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

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

このコミットは、Goコンパイラ (cmd/gc) における内部エラーを修正するものです。具体的には、定数(特に浮動小数点数や文字列)の型変換処理において、変換後の新しい型に対する範囲チェックを行う際に、誤って変換前の古い値を使用してしまうことで発生していた「internal compiler error」(内部コンパイラエラー)を解決します。これにより、コンパイラの安定性と正確性が向上しました。

コミット

commit 74ce581b06fc4f0de1c862604830ce312283a7db
Author: Russ Cox <rsc@golang.org>
Date:   Tue May 27 21:38:19 2014 -0400

    cmd/gc: fix conversion of runtime constant

    The code cannot have worked before, because it was
    trying to use the old value in a range check for the new
    type, which might have a different representation
    (hence the 'internal compiler error').

    Fixes #8073.

    LGTM=iant
    R=golang-codereviews, iant
    CC=golang-codereviews
    https://golang.org/cl/98630045
---
 src/cmd/gc/const.c          |  1 +
 test/fixedbugs/issue8073.go | 15 +++++++++++++++
 2 files changed, 16 insertions(+)

diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
index 1b46974581..143c1730d2 100644
--- a/src/cmd/gc/const.c
+++ b/src/cmd/gc/const.c
@@ -951,6 +951,7 @@ unary:
 	case TUP(OCONV, CTFLT):\n 	case TUP(OCONV, CTSTR):\n 	\tconvlit1(&nl, n->type, 1);\n+\t\tv = nl->val;\n 	\tbreak;\n \n 	case TUP(OPLUS, CTINT):\ndiff --git a/test/fixedbugs/issue8073.go b/test/fixedbugs/issue8073.go
new file mode 100644
index 0000000000..6601221104
--- /dev/null
+++ b/test/fixedbugs/issue8073.go
@@ -0,0 +1,15 @@
+// compile
+
+// Copyright 2014 The Go Authors.  All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n
+// issue 8073.\n// was "internal compiler error: overflow: float64 integer constant"\n
+package main\n
+func main() {\n\tvar x int\n\t_ = float64(x * 0)\n}\n

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/74ce581b06fc4f0de1c862604830ce312283a7db

元コミット内容

cmd/gc: fix conversion of runtime constant

The code cannot have worked before, because it was
trying to use the old value in a range check for the new
type, which might have a different representation
(hence the 'internal compiler error').

Fixes #8073.

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/98630045

変更の背景

この変更は、Goコンパイラ (cmd/gc) が特定の定数変換を処理する際に発生していた「internal compiler error: overflow: float64 integer constant」という内部エラーを修正するために行われました。

問題の根本原因は、コンパイラが定数リテラルをある型から別の型へ変換した後、その変換結果の値を正しく利用できていなかったことにあります。具体的には、convlit1 という内部関数が定数リテラルの変換処理を行いますが、この関数が完了した後も、コンパイラは変換前の古い値(v 変数に格納されていた可能性のある値)を使って、変換後の新しい型に対する範囲チェックを行おうとしていました。

新しい型は、変換前の型とは異なる内部表現を持つ可能性があるため、古い値を使って範囲チェックを行うと、誤った結果(この場合はオーバーフロー)が生じ、結果としてコンパイラが異常終了するという問題が発生していました。コミットメッセージにある「The code cannot have worked before」という記述は、このロジックの根本的な欠陥を指摘しています。

このバグは、Go言語の定数処理の正確性に影響を与え、特定のコードパターンでコンパイルが失敗する原因となっていました。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびGoコンパイラに関する前提知識が必要です。

  • Goコンパイラ (cmd/gc): Go言語の公式コンパイラであり、Goソースコードを機械語に変換する役割を担います。gc は "Go compiler" の略です。コンパイルプロセスは、字句解析、構文解析、型チェック、最適化、コード生成など、複数のフェーズに分かれています。このコミットが関連するのは、主に型チェックと定数処理のフェーズです。

  • Goにおける定数 (Constants): Go言語の定数は、コンパイル時に値が決定される不変のエンティティです。Goの定数には、大きく分けて「型なし定数 (untyped constants)」と「型付き定数 (typed constants)」があります。

    • 型なし定数: リテラル(例: 10, 3.14, "hello") はデフォルトで型なし定数として扱われます。これらは、使用される文脈に応じて適切な型に「デフォルト型付け (default-typed)」されるか、明示的な型変換によって型が与えられます。型なし定数は、その値が表現できる限り、任意の数値型や文字列型に変換できます。
    • 型付き定数: const x int = 10 のように、明示的に型が指定された定数です。 このコミットは、特に型なし定数が型付きの変数に代入される際や、明示的に型変換される際に発生する問題に関連しています。
  • 型変換 (Type Conversion): Go言語では、異なる型の間の代入や演算には厳格な型チェックが行われます。異なる型間で値を扱う場合、明示的な型変換(例: float64(x))が必要になることがあります。コンパイラは、この型変換が有効であるか、また変換によって値がオーバーフローしないかなどをチェックします。

  • コンパイラ内部のノード表現: Goコンパイラは、ソースコードを抽象構文木 (AST) として内部的に表現します。ASTの各ノードは、演算子、変数、定数などを表します。

    • OCONV: ASTノードの一種で、型変換操作を表します。
    • CTFLT: 定数ノードのカテゴリで、浮動小数点数定数を表します。
    • CTSTR: 定数ノードのカテゴリで、文字列定数を表します。
    • convlit1: src/cmd/gc/const.c に存在するコンパイラ内部関数で、定数リテラルの型変換処理を担当します。この関数は、与えられた定数ノードを目的の型に変換し、その結果を新しいノードとして返します。

技術的詳細

このバグは、src/cmd/gc/const.c ファイル内の unary ラベルが付いたセクション、特に OCONV(型変換)操作を処理する部分で発生していました。

Goコンパイラは、convlit1(&nl, n->type, 1); という関数呼び出しによって、定数リテラル nl を目的の型 n->type に変換します。この convlit1 関数は、変換後の新しい定数ノードを nl に書き戻します。しかし、問題のコードでは、convlit1 の呼び出し後、v という変数が nl の新しい値ではなく、convlit1 呼び出し前の古い nl の値(つまり変換前の定数)を保持したままになっていました。

その後の処理で、この古い v の値を使って、変換後の新しい型に対する範囲チェック(例えば、float64 から int への変換で、float64 の値が int の範囲に収まるかどうかのチェック)が行われていました。

コミットメッセージにある「trying to use the old value in a range check for the new type, which might have a different representation」とは、まさにこの状況を指しています。例えば、非常に大きな浮動小数点数定数を int に変換しようとした場合、convlit1 はその変換を試みますが、v が古い浮動小数点数の値を保持しているため、int の範囲チェックでオーバーフローが発生し、「overflow: float64 integer constant」という内部コンパイラエラーを引き起こしていました。

修正は非常にシンプルで、convlit1(&nl, n->type, 1); の直後に v = nl->val; を追加することです。この一行が追加されることで、v 変数は convlit1 によって更新された nl の新しい値(つまり変換後の定数)を確実に取得するようになります。これにより、その後の範囲チェックが変換後の正しい値に対して行われるようになり、内部コンパイラエラーが解消されました。

test/fixedbugs/issue8073.go は、このバグを再現するための最小限のテストケースです。_ = float64(x * 0) というコードは、x * 0 が型なしの整数定数 0 となり、それを float64 に変換するという、一見すると無害な操作に見えます。しかし、コンパイラの内部処理において、この定数 0 の変換が問題のロジックパスをトリガーし、修正前のコンパイラでは内部エラーを引き起こしていました。

コアとなるコードの変更箇所

このコミットによる主要なコード変更は以下の2箇所です。

  1. src/cmd/gc/const.c の変更:

    --- a/src/cmd/gc/const.c
    +++ b/src/cmd/gc/const.c
    @@ -951,6 +951,7 @@ unary:
     	case TUP(OCONV, CTFLT):
     	case TUP(OCONV, CTSTR):
     		convlit1(&nl, n->type, 1);
    +		v = nl->val;
     		break;
    
     	case TUP(OPLUS, CTINT):
    
  2. test/fixedbugs/issue8073.go の新規追加:

    // compile
    
    // Copyright 2014 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.
    
    // issue 8073.
    // was "internal compiler error: overflow: float64 integer constant"
    
    package main
    
    func main() {
    	var x int
    	_ = float64(x * 0)
    }
    

コアとなるコードの解説

  • src/cmd/gc/const.c の変更: このファイルはGoコンパイラの定数処理ロジックを含んでいます。変更が加えられたのは、unary ラベル内の OCONV(型変換)操作を処理する部分です。 convlit1(&nl, n->type, 1); は、定数リテラル nl を、ターゲットの型 n->type に変換するコンパイラ内部関数です。この関数は nl をポインタで受け取り、変換結果をそのポインタが指す先に書き込みます。 追加された v = nl->val; の行は、convlit1 が完了した直後に、nl が指す新しい定数ノードの値 (nl->val) を v 変数に代入することを保証します。これにより、その後のコンパイラ処理(特に範囲チェックなど)が、変換後の正しい定数値に基づいて行われるようになり、以前発生していた「internal compiler error」が解消されます。

  • test/fixedbugs/issue8073.go の新規追加: このファイルは、Goコンパイラのバグをテストするためのものです。// compile コメントは、このファイルがコンパイル可能であるべきことを示します。 // was "internal compiler error: overflow: float64 integer constant" というコメントは、このテストケースが修正前のコンパイラでどのようなエラーを引き起こしていたかを明示しています。 テストケースの核心は _ = float64(x * 0) という行です。

    • x * 0: xint 型の変数であるため、x * 0 は型なしの整数定数 0 と評価されます。
    • float64(...): この型なし定数 0float64 型に明示的に変換しています。 この一見単純な変換が、修正前のコンパイラでは convlit1 呼び出し後の v 変数の値の不整合を引き起こし、結果として内部エラーを発生させていました。このテストケースが追加されたことで、将来的に同様の回帰バグが発生しないことが保証されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(定数、型変換に関する一般的な情報)
  • Goコンパイラのソースコード(src/cmd/gc/const.c の内容)
  • コミットメッセージ自体
  • Go言語のバグトラッカー(Issue #8073に関する情報、ただし公開されている情報が少ないため、コミットメッセージからの推測が主)