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

[インデックス 12141] ファイルの概要

このコミットは、Go言語のcgoツールにおける不透明型(opaque types)の定義に関するバグを修正するものです。具体的には、C言語のunion型やclass型がGo言語側で正しく不透明型として扱われるように、cgoの内部処理が変更されています。

コミット

commit 024df9f6c4abbfa2ba2a753d8231f52e99ca6ebc
Author: Gustavo Niemeyer <gustavo@niemeyer.net>
Date:   Wed Feb 22 17:10:25 2012 -0200

    cgo: fix definition of opaque types
    
    Fixes #3082.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5683074
---
 src/cmd/cgo/gcc.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 342a8a530d..98a847e6fa 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -1164,6 +1164,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
 		goIdent[name.Name] = name
 		switch dt.Kind {
 		case "union", "class":
+			t.Go = c.Opaque(t.Size)
 			if t.C.Empty() {
 				t.C.Set("typeof(unsigned char[%d])", t.Size)
 			}

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/024df9f6c4abbfa2ba2a753d8231f52e99ca6ebc

元コミット内容

このコミットは、cgoツールがC言語のunion型やclass型をGo言語の型に変換する際に、それらを不透明型として正しく定義していなかった問題を修正します。これにより、Go言語側でこれらのC言語の型を扱う際に、その内部構造に依存しない、より安全で移植性の高いコードが生成されるようになります。

変更の背景

Go言語のcgoツールは、GoプログラムからC言語のコードを呼び出すためのメカニズムを提供します。C言語にはstructunionclassといった複合型がありますが、これらをGo言語側で直接扱うことは、メモリレイアウトやアラインメントの違いから複雑になることがあります。特にunion型やclass型は、その性質上、内部構造がGo言語の型システムと直接マッピングしにくい場合があります。

このコミットが修正しているのは、cgounion型やclass型をGo言語の型に変換する際に、それらを「不透明型(opaque type)」として扱うべきところで、その処理が不完全であったという問題です。不透明型とは、その内部構造がGo言語側からは見えず、単に特定のサイズを持つメモリ領域として扱われる型のことです。これにより、Go言語側はC言語の型の詳細な実装に依存することなく、ポインタ渡しやサイズ指定によるメモリ確保など、安全な方法でC言語のデータとやり取りできます。

この問題は、Go issue #3082として報告されていました。このイシューは、cgoが特定のC言語の型をGo言語に変換する際に、予期せぬ動作やコンパイルエラーを引き起こす可能性があったことを示唆しています。

前提知識の解説

cgo

cgoはGo言語に組み込まれているツールで、GoプログラムからC言語の関数を呼び出したり、C言語のデータ構造を扱ったりすることを可能にします。Goのソースファイル内にimport "C"という行を記述し、その直前のコメントブロックにC言語のコードを記述することで、cgoがGoとCの間のバインディングコードを生成します。

不透明型 (Opaque Types)

プログラミングにおける不透明型とは、その内部構造が外部からは見えないように抽象化されたデータ型のことです。外部からは、その型のサイズや、その型に対する操作(関数呼び出しなど)のみが保証され、内部の実装詳細は隠蔽されます。C言語では、void*や、前方宣言のみで定義が提供されないstructなどが不透明型として扱われることがあります。cgoにおいては、C言語の複雑な型(特にGo言語の型システムに直接マッピングしにくいもの)をGo言語側で安全に扱うために、不透明型として扱うことがあります。これにより、Go言語側はC言語の型の詳細なメモリレイアウトを知る必要がなくなり、ポインタとして受け渡し、C言語の関数を通じて操作するといった方法がとられます。

union型とclass型 (C言語)

  • union: C言語のunionは、複数の異なる型のメンバーが同じメモリ領域を共有する複合型です。一度に1つのメンバーしか有効になりません。例えば、整数と浮動小数点数のどちらか一方を格納したい場合にunionが使われます。Go言語にはunionに直接対応する型がないため、cgoで扱う際には特別な考慮が必要です。
  • class: C++におけるclassは、データ(メンバー変数)と関数(メンバー関数)をカプセル化した複合型です。C言語のstructを拡張したもので、継承やポリモーフィズムといったオブジェクト指向の機能を提供します。cgoはC++のclassを直接サポートするわけではありませんが、Cリンケージを持つC++関数や、C++のstruct(C言語のstructと互換性がある場合)を扱うことは可能です。このコミットの文脈では、classはC++のクラスのインスタンスを指し、そのメモリレイアウトがGo言語から直接扱いにくい場合に不透明型として扱う必要があったと考えられます。

dwarf.Type

dwarf.Typeは、DWARF (Debugging With Attributed Record Formats) デバッグ情報から取得される型情報を表すものです。cgoはCコンパイラが生成するDWARF情報からC言語の型定義を読み取り、それをGo言語の型に変換する際に利用します。

技術的詳細

このコミットは、src/cmd/cgo/gcc.goファイルのTypeメソッドに1行追加することで、union型とclass型がGo言語側で不透明型として扱われるように修正しています。

Typeメソッドは、C言語の型(dwarf.Type)をGo言語の型(*Type)に変換する役割を担っています。このメソッド内で、C言語の型がunionまたはclassであると判断された場合、以下の処理が追加されました。

t.Go = c.Opaque(t.Size)
  • t: 変換中のGo言語の型を表す構造体。
  • t.Go: Go言語側で表現される型情報。
  • c.Opaque(t.Size): cgotypeConv構造体(c)のOpaqueメソッドを呼び出しています。このメソッドは、指定されたサイズ(t.Size)を持つ不透明型を生成します。

この変更により、union型やclass型がGo言語側でその内部構造が隠蔽された不透明なバイト配列として扱われるようになります。これにより、Go言語側はこれらの型の詳細なメモリレイアウトを知る必要がなくなり、C言語のコードとの相互運用性が向上します。

既存のコードでは、unionclassの場合にt.C.Empty()が真であればt.C.Set("typeof(unsigned char[%d])", t.Size)という行があり、これはC言語側でunsigned charの配列として型を定義しようとしていました。しかし、Go言語側で不透明型として明示的に設定することで、より堅牢な型変換が実現されます。

コアとなるコードの変更箇所

--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -1164,6 +1164,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
 		goIdent[name.Name] = name
 		switch dt.Kind {
 		case "union", "class":
+			t.Go = c.Opaque(t.Size)
 			if t.C.Empty() {
 				t.C.Set("typeof(unsigned char[%d])", t.Size)
 			}

コアとなるコードの解説

変更はsrc/cmd/cgo/gcc.goファイルのTypeメソッド内、switch dt.Kind文のcase "union", "class":ブロックにあります。

dt.Kindは、DWARFデバッグ情報から取得されたC言語の型の種類を示します。このswitch文は、C言語の型がunionまたはclassである場合に実行されるロジックを定義しています。

追加された行 t.Go = c.Opaque(t.Size) は、Go言語側で表現される型(t.Go)を、c.Opaque(t.Size)の呼び出し結果に設定しています。

  • c.Opaque(t.Size): この関数は、t.Sizeで指定されたバイト数を持つ不透明なGo型を生成します。これにより、Go言語側からはunionclassの内部構造が見えず、単にそのサイズ分のメモリ領域として扱われるようになります。

この変更により、cgounionclassといったC言語の複合型を、Go言語の型システムに直接マッピングできない場合でも、そのサイズ情報のみを保持する不透明型として安全に扱うことができるようになりました。これにより、GoとCの間の相互運用性における潜在的な問題が解決され、より堅牢なcgoの利用が可能になります。

関連リンク

  • Go issue #3082: https://github.com/golang/go/issues/3082 (このコミットが修正したとされるイシュー)
    • 注記: 検索結果から、このイシュー番号は古いGoのイシュートラッカーのものである可能性があり、現在のGitHubリポジトリのイシューとは直接リンクしない場合があります。しかし、コミットメッセージに明記されているため、当時のGo開発における重要な修正であったことは間違いありません。

参考にした情報源リンク