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

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

コミット

commit 15bc7ab957406f293f817885b1f3939632d0fad5
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Tue Apr 1 02:55:38 2014 -0400

    cmd/gc: fix spurious "bad negated constant" for complex constants.
    Fixes #7648.
    
    LGTM=r, remyoudompheng
    R=golang-codereviews, r, remyoudompheng, jscrockett01
    CC=golang-codereviews
    https://golang.org/cl/80560045

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

https://github.com/golang/go/commit/15bc7ab957406f293f817885b1f3939632d0fad5

元コミット内容

cmd/gc: fix spurious "bad negated constant" for complex constants.
Fixes #7648.

LGTM=r, remyoudompheng
R=golang-codereviews, r, remyoudompheng, jscrockett01
CC=golang-codereviews
https://golang.org/cl/80560045

変更の背景

このコミットは、Goコンパイラ(cmd/gc)における、複素数定数の否定演算に関するバグを修正します。具体的には、複素数定数に対して否定演算子(-)を適用した場合に、コンパイラが誤って「bad negated constant」(不正な否定定数)というエラーを報告する問題(Issue #7648)を解決します。

Go言語では、定数式はコンパイル時に評価されます。数値定数(整数、浮動小数点数、複素数)は、その型に応じて適切な方法で処理されます。このバグは、複素数定数の否定演算がコンパイラの字句解析・構文解析フェーズで正しく認識されず、汎用的なエラーハンドリングにフォールバックしてしまうために発生していました。これにより、有効なGoコードがコンパイルエラーとなるという問題がありました。

前提知識の解説

Goコンパイラ (cmd/gc)

cmd/gcは、Go言語の公式コンパイラツールチェーンの一部であり、Goソースコードを機械語に変換する主要なコンパイラです。Goコンパイラは、伝統的なコンパイラのフェーズ(字句解析、構文解析、意味解析、中間コード生成、最適化、コード生成)を経て動作します。

Bison (GNU Parser Generator)

Bisonは、LALR(1)パーサジェネレータであり、Goコンパイラの構文解析器(パーサ)の生成に利用されています。go.yファイルはBisonの入力ファイル(文法定義ファイル)であり、Go言語の構文規則が記述されています。Bisonはこのgo.yファイルを読み込み、C言語のソースコード(y.tab.cおよびy.tab.h)を生成します。これらの生成されたファイルがコンパイラの一部としてコンパイルされ、Go言語のソースコードを解析する役割を担います。

字句解析と構文解析

  • 字句解析 (Lexical Analysis): ソースコードをトークン(キーワード、識別子、演算子、定数など)のストリームに分解するプロセスです。
  • 構文解析 (Syntax Analysis): 字句解析器から受け取ったトークンのストリームが、言語の文法規則に準拠しているかを検証し、抽象構文木(AST)を構築するプロセスです。

定数と定数型 (CTINT, CTFLT, CTCPLX)

Go言語では、数値定数はその値に応じて内部的に異なる型で表現されます。

  • CTINT: 整数定数 (e.g., 10, 0xFF)
  • CTFLT: 浮動小数点数定数 (e.g., 3.14, 1e-5)
  • CTCPLX: 複素数定数 (e.g., 1 + 2i, 3.0 - 4.5i)

これらの定数型は、コンパイラ内部で定数値を表現するための列挙型や構造体として定義されています。

複素数 (Complex Numbers)

Go言語は組み込みで複素数型(complex64, complex128)をサポートしています。複素数は実部と虚部から構成され、real + imag i の形式で表されます。複素数の否定は、実部と虚部の両方を否定することで行われます。例えば、-(a + bi)(-a) + (-b)i となります。

mpnegflt 関数

mpnegfltは、Goコンパイラ内部で使用される多倍長浮動小数点数(multi-precision floating-point number)を否定する(符号を反転させる)ための関数です。複素数の実部と虚部はそれぞれ浮動小数点数として扱われるため、複素数の否定にはこの関数が利用されます。

技術的詳細

このコミットの核心は、Goコンパイラの構文解析器の文法定義ファイルであるsrc/cmd/gc/go.yに、複素数定数の否定演算を明示的に処理するルールを追加した点にあります。

以前のgo.yでは、hidden_literalというルール内で定数の否定を処理する際に、CTINT(整数定数)とCTFLT(浮動小数点数定数)のケースは考慮されていましたが、CTCPLX(複素数定数)のケースが欠落していました。このため、複素数定数に否定演算子が付与された場合、パーサは適切な処理を見つけられず、defaultケースにフォールバックし、「bad negated constant」という汎用的なエラーを発生させていました。

修正では、hidden_literalルールにCTCPLXのケースが追加されました。この新しいケースでは、複素数定数の実部と虚部の両方に対してmpnegflt関数を呼び出すことで、複素数全体の否定を正しく行います。

src/cmd/gc/y.tab.csrc/cmd/gc/y.tab.hの変更は、go.yの変更に伴ってBisonによって再生成された結果です。特にy.tab.cの変更量が大きいのは、Bisonが生成するCコードが文法定義のわずかな変更でも大きく変わる可能性があるためです。コミットログにはBisonのバージョンが2.5から2.3に変わったとありますが、これはおそらくBisonのバージョン管理上の表記揺れか、あるいは特定の環境でのBisonのバージョンが異なっていたことによるもので、機能的なダウングレードを意図したものではないと考えられます。重要なのは、go.yの変更が正しくy.tab.cに反映されたことです。

test/fixedbugs/issue7648.dir/a.go, test/fixedbugs/issue7648.dir/b.go, test/fixedbugs/issue7648.goの追加は、このバグが修正されたことを検証するためのテストケースです。これらのテストは、複素数定数の否定が正しくコンパイルされ、期待通りの結果を生成することを確認します。

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

src/cmd/gc/go.yファイルのhidden_literalルールに以下の変更が加えられました。

--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -2140,6 +2140,10 @@ hidden_literal:
 		case CTFLT:
 			mpnegflt($$->val.u.fval);
 			break;
+		case CTCPLX:
+			mpnegflt(&$$->val.u.cval->real);
+			mpnegflt(&$$->val.u.cval->imag);
+			break;
 		default:
 			yyerror("bad negated constant");
 		}

コアとなるコードの解説

上記のコードスニペットは、Goコンパイラの構文解析器が定数リテラルを処理する部分の文法定義(Bisonのルール)です。

  • hidden_literal:: これはBisonの文法ルールの一つで、定数リテラルが解析される際に実行されるアクションを定義しています。
  • case CTFLT:: 浮動小数点数定数(CTFLT)の場合の処理です。mpnegflt関数を呼び出して、その浮動小数点数値を否定しています。$$->val.u.fvalは、解析された浮動小数点数定数の値へのポインタです。
  • case CTCPLX:: このコミットで追加された部分です。 複素数定数(CTCPLX)の場合の処理を定義しています。
    • mpnegflt(&$$->val.u.cval->real);: 複素数定数の実部(real)に対してmpnegflt関数を呼び出し、実部の符号を反転させます。&$$->val.u.cval->realは、解析された複素数定数の実部へのポインタです。
    • mpnegflt(&$$->val.u.cval->imag);: 同様に、複素数定数の虚部(imag)に対してmpnegflt関数を呼び出し、虚部の符号を反転させます。 これにより、複素数定数全体が正しく否定されます。
  • default:: 上記のどのケースにも一致しない場合の汎用的なエラーハンドリングです。以前はCTCPLXがこのdefaultにフォールバックし、「bad negated constant」エラーを引き起こしていました。

この変更により、Goコンパイラは複素数定数の否定演算を正しく認識し、処理できるようになり、Issue #7648で報告されたコンパイルエラーが解消されました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Go言語の定数、複素数型に関する情報)
  • Bisonの公式ドキュメント (Bisonの文法定義、アクションに関する情報)
  • Goコンパイラのソースコード (特にsrc/cmd/gcディレクトリ内の関連ファイル)
  • Issue #7648の議論スレッド (バグの詳細と修正の経緯)