[インデックス 13931] ファイルの概要
このコミットは、Go言語のCgo(C言語との相互運用機能)関連のテストコードにおいて、Goのint
型とCのint
型のサイズに関する仮定を修正するものです。特に、Goのint
型が将来的に64ビットプラットフォームで64ビット幅になることを見越して、既存のテストがその変更に耐えられるように準備を進めています。
コミット
commit 0a006b4923c129a4b699f5ba17114fc98e20ce4c
Author: Russ Cox <rsc@golang.org>
Date: Mon Sep 24 14:58:45 2012 -0400
misc/cgo: prepare for 64-bit ints
In a few places, the existing cgo tests assume that a
Go int is the same as a C int. Making int 64 bits wide
on 64-bit platforms violates this assumption.
Change that code to assume that Go int32 and C int
are the same instead. That's still not great, but it's better,
and I am unaware of any systems we run on where it is not true.
Update #2188.
R=iant, r
CC=golang-dev
https://golang.org/cl/6552064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0a006b4923c129a4b699f5ba17114fc98e20ce4c
元コミット内容
Goのint
型とCのint
型が同じサイズであるという仮定に基づいて書かれた既存のCgoテストコードを修正します。64ビットプラットフォームでGoのint
型が64ビット幅になる場合に、この仮定が破綻するため、Goのint32
型とCのint
型が同じであるという仮定に変更します。これは理想的ではありませんが、現状のGoが動作するシステムではこの仮定が真であるため、より良い解決策とされています。この変更はIssue #2188に関連しています。
変更の背景
Go言語のint
型は、その実行環境のCPUアーキテクチャに依存してサイズが変わる可変長整数型です。具体的には、32ビットシステムでは32ビット(4バイト)、64ビットシステムでは64ビット(8バイト)の幅を持ちます。一方、C言語のint
型も標準でサイズが保証されておらず、通常はコンパイラやプラットフォームに依存しますが、多くのUnix系システムやWindowsでは32ビット幅であることが一般的です。
このコミットが作成された2012年当時、Go言語はまだ発展途上にあり、特に64ビットシステムにおけるint
型の挙動について、より厳密な定義と実装の調整が進められていました。既存のCgoテストコードの一部には、Goのint
型とCのint
型が常に同じサイズであるという暗黙の仮定が含まれていました。しかし、Goが64ビットシステムでint
型を64ビット幅として扱うようになると、この仮定は破綻し、Cgoを介したC関数とのやり取りで型の不一致による問題が発生する可能性がありました。
この変更の背景には、Go言語の型システム、特に整数型のセマンティクスをより堅牢にし、異なるアーキテクチャ間での互換性と移植性を高めるというGo開発チームの意図があります。CgoはGoとCの間の橋渡しをする重要な機能であり、その型変換の挙動は非常に重要です。将来的なint
型の64ビット化に備え、Cgoテストが正しく動作するように事前に修正を行うことで、Go言語全体の安定性と信頼性を向上させる狙いがありました。
前提知識の解説
Go言語の整数型
Go言語には、固定幅の整数型と可変幅の整数型があります。
- 固定幅整数型:
int8
,int16
,int32
,int64
(それぞれ8, 16, 32, 64ビットの符号付き整数)、uint8
,uint16
,uint32
,uint64
(符号なし整数)。これらの型は、どのプラットフォームでも常に同じビット幅を持ちます。 - 可変幅整数型:
int
,uint
,uintptr
。int
とuint
は、実行環境のCPUアーキテクチャ(32ビットまたは64ビット)に応じて、それぞれ32ビットまたは64ビットの幅を持ちます。これは、C言語のint
型がプラットフォーム依存であるのと似ています。uintptr
は、ポインタを保持するのに十分な大きさの符号なし整数型で、これもアーキテクチャに依存します。
Cgo
Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Goのソースファイルにimport "C"
という行を追加することで有効になります。Cgoを使用すると、GoとCの間でデータを受け渡す際に、型のマッピングと変換が行われます。
CgoにおけるGoとCの型の対応は以下のようになります(一部抜粋):
- Go
int8
,uint8
<-> Cchar
,signed char
,unsigned char
- Go
int16
,uint16
<-> Cshort
,unsigned short
- Go
int32
,uint32
<-> Cint
,unsigned int
(ただし、Cのint
の実際のサイズはプラットフォーム依存) - Go
int64
,uint64
<-> Clong long
,unsigned long long
重要なのは、Goのint
型がCのどの型にマッピングされるかです。Goのint
はプラットフォーム依存であるため、Cのint
(これもプラットフォーム依存)と常に一致するとは限りません。このコミットの時点では、Goのint
が32ビットシステムでは32ビット、64ビットシステムでは64ビットになるというGoの設計意図と、Cのint
が多くのシステムで32ビットであるという現実との間にギャップがありました。
Issue #2188
Go言語のIssue #2188は、「int
should be 32-bit on 32-bit systems, 64-bit on 64-bit systems」というタイトルで、Goのint
型のサイズに関する議論と決定を扱ったものです。このIssueにより、Goのint
型が実行環境のネイティブなワードサイズに合わせられることが明確にされました。このコミットは、その決定がCgoの既存のコードベースに与える影響を緩和するための準備作業の一環です。
技術的詳細
このコミットの技術的な核心は、GoとCの間の整数型変換における「安全な仮定」の変更にあります。
-
Go
int
のサイズ変更の予期: Go言語の設計では、int
型はプラットフォームのネイティブなワードサイズに合わせるという方針がありました。これは、32ビットシステムではint
が32ビット、64ビットシステムではint
が64ビットになることを意味します。コミットが作成された2012年当時、Goのint
が64ビットシステムで実際に64ビット幅になる実装変更が進行中、または計画されていました。 -
C
int
の一般的なサイズ: 多くのCコンパイラとプラットフォームにおいて、int
型は32ビット幅です。これは歴史的な経緯やABI(Application Binary Interface)の互換性によるものです。 -
既存のCgoテストの仮定: 変更前のCgoテストコードでは、Goの
int
型とCのint
型が同じサイズであるという暗黙の仮定に基づいて、Goのint
型の変数をCのint
型にキャストしてC関数に渡したり、その逆を行ったりしていました。例えば、C.int(go_int_var)
のようなコードです。 -
仮定の破綻: Goの
int
が64ビットシステムで64ビット幅になると、Goのint
(64ビット)とCのint
(32ビット)の間でサイズが不一致になります。この状態でGoの64ビットint
をCの32ビットint
に直接キャストして渡すと、上位32ビットの情報が失われる(切り捨てられる)可能性があります。これは、特に大きな数値を扱う場合にバグの原因となります。 -
新しい仮定への変更: このコミットでは、Goの
int
型ではなく、Goの**int32
型**がCのint
型と同じサイズであるという仮定に変更します。Goのint32
型は、どのプラットフォームでも常に32ビット幅であることが保証されています。これにより、Cのint
が32ビットであるという一般的な事実とGoのint32
が一致するため、より安全な型変換が可能になります。 -
「まだ理想的ではない」というコメントの意図: コミットメッセージには「That's still not great, but it's better」とあります。これは、Cの
int
型も厳密には標準でサイズが保証されているわけではないため、Goのint32
とCのint
が常に完全に一致するという保証はない、というニュアンスを含んでいます。しかし、Goが動作するほとんどのシステムではCのint
が32ビットであるため、実用上は問題ないという判断です。より厳密な型変換が必要な場合は、Cのint32_t
やint64_t
のような固定幅型を使用することが推奨されますが、このコミットの目的は既存のテストコードの修正に限定されています。
この変更は、Go言語が異なるアーキテクチャで一貫した動作を保証し、Cgoのような低レベルな相互運用機能においても型の安全性を高めるための重要なステップでした。
コアとなるコードの変更箇所
このコミットでは、主にmisc/cgo
ディレクトリ内のテストおよびサンプルコードが修正されています。具体的には、Goのint
型を使用していた箇所がint32
型に変更されています。
-
misc/cgo/life/life.go
:func Run(gen, x, y int, a []int)
の関数シグネチャがfunc Run(gen, x, y int, a []int32)
に変更されました。- 関数内部のローカル変数
n
の宣言がn := make([]int, x*y)
からn := make([]int32, x*y)
に変更されました。 C.Step
関数への引数a[0]
とn[0]
のポインタキャストは、(*C.int)(unsafe.Pointer(&a[0]))
と(*C.int)(unsafe.Pointer(&n[0]))
のままであり、Goのint32
がCのint
にマッピングされることを前提としています。
-
misc/cgo/life/main.go
:- 配列
a
の宣言がvar a [MAXDIM * MAXDIM]int
からvar a [MAXDIM * MAXDIM]int32
に変更されました。
- 配列
-
misc/cgo/test/issue1560.go
://export BackgroundSleep
でエクスポートされるGo関数のシグネチャがfunc BackgroundSleep(n int)
からfunc BackgroundSleep(n int32)
に変更されました。
コアとなるコードの解説
これらの変更は、Goのint
型が64ビットシステムで64ビット幅になることを見越して、Cgoを介してC言語とやり取りする部分の型安全性を確保するためのものです。
-
life.go
とmain.go
の変更: Goのint
型はプラットフォーム依存のサイズを持つため、32ビットシステムでは32ビット、64ビットシステムでは64ビットになります。一方、Cのint
型は多くのシステムで32ビットです。life.go
とmain.go
は、C言語で実装されたライフゲームのシミュレーションロジック(C.Step
関数)とGo言語のコードをCgoで連携させるサンプルです。このサンプルでは、ゲームの状態を保持する配列a
や一時配列n
の要素として整数を使用しています。 変更前はこれらの配列が[]int
や[... ]int
として宣言されていました。もし64ビットシステムでGoのint
が64ビットになると、これらの配列要素が64ビット幅になります。しかし、C.Step
関数が期待するCのint
は通常32ビットです。この不一致は、unsafe.Pointer
を使ったポインタ変換とC.int
へのキャストにおいて、データの切り捨てや予期せぬ動作を引き起こす可能性がありました。int
をint32
に変更することで、Go側で扱う整数が明示的に32ビット幅であることが保証されます。これにより、Cのint
(一般的な32ビット)との間でサイズが一致し、Cgoを介したデータ受け渡しが安全に行えるようになります。 -
issue1560.go
の変更:issue1560.go
は、GoからC関数を呼び出し、さらにCからGoの関数をコールバックするような複雑なCgoのテストケースです。//export BackgroundSleep
は、C言語から呼び出されるGoの関数を定義しています。 変更前はBackgroundSleep
関数の引数n
がint
型でした。これもlife.go
と同様に、64ビットシステムでGoのint
が64ビットになると、C言語側からこの関数を呼び出す際に、Cのint
(32ビット)とGoのint
(64ビット)の間で型の不一致が生じる可能性がありました。 引数をint32
に変更することで、C言語側から32ビットの整数が渡されても、Go側で正確に32ビットのint32
として受け取ることが保証され、型の不一致による潜在的なバグが回避されます。
これらの変更は、Goのint
型のプラットフォーム依存性という特性と、Cgoを介したC言語との相互運用における型変換の厳密性を考慮した、堅実な対応と言えます。
関連リンク
- Go言語の
int
型に関する公式ドキュメント(Goのバージョンによって記述が異なる場合がありますが、int
のサイズがプラットフォーム依存であることは一貫しています) - Cgoの公式ドキュメント(GoとCの型マッピングに関する詳細)
- Go Issue #2188:
int
should be 32-bit on 32-bit systems, 64-bit on 64-bit systems
参考にした情報源リンク
- Go言語の公式ドキュメント
- Cgoの公式ドキュメント
- Go言語のIssueトラッカー (特にIssue #2188)
- C言語のデータ型に関する一般的な情報(
int
のサイズなど) - Go言語の歴史と設計に関する記事や議論