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

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

このコミットは、Goコンパイラの内部コードにおける書式設定の正確性と型安全性を向上させるための修正です。具体的には、src/cmd/gc/bits.c内のfmtprint関数の書式指定子を修正し、src/cmd/gc/go.hにおいてカスタム書式指定子%Eunsigned 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における修正は、変数iint型であるにもかかわらず、誤ってlong long型用の書式指定子%lldが使われていた問題を解決します。このような不一致は、未定義の動作を引き起こす可能性があり、特に異なるアーキテクチャやコンパイラ設定で問題となることがあります。

一方、src/cmd/gc/go.hにおける変更は、カスタム書式指定子%Eunsigned 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つの異なるファイルに対する修正を含んでいます。

  1. 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に対して正しい書式指定子が適用され、コードの堅牢性と移植性が向上します。

  2. src/cmd/gc/go.hにおけるpragma varargckの追加: このファイルには、#pragma varargck type "E" intという行が既に存在していました。このコミットでは、その直下に#pragma varargck type "E" uintという行が追加されました。 この#pragmaディレクティブは、コンパイラに対して、カスタム書式指定子%Eint型だけでなく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); %dint型に対応する標準的な書式指定子です。この修正により、変数iの実際の型と書式指定子が一致し、コードの正確性と安全性が確保されます。

src/cmd/gc/go.hの変更

go.hファイルは、Goコンパイラのグローバルな定義や宣言を含むヘッダーファイルです。ここには、#pragma varargckディレクティブが多数記述されており、fmtprintなどの可変引数関数の型チェックルールを定義しています。

  • 変更前: #pragma varargck type "E" int この行は、カスタム書式指定子%Eint型の引数を受け入れることをコンパイラに指示していました。
  • 変更後: #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などの標準的な書式指定子の動作を確認するために参照しました。