[インデックス 16223] ファイルの概要
このコミットは、Go言語のツールチェーンの一部である cmd/dist
内の goc2c
ツールにおけるバグ修正に関するものです。具体的には、goc2c
がGoのソースファイル(.goc
)からC言語のソースファイル(.c
)を生成する際に、生成されるCファイル内の #line
ディレクティブの行番号が誤ってオフセットされてしまう問題を修正しています。これにより、デバッグ時やエラー報告時に正確な元のGoソースコードの行番号が参照できるようになります。
コミット
- コミットハッシュ:
d33f09bcc0814d900510c23ad5285b3a7c3b2e5c
- Author: Anthony Martin ality@pbrane.org
- Date: Tue Apr 23 16:02:50 2013 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d33f09bcc0814d900510c23ad5285b3a7c3b2e5c
元コミット内容
cmd/dist: fix line numbers in goc2c generated files
We have to reset the global lineno variable before
processing each file otherwise line numbers will be
offset by the number of lines in the previous file.
The following examples are from the beginning of the
ztime_linux_amd64.c file which is generated from
time.goc in the runtime package.
Before:
#line 2483 "/home/apm/src/go/src/pkg/runtime/time.goc"
static Timers timers;
static void addtimer ( Timer* ) ;
void
time·Sleep(int64 ns)
{
#line 2492 "/home/apm/src/go/src/pkg/runtime/time.goc"
After:
#line 16 "/home/apm/src/go/src/pkg/runtime/time.goc"
static Timers timers;
static void addtimer ( Timer* ) ;
void
time·Sleep(int64 ns)
{
#line 25 "/home/apm/src/go/src/pkg/runtime/time.goc"
R=golang-dev, minux.ma, iant, r, adg
CC=golang-dev
https://golang.org/cl/8653045
変更の背景
Go言語の初期のバージョンでは、一部のランタイムコードはGoとCのハイブリッド形式で記述されており、.goc
という拡張子を持つファイルが使用されていました。これらの .goc
ファイルは、cmd/dist
ツールチェーンの一部である goc2c
というツールによってC言語のソースファイル(.c
)に変換されていました。
この変換プロセスにおいて、goc2c
は変換元のGoソースコードの行番号情報を、生成されるCファイル内の #line
プリプロセッサディレクティブとして埋め込んでいました。これは、Cコンパイラがエラーや警告を報告する際に、元のGoソースコードの正確な行番号を示すために非常に重要です。
しかし、このコミット以前の goc2c
にはバグがありました。複数の .goc
ファイルを処理する際に、内部で使用されるグローバルな行番号カウンタ lineno
がファイルごとにリセットされていませんでした。このため、2番目以降のファイルから生成されたCコードでは、#line
ディレクティブの行番号が、前のファイルの行数分だけオフセットされてしまい、誤った行番号が報告される問題が発生していました。
コミットメッセージに示されている例では、runtime
パッケージの time.goc
から生成された ztime_linux_amd64.c
ファイルの冒頭部分で、本来 16
行目であるべき #line
ディレクティブが 2483
行目と誤って表示されていることが示されています。これは、time.goc
の前に処理された別の .goc
ファイルの行数が加算されてしまった結果です。この誤った行番号は、デバッグの際に開発者を混乱させ、問題の特定を困難にする可能性がありました。
このコミットは、この行番号のオフセット問題を解決し、生成されるCファイル内の #line
ディレクティブが常に元のGoソースコードの正確な行番号を指すようにすることを目的としています。
前提知識の解説
cmd/dist
cmd/dist
は、Go言語のビルドシステムの中核をなすツールです。Goのソースコードからコンパイラ、リンカ、アセンブラなどのツールチェーン全体をビルドするために使用されます。Goのソースコード自体もGoで書かれているため、cmd/dist
はブートストラッププロセスにおいて重要な役割を果たします。
goc2c
goc2c
は cmd/dist
の一部として機能するツールで、Go言語のランタイムの一部が記述されていた .goc
ファイルをC言語のソースファイルに変換する役割を担っていました。Go言語の初期段階では、パフォーマンスが重要な部分や、既存のCコードとの連携が必要な部分で、GoとCのハイブリッドコードが用いられることがありました。goc2c
は、このようなハイブリッドコードをビルドプロセスに統合するための橋渡し役でした。
.goc
ファイル
.goc
ファイルは、Go言語の初期のランタイム実装で使われていた特殊なファイル形式です。Goの構文とCの構文が混在しており、goc2c
によってCコードに変換されていました。現在では、Goランタイムのほとんどが純粋なGoで記述されており、.goc
ファイルはほとんど使われていません。
#line
ディレクティブ
C/C++言語のプリプロセッサには #line
ディレクティブというものがあります。これは、コンパイラに対して、現在のソースコードの行番号とファイル名を指定された値に変更するように指示するものです。
構文は以下の通りです。
#line number "filename"
number
: 次の行から始まる行番号を指定します。filename
: 現在のファイル名を指定します。
このディレクティブは、主に以下のような目的で使用されます。
- コード生成ツール: 別の言語やテンプレートからC/C++コードを生成するツールが、生成されたコードのエラーメッセージを元のソースファイルの行番号とファイル名で報告するために使用します。
goc2c
の場合がこれに該当します。 - デバッグ: デバッガがソースコードの正確な位置を特定できるようにします。
- マクロ展開: 複雑なマクロが展開された結果のコードをデバッグしやすくするために使用されることもあります。
このコミットの文脈では、goc2c
が time.goc
から ztime_linux_amd64.c
を生成する際に、time.goc
内のGoコードの行番号を ztime_linux_amd64.c
内の #line
ディレクティブとして埋め込んでいました。
技術的詳細
この問題の根本原因は、goc2c
ツールが複数の .goc
ファイルを処理する際に、内部で管理している行番号カウンタ lineno
を各ファイルの処理開始時にリセットしていなかったことにあります。
goc2c
は、.goc
ファイルを読み込み、Cコードを生成する過程で、元のGoコードの行番号を追跡し、それを #line
ディレクティブとして出力します。この追跡には、static unsigned int lineno = 1;
というグローバル変数(またはそれに準ずるスコープの変数)が使用されていました。
問題は、goc2c
が最初の .goc
ファイルの処理を終えた後も lineno
の値が保持され、次の .goc
ファイルの処理がその保持された値から開始されてしまっていた点です。これにより、2番目以降のファイルで生成される #line
ディレクティブの行番号は、前のファイルの総行数分だけ加算された、誤った値になっていました。
解決策はシンプルかつ効果的です。goc2c
が新しい .goc
ファイルの処理を開始する直前に、lineno
変数を 1
に明示的にリセットすることです。これにより、各ファイルは独立して行番号がカウントされ、生成されるCファイル内の #line
ディレクティブは常に元のGoソースコードの正確な行番号を指すようになります。
コミットメッセージの例で示されているように、この修正により、ztime_linux_amd64.c
の冒頭の #line
ディレクティブが 2483
から 16
に修正され、正しい行番号が反映されるようになりました。
コアとなるコードの変更箇所
--- a/src/cmd/dist/goc2c.c
+++ b/src/cmd/dist/goc2c.c
@@ -66,7 +66,7 @@ static int gcc;
/* File and line number */
static const char *file;
-static unsigned int lineno = 1;
+static unsigned int lineno;
/* List of names and types. */
struct params {
@@ -754,6 +754,7 @@ goc2c(char *goc, char *c)\n \tinput = bstr(&in);\n \toutput = &out;\n \n+\tlineno = 1;\n \tprocess_file();\n \t\n \twritefile(&out, c, 0);\n```
## コアとなるコードの解説
変更は `src/cmd/dist/goc2c.c` ファイルの2箇所で行われています。
1. **`static unsigned int lineno = 1;` から `static unsigned int lineno;` への変更**:
* 元のコードでは、`lineno` 変数が宣言時に `1` で初期化されていました。これは、プログラムが開始されたときに一度だけ行われる初期化です。
* 変更後、`lineno` は宣言時に初期化されなくなりました(デフォルト値は0)。これは、後続の明示的な初期化に依存することを示唆しています。この変更自体が直接的な修正というよりは、次の変更と合わせて意図を明確にするためのものです。
2. **`goc2c` 関数の冒頭に `lineno = 1;` の追加**:
* `goc2c` 関数は、個々の `.goc` ファイルをCファイルに変換する主要なロジックを含んでいます。
* この変更により、`goc2c` 関数が呼び出されるたびに、つまり新しい `.goc` ファイルの処理が開始されるたびに、`lineno` 変数が明示的に `1` にリセットされるようになりました。
* `process_file()` は、実際にファイルの内容を読み込み、変換処理を行う関数です。この `process_file()` が呼び出される前に `lineno` がリセットされることで、各ファイルの行番号が常に `1` から正しくカウントされることが保証されます。
この2つの変更により、`goc2c` が複数の `.goc` ファイルを連続して処理しても、それぞれのファイルに対して行番号が独立して `1` から始まるようになり、生成されるCファイル内の `#line` ディレクティブが常に正確な元のGoソースコードの行番号を指すようになりました。
## 関連リンク
- Go CL 8653045: [https://golang.org/cl/8653045](https://golang.org/cl/8653045)
## 参考にした情報源リンク
- C言語の `#line` ディレクティブに関する一般的な情報源 (例: GCC documentation, C standard draftsなど)
- Go言語の `cmd/dist` および `goc2c` に関するGoプロジェクトのドキュメントやソースコード (GoのGitHubリポジトリ)
- Go言語の初期のランタイム実装に関する歴史的情報 (Goのメーリングリストや設計ドキュメントなど)
- `time.goc` や `ztime_linux_amd64.c` といったファイル名から、Goランタイムの内部構造に関する情報 (GoのGitHubリポジトリ)