[インデックス 13500] ファイルの概要
コミット
cgo: _cgo_export.c 内の宣言を修正
crosscall2 を宣言する。それに渡される関数を、暗黙的な戻り値の型に依存するのではなく、void を返すものとして宣言する。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/24ae7e686d51954b8befaaaef27f55a245d0050e
元コミット内容
commit 24ae7e686d51954b8befaaaef27f55a245d0050e
Author: Ian Lance Taylor <iant@golang.org>
Date: Wed Jul 25 14:14:37 2012 -0700
cgo: fix declarations in _cgo_export.c
Declare crosscall2. Declare the functions passed to it as
returning void, rather than relying on implicit return type.
R=golang-dev, minux.ma
CC=golang-dev
https://golang.org/cl/6432060
変更の背景
このコミットは、Go言語のcgo
ツールが生成するC言語のコード(特に_cgo_export.c
ファイル)における宣言の不備を修正することを目的としています。具体的には、crosscall2
関数の宣言が不足していた点と、crosscall2
に渡されるコールバック関数の戻り値の型が明示的にvoid
として宣言されていなかった点です。
C言語では、関数が明示的に戻り値の型を指定しない場合、コンパイラはデフォルトでint
型を返すと解釈する「暗黙的なint
型」のルールが存在しました(C99以降は非推奨、C11以降は削除)。cgo
が生成するコードにおいて、これらの関数が実際には値を返さない(void
を返す)にもかかわらず、明示的なvoid
宣言が欠けていたため、コンパイラによっては警告や予期せぬ動作を引き起こす可能性がありました。
この修正は、生成されるCコードの堅牢性を高め、コンパイラの警告を抑制し、将来的なC言語標準の変更にも対応できるようにするために行われました。
前提知識の解説
cgo
cgo
はGo言語のツールの一つで、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのメカニズムを提供します。GoとCの間の相互運用を可能にするために、cgo
はGoのソースコード内に記述されたCのコードを解析し、GoとCの間の橋渡しをするためのC言語のスタブファイルやGo言語のラッパーコードを生成します。
_cgo_export.c
cgo
がGoの関数をCから呼び出せるようにするために生成するファイルの一つが_cgo_export.c
です。このファイルには、Goの関数をCから呼び出すためのエクスポートされたC関数や、GoとCのランタイム間の連携に必要なヘルパー関数などが含まれます。Goの関数がCから呼び出される際、cgo
は通常、crosscall2
のような内部的なメカニズムを使用して、Goランタイムに制御を渡し、Goの関数を実行します。
crosscall2
crosscall2
はcgo
の内部的なヘルパー関数で、CコードからGoコードへの呼び出し(クロス呼び出し)を処理するために使用されます。これは、Goの関数ポインタと引数をCのコンテキストからGoのコンテキストへ安全に渡すための低レベルなメカニズムを提供します。この関数は通常、_cgo_export.c
のようなcgo
が生成するファイル内で宣言され、使用されます。
C言語における暗黙的な戻り値の型 (Implicit int
Return Type)
C言語の古い標準(C89/C90)では、関数の戻り値の型が明示的に指定されていない場合、コンパイラはデフォルトでその関数がint
型を返すと解釈するルールがありました。これを「暗黙的なint
型」と呼びます。
例:
// C89/C90 では、これは int を返すと解釈される
myFunction() {
// 何らかの処理
}
// C99以降では警告、C11以降ではエラーになる可能性が高い
void anotherFunction() {
// 何らかの処理
}
このルールは、現代のC言語プログラミングでは推奨されず、C99標準で非推奨となり、C11標準では削除されました。明示的にvoid
を返す関数は、必ずvoid
と宣言する必要があります。このコミットが行われた2012年当時、Goのcgo
が生成するCコードが古いC標準の暗黙的なint
型に依存していると、新しいコンパイラで警告が出たり、将来的にコンパイルエラーになるリスクがありました。
技術的詳細
このコミットの技術的な核心は、cgo
が生成するCコードの宣言を、C言語の現代的なベストプラクティスに合わせることにあります。
-
crosscall2
の明示的な宣言: 以前のcgo
のコード生成では、crosscall2
関数が使用される前に明示的に宣言されていなかった可能性があります。C言語では、関数が使用される前に宣言されていない場合、コンパイラは関数の呼び出しからそのシグネチャを推測しようとします。しかし、これはエラーの原因となる可能性があり、特にcrosscall2
のような重要な内部関数では、その正確なシグネチャ(戻り値の型、引数の型と数)を明示的に宣言することが不可欠です。この修正により、crosscall2
がvoid (*fn)(void *, int), void *, int
という引数を取り、void
を返す関数として明示的に宣言されるようになりました。 -
コールバック関数の戻り値の型を
void
として明示的に宣言:cgo
がGoの関数をCから呼び出すために生成するラッパー関数(例:_cgoexp_...
)は、通常、値を返しません。しかし、以前のコード生成では、これらの関数の戻り値の型が明示的にvoid
と宣言されていなかったため、Cコンパイラが「暗黙的なint
型」のルールを適用し、これらの関数がint
を返すと解釈してしまう可能性がありました。これは、コンパイラの警告を引き起こしたり、厳密なコンパイル設定ではエラーになったりする原因となります。この修正により、_cgoexp...
関数の宣言に明示的にvoid
が追加され、これらの関数が実際に値を返さないことがコンパイラに正しく伝えられるようになりました。
これらの変更は、生成されるCコードの移植性と堅牢性を向上させ、異なるCコンパイラやコンパイルオプションの下での互換性の問題を回避するのに役立ちます。
コアとなるコードの変更箇所
変更はsrc/cmd/cgo/out.go
ファイルに集中しています。このファイルはcgo
ツールの一部であり、C言語のスタブコードを生成するロジックを含んでいます。
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -474,6 +474,8 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\\n")
fmt.Fprintf(fgcc, "#include \\"_cgo_export.h\\"\\n")
+ fmt.Fprintf(fgcc, "\\nextern void crosscall2(void (*fn)(void *, int), void *, int);\\n\\n")
+
for _, exp := range p.ExpFunc {
\t\tfn := exp.Func
@@ -565,7 +567,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {\n \t\ts += \")\"\n \t\tfmt.Fprintf(fgcch, "\\nextern %s;\\n\", s)\n \n-\t\tfmt.Fprintf(fgcc, "extern _cgoexp%s_%s(void *, int);\\n", cPrefix, exp.ExpName)\n+\t\tfmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int);\\n", cPrefix, exp.ExpName)\n \t\tfmt.Fprintf(fgcc, "\\n%s\\n\", s)\n \t\tfmt.Fprintf(fgcc, "{\\n")
\t\tfmt.Fprintf(fgcc, "\\t%s __attribute__((packed)) a;\\n", ctype)\n```
## コアとなるコードの解説
このコミットでは、`src/cmd/cgo/out.go`内の2つの`fmt.Fprintf`呼び出しが変更されています。これらは、`cgo`が生成するC言語のソースファイル(`_cgo_export.c`など)に特定のCコードを出力するためのものです。
1. **`crosscall2`の宣言の追加**:
```diff
+ fmt.Fprintf(fgcc, "\\nextern void crosscall2(void (*fn)(void *, int), void *, int);\\n\\n")
```
この行は、`_cgo_export.c`ファイルに`crosscall2`関数の前方宣言を追加します。
* `extern`: この関数が他の翻訳単位で定義されていることを示します。
* `void`: `crosscall2`関数が値を返さないことを明示します。
* `crosscall2`: 関数の名前です。
* `void (*fn)(void *, int)`: 最初の引数が関数ポインタであることを示します。この関数ポインタは、`void`を返し、`void *`と`int`の2つの引数を取ります。これは、Goの関数をCから呼び出す際のコールバック関数を指します。
* `void *, int`: 残りの引数です。
この宣言により、`crosscall2`が使用される前にそのシグネチャがコンパイラに正確に伝えられ、型チェックが適切に行われるようになります。
2. **`_cgoexp...`関数の戻り値の型を`void`に修正**:
```diff
-\t\tfmt.Fprintf(fgcc, "extern _cgoexp%s_%s(void *, int);\\n", cPrefix, exp.ExpName)\n
+\t\tfmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int);\\n", cPrefix, exp.ExpName)\n
```
この変更は、`cgo`がGoの関数をCにエクスポートするために生成するラッパー関数(例: `_cgoexp_myGoFunction`)の宣言を修正します。
* 変更前は、`extern _cgoexp%s_%s(void *, int);`のように、戻り値の型が明示されていませんでした。これにより、Cコンパイラは「暗黙的な`int`型」のルールを適用し、これらの関数が`int`を返すと解釈する可能性がありました。
* 変更後は、`extern void _cgoexp%s_%s(void *, int);`のように、明示的に`void`が追加されています。これにより、これらの関数が実際には値を返さないことがコンパイラに正しく伝えられ、コンパイラの警告や潜在的な問題を回避できます。
これらの修正は、`cgo`が生成するCコードの品質と互換性を向上させる上で重要な役割を果たします。
## 関連リンク
* Go CL 6432060: [https://golang.org/cl/6432060](https://golang.org/cl/6432060)
## 参考にした情報源リンク
* Go言語の`cgo`に関する公式ドキュメントやブログ記事 (一般的な`cgo`の動作理解のため)
* C言語の「暗黙的な`int`型」に関するC標準のドキュメントや解説記事 (C言語の歴史的背景と現代的なプラクティス理解のため)
* GitHub上のGo言語リポジトリの関連コード (変更箇所のコンテキスト理解のため)