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

[インデックス 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
    • intuintは、実行環境の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 <-> C char, signed char, unsigned char
  • Go int16, uint16 <-> C short, unsigned short
  • Go int32, uint32 <-> C int, unsigned int (ただし、Cのintの実際のサイズはプラットフォーム依存)
  • Go int64, uint64 <-> C long 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の間の整数型変換における「安全な仮定」の変更にあります。

  1. Go intのサイズ変更の予期: Go言語の設計では、int型はプラットフォームのネイティブなワードサイズに合わせるという方針がありました。これは、32ビットシステムではintが32ビット、64ビットシステムではintが64ビットになることを意味します。コミットが作成された2012年当時、Goのintが64ビットシステムで実際に64ビット幅になる実装変更が進行中、または計画されていました。

  2. C intの一般的なサイズ: 多くのCコンパイラとプラットフォームにおいて、int型は32ビット幅です。これは歴史的な経緯やABI(Application Binary Interface)の互換性によるものです。

  3. 既存のCgoテストの仮定: 変更前のCgoテストコードでは、Goのint型とCのint型が同じサイズであるという暗黙の仮定に基づいて、Goのint型の変数をCのint型にキャストしてC関数に渡したり、その逆を行ったりしていました。例えば、C.int(go_int_var)のようなコードです。

  4. 仮定の破綻: Goのintが64ビットシステムで64ビット幅になると、Goのint(64ビット)とCのint(32ビット)の間でサイズが不一致になります。この状態でGoの64ビットintをCの32ビットintに直接キャストして渡すと、上位32ビットの情報が失われる(切り捨てられる)可能性があります。これは、特に大きな数値を扱う場合にバグの原因となります。

  5. 新しい仮定への変更: このコミットでは、Goのint型ではなく、Goの**int32型**がCのint型と同じサイズであるという仮定に変更します。Goのint32型は、どのプラットフォームでも常に32ビット幅であることが保証されています。これにより、Cのintが32ビットであるという一般的な事実とGoのint32が一致するため、より安全な型変換が可能になります。

  6. 「まだ理想的ではない」というコメントの意図: コミットメッセージには「That's still not great, but it's better」とあります。これは、Cのint型も厳密には標準でサイズが保証されているわけではないため、Goのint32とCのintが常に完全に一致するという保証はない、というニュアンスを含んでいます。しかし、Goが動作するほとんどのシステムではCのintが32ビットであるため、実用上は問題ないという判断です。より厳密な型変換が必要な場合は、Cのint32_tint64_tのような固定幅型を使用することが推奨されますが、このコミットの目的は既存のテストコードの修正に限定されています。

この変更は、Go言語が異なるアーキテクチャで一貫した動作を保証し、Cgoのような低レベルな相互運用機能においても型の安全性を高めるための重要なステップでした。

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

このコミットでは、主にmisc/cgoディレクトリ内のテストおよびサンプルコードが修正されています。具体的には、Goのint型を使用していた箇所がint32型に変更されています。

  1. 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にマッピングされることを前提としています。
  2. misc/cgo/life/main.go:

    • 配列 a の宣言が var a [MAXDIM * MAXDIM]int から var a [MAXDIM * MAXDIM]int32 に変更されました。
  3. misc/cgo/test/issue1560.go:

    • //export BackgroundSleep でエクスポートされるGo関数のシグネチャが func BackgroundSleep(n int) から func BackgroundSleep(n int32) に変更されました。

コアとなるコードの解説

これらの変更は、Goのint型が64ビットシステムで64ビット幅になることを見越して、Cgoを介してC言語とやり取りする部分の型安全性を確保するためのものです。

  • life.gomain.goの変更: Goのint型はプラットフォーム依存のサイズを持つため、32ビットシステムでは32ビット、64ビットシステムでは64ビットになります。一方、Cのint型は多くのシステムで32ビットです。 life.gomain.goは、C言語で実装されたライフゲームのシミュレーションロジック(C.Step関数)とGo言語のコードをCgoで連携させるサンプルです。このサンプルでは、ゲームの状態を保持する配列aや一時配列nの要素として整数を使用しています。 変更前はこれらの配列が[]int[... ]intとして宣言されていました。もし64ビットシステムでGoのintが64ビットになると、これらの配列要素が64ビット幅になります。しかし、C.Step関数が期待するCのintは通常32ビットです。この不一致は、unsafe.Pointerを使ったポインタ変換とC.intへのキャストにおいて、データの切り捨てや予期せぬ動作を引き起こす可能性がありました。 intint32に変更することで、Go側で扱う整数が明示的に32ビット幅であることが保証されます。これにより、Cのint(一般的な32ビット)との間でサイズが一致し、Cgoを介したデータ受け渡しが安全に行えるようになります。

  • issue1560.goの変更: issue1560.goは、GoからC関数を呼び出し、さらにCからGoの関数をコールバックするような複雑なCgoのテストケースです。//export BackgroundSleepは、C言語から呼び出されるGoの関数を定義しています。 変更前はBackgroundSleep関数の引数nint型でした。これも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言語の歴史と設計に関する記事や議論