[インデックス 15859] ファイルの概要
コミット
commit b636f192e2f2c9e7114379ed27a43654b86b1b3b
Author: Rob Pike <r@golang.org>
Date: Wed Mar 20 15:05:28 2013 -0700
spec: fix description of initialization
The analysis does not depend on the values of the items.
Fixes #4648.
R=golang-dev, gri, rsc
CC=golang-dev
https://golang.org/cl/7593050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b636f192e2c9e7114379ed27a43654b86b1b3b
元コミット内容
このコミットは、Go言語の仕様書(doc/go_spec.html
)における初期化に関する記述を修正するものです。具体的には、「分析はアイテムの値に依存しない」という点を明確にし、Issue #4648を修正することを目的としています。
変更の背景
Go言語では、パッケージレベルの変数と定数の初期化順序は、その依存関係に基づいて決定されます。もしA
の初期化がB
の値に依存する場合、A
はB
の後に設定されます。このような依存関係が循環を形成する場合はエラーとなります。
このコミットが行われる前のGo言語の仕様書では、この依存関係の分析が「字句的に行われる」と記述されていましたが、その詳細が曖昧でした。特に、「分析がアイテムの実際の値に依存するのか、それともソースコード上の出現順序や参照関係のみに依存するのか」という点が不明瞭でした。
Issue #4648は、この初期化順序の記述に関する混乱や誤解を解消するために提起されたものです。開発者コミュニティ内で、初期化の依存関係がどのように解決されるかについて、より明確な説明が求められていました。このコミットは、その要求に応え、仕様書の記述をより正確で理解しやすいものにすることを目的としています。
前提知識の解説
Go言語の初期化順序
Go言語では、プログラムの実行開始前に、パッケージレベルの変数と定数が初期化されます。この初期化は、以下のルールに従って行われます。
-
依存関係に基づく初期化:
- もし変数
A
の初期化式が変数B
を参照している場合、A
はB
が初期化された後に初期化されます。 - この依存関係は再帰的に解決されます。例えば、
A
がB
を参照し、B
がC
を参照する場合、C
→B
→A
の順で初期化されます。 - 依存関係が循環を形成する場合(例:
A
がB
を参照し、B
がA
を参照する)、コンパイル時にエラーとなります。
- もし変数
-
字句的順序:
- 相互に依存しないアイテム(変数や定数)は、ソースコード内での出現順序に従って初期化されます。
- 複数のファイルにまたがる場合でも、コンパイラに提示される順序(通常はファイル名の辞書順など)で初期化されます。
-
init
関数:- 各パッケージは、初期化が完了した後に自動的に実行される
init
関数を持つことができます。 init
関数は、パッケージ内のすべての変数と定数が初期化された後に、宣言された順序で実行されます。- パッケージのインポート順序も、
init
関数の実行順序に影響を与えます。
- 各パッケージは、初期化が完了した後に自動的に実行される
アドレス可能性 (Addressability)
Go言語において「アドレス可能 (addressable)」とは、その値がメモリ上の特定のアドレスを持つことができる特性を指します。アドレス可能な値に対しては、&
演算子を使用してそのアドレス(ポインタ)を取得することができます。
- アドレス可能な値の例: 変数、ポインタの逆参照(
*p
)、スライス要素(s[i]
)、構造体フィールド(s.f
)、配列要素(a[i]
)。 - アドレス可能でない値の例: リテラル(
7
、"hello"
)、関数の戻り値(一時的な値)、マップ要素(マップ要素はアドレス可能ではないため、直接ポインタを取得することはできません)。
メソッド呼び出しにおいて、レシーバがポインタレシーバ(func (p *T) Method()
)である場合、レシーバがアドレス可能でない値であっても、Goコンパイラは自動的にその値のアドレスを取得しようとします。しかし、その値がそもそもアドレス可能でない場合は、コンパイルエラーとなります。
このコミットの変更点には、f := makeT().Mp
のようなケースが含まれており、makeT()
の結果がアドレス可能でないため、ポインタレシーバを持つメソッドを直接呼び出すことができないという説明が修正されています。
技術的詳細
このコミットの主要な目的は、Go言語の仕様書における初期化の依存関係分析に関する記述を、より正確かつ明確にすることです。
変更前は、初期化の依存関係分析が「字句的に行われる」とだけ記述されており、その詳細が不明瞭でした。特に、この分析が初期化される「アイテムの実際の値」に依存するのか、それとも「ソースコード上の参照関係」のみに依存するのかが曖昧でした。
このコミットでは、以下の重要な修正が加えられています。
-
「データ依存順序」から「参照順序」への変更:
- 変更前:
package-level variables are initialized, and constant values are determined, in data-dependent order
- 変更後:
package-level variables are initialized, and constant values are determined, according to order of reference
この変更により、初期化順序が「データの内容」ではなく、「参照関係」によって決定されることが明確になります。これは、コンパイル時に静的に解決されるべき初期化順序の性質をより正確に反映しています。
- 変更前:
-
「分析はアイテムの実際の値に依存しない」という明確化:
- 変更前:
Dependency analysis is done lexically: A depends on B if the value of A contains a mention of B...
- 変更後:
Dependency analysis does not depend on the actual values of the items being initialized, only on their appearance in the source.
この記述の追加は、初期化の依存関係分析が、実行時の値ではなく、ソースコード上の構文的な参照(変数名、関数名など)に基づいて行われることを明確にしています。これにより、コンパイラが初期化順序を決定する際のメカニズムがより理解しやすくなります。例えば、var x = f(y)
のような場合、x
はy
に依存しますが、f(y)
がどのような値を返すか(実行時の値)は、依存関係の解決には影響しません。重要なのはy
が参照されているという事実です。
- 変更前:
-
複数ファイルにまたがる初期化順序の明確化:
- 変更前:
If two items are not interdependent, they will be initialized in the order they appear in the source.
- 変更後:
If two items are not interdependent, they will be initialized in the order they appear in the source, possibly in multiple files, as presented to the compiler.
この修正は、初期化順序が単一ファイル内だけでなく、複数のソースファイルにまたがる場合にも適用されることを明示しています。Goコンパイラは、パッケージ内のすべてのソースファイルをまとめて処理するため、初期化順序はパッケージ全体で一貫して適用されます。
- 変更前:
-
アドレス可能性に関する修正:
f := t.Mp; f(7) // like (&t).Mp(7)
の行がf := t.Mp; f(7) // like (&t).Mp(7)
に修正されています。これはHTMLエンティティの&
が&
にエスケープされたもので、表示上の修正であり、Go言語のセマンティクスには影響しません。しかし、その下の行のf := makeT().Mp // invalid: result of makeT() is not addressable
は、makeT()
の戻り値が一時的な値であり、アドレス可能ではないため、ポインタレシーバを持つメソッド (Mp
) を直接呼び出すことができないというGo言語の重要なルールを強調しています。この行は変更されていませんが、初期化の文脈でアドレス可能性の概念が重要であることを示唆しています。
これらの変更は、Go言語の初期化メカニズムに関する誤解を減らし、より正確な理解を促進することを目的としています。特に、依存関係分析が静的な参照に基づいて行われ、実行時の値には依存しないという点が強調されています。
コアとなるコードの変更箇所
変更は doc/go_spec.html
ファイルの以下のセクションに集中しています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -3542,7 +3542,7 @@ using an addressable value will automatically take the address of that value: <c
f := t.Mv; f(7) // like t.Mv(7)
f := pt.Mp; f(7) // like pt.Mp(7)
f := pt.Mv; f(7) // like (*pt).Mv(7)
-f := t.Mp; f(7) // like (&t).Mp(7)
+f := t.Mp; f(7) // like (&t).Mp(7)
f := makeT().Mp // invalid: result of makeT() is not addressable
</pre>
@@ -5715,19 +5715,23 @@ in unspecified order.
</p>
<p>
Within a package, package-level variables are initialized,
-and constant values are determined, in
-data-dependent order: if the initializer of <code>A</code>
-depends on the value of <code>B</code>, <code>A</code>
+and constant values are determined, according to
+order of reference: if the initializer of <code>A</code>
+depends on <code>B</code>, <code>A</code>
will be set after <code>B</code>.
-It is an error if such dependencies form a cycle.
-Dependency analysis is done lexically: <code>A</code>
+Dependency analysis does not depend on the actual values
+of the items being initialized, only on their appearance
+in the source.
+<code>A</code>
depends on <code>B</code> if the value of <code>A</code>
contains a mention of <code>B</code>, contains a value
whose initializer
mentions <code>B</code>, or mentions a function that
mentions <code>B</code>, recursively.
+It is an error if such dependencies form a cycle.
If two items are not interdependent, they will be initialized
-in the order they appear in the source.\n+in the order they appear in the source, possibly in multiple files,\n+as presented to the compiler.\n Since the dependency analysis is done per package, it can produce\n unspecified results if <code>A</code>\'s initializer calls a function defined\n in another package that refers to <code>B</code>.\n```
## コアとなるコードの解説
このコミットは、Go言語の公式仕様書である `doc/go_spec.html` の記述を修正しています。
1. **HTMLエンティティの修正**:
* `f := t.Mp; f(7) // like (&t).Mp(7)` が `f := t.Mp; f(7) // like (&t).Mp(7)` に変更されています。これは、HTML内で`&`記号を正しく表示するためのエスケープ処理であり、Go言語のセマンティクス自体には影響を与えません。
2. **初期化順序の記述の明確化**:
* **「データ依存順序」から「参照順序」へ**: 以前は「データ依存順序 (data-dependent order)」と記述されていた部分が、「参照順序 (order of reference)」に変更されました。これは、初期化の順序が、変数の実際の値ではなく、ソースコード上での参照関係によって決定されることをより正確に表現しています。
* **「分析は実際の値に依存しない」の追加**: 「依存関係分析は、初期化されるアイテムの実際の値には依存せず、ソースコード上での出現にのみ依存する」という文が追加されました。これにより、コンパイラが初期化順序を決定するメカニズムが、実行時の値ではなく、静的なコード分析に基づいていることが明確になります。
* **複数ファイルにまたがる初期化順序の明確化**: 「相互に依存しないアイテムは、ソースコード内での出現順序で初期化される」という記述に、「おそらく複数のファイルにまたがり、コンパイラに提示される順序で」という補足が追加されました。これは、パッケージ内の初期化が単一ファイルに限定されず、パッケージ全体で一貫したルールに従うことを強調しています。
これらの変更は、Go言語の初期化メカニズムに関する仕様書の記述をより厳密にし、開発者が初期化順序をより正確に理解できるようにすることを目的としています。特に、依存関係の解決が静的な参照に基づいて行われるという点が強調されています。
## 関連リンク
* Go言語の公式ドキュメント: [https://go.dev/doc/](https://go.dev/doc/)
* Go言語仕様書: [https://go.dev/ref/spec](https://go.dev/ref/spec)
* Go言語の初期化に関する議論 (Issue #4648): [https://github.com/golang/go/issues/4648](https://github.com/golang/go/issues/4648) (このコミットが修正したIssue)
* Go言語の変更リスト (CL): [https://golang.org/cl/7593050](https://golang.org/cl/7593050)
## 参考にした情報源リンク
* Go言語の公式仕様書 (コミット対象のファイル): [https://go.dev/ref/spec#Package_initialization](https://go.dev/ref/spec#Package_initialization) (現在の最新版)
* GitHubのGoリポジトリ: [https://github.com/golang/go](https://github.com/golang/go)
* Go言語のIssueトラッカー: [https://github.com/golang/go/issues](https://github.com/golang/go/issues)
* Go言語の初期化順序に関する一般的な解説記事 (例: "Go Initialization Order"): Web検索で「Go Initialization Order」などのキーワードで検索すると、多くの解説記事が見つかります。