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

[インデックス 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

crosscall2cgoの内部的なヘルパー関数で、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言語の現代的なベストプラクティスに合わせることにあります。

  1. crosscall2の明示的な宣言: 以前のcgoのコード生成では、crosscall2関数が使用される前に明示的に宣言されていなかった可能性があります。C言語では、関数が使用される前に宣言されていない場合、コンパイラは関数の呼び出しからそのシグネチャを推測しようとします。しかし、これはエラーの原因となる可能性があり、特にcrosscall2のような重要な内部関数では、その正確なシグネチャ(戻り値の型、引数の型と数)を明示的に宣言することが不可欠です。この修正により、crosscall2void (*fn)(void *, int), void *, intという引数を取り、voidを返す関数として明示的に宣言されるようになりました。

  2. コールバック関数の戻り値の型を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言語リポジトリの関連コード (変更箇所のコンテキスト理解のため)