[インデックス 10778] ファイルの概要
このコミットは、Goコンパイラのgc
(Go Compiler)におけるエラーメッセージの書式設定に関するバグ修正です。具体的には、浮動小数点数(CTFLT
)および複素数(CTCPLX
)がエラーメッセージ内で表示される際に、その書式をより正確でデバッグに役立つものにするため、%F
書式指定子を%#F
書式指定子に置き換える変更を行っています。これにより、数値の完全な精度が保持され、特に浮動小数点数の末尾のゼロが省略されずに表示されるようになります。
コミット
- コミットハッシュ:
3c638f2892471b55ded3982d2639e5c62f00d506
- Author: Luuk van Dijk lvd@golang.org
- Date: Wed Dec 14 08:22:36 2011 +0100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3c638f2892471b55ded3982d2639e5c62f00d506
元コミット内容
gc: Use %#F in error messages instead of %F.
Fixes #2520
R=rsc
CC=golang-dev
https://golang.org/cl/5482056
変更の背景
この変更は、Goコンパイラが生成するエラーメッセージにおいて、浮動小数点数や複素数のリテラルが不正確に表示されるという問題(Issue 2520)を修正するために行われました。
元のGoコンパイラでは、エラーメッセージ内で浮動小数点数や複素数を表示する際に、%F
という書式指定子を使用していました。この%F
は、Go言語のfmt
パッケージにおける%f
と同様に、デフォルトでは末尾のゼロを省略したり、必要に応じて指数表記に切り替えたりする挙動を持っていました。しかし、コンパイラのエラーメッセージにおいては、ユーザーが記述したリテラル値が正確に、かつデバッグしやすい形で表示されることが重要です。例えば、2e9
というリテラルがエラーメッセージで2e9
と表示されるべきなのに、2e+09
のように表示されたり、3.14+1i
が3.14+1i
と表示されるべきなのに、3.14 + 1i
のようにスペースが入ったり、精度が失われたりする可能性がありました。
Issue 2520では、特に2e9
のような浮動小数点リテラルが、エラーメッセージで2e.09
と表示されるという具体的な問題が報告されていました。これは、コンパイラが内部的に数値を処理し、それを文字列に変換する際に、元のリテラルの意図と異なる書式になってしまうことが原因でした。
この問題を解決するため、fmt
パッケージの%#f
(または%#F
)書式指定子と同様の「代替書式」を使用することが決定されました。%#f
は、浮動小数点数の末尾のゼロを省略せず、常に小数点を含めるなど、より厳密な書式を提供します。これにより、コンパイラのエラーメッセージが、ユーザーが記述したリテラルをより忠実に反映するようになります。
前提知識の解説
Go言語のfmt
パッケージと書式指定子
Go言語の標準ライブラリには、書式付きI/Oを扱うfmt
パッケージがあります。このパッケージは、C言語のprintf
関数に似た書式指定子を提供しており、様々な型の値を文字列に変換する際にその表示形式を制御できます。
%f
(または%F
): 浮動小数点数を通常の表記(例:123.456
)で表示します。デフォルトでは、末尾のゼロは省略され、必要に応じて指数表記に切り替わることがあります。%#f
(または%#F
): 「代替書式」と呼ばれる形式で浮動小数点数を表示します。この形式では、以下の特徴があります。- 常に小数点を含めます(例:
123.
ではなく123.0
)。 - 末尾のゼロを省略しません(例:
1.2300
は1.23
ではなく1.2300
と表示されます)。 - 複素数の場合、実部と虚部がそれぞれ代替書式で表示されます。
- 常に小数点を含めます(例:
コンパイラのエラーメッセージでは、ユーザーが入力したコードのリテラル値を正確に再現することが重要であるため、この%#F
のような代替書式が望ましいと判断されました。
Goコンパイラ(gc
)の内部構造とfmt.c
Goコンパイラは、Go言語で書かれたソースコードを機械語に変換するツールチェーンの中核をなす部分です。gc
は、Goの初期バージョンから存在する伝統的なコンパイラであり、その一部はC言語で書かれています。
src/cmd/gc/fmt.c
: このファイルは、Goコンパイラ内部で型や値を文字列に変換するための書式設定ロジックを実装しています。コンパイラがエラーメッセージやデバッグ情報を出力する際に、このファイル内の関数が利用されます。Vconv
関数:fmt.c
内のVconv
関数は、様々な型の値を文字列に変換する役割を担っています。これは、Goコンパイラが内部的に使用する独自の書式設定エンジンの一部です。CTFLT
: コンパイラ内部で浮動小数点定数を表す型です。CTCPLX
: コンパイラ内部で複素数定数を表す型です。Fmt *fp
: 書式設定の状態を保持する構造体へのポインタです。fp->flags
には、書式指定子に付加されたフラグ(例:#
フラグ)が格納されます。FmtSharp
:fp->flags
に含まれるフラグの一つで、書式指定子に#
(シャープ)が付いている場合にセットされます。fmtmode == FExp
: 書式設定モードが指数表記(Exponential notation)の場合を示します。これは、非常に大きいまたは小さい浮動小数点数を表示する際に使用されるモードです。
技術的詳細
このコミットの技術的な核心は、Goコンパイラ内部の書式設定ロジック、特にsrc/cmd/gc/fmt.c
ファイル内のVconv
関数における浮動小数点数(CTFLT
)と複素数(CTCPLX
)の処理方法の変更にあります。
以前のバージョンでは、CTFLT
の場合、常にfmtprint(fp, "%F", v->u.fval);
を使用していました。これは、Goのfmt
パッケージにおける%f
と同様に、デフォルトの浮動小数点数書式を使用することを意味します。同様に、CTCPLX
の場合もfmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag);
を使用していました。
この変更では、以下の条件分岐が導入されました。
-
浮動小数点数 (
CTFLT
) の場合:if((fp->flags & FmtSharp) || fmtmode == FExp)
: もし書式指定子に#
フラグが設定されているか、または書式設定モードが指数表記である場合、以前と同様に%F
を使用します。これは、ユーザーが明示的に%#F
を指定した場合や、数値が非常に大きいため指数表記が適切である場合に、その意図を尊重するためです。else
: 上記の条件に当てはまらない場合(つまり、通常の浮動小数点数をエラーメッセージなどで表示する場合)、fmtprint(fp, "%#F", v->u.fval);
を使用します。これにより、末尾のゼロが省略されず、常に小数点が含まれる「代替書式」で出力されるようになります。
-
複素数 (
CTCPLX
) の場合:if((fp->flags & FmtSharp) || fmtmode == FExp)
: 浮動小数点数と同様に、#
フラグが設定されているか、指数表記モードの場合、以前と同様に(%F+%F)
を使用します。else
: それ以外の場合、fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag);
を使用します。ここで注目すべきは、実部と虚部の両方に%#F
が適用され、さらに虚部には明示的にi
が追加されている点です。これにより、3.14+1i
のようなリテラルが、エラーメッセージで3.14 + 1i
のように正確に表示されるようになります。
この変更により、コンパイラが生成するエラーメッセージ内の数値リテラルが、よりユーザーの意図に沿った、正確でデバッグしやすい形式で表示されるようになりました。特に、浮動小数点数の精度が失われたり、不必要な書式変更が行われたりする問題が解消されます。
コアとなるコードの変更箇所
このコミットでは、主に以下の2つのファイルが変更されています。
src/cmd/gc/fmt.c
: Goコンパイラの書式設定ロジックが実装されているC言語のソースファイル。test/fixedbugs/bug383.go
: Issue 2520の修正を検証するための新しいテストケース。
src/cmd/gc/fmt.c
の変更
--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -371,9 +371,13 @@ Vconv(Fmt *fp)
return fmtprint(fp, "'\\U%08llux'", x);
return fmtprint(fp, "('\\x00' + %B)", v->u.xval);
case CTFLT:
- return fmtprint(fp, "%F", v->u.fval);
- case CTCPLX: // ? 1234i -> (0p+0+617p+1)
- return fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag);
+ if((fp->flags & FmtSharp) || fmtmode == FExp)
+ return fmtprint(fp, "%F", v->u.fval);
+ return fmtprint(fp, "%#F", v->u.fval);
+ case CTCPLX:
+ if((fp->flags & FmtSharp) || fmtmode == FExp)
+ return fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag);
+ return fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag);
case CTSTR:
return fmtprint(fp, "\"%Z\"", v->u.sval);
case CTBOOL:
test/fixedbugs/bug383.go
の追加
--- /dev/null
+++ b/test/fixedbugs/bug383.go
@@ -0,0 +1,13 @@
+// errchk $G $D/$F.go
+
+// Copyright 2011 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 2520
+
+package main
+func main() {
+ if 2e9 { } // ERROR "2e.09"
+ if 3.14+1i { } // ERROR "3.14 . 1i"
+}
\ No newline at end of file
コアとなるコードの解説
src/cmd/gc/fmt.c
の変更点
Vconv
関数は、Goコンパイラが内部的な値を文字列に変換する際に呼び出される汎用的な変換関数です。この関数内のswitch
文で、変換対象の定数型(CTFLT
、CTCPLX
など)に応じて処理が分岐します。
-
case CTFLT:
(浮動小数点数)- 変更前は、常に
fmtprint(fp, "%F", v->u.fval);
を使用していました。これは、デフォルトの浮動小数点数書式で値を表示します。 - 変更後は、
if((fp->flags & FmtSharp) || fmtmode == FExp)
という条件が追加されました。fp->flags & FmtSharp
: これは、書式指定子に#
フラグ(例:%#F
)が明示的に指定されているかどうかをチェックします。fmtmode == FExp
: これは、現在の書式設定モードが指数表記(例:1.23e+09
)であるかどうかをチェックします。- この条件が真の場合(つまり、
#
フラグがあるか、指数表記モードの場合)、引き続き%F
を使用します。これは、ユーザーの意図や数値の性質に応じて、従来の書式を維持するためです。
- 上記の条件が偽の場合(つまり、通常の浮動小数点数をエラーメッセージなどで表示する場合)、
return fmtprint(fp, "%#F", v->u.fval);
が実行されます。これにより、%#F
の代替書式が適用され、末尾のゼロが省略されず、常に小数点が含まれる形式で出力されます。
- 変更前は、常に
-
case CTCPLX:
(複素数)- 変更前は、常に
fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag);
を使用していました。実部と虚部をそれぞれデフォルトの%F
で表示していました。 - 変更後も、
if((fp->flags & FmtSharp) || fmtmode == FExp)
という同様の条件が追加されました。この条件が真の場合、以前と同様に(%F+%F)
を使用します。 - 上記の条件が偽の場合、
return fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag);
が実行されます。- 実部と虚部の両方に
%#F
が適用されます。 - 虚部には明示的に
i
が追加され、+
記号の前後にはスペースが挿入されます。これにより、3.14+1i
のようなリテラルが、エラーメッセージで3.14 + 1i
のように、より読みやすく、かつ元のリテラルに近い形で表示されるようになります。
- 実部と虚部の両方に
- 変更前は、常に
test/fixedbugs/bug383.go
の追加点
このファイルは、Goコンパイラのバグ修正を検証するためのテストケースです。errchk
ディレクティブは、コンパイル時に指定されたエラーメッセージが出力されることを期待するテストであることを示しています。
package main
func main() {
if 2e9 { } // ERROR "2e.09"
if 3.14+1i { } // ERROR "3.14 . 1i"
}
if 2e9 { }
: この行は、2e9
という浮動小数点リテラルが条件式として使用されており、Goの文法上は無効です。このテストは、コンパイラがこの無効なリテラルに対してどのようなエラーメッセージを生成するかを検証します。// ERROR "2e.09"
は、修正前のコンパイラが2e.09
という誤った書式でエラーメッセージを出力していたことを示唆しています。このコミットの修正により、このエラーメッセージが正しく表示されるようになることが期待されます。if 3.14+1i { }
: 同様に、3.14+1i
という複素数リテラルが条件式として使用されており、これも無効です。// ERROR "3.14 . 1i"
は、修正前のコンパイラが3.14 . 1i
という誤った書式でエラーメッセージを出力していたことを示唆しています。修正後には、このエラーメッセージも正しく表示されるようになることが期待されます。
これらのテストケースは、%#F
への変更が、コンパイラのエラーメッセージにおける浮動小数点数および複素数の表示を正確にするという目的を達成していることを確認するために追加されました。
関連リンク
- Go Change List (CL): https://golang.org/cl/5482056
- Go Issue 2520: https://go.dev/issue/2520
参考にした情報源リンク
- Go Issue Tracker: https://go.dev/issue/2520
- Go
fmt
package documentation: https://pkg.go.dev/fmt - Go source code (for
src/cmd/gc/fmt.c
context): https://github.com/golang/go - Go
errchk
test directive: https://go.dev/doc/go1.1#errchk (Go 1.1 release notes, for general understanding oferrchk
) - Understanding Go's
fmt
package format verbs: https://yourbasic.org/golang/format-string-printf-reference/ (General reference forfmt
verbs) - Go compiler internals (general knowledge)