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

[インデックス 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進数の数値(例: 0x00x1)として表現されていました。

問題は、これらのブーリアン値が一度エクスポートされ、その後別のコンパイルプロセスで再インポートされる際に発生しました。元のコミットメッセージによると、「re-export of bools was changing format from hex to decimal」とあり、ブーリアン値の表現が16進数から10進数に変わってしまうというバグがありました。

この形式の変更が、arユーティリティの動作に影響を与えました。arは、複数のオブジェクトファイルやライブラリを一つのアーカイブファイルにまとめるために使用される標準的なUnixユーティリティです。Goコンパイラは、コンパイルされたパッケージのメタデータやシンボル情報をarアーカイブ内に格納し、arstrcmp(文字列比較)ベースのメカニズムを利用して、これらの値やシンボルを比較・管理していたと考えられます。

ブーリアン値の表現が0x1(16進数)から1(10進数)のように変わってしまうと、arstrcmpはこれらを異なる値として認識してしまい、結果としてコンパイルプロセスやリンケージに予期せぬ問題を引き起こしていました。このバグは、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)を構築する役割を担います。

技術的詳細

このコミットの技術的解決策は、ブーリアン値のエクスポート形式を根本的に変更することにあります。

  1. 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" としてエクスポートされるようになります。
    • この変更により、ブーリアン値の表現が数値から文字列に変わり、arstrcmpが常に一貫した文字列("true"または"false")を比較するようになり、形式の不一致による問題を回避できます。
    • また、Bprint(bout, "const %lS ", s);からBprint(bout, "const %lS", s);、およびBprint(bout, "%#T ", t);からBprint(bout, " %#T", t);への変更は、エクスポートされる文字列のフォーマット調整であり、新しいブーリアン形式との整合性を保つための微調整と考えられます。
  2. src/cmd/gc/go.y の変更:

    • export.cでブーリアン値が文字列としてエクスポートされるようになったため、コンパイラがこれらの文字列を正しくパースできるように、パーサーの文法定義も更新する必要があります。
    • go.yファイルにLTRUELFALSEという新しいトークンが追加され、それぞれがbooltrue->valboolfalse->valという内部のブーリアン定数にマッピングされるようになりました。
    • これは、Goのソースコードやエクスポートされたメタデータ内でtruefalseという文字列リテラルが出現した場合に、パーサーがそれをブーリアン値として認識し、適切な内部表現に変換するためのルールです。これにより、エクスポートされたブーリアン値が再インポートされる際に、正しく解釈されることが保証されます。

この修正は、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 という文字列リテラルとして出力されるようになりました。これにより、数値としての曖昧さがなくなり、arstrcmpが常に期待通りの比較を行うことが保証されます。
  • フォーマットの微調整:
    • 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: ルールに LTRUELFALSE という新しいプロダクションが追加されました。
    • LTRUE が検出された場合、$$ = booltrue->val; が実行されます。これは、パーサーが "true" という文字列リテラルをGo言語の内部的なブーリアン真値 (booltrue->val) にマッピングすることを示します。
    • 同様に、LFALSE が検出された場合、$$ = boolfalse->val; が実行され、"false" が内部的なブーリアン偽値 (boolfalse->val) にマッピングされます。
    • この変更は、export.c でブーリアン値が文字列としてエクスポートされるようになったため、そのエクスポートされた情報を他のコンパイルユニットが正しく読み込み、解釈できるようにするために不可欠です。これにより、Goコンパイラは "true""false" という文字列を、Go言語のブーリアン型として正しく扱えるようになります。

これらの変更は、Go言語のコンパイラが、ブーリアン値の表現を数値からより明確な文字列リテラルへと移行させ、それによってコンパイルチェーン全体での一貫性と正確性を向上させたことを示しています。

関連リンク

参考にした情報源リンク

  • Go言語の初期のコンパイラ設計に関するドキュメントや議論(Goのメーリングリストやデザインドキュメントなど、当時の情報源を特定するのは困難ですが、Goの歴史に関する一般的な知識に基づいています)。
  • arユーティリティの一般的な動作と、それがコンパイラツールチェーンでどのように使用されるかに関する情報。
  • コンパイラのフロントエンド(字句解析、構文解析)とバックエンド(コード生成、最適化)に関する一般的な知識。
  • Go言語のソースコード内のsrc/cmd/gcディレクトリの構造と、export.cgo.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進数の数値(例: 0x00x1)として表現されていました。

問題は、これらのブーリアン値が一度エクスポートされ、その後別のコンパイルプロセスで再インポートされる際に発生しました。元のコミットメッセージによると、「re-export of bools was changing format from hex to decimal」とあり、ブーリアン値の表現が16進数から10進数に変わってしまうというバグがありました。

この形式の変更が、arユーティリティの動作に影響を与えました。arは、複数のオブジェクトファイルやライブラリを一つのアーカイブファイルにまとめるために使用される標準的なUnixユーティリティです。Goコンパイラは、コンパイルされたパッケージのメタデータやシンボル情報をarアーカイブ内に格納し、arstrcmp(文字列比較)ベースのメカニズムを利用して、これらの値やシンボルを比較・管理していたと考えられます。

ブーリアン値の表現が0x1(16進数)から1(10進数)のように変わってしまうと、arstrcmpはこれらを異なる値として認識してしまい、結果としてコンパイルプロセスやリンケージに予期せぬ問題を引き起こしていました。このバグは、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)を構築する役割を担います。

技術的詳細

このコミットの技術的解決策は、ブーリアン値のエクスポート形式を根本的に変更することにあります。

  1. 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" としてエクスポートされるようになります。
    • この変更により、ブーリアン値の表現が数値から文字列に変わり、arstrcmpが常に一貫した文字列("true"または"false")を比較するようになり、形式の不一致による問題を回避できます。
    • また、Bprint(bout, "const %lS ", s);からBprint(bout, "const %lS", s);、およびBprint(bout, "%#T ", t);からBprint(bout, " %#T", t);への変更は、エクスポートされる文字列のフォーマット調整であり、新しいブーリアン形式との整合性を保つための微調整と考えられます。
  2. src/cmd/gc/go.y の変更:

    • export.cでブーリアン値が文字列としてエクスポートされるようになったため、コンパイラがこれらの文字列を正しくパースできるように、パーサーの文法定義も更新する必要があります。
    • go.yファイルにLTRUELFALSEという新しいトークンが追加され、それぞれがbooltrue->valboolfalse->valという内部のブーリアン定数にマッピングされるようになりました。
    • これは、Goのソースコードやエクスポートされたメタデータ内でtruefalseという文字列リテラルが出現した場合に、パーサーがそれをブーリアン値として認識し、適切な内部表現に変換するためのルールです。これにより、エクスポートされたブーリアン値が再インポートされる際に、正しく解釈されることが保証されます。

この修正は、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 という文字列リテラルとして出力されるようになりました。これにより、数値としての曖昧さがなくなり、arstrcmpが常に期待通りの比較を行うことが保証されます。
  • フォーマットの微調整:
    • 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: ルールに LTRUELFALSE という新しいプロダクションが追加されました。
    • LTRUE が検出された場合、$$ = booltrue->val; が実行されます。これは、パーサーが "true" という文字列リテラルをGo言語の内部的なブーリアン真値 (booltrue->val) にマッピングすることを示します。
    • 同様に、LFALSE が検出された場合、$$ = boolfalse->val; が実行され、"false" が内部的なブーリアン偽値 (boolfalse->val) にマッピングされます。
    • この変更は、export.c でブーリアン値が文字列としてエクスポートされるようになったため、そのエクスポートされた情報を他のコンパイルユニットが正しく読み込み、解釈できるようにするために不可欠です。これにより、Goコンパイラは "true""false" という文字列を、Go言語のブーリアン型として正しく扱えるようになります。

これらの変更は、Go言語のコンパイラが、ブーリアン値の表現を数値からより明確な文字列リテラルへと移行させ、それによってコンパイルチェーン全体での一貫性と正確性を向上させたことを示しています。

関連リンク

参考にした情報源リンク

  • Go言語の初期のコンパイラ設計に関するドキュメントや議論(Goのメーリングリストやデザインドキュメントなど、当時の情報源を特定するのは困難ですが、Goの歴史に関する一般的な知識に基づいています)。
  • arユーティリティの一般的な動作と、それがコンパイラツールチェーンでどのように使用されるかに関する情報。
  • コンパイラのフロントエンド(字句解析、構文解析)とバックエンド(コード生成、最適化)に関する一般的な知識。
  • Go言語のソースコード内のsrc/cmd/gcディレクトリの構造と、export.cgo.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