[インデックス 10270] ファイルの概要
このコミットは、Goコンパイラの内部コードにおける書式設定の正確性と型安全性を向上させるための修正です。具体的には、src/cmd/gc/bits.c
内のfmtprint
関数の書式指定子を修正し、src/cmd/gc/go.h
においてカスタム書式指定子%E
がunsigned int
型も受け入れられるようにpragma varargck
ディレクティブを追加しています。
コミット
commit 2e1bb76f9b7264b8d6cdb4d244746fd8ac45f160
Author: Lucio De Re <lucio.dere@gmail.com>
Date: Mon Nov 7 11:42:08 2011 -0500
gc: format nits
src/cmd/gc/bits.c: corrected a mistaken format;
src/cmd/gc/go.h: %E can accept uints.
R=rsc
CC=golang-dev
https://golang.org/cl/5331041
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2e1bb76f9b7264b8d6cdb4d244746fd8ac45f160
元コミット内容
このコミットの目的は「gc: format nits」(gc: 書式に関する些細な修正)です。具体的には以下の2点です。
src/cmd/gc/bits.c
: 誤った書式指定子を修正しました。src/cmd/gc/go.h
:%E
書式指定子がuint
(符号なし整数)型を受け入れられるようにしました。
変更の背景
このコミットは、Goコンパイラの内部コードにおける書式設定の正確性と柔軟性を向上させることを目的としています。src/cmd/gc/bits.c
における修正は、変数i
がint
型であるにもかかわらず、誤ってlong long
型用の書式指定子%lld
が使われていた問題を解決します。このような不一致は、未定義の動作を引き起こす可能性があり、特に異なるアーキテクチャやコンパイラ設定で問題となることがあります。
一方、src/cmd/gc/go.h
における変更は、カスタム書式指定子%E
がunsigned int
型も正しく処理できるようにするためのものです。これは、Goコンパイラの内部でunsigned int
型のデータが%E
で表示されるケースがあったことを示唆しており、そのための型チェックルールを追加することで、より柔軟な書式設定と型安全性を実現しています。
これらの修正は、Goコンパイラのデバッグ出力や内部表現の表示において、より正確で堅牢な書式設定を保証するための、細かながらも重要な改善と言えます。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
- Goコンパイラ (gc): Go言語の公式コンパイラです。Go言語の初期段階では、コンパイラ自体がC言語で書かれていました。
src/cmd/gc
ディレクトリは、そのC言語で書かれたコンパイラのソースコードの一部を含んでいます。 fmtprint
: Plan 9 C言語の標準ライブラリ関数の一つで、C言語のprintf
関数に似た書式付き出力関数です。初期のGoコンパイラでは、デバッグ情報の出力や内部状態の表示などに広く利用されていました。#pragma varargck type
: これは、Plan 9 Cコンパイラ(および初期のGoコンパイラ)がサポートしていた#pragma
ディレクティブの一種です。C言語の可変引数関数(...
を使って任意の数の引数を受け取る関数、例:fmtprint
)において、コンパイル時に引数の型チェックを強化するために使用されます。このディレクティブは、特定の書式指定子(例:%E
)がどのような型の引数を受け取るべきかをコンパイラに明示的に指示することで、型安全性を向上させ、実行時エラー(例えば、誤った型の引数を渡すことによるクラッシュ)を防ぐのに役立ちます。- 書式指定子:
%d
: 符号付き10進整数(int
型など)を出力するための標準的な書式指定子です。%lld
: 符号付き長々整数(long long
型)を出力するための書式指定子です。long long
はC99で導入された、long
よりも広い範囲の整数を表現できる型です。%E
: このコミットの文脈では、go.h
ファイル内で定義されているカスタム書式指定子です。これは、Goコンパイラの内部で特定のデータ型(この場合はint
、そして今回の変更でuint
)を特定の形式で表示するために使われます。標準Cライブラリのprintf
における%E
(指数表記の浮動小数点数)とは意味が異なります。
技術的詳細
このコミットは、Goコンパイラの内部における書式設定の正確性と型チェックの厳密性を高めるための、2つの異なるファイルに対する修正を含んでいます。
-
src/cmd/gc/bits.c
における書式指定子の修正: このファイルでは、Qconv
という関数内でfmtprint
が使用されています。元のコードでは、fmtprint(fp, "$%lld", i);
という行がありましたが、これはfmtprint(fp, "$%d", i);
に修正されました。 この変更の技術的な意味は、変数i
が実際にはint
型であるにもかかわらず、誤ってlong long
型用の書式指定子である%lld
が使われていたという点にあります。C言語のprintf
系関数では、書式指定子と対応する引数の型が一致しない場合、未定義の動作(Undefined Behavior)を引き起こす可能性があります。これは、プログラムがクラッシュしたり、予期せぬ結果を生成したりする原因となります。%d
に修正することで、int
型の変数i
に対して正しい書式指定子が適用され、コードの堅牢性と移植性が向上します。 -
src/cmd/gc/go.h
におけるpragma varargck
の追加: このファイルには、#pragma varargck type "E" int
という行が既に存在していました。このコミットでは、その直下に#pragma varargck type "E" uint
という行が追加されました。 この#pragma
ディレクティブは、コンパイラに対して、カスタム書式指定子%E
がint
型だけでなくunsigned int
型も引数として受け入れることを指示します。これにより、fmtprint
のような可変引数関数が%E
書式指定子とともにunsigned int
型の引数を受け取った際に、コンパイラが型チェックエラーを報告しなくなります。これは、Goコンパイラの内部でunsigned int
型の値を%E
で表示する必要が生じたため、そのための型チェックルールを拡張したものです。この変更により、コンパイラはより柔軟な書式設定を許容しつつ、依然として型安全性を維持することができます。
これらの変更は、Goコンパイラの開発者が、コードの品質と信頼性を維持するために、細部にわたる注意を払っていたことを示しています。
コアとなるコードの変更箇所
src/cmd/gc/bits.c
--- a/src/cmd/gc/bits.c
+++ b/src/cmd/gc/bits.c
@@ -151,7 +151,7 @@ Qconv(Fmt *fp)
else
fmtprint(fp, " ");
if(var[i].node == N || var[i].node->sym == S)
- fmtprint(fp, "$%lld", i);
+ fmtprint(fp, "$%d", i);
else {
fmtprint(fp, "%s", var[i].node->sym->name);
if(var[i].offset != 0)
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -1331,6 +1331,7 @@ void zname(Biobuf *b, Sym *s, int t);
#pragma varargck type "D" Addr*
#pragma varargck type "lD" Addr*
#pragma varargck type "E" int
+#pragma varargck type "E" uint
#pragma varargck type "F" Mpflt*
#pragma varargck type "H" NodeList*
#pragma varargck type "J" Node*
コアとなるコードの解説
src/cmd/gc/bits.c
の変更
Qconv
関数は、Goコンパイラの内部でシンボルやノードの情報を書式化して出力するために使用される関数の一部です。この関数内で、var[i].node
が特定の条件(N
またはS
)を満たす場合に、変数i
の値をfmtprint
で出力しています。
- 変更前:
fmtprint(fp, "$%lld", i);
ここでは、%lld
という書式指定子が使用されていました。これはlong long int
型に対応するものです。しかし、文脈から判断すると、変数i
は通常、配列のインデックスやループカウンタとして使用されるint
型である可能性が高いです。int
型の変数に対して%lld
を使用すると、コンパイラによっては警告が出たり、実行時に予期せぬ値が出力されたり、最悪の場合プログラムがクラッシュしたりする可能性があります。 - 変更後:
fmtprint(fp, "$%d", i);
%d
はint
型に対応する標準的な書式指定子です。この修正により、変数i
の実際の型と書式指定子が一致し、コードの正確性と安全性が確保されます。
src/cmd/gc/go.h
の変更
go.h
ファイルは、Goコンパイラのグローバルな定義や宣言を含むヘッダーファイルです。ここには、#pragma varargck
ディレクティブが多数記述されており、fmtprint
などの可変引数関数の型チェックルールを定義しています。
- 変更前:
#pragma varargck type "E" int
この行は、カスタム書式指定子%E
がint
型の引数を受け入れることをコンパイラに指示していました。 - 変更後:
#pragma varargck type "E" uint
の追加 既存の行の下に、#pragma varargck type "E" uint
が追加されました。これにより、コンパイラは%E
書式指定子がint
型だけでなく、unsigned int
型も有効な引数として受け入れると認識するようになります。これは、Goコンパイラの内部でunsigned int
型の値を%E
で表示する必要が生じたため、そのための型チェックルールを拡張したものです。この変更により、コンパイラはより柔軟な書式設定を許容しつつ、依然として型安全性を維持することができます。
関連リンク
- Goの公式リポジトリ: https://github.com/golang/go
- このコミットのChangeList (CL): https://golang.org/cl/5331041 (これはGoの古いコードレビューシステムであるGerritのリンクです。現在はGoのコードレビューはGerritからGitHubに移行していますが、古いCLはまだ参照可能です。)
参考にした情報源リンク
- Plan 9 from Bell Labs: C(1) - The C compiler: https://9p.io/sys/doc/compiler.html
このドキュメントは、Plan 9 Cコンパイラの詳細を説明しており、
#pragma varargck
ディレクティブに関する情報が含まれています。初期のGoコンパイラがPlan 9のツールチェインに強く影響を受けていたことを理解する上で重要です。 - Go's original compiler was written in C: https://go.dev/blog/go-compiler-internals Goコンパイラの内部構造と歴史に関する公式ブログ記事です。初期のGoコンパイラがC言語で書かれていたことや、その設計思想について言及されており、このコミットの背景を理解するのに役立ちます。
- C言語の書式指定子に関する一般的な情報:
- printf - cppreference.com
printf
ファミリー関数の書式指定子に関する詳細なリファレンスです。%d
や%lld
などの標準的な書式指定子の動作を確認するために参照しました。
- printf - cppreference.com