[インデックス 13840] ファイルの概要
このコミットは、Go言語の公式仕様書(doc/go_spec.html
)の更新に関するものです。具体的には、unsafe.Alignof
および unsafe.Sizeof
関数が、変数だけでなく非変数引数(リテラルや定数など)も受け入れるように仕様が変更されました。これは、既存のgc
およびgccgo
コンパイラの挙動に合わせて仕様を修正するもので、後方互換性のある変更です。
コミット
commit c7631f555f880409fb13143d0d8236ad9fb99c2c
Author: Robert Griesemer <gri@golang.org>
Date: Mon Sep 17 12:23:41 2012 -0700
spec: unsafe.Alignof/Sizeof also accept non-variable arguments
Both gc and gccgo permit calls such as unsafe.Sizeof(42). The
spec only permits variable arguments. This is a (backward-compatible)
spec change reflecting the status quo. Seems preferrable over
restricting the compilers.
R=r, rsc, iant, ken
CC=golang-dev
https://golang.org/cl/6494140
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c7631f555f880409fb13143d0d8236ad9fb99c2c
元コミット内容
このコミットの元の内容は、Go言語の仕様書において、unsafe.Alignof
および unsafe.Sizeof
関数が、変数を引数として取るだけでなく、非変数(リテラルや定数など)も引数として受け入れるように変更されたことを示しています。これは、既存のGoコンパイラ(gc
とgccgo
)が既にそのような呼び出し(例: unsafe.Sizeof(42)
)を許可していたため、その現状に合わせて仕様を更新するものです。コンパイラの挙動を制限するよりも、仕様を実態に合わせる方が望ましいという判断がなされました。
変更の背景
この変更の背景には、Go言語のコンパイラ(gc
とgccgo
)と公式仕様書との間に乖離があったことが挙げられます。具体的には、コンパイラはunsafe.Sizeof(42)
のような非変数引数を持つ呼び出しを許可していたにもかかわらず、当時の仕様書ではこれらの関数は「変数を引数として取る」と明記されていました。
このような乖離は、開発者が仕様書を読んだ際に混乱を招く可能性があり、またコンパイラの挙動が仕様に準拠していないという問題も生じさせます。このコミットは、コンパイラの既存の挙動を「正しい」ものとみなし、それに合わせて仕様書を更新することで、この乖離を解消することを目的としています。
コンパイラの挙動を制限して仕様に合わせるという選択肢もありましたが、それは既存のコードベースに影響を与える可能性があり、後方互換性を損なう恐れがありました。そのため、後方互換性を維持しつつ、現状のコンパイラの挙動を反映する形で仕様を変更する方が、より実用的で望ましいと判断されました。
前提知識の解説
Go言語の unsafe
パッケージ
Go言語の unsafe
パッケージは、Goの型システムやメモリ安全性の保証を意図的にバイパスするための機能を提供します。通常、Goは厳格な型チェックとメモリ管理によって安全なプログラミングを促進しますが、特定の高度な最適化やシステムプログラミングのシナリオでは、これらの制約を一時的に緩和する必要が生じることがあります。unsafe
パッケージはそのような場合に利用されますが、使用には細心の注意が必要です。誤用すると、メモリ破壊や未定義の動作を引き起こす可能性があります。
unsafe
パッケージが提供する主な関数には以下のものがあります。
unsafe.Alignof(x)
: 変数x
のアライメント(メモリ上での配置の制約)をバイト単位で返します。アライメントは、特定のデータ型がメモリ上で配置される際に、アドレスが特定の値の倍数でなければならないという制約です。例えば、32ビットシステムでは4バイトアライメント、64ビットシステムでは8バイトアライメントが一般的です。unsafe.Sizeof(x)
: 変数x
がメモリ上で占めるサイズをバイト単位で返します。これは、変数の型に基づいて決定されます。unsafe.Offsetof(x.f)
: 構造体x
のフィールドf
が、構造体の先頭からどれだけオフセット(距離)にあるかをバイト単位で返します。unsafe.Pointer
: 任意の型のポインタを保持できる特殊なポインタ型です。uintptr
との間で相互変換が可能で、これによりポインタ演算が可能になります。
これらの関数は、主に低レベルのメモリ操作、C言語とのインターフェース、または特定のパフォーマンス最適化のために使用されます。
アライメントとサイズ
- アライメント (Alignment): コンピュータのメモリは、バイト単位でアドレスが付けられています。しかし、CPUが効率的にデータを読み書きするためには、データが特定のアドレス境界に配置されている必要があります。この境界がアライメントです。例えば、4バイトのアライメントを持つデータは、アドレスが4の倍数であるメモリ位置に配置される必要があります。アライメントは、プロセッサのアーキテクチャやデータ型によって異なります。適切にアライメントされていないデータにアクセスしようとすると、パフォーマンスの低下や、場合によってはハードウェア例外(アライメント違反)が発生することがあります。
- サイズ (Size): データ型がメモリ上で占めるバイト数です。例えば、Goの
int32
型は4バイト、int64
型は8バイトを占めます。構造体の場合、そのフィールドのサイズとアライメントの要件に基づいて、パディング(埋め草)が挿入されることがあり、実際のサイズは個々のフィールドのサイズの合計よりも大きくなることがあります。
Go言語の仕様書
Go言語の仕様書(The Go Programming Language Specification)は、Go言語の構文、セマンティクス、標準ライブラリの動作などを厳密に定義した公式文書です。Go言語の設計者によって維持されており、Goコンパイラやツールの実装、そしてGoプログラムの動作の基準となります。仕様書は、Go言語の挙動に関する最終的な権威であり、開発者はこの文書を参照して言語の正確な動作を理解します。
gc
と gccgo
コンパイラ
Go言語には主に2つの主要なコンパイラ実装があります。
gc
(Go Compiler): Goチームによって開発された公式のコンパイラです。Go言語のリリースサイクルに合わせて更新され、Goの標準ツールチェーンの一部として提供されます。ほとんどのGo開発者が日常的に使用するコンパイラです。gccgo
: GCC (GNU Compiler Collection) のフロントエンドとして実装されたGoコンパイラです。GCCの最適化パスやバックエンドを利用できるため、特定のプラットフォームや最適化のニーズに合わせて使用されることがあります。
これらのコンパイラは、Go言語の仕様に準拠してGoプログラムをコンパイルしますが、実装の詳細や特定の挙動において微妙な違いがある場合があります。このコミットの背景にあるように、過去には仕様とコンパイラの挙動との間に不一致が生じることもありました。
技術的詳細
このコミットの技術的な核心は、Go言語の unsafe
パッケージ内の Alignof
および Sizeof
関数の引数に関する仕様の変更です。
変更前、Goの仕様書ではこれらの関数は「変数を引数として取る」と記述されていました。これは、unsafe.Sizeof(myVar)
のように、既に宣言された変数のみを引数として渡すことを意味していました。しかし、実際の gc
および gccgo
コンパイラの実装では、unsafe.Sizeof(42)
や unsafe.Sizeof(struct{}{})
のように、リテラル値や型リテラルなどの「非変数」の式も引数として受け入れ、それらのサイズやアライメントを計算することが可能でした。
この不一致は、仕様と実装の間のギャップを生み出していました。このコミットは、このギャップを埋めるために、仕様書をコンパイラの既存の挙動に合わせることを選択しました。
新しい仕様では、Alignof
および Sizeof
関数は「任意の型の式 x
を取り、var v = x
のように v
が宣言されたかのような仮想的な変数 v
のアライメントまたはサイズをそれぞれ返す」と定義されました。
この変更のポイントは以下の通りです。
- 「非変数引数」の許容: これにより、リテラル値(例:
42
)、複合リテラル(例:[]int{1, 2, 3}
)、型リテラル(例:struct{}
)、関数呼び出しの結果など、変数ではない任意の式をAlignof
やSizeof
の引数として渡すことが明示的に許可されます。 - 「仮想的な変数
v
」の概念:var v = x
という表現は、引数x
がどのような式であっても、それが一時的な変数v
に代入されたと仮定した場合のv
の型と値に基づいて、サイズやアライメントが計算されることを意味します。これにより、引数x
が変数であるか非変数であるかに関わらず、一貫したセマンティクスが提供されます。例えば、unsafe.Sizeof(42)
の場合、42
はint
型のリテラルであるため、var v = 42
とした場合のv
はint
型となり、int
型のサイズが返されます。 - 後方互換性: この変更は、既存のコンパイラの挙動を追認するものであり、既存のGoプログラムの動作に影響を与えることはありません。むしろ、これまで仕様上は未定義または不正とされていた一部のコードが、正式に仕様に準拠する形になります。
- コンパイラの制限回避: もし仕様を厳格に適用し、コンパイラが非変数引数を拒否するように変更されていた場合、多くの既存のGoコードがコンパイルエラーになる可能性がありました。このコミットは、そのような破壊的な変更を避け、より実用的なアプローチを採用しています。
この変更は、Go言語の仕様が単なる理論的な定義だけでなく、実際のコンパイラの実装や開発者の利用実態を考慮して進化していることを示しています。
コアとなるコードの変更箇所
変更は doc/go_spec.html
ファイルにのみ行われています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
-\t"Subtitle": "Version of September 13, 2012",
+\t"Subtitle": "Version of September 17, 2012",
"Path": "/ref/spec"
}-->
@@ -5487,8 +5487,9 @@ Any pointer or value of <a href=\"#Types\">underlying type</a> <code>uintptr</code
a <code>Pointer</code> and vice versa.\n </p>\n <p>\n-The function <code>Sizeof</code> takes an expression denoting a\n-variable of any type and returns the size of the variable in bytes.\n+The functions <code>Alignof</code> and <code>Sizeof</code> take an expression <code>x</code>\n+of any type and return the alignment or size, respectively, of a hypothetical variable <code>v</code>\n+as if <code>v</code> was declared via <code>var v = x</code>.\n </p>\n <p>\n The function <code>Offsetof</code> takes a selector (§<a href=\"#Selectors\">Selectors</a>) denoting a struct
具体的には、以下の2つの変更が行われています。
-
仕様書のバージョン日付の更新:
- "Subtitle": "Version of September 13, 2012",
+ "Subtitle": "Version of September 17, 2012",
これは、仕様書が更新された日付を反映するものです。 -
Alignof
およびSizeof
関数の説明の変更:-The function <code>Sizeof</code> takes an expression denoting a
-variable of any type and returns the size of the variable in bytes.
+The functions <code>Alignof</code> and <code>Sizeof</code> take an expression <code>x</code>
+of any type and return the alignment or size, respectively, of a hypothetical variable <code>v</code>
+as if <code>v</code> was declared via <code>var v = x</code>.
これがこのコミットの主要な変更点です。
- 変更前は
Sizeof
関数のみが言及され、「任意の型の変数を表す式を取り、その変数のサイズをバイト単位で返す」と記述されていました。 - 変更後は
Alignof
とSizeof
の両方が言及され、「任意の型の式x
を取り、var v = x
のようにv
が宣言されたかのような仮想的な変数v
のアライメントまたはサイズをそれぞれ返す」と記述されています。
- 変更前は
コアとなるコードの解説
このコミットにおける「コアとなるコード」とは、Go言語の公式仕様書の一部である doc/go_spec.html
ファイル内の記述です。このファイルはHTML形式で書かれており、Go言語の各要素の定義や挙動が詳細に記述されています。
変更された部分は、unsafe
パッケージの Alignof
および Sizeof
関数に関する説明です。
変更前の記述:
The function <code>Sizeof</code> takes an expression denoting a
variable of any type and returns the size of the variable in bytes.
この記述は、Sizeof
関数が引数として「変数」のみを受け入れることを示唆していました。例えば、var i int; unsafe.Sizeof(i)
は許可されますが、unsafe.Sizeof(42)
のようなリテラルは仕様上は許可されないと解釈されかねませんでした。
変更後の記述:
The functions <code>Alignof</code> and <code>Sizeof</code> take an expression <code>x</code>
of any type and return the alignment or size, respectively, of a hypothetical variable <code>v</code>
as if <code>v</code> was declared via <code>var v = x</code>.
この新しい記述は、以下の重要な点を明確にしています。
- 対象関数の拡張:
Sizeof
だけでなく、Alignof
もこの説明の対象に含まれるようになりました。これは、両関数が同様の引数処理ロジックを持つことを反映しています。 - 引数の柔軟性: 「
x
という任意の型の式」という表現により、引数が変数である必要がなく、リテラル、定数、他の式の評価結果など、あらゆる種類の式が許容されることが明確になりました。 - セマンティクスの明確化: 「
var v = x
のようにv
が宣言されたかのような仮想的な変数v
」という表現は、引数x
の型と値に基づいて、その式がもし変数に代入されたとしたらどのような型になるかを基準に、サイズやアライメントが計算されることを示しています。これにより、unsafe.Sizeof(42)
のような呼び出しが、42
がint
型のリテラルであるため、int
型のサイズを返すという挙動が仕様上も正当化されます。
この変更は、Go言語の仕様が実際のコンパイラの実装と整合性を保つように更新されたことを示しており、開発者がより正確に unsafe
パッケージの挙動を理解できるようになります。
関連リンク
- Go Programming Language Specification: https://go.dev/ref/spec
unsafe
パッケージのドキュメント: https://pkg.go.dev/unsafe- このコミットが参照している Go の変更リスト (CL): https://golang.org/cl/6494140
参考にした情報源リンク
- Go Programming Language Specification (公式ドキュメント)
- Go
unsafe
パッケージの公式ドキュメント - Go言語のコミット履歴 (GitHub)
- Go言語の変更リスト (Gerrit)
- アライメントとパディングに関する一般的なプログラミング知識
- Go言語のコンパイラに関する一般的な知識 (gc, gccgo)
- Go言語の型システムに関する知識
- Go言語のリリースノートやブログ記事 (関連情報があれば)
- Stack Overflow や Go のフォーラムでの関連議論 (もしあれば)