[インデックス 1501] ファイルの概要
このコミットは、Go言語のコンパイラ(gc
)におけるブーリアン値のエクスポート形式のバグ修正に関するものです。具体的には、ブーリアン値が再エクスポートされる際に、その表現形式が16進数から10進数に変わってしまう問題に対処しています。この形式の変更が、アーカイブユーティリティであるar
の文字列比較に基づく値の比較を混乱させ、問題を引き起こしていました。
コミット
このコミットは、ブーリアン値のエクスポート形式を数値(16進数)から文字列リテラル "true"
または "false"
に変更することで、この問題を解決しています。これにより、ブーリアン値が整数と明確に区別され、ar
ユーティリティが誤って解釈するのを防ぎます。また、この変更に伴い、コンパイラのパーサーも "true"
と "false"
の文字列リテラルを正しく認識し、内部のブーリアン定数にマッピングするように更新されています。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dec12d365495d82740176650e3ad3d587913cff8
元コミット内容
commit dec12d365495d82740176650e3ad3d587913cff8
Author: Russ Cox <rsc@golang.org>
Date: Fri Jan 16 10:45:28 2009 -0800
re-export of bools was changing
format from hex to decimal, confusing
ar's strcmp-based value comparison.
switched export format to "true" or "false"
to keep bools separate from ints.
R=ken
OCL=22944
CL=22944
---\n src/cmd/gc/export.c | 9 ++++++---\n src/cmd/gc/go.y | 8 ++++++++\n 2 files changed, 14 insertions(+), 3 deletions(-)\n
変更の背景
このコミットは、Go言語の初期開発段階(2009年1月)に行われたものです。当時のGoコンパイラ(gc
)は、コンパイルされたコードや型情報を他のコンパイルユニットと共有するために、独自のエクスポート形式を使用していました。このエクスポート形式では、ブーリアン値が16進数の数値(例: 0x0
や 0x1
)として表現されていました。
問題は、これらのブーリアン値が一度エクスポートされ、その後別のコンパイルプロセスで再インポートされる際に発生しました。元のコミットメッセージによると、「re-export of bools was changing format from hex to decimal」とあり、ブーリアン値の表現が16進数から10進数に変わってしまうというバグがありました。
この形式の変更が、ar
ユーティリティの動作に影響を与えました。ar
は、複数のオブジェクトファイルやライブラリを一つのアーカイブファイルにまとめるために使用される標準的なUnixユーティリティです。Goコンパイラは、コンパイルされたパッケージのメタデータやシンボル情報をar
アーカイブ内に格納し、ar
のstrcmp
(文字列比較)ベースのメカニズムを利用して、これらの値やシンボルを比較・管理していたと考えられます。
ブーリアン値の表現が0x1
(16進数)から1
(10進数)のように変わってしまうと、ar
のstrcmp
はこれらを異なる値として認識してしまい、結果としてコンパイルプロセスやリンケージに予期せぬ問題を引き起こしていました。このバグは、Go言語の型システムとコンパイルチェーンの整合性を保つ上で重要な問題でした。
前提知識の解説
- Goコンパイラ
gc
: Go言語の公式コンパイラの一つで、Goのソースコードを機械語に変換します。初期のGoコンパイラはC言語で書かれており、src/cmd/gc
ディレクトリにそのソースコードが格納されていました。 - エクスポート形式 (Export Format): コンパイラが、コンパイル済みのパッケージの型情報、関数シグネチャ、定数などのメタデータを他のパッケージが利用できるように出力する形式です。Go言語では、このエクスポート形式を通じて、異なるパッケージ間の依存関係が解決されます。初期のGoでは、この形式はテキストベースで、人間が読める形式に近いものでした。
ar
ユーティリティ: Unix系システムで広く使われているアーカイブユーティリティです。複数のファイルを一つのアーカイブファイル(通常は.a
拡張子を持つ静的ライブラリ)にまとめるために使用されます。ar
は、アーカイブ内の各メンバー(ファイル)の名前や属性を管理し、必要に応じてメンバーの追加、削除、抽出を行います。Goコンパイラは、パッケージのコンパイル結果をar
形式のアーカイブとして出力し、その中に型情報やシンボル情報を埋め込んでいました。strcmp
(String Compare): C言語の標準ライブラリ関数で、2つの文字列を辞書順に比較します。ar
が内部で値の比較にstrcmp
を使用していたということは、エクスポートされたブーリアン値が文字列として扱われ、その文字列表現が比較の対象となっていたことを示唆しています。- Yacc/Bison (
.y
ファイル):go.y
ファイルは、Go言語の文法を定義するYacc(Yet Another Compiler Compiler)またはBisonの入力ファイルです。これらのツールは、文法定義からパーサー(構文解析器)のC言語ソースコードを生成します。パーサーは、ソースコードを読み込み、その構造を解析して抽象構文木(AST)を構築する役割を担います。
技術的詳細
このコミットの技術的解決策は、ブーリアン値のエクスポート形式を根本的に変更することにあります。
-
src/cmd/gc/export.c
の変更:- このファイルは、Goコンパイラが型情報や定数などをエクスポートする際のロジックを実装しています。
- 以前は、ブーリアン定数(
CTBOOL
)がBprint(bout, "0x%llux\\n", n->val.u.bval);
という形式で出力されていました。これは、ブーリアン値をn->val.u.bval
(おそらく0
または1
)として取得し、それを16進数形式(0x0
または0x1
)で出力することを意味します。 - 変更後、この部分は
if(n->val.u.bval) Bprint(bout, "true\\n"); else Bprint(bout, "false\\n");
となりました。これにより、ブーリアン値は数値ではなく、明示的な文字列リテラル"true"
または"false"
としてエクスポートされるようになります。 - この変更により、ブーリアン値の表現が数値から文字列に変わり、
ar
のstrcmp
が常に一貫した文字列("true"または"false")を比較するようになり、形式の不一致による問題を回避できます。 - また、
Bprint(bout, "const %lS ", s);
からBprint(bout, "const %lS", s);
、およびBprint(bout, "%#T ", t);
からBprint(bout, " %#T", t);
への変更は、エクスポートされる文字列のフォーマット調整であり、新しいブーリアン形式との整合性を保つための微調整と考えられます。
-
src/cmd/gc/go.y
の変更:export.c
でブーリアン値が文字列としてエクスポートされるようになったため、コンパイラがこれらの文字列を正しくパースできるように、パーサーの文法定義も更新する必要があります。go.y
ファイルにLTRUE
とLFALSE
という新しいトークンが追加され、それぞれがbooltrue->val
とboolfalse->val
という内部のブーリアン定数にマッピングされるようになりました。- これは、Goのソースコードやエクスポートされたメタデータ内で
true
やfalse
という文字列リテラルが出現した場合に、パーサーがそれをブーリアン値として認識し、適切な内部表現に変換するためのルールです。これにより、エクスポートされたブーリアン値が再インポートされる際に、正しく解釈されることが保証されます。
この修正は、Go言語のコンパイラが、異なるコンパイルフェーズや異なるパッケージ間で一貫したデータ表現を維持することの重要性を示しています。特に、外部ツール(この場合はar
)との連携において、データ形式の厳密な管理がいかに重要であるかを浮き彫りにしています。
コアとなるコードの変更箇所
src/cmd/gc/export.c
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -119,9 +119,9 @@ dumpexportconst(Sym *s)
Bprint(bout, "export ");
else if(s->export == 2)
Bprint(bout, "package ");
- Bprint(bout, "const %lS ", s);
+ Bprint(bout, "const %lS", s);
if(t != T)
- Bprint(bout, "%#T ", t);
+ Bprint(bout, " %#T", t);
Bprint(bout, " = ");
switch(n->val.ctype) {
@@ -133,7 +133,10 @@ dumpexportconst(Sym *s)
Bprint(bout, "%B\n", n->val.u.xval);
break;
case CTBOOL:
- Bprint(bout, "0x%llux\n", n->val.u.bval);
+ if(n->val.u.bval)
+ Bprint(bout, "true\n");
+ else
+ Bprint(bout, "false\n");
break;
case CTFLT:
Bprint(bout, "%F\n", n->val.u.fval);
src/cmd/gc/go.y
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -1969,6 +1969,14 @@ hidden_constant:
yyerror("bad negated constant");
}
}
+| LTRUE
+| {
+| $$ = booltrue->val;
+| }
+| LFALSE
+| {
+| $$ = boolfalse->val;
+| }
hidden_importsym:
sym1 '.' sym2
コアとなるコードの解説
src/cmd/gc/export.c
の変更点
- ブーリアン値のエクスポート形式の変更:
case CTBOOL:
のブロックが最も重要な変更点です。- 変更前は
Bprint(bout, "0x%llux\n", n->val.u.bval);
となっており、ブーリアン値n->val.u.bval
(0または1) を16進数形式 (0x0
または0x1
) で出力していました。 - 変更後は
if(n->val.u.bval) Bprint(bout, "true\n"); else Bprint(bout, "false\n");
となり、ブーリアン値がtrue
またはfalse
という文字列リテラルとして出力されるようになりました。これにより、数値としての曖昧さがなくなり、ar
のstrcmp
が常に期待通りの比較を行うことが保証されます。
- フォーマットの微調整:
Bprint(bout, "const %lS ", s);
からBprint(bout, "const %lS", s);
へ、およびBprint(bout, "%#T ", t);
からBprint(bout, " %#T", t);
への変更は、エクスポートされる定数宣言のフォーマットを調整するものです。これは、新しいブーリアン形式との視覚的な整合性を高めるか、あるいは単に既存のフォーマットの不整合を修正するためのものと考えられます。
src/cmd/gc/go.y
の変更点
- ブーリアンリテラルのパーサーへの追加:
hidden_constant:
ルールにLTRUE
とLFALSE
という新しいプロダクションが追加されました。LTRUE
が検出された場合、$$ = booltrue->val;
が実行されます。これは、パーサーが"true"
という文字列リテラルをGo言語の内部的なブーリアン真値 (booltrue->val
) にマッピングすることを示します。- 同様に、
LFALSE
が検出された場合、$$ = boolfalse->val;
が実行され、"false"
が内部的なブーリアン偽値 (boolfalse->val
) にマッピングされます。 - この変更は、
export.c
でブーリアン値が文字列としてエクスポートされるようになったため、そのエクスポートされた情報を他のコンパイルユニットが正しく読み込み、解釈できるようにするために不可欠です。これにより、Goコンパイラは"true"
や"false"
という文字列を、Go言語のブーリアン型として正しく扱えるようになります。
これらの変更は、Go言語のコンパイラが、ブーリアン値の表現を数値からより明確な文字列リテラルへと移行させ、それによってコンパイルチェーン全体での一貫性と正確性を向上させたことを示しています。
関連リンク
- Go言語の公式ウェブサイト: https://go.dev/
- Go言語のソースコードリポジトリ (GitHub): https://github.com/golang/go
ar
(Unix) - Wikipedia: https://en.wikipedia.org/wiki/Ar_(Unix)- Yacc - Wikipedia: https://en.wikipedia.org/wiki/Yacc
参考にした情報源リンク
- Go言語の初期のコンパイラ設計に関するドキュメントや議論(Goのメーリングリストやデザインドキュメントなど、当時の情報源を特定するのは困難ですが、Goの歴史に関する一般的な知識に基づいています)。
ar
ユーティリティの一般的な動作と、それがコンパイラツールチェーンでどのように使用されるかに関する情報。- コンパイラのフロントエンド(字句解析、構文解析)とバックエンド(コード生成、最適化)に関する一般的な知識。
- Go言語のソースコード内の
src/cmd/gc
ディレクトリの構造と、export.c
やgo.y
のようなファイルが果たす役割に関する理解。
[インデックス 1501] ファイルの概要
このコミットは、Go言語のコンパイラ(gc
)におけるブーリアン値のエクスポート形式のバグ修正に関するものです。具体的には、ブーリアン値が再エクスポートされる際に、その表現形式が16進数から10進数に変わってしまう問題に対処しています。この形式の変更が、アーカイブユーティリティであるar
の文字列比較に基づく値の比較を混乱させ、問題を引き起こしていました。
コミット
このコミットは、ブーリアン値のエクスポート形式を数値(16進数)から文字列リテラル "true"
または "false"
に変更することで、この問題を解決しています。これにより、ブーリアン値が整数と明確に区別され、ar
ユーティリティが誤って解釈するのを防ぎます。また、この変更に伴い、コンパイラのパーサーも "true"
と "false"
の文字列リテラルを正しく認識し、内部のブーリアン定数にマッピングするように更新されています。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dec12d365495d82740176650e3ad3d587913cff8
元コミット内容
commit dec12d365495d82740176650e3ad3d587913cff8
Author: Russ Cox <rsc@golang.org>
Date: Fri Jan 16 10:45:28 2009 -0800
re-export of bools was changing
format from hex to decimal, confusing
ar's strcmp-based value comparison.
switched export format to "true" or "false"
to keep bools separate from ints.
R=ken
OCL=22944
CL=22944
変更の背景
このコミットは、Go言語の初期開発段階(2009年1月)に行われたものです。当時のGoコンパイラ(gc
)は、コンパイルされたコードや型情報を他のコンパイルユニットと共有するために、独自のエクスポート形式を使用していました。このエクスポート形式では、ブーリアン値が16進数の数値(例: 0x0
や 0x1
)として表現されていました。
問題は、これらのブーリアン値が一度エクスポートされ、その後別のコンパイルプロセスで再インポートされる際に発生しました。元のコミットメッセージによると、「re-export of bools was changing format from hex to decimal」とあり、ブーリアン値の表現が16進数から10進数に変わってしまうというバグがありました。
この形式の変更が、ar
ユーティリティの動作に影響を与えました。ar
は、複数のオブジェクトファイルやライブラリを一つのアーカイブファイルにまとめるために使用される標準的なUnixユーティリティです。Goコンパイラは、コンパイルされたパッケージのメタデータやシンボル情報をar
アーカイブ内に格納し、ar
のstrcmp
(文字列比較)ベースのメカニズムを利用して、これらの値やシンボルを比較・管理していたと考えられます。
ブーリアン値の表現が0x1
(16進数)から1
(10進数)のように変わってしまうと、ar
のstrcmp
はこれらを異なる値として認識してしまい、結果としてコンパイルプロセスやリンケージに予期せぬ問題を引き起こしていました。このバグは、Go言語の型システムとコンパイルチェーンの整合性を保つ上で重要な問題でした。
前提知識の解説
- Goコンパイラ
gc
: Go言語の公式コンパイラの一つで、Goのソースコードを機械語に変換します。初期のGoコンパイラはC言語で書かれており、src/cmd/gc
ディレクトリにそのソースコードが格納されていました。 - エクスポート形式 (Export Format): コンパイラが、コンパイル済みのパッケージの型情報、関数シグネチャ、定数などのメタデータを他のパッケージが利用できるように出力する形式です。Go言語では、このエクスポート形式を通じて、異なるパッケージ間の依存関係が解決されます。初期のGoでは、この形式はテキストベースで、人間が読める形式に近いものでした。現在のGoコンパイラ
gc
は、他のパッケージがインポートする可能性のあるコンパイル済みパッケージに関する情報を保存するために「エクスポートデータ」ファイルを使用します。このエクスポートデータには、エクスポートされたすべての宣言の型情報、インライン化の候補となる関数本体の中間表現 (IR)、ジェネリック関数の中間表現、および関数パラメータのエスケープ解析結果の概要が含まれます。このエクスポートデータの形式は進化しており、現在の形式は「unified」と呼ばれ、オブジェクトグラフのシリアル化された表現であり、データの一部を遅延デコードするように設計されています。 ar
ユーティリティ: Unix系システムで広く使われているアーカイブユーティリティです。複数のファイルを一つのアーカイブファイル(通常は.a
拡張子を持つ静的ライブラリ)にまとめるために使用されます。ar
は、アーカイブ内の各メンバー(ファイル)の名前や属性を管理し、必要に応じてメンバーの追加、削除、抽出を行います。Goコンパイラは、パッケージのコンパイル結果をar
形式のアーカイブとして出力し、その中に型情報やシンボル情報を埋め込んでいました。strcmp
(String Compare): C言語の標準ライブラリ関数で、2つの文字列を辞書順に比較します。ar
が内部で値の比較にstrcmp
を使用していたということは、エクスポートされたブーリアン値が文字列として扱われ、その文字列表現が比較の対象となっていたことを示唆しています。- Yacc/Bison (
.y
ファイル):go.y
ファイルは、Go言語の文法を定義するYacc(Yet Another Compiler Compiler)またはBisonの入力ファイルです。これらのツールは、文法定義からパーサー(構文解析器)のC言語ソースコードを生成します。パーサーは、ソースコードを読み込み、その構造を解析して抽象構文木(AST)を構築する役割を担います。
技術的詳細
このコミットの技術的解決策は、ブーリアン値のエクスポート形式を根本的に変更することにあります。
-
src/cmd/gc/export.c
の変更:- このファイルは、Goコンパイラが型情報や定数などをエクスポートする際のロジックを実装しています。
- 以前は、ブーリアン定数(
CTBOOL
)がBprint(bout, "0x%llux\\n", n->val.u.bval);
という形式で出力されていました。これは、ブーリアン値をn->val.u.bval
(おそらく0
または1
)として取得し、それを16進数形式(0x0
または0x1
)で出力することを意味します。 - 変更後、この部分は
if(n->val.u.bval) Bprint(bout, "true\\n"); else Bprint(bout, "false\\n");
となりました。これにより、ブーリアン値は数値ではなく、明示的な文字列リテラル"true"
または"false"
としてエクスポートされるようになります。 - この変更により、ブーリアン値の表現が数値から文字列に変わり、
ar
のstrcmp
が常に一貫した文字列("true"または"false")を比較するようになり、形式の不一致による問題を回避できます。 - また、
Bprint(bout, "const %lS ", s);
からBprint(bout, "const %lS", s);
、およびBprint(bout, "%#T ", t);
からBprint(bout, " %#T", t);
への変更は、エクスポートされる文字列のフォーマット調整であり、新しいブーリアン形式との整合性を保つための微調整と考えられます。
-
src/cmd/gc/go.y
の変更:export.c
でブーリアン値が文字列としてエクスポートされるようになったため、コンパイラがこれらの文字列を正しくパースできるように、パーサーの文法定義も更新する必要があります。go.y
ファイルにLTRUE
とLFALSE
という新しいトークンが追加され、それぞれがbooltrue->val
とboolfalse->val
という内部のブーリアン定数にマッピングされるようになりました。- これは、Goのソースコードやエクスポートされたメタデータ内で
true
やfalse
という文字列リテラルが出現した場合に、パーサーがそれをブーリアン値として認識し、適切な内部表現に変換するためのルールです。これにより、エクスポートされたブーリアン値が再インポートされる際に、正しく解釈されることが保証されます。
この修正は、Go言語のコンパイラが、異なるコンパイルフェーズや異なるパッケージ間で一貫したデータ表現を維持することの重要性を示しています。特に、外部ツール(この場合はar
)との連携において、データ形式の厳密な管理がいかに重要であるかを浮き彫りにしています。
コアとなるコードの変更箇所
src/cmd/gc/export.c
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -119,9 +119,9 @@ dumpexportconst(Sym *s)
Bprint(bout, "export ");
else if(s->export == 2)
Bprint(bout, "package ");
- Bprint(bout, "const %lS ", s);
+ Bprint(bout, "const %lS", s);
if(t != T)
- Bprint(bout, "%#T ", t);
+ Bprint(bout, " %#T", t);
Bprint(bout, " = ");
switch(n->val.ctype) {
@@ -133,7 +133,10 @@ dumpexportconst(Sym *s)
Bprint(bout, "%B\n", n->val.u.xval);
break;
case CTBOOL:
- Bprint(bout, "0x%llux\n", n->val.u.bval);
+ if(n->val.u.bval)
+ Bprint(bout, "true\n");
+ else
+ Bprint(bout, "false\n");
break;
case CTFLT:
Bprint(bout, "%F\n", n->val.u.fval);
src/cmd/gc/go.y
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -1969,6 +1969,14 @@ hidden_constant:
yyerror("bad negated constant");
}
}
+| LTRUE
+| {
+| $$ = booltrue->val;
+| }
+| LFALSE
+| {
+| $$ = boolfalse->val;
+| }
hidden_importsym:
sym1 '.' sym2
コアとなるコードの解説
src/cmd/gc/export.c
の変更点
- ブーリアン値のエクスポート形式の変更:
case CTBOOL:
のブロックが最も重要な変更点です。- 変更前は
Bprint(bout, "0x%llux\n", n->val.u.bval);
となっており、ブーリアン値n->val.u.bval
(0または1) を16進数形式 (0x0
または0x1
) で出力していました。 - 変更後は
if(n->val.u.bval) Bprint(bout, "true\n"); else Bprint(bout, "false\n");
となり、ブーリアン値がtrue
またはfalse
という文字列リテラルとして出力されるようになりました。これにより、数値としての曖昧さがなくなり、ar
のstrcmp
が常に期待通りの比較を行うことが保証されます。
- フォーマットの微調整:
Bprint(bout, "const %lS ", s);
からBprint(bout, "const %lS", s);
へ、およびBprint(bout, "%#T ", t);
からBprint(bout, " %#T", t);
への変更は、エクスポートされる定数宣言のフォーマットを調整するものです。これは、新しいブーリアン形式との視覚的な整合性を高めるか、あるいは単に既存のフォーマットの不整合を修正するためのものと考えられます。
src/cmd/gc/go.y
の変更点
- ブーリアンリテラルのパーサーへの追加:
hidden_constant:
ルールにLTRUE
とLFALSE
という新しいプロダクションが追加されました。LTRUE
が検出された場合、$$ = booltrue->val;
が実行されます。これは、パーサーが"true"
という文字列リテラルをGo言語の内部的なブーリアン真値 (booltrue->val
) にマッピングすることを示します。- 同様に、
LFALSE
が検出された場合、$$ = boolfalse->val;
が実行され、"false"
が内部的なブーリアン偽値 (boolfalse->val
) にマッピングされます。 - この変更は、
export.c
でブーリアン値が文字列としてエクスポートされるようになったため、そのエクスポートされた情報を他のコンパイルユニットが正しく読み込み、解釈できるようにするために不可欠です。これにより、Goコンパイラは"true"
や"false"
という文字列を、Go言語のブーリアン型として正しく扱えるようになります。
これらの変更は、Go言語のコンパイラが、ブーリアン値の表現を数値からより明確な文字列リテラルへと移行させ、それによってコンパイルチェーン全体での一貫性と正確性を向上させたことを示しています。
関連リンク
- Go言語の公式ウェブサイト: https://go.dev/
- Go言語のソースコードリポジトリ (GitHub): https://github.com/golang/go
ar
(Unix) - Wikipedia: https://en.wikipedia.org/wiki/Ar_(Unix)- Yacc - Wikipedia: https://en.wikipedia.org/wiki/Yacc
参考にした情報源リンク
- Go言語の初期のコンパイラ設計に関するドキュメントや議論(Goのメーリングリストやデザインドキュメントなど、当時の情報源を特定するのは困難ですが、Goの歴史に関する一般的な知識に基づいています)。
ar
ユーティリティの一般的な動作と、それがコンパイラツールチェーンでどのように使用されるかに関する情報。- コンパイラのフロントエンド(字句解析、構文解析)とバックエンド(コード生成、最適化)に関する一般的な知識。
- Go言語のソースコード内の
src/cmd/gc
ディレクトリの構造と、export.c
やgo.y
のようなファイルが果たす役割に関する理解。 - Go compiler
gc
export data: https://go.dev/blog/go1.18-unified-export-data golang.org/x/tools/go/gcexportdata
package: https://pkg.go.dev/golang.org/x/tools/go/gcexportdata