[インデックス 15329] ファイルの概要
このコミットは、Go言語のgo/types
パッケージにおける型チェックの挙動を改善し、unsafe.Alignof
、unsafe.Sizeof
、およびunsafe.Offsetof
といったメモリレイアウト関連の組み込み関数のカスタマイズを可能にするものです。これにより、特定のプラットフォームやアーキテクチャに合わせたメモリ配置の計算を、型チェッカーがより柔軟に扱えるようになります。
コミット
commit 75e7308be8dc13e53b4f39aad67f286e79ac5313
Author: Robert Griesemer <gri@golang.org>
Date: Wed Feb 20 11:10:17 2013 -0800
go/types: support for customizable Alignof, Sizeof
(Offsetof is a function of Alignof and Sizeof.)
- removed IntSize, PtrSize from Context (set Sizeof instead)
- GcImporter needs a Context now (it needs to have
access to Sizeof/Alignof)
- removed exported Size field from Basic (use Sizeof)
- added Offset to Field
- added Alignment, Size to Struct
R=adonovan
CC=golang-dev
https://golang.org/cl/7357046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/75e7308be8dc13e53b4f39aad67f286e79ac5313
元コミット内容
Go言語のgo/types
パッケージにおいて、Alignof
とSizeof
のカスタマイズをサポートする変更です。Offsetof
はAlignof
とSizeof
の関数として機能します。
主な変更点は以下の通りです。
Context
構造体からIntSize
とPtrSize
フィールドを削除し、代わりにSizeof
関数を設定するように変更。GcImporter
がContext
を必要とするようになり、Sizeof
/Alignof
へのアクセスが可能に。Basic
型からエクスポートされていたSize
フィールドを削除し、Sizeof
関数を使用するように変更。Field
構造体にOffset
フィールドを追加。Struct
構造体にAlignment
とSize
フィールドを追加。
変更の背景
Go言語の型システムは、異なるアーキテクチャやオペレーティングシステム上で一貫した動作を保証するために、型のサイズやアライメントに関する厳密なルールを持っています。しかし、unsafe
パッケージのSizeof
、Alignof
、Offsetof
といった関数は、実行環境の特性に依存するメモリレイアウト情報を提供します。
このコミット以前は、go/types
パッケージが持つContext
構造体には、IntSize
やPtrSize
といった固定のサイズ情報がハードコードされていました。これは、異なる環境での型チェックの正確性を損なう可能性がありました。例えば、32ビットシステムと64ビットシステムではポインタのサイズが異なるため、これらの値を固定してしまうと、クロスコンパイルや異なる環境での型チェック時に問題が生じる可能性がありました。
この変更の背景には、go/types
パッケージがより汎用的に、かつ正確に型チェックを行えるようにするため、メモリレイアウトに関する情報を外部から注入可能にする必要があったと考えられます。これにより、型チェッカーは、ターゲットとする環境のメモリレイアウト規則を正確に反映できるようになり、unsafe
パッケージの関数が返す値との整合性を保つことができます。特に、Offsetof
がAlignof
とSizeof
に依存するという性質上、これらのカスタマイズは不可欠でした。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と、コンピュータサイエンスにおけるメモリ管理の基礎知識が必要です。
Go言語のunsafe
パッケージ
Go言語のunsafe
パッケージは、Goの型システムやメモリ安全性の保証をバイパスする低レベルな操作を可能にするためのパッケージです。通常、Goは厳格な型チェックとメモリ管理によって安全なプログラミングを促進しますが、特定の高性能な処理やシステムプログラミングにおいては、メモリの直接操作が必要になる場合があります。
unsafe.Sizeof(x)
: 式x
のGo言語表現におけるバイト単位のサイズを返します。これは、変数がメモリ上で占める固定部分のサイズを示します。例えば、文字列の場合、文字列ヘッダ(ポインタと長さ)のサイズを返し、文字列の内容自体のサイズは返しません。unsafe.Alignof(x)
: 式x
の型の変数が必要とするアライメント(バイト単位)を返します。アライメントとは、データがメモリ上で配置される際のアドレスの制約です。例えば、4バイトのアライメントを持つデータは、アドレスが4の倍数である場所に配置されます。これにより、CPUが効率的にデータにアクセスできるようになります。unsafe.Offsetof(s.f)
: 構造体s
のフィールドf
が、構造体の先頭から何バイト目に位置するか(オフセット)を返します。構造体内のフィールド間には、アライメント要件を満たすためにパディング(埋め草)が挿入されることがあり、その結果、構造体の合計サイズは個々のフィールドのサイズの合計よりも大きくなることがあります。
これらの関数は、コンパイル時に定数として評価されるため、実行時のオーバーヘッドはありません。
go/types
パッケージ
go/types
パッケージは、Go言語のソースコードを解析し、型情報を構築・検証するためのライブラリです。Goコンパイラや各種ツール(リンター、IDEなど)が、コードの静的解析やセマンティックチェックを行う際に利用されます。このパッケージは、Goのプログラムが型規則に準拠しているかを確認し、型エラーを検出する役割を担っています。
Context
構造体:go/types
パッケージにおける型チェックのコンテキストを定義する構造体です。エラーハンドリング、識別子の解決、式の型情報取得など、型チェックプロセス全体にわたる設定やコールバック関数を保持します。このコミット以前は、IntSize
やPtrSize
といった、特定のアーキテクチャに依存するサイズ情報も含まれていました。Package
オブジェクト: 型チェックされたGoパッケージの情報を表します。パッケージ名、インポートされたパッケージ、スコープ内のオブジェクト(変数、関数、型など)などが含まれます。
gcimporter
パッケージ
gcimporter
は、Goコンパイラ(gc
)が生成するオブジェクトファイルからエクスポートされた型情報をインポートするための内部パッケージです。Goのツールチェインの一部として機能し、コンパイル済みのGoパッケージの型定義を読み込み、go/types
パッケージのPackage
オブジェクトとして再構築します。これにより、他のGoツールがコンパイル済みコードの型システムを理解し、相互作用できるようになります。
メモリのアライメントとパディング
コンピュータのメモリはバイト単位でアドレス指定されますが、CPUは通常、ワード単位(例えば4バイトや8バイト)でメモリにアクセスします。効率的なアクセスのためには、データが特定のバイト境界に配置されている必要があります。これが「アライメント」です。
- アライメント: データ型がメモリ上で配置される際のアドレスの制約。例えば、4バイト整数は4の倍数のアドレスに配置されるべき、といったルールです。
- パディング: アライメント要件を満たすために、構造体内のフィールド間や構造体の末尾に挿入される未使用のバイト。これにより、構造体の合計サイズは、個々のフィールドのサイズの合計よりも大きくなることがあります。
これらの概念は、unsafe.Sizeof
、unsafe.Alignof
、unsafe.Offsetof
が返す値の根拠となります。
技術的詳細
このコミットは、go/types
パッケージの内部構造を大きく変更し、メモリレイアウトに関する情報をより柔軟に扱えるようにしています。
-
Context
構造体の変更:IntSize
とPtrSize
フィールドが削除されました。これらの固定値は、特定のアーキテクチャに依存するため、汎用的な型チェックには不適切でした。- 代わりに、
Alignof func(Type) int64
とSizeof func(Type) int64
という関数型のフィールドが追加されました。これにより、型チェッカーは、外部から提供される関数を通じて、任意の型のサイズとアライメントを動的に決定できるようになります。デフォルトの実装(DefaultAlignof
、DefaultSizeof
)も提供されますが、必要に応じてカスタマイズ可能です。 Context
が空の場合、すぐに使用できるデフォルトのコンテキストとして機能するように変更されました。
-
GcImporter
の変更:GcImport
関数およびgcParser
のinit
メソッドが、Context
型の引数を取るようになりました。これは、インポートされたパッケージの型情報を解析する際に、現在の型チェックコンテキスト(特にカスタマイズされたAlignof
やSizeof
関数)にアクセスする必要があるためです。これにより、インポートされた型に対しても正確なメモリレイアウト情報が適用されるようになります。
-
型情報の内部表現の変更:
Basic
型からエクスポートされていたSize
フィールドが削除され、非エクスポートのsize
フィールドに変更されました。これは、基本型のサイズがContext.Sizeof
を通じて取得されるべきであり、直接アクセスされるべきではないという設計思想の変更を反映しています。Field
構造体にOffset int64
フィールドが追加されました。これにより、構造体のフィールドが構造体内で占めるオフセットが明示的に保持されるようになります。これはunsafe.Offsetof
の実装に直接関連します。Struct
構造体にAlignment int64
とSize int64
フィールドが追加されました。これにより、構造体全体のメモリレイアウト情報が、その構造体型自体に保持されるようになります。これは、構造体のアライメントとサイズが、そのフィールドのアライメントとサイズに基づいて計算されるためです。
-
unsafe
組み込み関数の実装変更:_Alignof
、_Offsetof
、_Sizeof
といった組み込み関数の実装が、check.ctxt.alignof(x.typ)
やcheck.ctxt.sizeof(x.typ)
を呼び出すように変更されました。これにより、これらの関数が返す値が、Context
に設定されたカスタマイズ可能なAlignof
およびSizeof
関数によって決定されるようになります。- 特に
_Offsetof
は、lookupField
の結果からres.offset
を取得するように変更され、Field
構造体に追加されたOffset
フィールドが利用されるようになりました。 - 構造体のサイズとアライメントを計算するための新しいヘルパー関数
align
とnewStruct
が追加されました。newStruct
は、フィールドの型とContext
のalignof
/sizeof
関数を使用して、構造体のAlignment
とSize
、および各フィールドのOffset
を計算します。
これらの変更により、go/types
パッケージは、Goのunsafe
パッケージが提供するメモリレイアウト情報とより密接に連携し、異なる環境での型チェックの正確性と柔軟性を向上させています。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は、src/pkg/go/types/api.go
、src/pkg/go/types/builtins.go
、src/pkg/go/types/expr.go
、src/pkg/go/types/gcimporter.go
、src/pkg/go/types/operand.go
、src/pkg/go/types/types.go
に集中しています。
-
src/pkg/go/types/api.go
:Context
構造体からIntSize
とPtrSize
が削除され、Alignof func(Type) int64
とSizeof func(Type) int64
が追加されました。Default
コンテキストが削除され、空のContext
がデフォルトとして機能するように変更されました。
-
src/pkg/go/types/builtins.go
:_Alignof
、_Offsetof
、_Sizeof
の組み込み関数の実装が、check.ctxt.alignof
、check.ctxt.sizeof
、およびlookupField
から取得したオフセットを使用するように変更されました。sizeof
関数がContext
のメソッド(*Context).sizeof
に変更され、alignof
関数とDefaultAlignof
、DefaultSizeof
が追加されました。
-
src/pkg/go/types/expr.go
:collectFields
関数が、フィールドのオフセットを計算する準備として、Field
構造体にOffset
を設定するロジックを間接的にサポートするように変更されました。align
ヘルパー関数と、構造体のサイズとアライメントを計算し、フィールドのオフセットを設定する(*Context).newStruct
関数が追加されました。rawExpr
内の*ast.StructType
の処理が、check.ctxt.newStruct
を呼び出すように変更されました。lookupField
の呼び出し箇所が、lookupResult
構造体を返すように変更されたため、その結果の利用方法が修正されました。
-
src/pkg/go/types/gcimporter.go
:GcImportData
とGcImport
関数がContext
型の引数を取るようになりました。gcParser
構造体にctxt *Context
フィールドが追加され、init
メソッドで初期化されるようになりました。parseStructType
がp.ctxt.newStruct
を呼び出すように変更されました。
-
src/pkg/go/types/operand.go
:lookupResult
構造体にoffset int64
フィールドが追加されました。lookupFieldBreadthFirst
とlookupField
関数が、フィールドのオフセットをlookupResult
に含めるように変更されました。
-
src/pkg/go/types/types.go
:Basic
構造体のSize
フィールドが非エクスポートのsize
に変更されました。Field
構造体にOffset int64
フィールドが追加されました。Struct
構造体にAlignment int64
とSize int64
フィールドが追加されました。
コアとなるコードの解説
このコミットの核心は、go/types
パッケージがメモリレイアウトに関する情報を扱う方法を、固定値からカスタマイズ可能な関数へと移行させた点にあります。
Context
の役割の拡張
以前のContext
は、IntSize
やPtrSize
といった固定のサイズ情報を持っていました。これは、特定のアーキテクチャに依存する値であり、クロスプラットフォームな型チェックには不向きでした。このコミットでは、これらの固定値を削除し、代わりにAlignof
とSizeof
という関数型のフィールドを導入しました。
// src/pkg/go/types/api.go
type Context struct {
// ...
Alignof func(Type) int64
Sizeof func(Type) int64
}
これにより、型チェッカーは、型Type
を受け取り、そのアライメントやサイズを計算するロジックを外部から注入できるようになります。デフォルトの実装(DefaultAlignof
、DefaultSizeof
)も提供されますが、必要に応じてユーザーが独自のロジックを提供することで、特定の環境のメモリレイアウトを正確にシミュレートできるようになります。
unsafe
組み込み関数の動的な解決
Goのunsafe.Alignof
、unsafe.Sizeof
、unsafe.Offsetof
は、コンパイル時にメモリレイアウト情報を取得するための組み込み関数です。このコミットでは、これらの関数の型チェック時の挙動が、Context
に設定されたAlignof
およびSizeof
関数に依存するように変更されました。
例えば、_Sizeof
の処理は以下のように変更されました。
// src/pkg/go/types/builtins.go (抜粋)
case _Sizeof:
x.mode = constant
x.val = check.ctxt.sizeof(x.typ) // Contextに設定されたsizeof関数を呼び出す
x.typ = Typ[Uintptr]
これにより、unsafe.Sizeof
が返す値は、型チェックが行われるコンテキスト(つまり、Context
に設定されたSizeof
関数)によって動的に決定されるようになります。これは、異なるターゲットアーキテクチャに対して正確な型チェックを行う上で非常に重要です。
構造体のメモリレイアウト計算の統合
構造体(Struct
型)のメモリレイアウトは、そのフィールドの型とアライメント要件に大きく依存します。このコミットでは、Struct
構造体にAlignment
とSize
フィールドが追加され、Field
構造体にはOffset
フィールドが追加されました。
// src/pkg/go/types/types.go (抜粋)
type Field struct {
// ...
Offset int64 // offset within struct, in bytes
IsAnonymous bool
}
type Struct struct {
Fields []*Field
Alignment int64 // struct alignment in bytes
Size int64 // struct size in bytes
}
そして、(*Context).newStruct
という新しいヘルパー関数が導入され、構造体型が構築される際に、そのフィールドの型とContext
のalignof
/sizeof
関数を使用して、構造体全体のAlignment
とSize
、および各フィールドのOffset
が正確に計算されるようになりました。
// src/pkg/go/types/expr.go (抜粋)
func (ctxt *Context) newStruct(fields []*Field) *Struct {
// ...
var offset int64
for _, f := range fields {
a := ctxt.alignof(f.Type) // フィールドのアライメントを取得
// ...
f.Offset = offset // フィールドのオフセットを設定
offset += ctxt.sizeof(f.Type) // フィールドのサイズを加算
}
// ...
return &Struct{fields, maxAlign, offset}
}
この変更により、go/types
パッケージは、Goのメモリレイアウト規則(特にアライメントとパディング)をより深く理解し、型チェック時に正確なメモリレイアウト情報を生成できるようになりました。これは、unsafe.Offsetof
が正確な値を返すために不可欠です。
GcImporter
とContext
の連携
GcImporter
は、コンパイル済みパッケージの型情報をインポートする際に、その型が持つメモリレイアウト情報を正しく解釈する必要があります。このコミットでは、GcImporter
がContext
を引数として受け取るように変更されました。
// src/pkg/go/types/gcimporter.go (抜粋)
func GcImport(ctxt *Context, imports map[string]*Package, path string) (pkg *Package, err error) {
// ...
}
これにより、GcImporter
は、インポートされた型を解析する際に、現在の型チェックコンテキスト(つまり、カスタマイズされたAlignof
やSizeof
関数)を利用できるようになり、インポートされた型に対しても正確なメモリレイアウト情報が適用されることが保証されます。
これらの変更は、Goの型システムが、異なるプラットフォームやアーキテクチャの特性をより柔軟に、かつ正確に反映できるようにするための重要なステップです。
関連リンク
- Go言語の
unsafe
パッケージのドキュメント: https://pkg.go.dev/unsafe - Go言語の
go/types
パッケージのドキュメント: https://pkg.go.dev/go/types - Go言語のメモリレイアウトに関する議論(Go 1.19以降の変更点など): https://go.dev/blog/go1.19-generics (ジェネリクスと関連するメモリレイアウトの変更について触れられている可能性があります)
参考にした情報源リンク
- Web search results for "Go types package Alignof Sizeof Offsetof gcimporter" (Google Search)
unsafe.Sizeof
,unsafe.Alignof
,unsafe.Offsetof
に関する説明gcimporter
パッケージの役割に関する説明
- Go言語の公式ドキュメントやブログ記事 (具体的なURLは検索結果から推測)
- Go言語のソースコード (このコミットのdiff)