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

[インデックス 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+1i3.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.23001.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);を使用していました。

この変更では、以下の条件分岐が導入されました。

  1. 浮動小数点数 (CTFLT) の場合:

    • if((fp->flags & FmtSharp) || fmtmode == FExp): もし書式指定子に#フラグが設定されているか、または書式設定モードが指数表記である場合、以前と同様に%Fを使用します。これは、ユーザーが明示的に%#Fを指定した場合や、数値が非常に大きいため指数表記が適切である場合に、その意図を尊重するためです。
    • else: 上記の条件に当てはまらない場合(つまり、通常の浮動小数点数をエラーメッセージなどで表示する場合)、fmtprint(fp, "%#F", v->u.fval);を使用します。これにより、末尾のゼロが省略されず、常に小数点が含まれる「代替書式」で出力されるようになります。
  2. 複素数 (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つのファイルが変更されています。

  1. src/cmd/gc/fmt.c: Goコンパイラの書式設定ロジックが実装されているC言語のソースファイル。
  2. 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文で、変換対象の定数型(CTFLTCTCPLXなど)に応じて処理が分岐します。

  • 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への変更が、コンパイラのエラーメッセージにおける浮動小数点数および複素数の表示を正確にするという目的を達成していることを確認するために追加されました。

関連リンク

参考にした情報源リンク