[インデックス 11633] ファイルの概要
このコミットは、Go言語のネットワークパッケージ(net
)におけるPlan 9オペレーティングシステム向けのビルド問題を修正するものです。具体的には、src/pkg/net/ipsock_plan9.go
ファイル内の変数宣言に関する小さな修正が含まれています。
コミット
commit b9917045dadeb8a2158cf291d71f4e0124cf6ced
Author: Anthony Martin <ality@pbrane.org>
Date: Sun Feb 5 16:59:32 2012 -0800
net: fix Plan 9 build
R=golang-dev, alex.brainman
CC=golang-dev
https://golang.org/cl/5631051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b9917045dadeb8a2158cf291d71f4e0124cf6ced
元コミット内容
net: fix Plan 9 build
変更の背景
このコミットは、Go言語のnet
パッケージがPlan 9環境でビルドされる際に発生していた問題を解決するために導入されました。Go言語はクロスプラットフォーム対応を重視しており、Plan 9もサポート対象の一つです。しかし、特定のコード記述がPlan 9のコンパイラやリンカの挙動と合致せず、ビルドエラーを引き起こしていました。
具体的には、src/pkg/net/ipsock_plan9.go
ファイル内のparsePlan9Addr
関数において、i
という変数が関数スコープ内で既に宣言されているにもかかわらず、再度短い変数宣言(:=
)で宣言しようとしていたことが問題でした。Go言語では、:=
演算子は新しい変数を宣言し、初期化するために使用されます。もし左辺の変数が既に現在のスコープで宣言されている場合、Goコンパイラはエラーを報告します。しかし、一部のコンテキストでは、既存の変数への代入と新しい変数の宣言が混在する形で許容される場合があります(例: 複数の変数を宣言する際に、一部が既存で一部が新規の場合)。Plan 9のビルド環境では、この特定のケースがエラーとして扱われたと考えられます。
この問題は、Goの初期のバージョンにおけるコンパイラの挙動や、特定のOS(この場合はPlan 9)向けのビルドツールチェーンの厳密さに起因している可能性があります。開発者は、Goのコードベースが様々な環境で一貫してビルドできることを保証するために、このようなプラットフォーム固有のビルドエラーを修正する必要があります。
前提知識の解説
Plan 9 from Bell Labs
Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの後継として設計され、Unixの概念をさらに推し進めたものとして知られています。特に「すべてをファイルとして扱う」という哲学を徹底しており、ネットワークリソースやデバイスなどもファイルシステムを通じてアクセスされます。Go言語は、その設計思想の一部をPlan 9から継承しており、初期のGo開発者にはPlan 9の設計者が多く含まれていました。そのため、Go言語はPlan 9環境での動作もサポートしています。
Go言語のnet
パッケージ
Go言語の標準ライブラリであるnet
パッケージは、ネットワークI/O機能を提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うためのAPIが含まれています。このパッケージは、OSのネットワークスタックと連携して動作するため、OSごとに異なる実装(例: ipsock_plan9.go
、ipsock_linux.go
など)を持つことがあります。
Go言語の変数宣言とスコープ
Go言語には、変数を宣言し初期化する方法がいくつかあります。
var
キーワードによる宣言:
またはvar i int i = 10 // 後で値を代入
var i int = 10 // 宣言と同時に初期化
- 短い変数宣言(
:=
):
このi := 10 // 宣言と同時に初期化。型はGoが推論する。
:=
演算子は、新しい変数を宣言する際にのみ使用できます。もし同じスコープ内でi
が既に宣言されている場合、i := 10
はコンパイルエラーとなります。ただし、以下のような複数変数宣言の場合は例外があります。
このコミットのケースでは、x, y := 1, 2 // xとyが新規の場合 x, z := 1, 3 // xが既存でzが新規の場合、これは許容される
i
が既にparsePlan9Addr
関数の戻り値として宣言されているにもかかわらず、関数内部でi := byteIndex(s, '!')
と再宣言しようとしたことが問題でした。Goのコンパイラは、関数パラメータや戻り値もその関数のスコープ内で宣言された変数として扱います。
byteIndex
関数
byteIndex
は、Goの標準ライブラリ(おそらくbytes
パッケージやstrings
パッケージの内部関数、またはnet
パッケージ内で定義されたヘルパー関数)に存在する可能性のある関数で、バイトスライスまたは文字列内で特定のバイト(文字)が最初に出現するインデックスを返します。C言語のstrchr
やPythonのstr.find()
に似た機能です。
技術的詳細
このコミットの技術的な核心は、Go言語の変数宣言ルールと、それが特定のコンパイラ(この場合はPlan 9向けのGoコンパイラ)によってどのように解釈されるかという点にあります。
src/pkg/net/ipsock_plan9.go
ファイル内のparsePlan9Addr
関数は、以下のように定義されています。
func parsePlan9Addr(s string) (ip IP, iport int, err error) {
// ...
i = byteIndex(s, '!') // 修正前
// ...
}
この関数のシグネチャを見ると、ip IP, iport int, err error
という名前付き戻り値が宣言されています。Goでは、名前付き戻り値は関数の先頭でゼロ値で初期化され、関数のスコープ内で通常の変数として利用できます。つまり、i
という変数は、parsePlan9Addr
関数のスコープ内で既に存在していることになります。
修正前のコードでは、i = byteIndex(s, '!')
と記述されていました。これは、i
という既存の変数にbyteIndex
関数の戻り値を代入する通常の代入文です。しかし、コミットのdiffを見ると、この行がi := byteIndex(s, '!')
に変更されています。
- i = byteIndex(s, '!')
+ i := byteIndex(s, '!')
この変更は、i
が関数スコープ内で既に宣言されているにもかかわらず、:=
(短い変数宣言)を使用しようとしたことが原因で、Plan 9環境でのビルドエラーが発生したことを示唆しています。
通常、Goコンパイラは、関数内で名前付き戻り値と同じ名前の変数を:=
で再宣言しようとすると、「no new variables on left side of :=」のようなエラーを報告します。しかし、このコミットが修正しているのは、まさにそのエラーがPlan 9ビルドで発生していたという状況です。
考えられるシナリオは以下の通りです。
- Goコンパイラのバージョンまたはプラットフォーム固有の挙動: 2012年当時のGoコンパイラ(特にPlan 9向け)が、この特定のケース(名前付き戻り値と短い変数宣言の衝突)に対して、他のプラットフォームのコンパイラよりも厳密なチェックを行っていた可能性があります。
- 誤った記述: 開発者が意図せず
i :=
と記述してしまい、それがPlan 9でたまたまエラーになった、という可能性もゼロではありませんが、コミットメッセージが「fix Plan 9 build」であることから、Plan 9固有の問題であったと考えるのが自然です。
修正後のi := byteIndex(s, '!')
は、i
が関数スコープ内で既に宣言されているため、Goの一般的なルールに照らすとエラーになるはずです。しかし、このコミットは「修正」としてこの変更を導入しています。これは、元のコードi = byteIndex(s, '!')
がPlan 9でエラーになっていたことを意味します。
この矛盾を解決する最も有力な説明は、元のコードi = byteIndex(s, '!')
が、実際にはi
が関数スコープ内で宣言されていない別のi
を参照しようとしていたか、あるいはGoの初期のバージョンにおけるコンパイラのバグや、Plan 9特有のコンパイラの挙動によって、この代入文が不正と判断されていた、というものです。
しかし、コミットのdiffが示すのはi =
からi :=
への変更です。これは、i
が新しい変数として宣言されることを意図しているように見えます。もしi
が名前付き戻り値として既に存在しているならば、i :=
はエラーになるはずです。
ここで、Goの仕様を再確認すると、短い変数宣言:=
は、左辺の少なくとも1つの変数が新しい変数である場合にのみ使用できます。もしi
が名前付き戻り値として既に宣言されている場合、i := byteIndex(s, '!')
はエラーになります。
したがって、このコミットの意図は、i
が名前付き戻り値のiport
とは別の、一時的なローカル変数i
として使われることを意図していたが、元のコードではそれが正しく解釈されなかった、という可能性が考えられます。
しかし、最も単純な解釈は、元のコードi = byteIndex(s, '!')
が、何らかの理由でPlan 9のビルドでエラーになっており、それを修正するためにi := byteIndex(s, '!')
に変更した、というものです。これは、Goのコンパイラが進化する過程で、特定のプラットフォームでの挙動が異なっていたり、バグがあったりした可能性を示唆しています。
最終的に、この修正は、i
という変数がparsePlan9Addr
関数の戻り値として既に宣言されているにもかかわらず、関数内部でi := byteIndex(s, '!')
と短い変数宣言で再宣言しようとしたことによるビルドエラーを解決したものです。これは、Goのコンパイラが、名前付き戻り値とローカル変数のスコープをどのように扱うかという点での、初期の挙動の調整またはバグ修正に関連していると考えられます。
コアとなるコードの変更箇所
変更はsrc/pkg/net/ipsock_plan9.go
ファイルの一箇所のみです。
--- a/src/pkg/net/ipsock_plan9.go
+++ b/src/pkg/net/ipsock_plan9.go
@@ -23,7 +23,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
// parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
func parsePlan9Addr(s string) (ip IP, iport int, err error) {
addr := IPv4zero // address contains port only
- i = byteIndex(s, '!')
+ i := byteIndex(s, '!')
if i >= 0 {
addr = ParseIP(s[:i])
if addr == nil {
コアとなるコードの解説
変更された行は、parsePlan9Addr
関数内の以下の部分です。
- 修正前:
i = byteIndex(s, '!')
- 修正後:
i := byteIndex(s, '!')
この変更の目的は、Plan 9環境でのビルドエラーを解消することです。
parsePlan9Addr
関数のシグネチャは以下の通りです。
func parsePlan9Addr(s string) (ip IP, iport int, err error)
ここで、iport int
という名前付き戻り値が宣言されています。Goでは、名前付き戻り値は関数のスコープ内で通常の変数として扱われます。つまり、iport
という変数は既に存在しています。
問題の行では、byteIndex(s, '!')
の戻り値をi
という変数に代入しています。
- 修正前の
i = byteIndex(s, '!')
は、i
という既存の変数への代入を意図しています。しかし、この関数シグネチャにはi
という名前の戻り値はありません。最も近いのはiport
です。もしi
がiport
のタイプミスであった場合、これは論理的なエラーになります。 - しかし、Goのコンパイラは、関数シグネチャで宣言された名前付き戻り値の変数名を、関数本体内でローカル変数として使用することを許可します。この場合、
iport
という名前付き戻り値があるため、i
というローカル変数を宣言することはできません。
このコミットの変更は、i
がiport
とは別の、新しいローカル変数として扱われることを意図しているように見えます。しかし、Goの言語仕様では、名前付き戻り値と同じ名前のローカル変数を:=
で宣言することはできません。
この矛盾を解決する最も妥当な説明は、Goの初期のバージョン(2012年当時)のコンパイラ、特にPlan 9向けのコンパイラが、この特定のコードパターンを誤って解釈していたか、またはより厳密なルールを適用していたということです。
- シナリオ1(最も可能性が高い): 元のコード
i = byteIndex(s, '!')
が、Plan 9のコンパイラにとって何らかの理由で不正な代入と判断されていた。例えば、i
が未宣言の変数として扱われたか、あるいはスコープ解決のバグがあったか。それを修正するために、明示的に新しいローカル変数i
を宣言するi := byteIndex(s, '!')
という形式に変更した。この変更が、当時のPlan 9コンパイラではエラーにならず、ビルドが通るようになった、という状況です。これは、Goコンパイラの進化の過程で、特定のプラットフォームでの挙動が異なっていたり、バグがあったりした可能性を示唆しています。
この修正により、parsePlan9Addr
関数はPlan 9環境で正しくビルドされるようになり、Go言語のクロスプラットフォーム互換性が維持されました。
関連リンク
- Go Change-Id:
https://golang.org/cl/5631051
参考にした情報源リンク
- Go言語の変数宣言に関する公式ドキュメントやチュートリアル(当時のGo言語の仕様とコンパイラの挙動を理解するため)
- Plan 9 from Bell Labsに関する情報源(Plan 9の基本的な概念とGo言語との関連性を理解するため)
- Go言語の
net
パッケージに関する公式ドキュメント - Go言語のコミット履歴と関連する議論(GitHubやGoのメーリングリストなど)
- Go言語のコンパイラの進化に関する情報(特に初期のバージョンにおける挙動の違い)
[インデックス 11633] ファイルの概要
このコミットは、Go言語のネットワークパッケージ(net
)におけるPlan 9オペレーティングシステム向けのビルド問題を修正するものです。具体的には、src/pkg/net/ipsock_plan9.go
ファイル内の変数宣言に関する小さな修正が含まれています。
コミット
commit b9917045dadeb8a2158cf291d71f4e0124cf6ced
Author: Anthony Martin <ality@pbrane.org>
Date: Sun Feb 5 16:59:32 2012 -0800
net: fix Plan 9 build
R=golang-dev, alex.brainman
CC=golang-dev
https://golang.org/cl/5631051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b9917045dadeb8a2158cf291d71f4e0124cf6ced
元コミット内容
net: fix Plan 9 build
変更の背景
このコミットは、Go言語のnet
パッケージがPlan 9環境でビルドされる際に発生していた問題を解決するために導入されました。Go言語はクロスプラットフォーム対応を重視しており、Plan 9もサポート対象の一つです。しかし、特定のコード記述がPlan 9のコンパイラやリンカの挙動と合致せず、ビルドエラーを引き起こしていました。
具体的には、src/pkg/net/ipsock_plan9.go
ファイル内のparsePlan9Addr
関数において、i
という変数が関数スコープ内で既に宣言されているにもかかわらず、再度短い変数宣言(:=
)で宣言しようとしていたことが問題でした。Go言語では、:=
演算子は新しい変数を宣言し、初期化するために使用されます。もし左辺の変数が既に現在のスコープで宣言されている場合、Goコンパイラはエラーを報告します。しかし、一部のコンテキストでは、既存の変数への代入と新しい変数の宣言が混在する形で許容される場合があります(例: 複数の変数を宣言する際に、一部が既存で一部が新規の場合)。Plan 9のビルド環境では、この特定のケースがエラーとして扱われたと考えられます。
この問題は、Goの初期のバージョンにおけるコンパイラの挙動や、特定のOS(この場合はPlan 9)向けのビルドツールチェーンの厳密さに起因している可能性があります。開発者は、Goのコードベースが様々な環境で一貫してビルドできることを保証するために、このようなプラットフォーム固有のビルドエラーを修正する必要があります。
前提知識の解説
Plan 9 from Bell Labs
Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの後継として設計され、Unixの概念をさらに推し進めたものとして知られています。特に「すべてをファイルとして扱う」という哲学を徹底しており、ネットワークリソースやデバイスなどもファイルシステムを通じてアクセスされます。Go言語は、その設計思想の一部をPlan 9から継承しており、初期のGo開発者にはPlan 9の設計者が多く含まれていました。そのため、Go言語はPlan 9環境での動作もサポートしています。
Go言語のnet
パッケージ
Go言語の標準ライブラリであるnet
パッケージは、ネットワークI/O機能を提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うためのAPIが含まれています。このパッケージは、OSのネットワークスタックと連携して動作するため、OSごとに異なる実装(例: ipsock_plan9.go
、ipsock_linux.go
など)を持つことがあります。
Go言語の変数宣言とスコープ
Go言語には、変数を宣言し初期化する方法がいくつかあります。
var
キーワードによる宣言:
またはvar i int i = 10 // 後で値を代入
var i int = 10 // 宣言と同時に初期化
- 短い変数宣言(
:=
):
このi := 10 // 宣言と同時に初期化。型はGoが推論する。
:=
演算子は、新しい変数を宣言する際にのみ使用できます。もし同じスコープ内でi
が既に宣言されている場合、i := 10
はコンパイルエラーとなります。ただし、以下のような複数変数宣言の場合は例外があります。
このコミットのケースでは、x, y := 1, 2 // xとyが新規の場合 x, z := 1, 3 // xが既存でzが新規の場合、これは許容される
i
が既にparsePlan9Addr
関数の戻り値として宣言されているにもかかわらず、関数内部でi := byteIndex(s, '!')
と再宣言しようとしたことが問題でした。Goのコンパイラは、関数パラメータや戻り値もその関数のスコープ内で宣言された変数として扱います。
byteIndex
関数
byteIndex
は、Goの標準ライブラリ(おそらくbytes
パッケージやstrings
パッケージの内部関数、またはnet
パッケージ内で定義されたヘルパー関数)に存在する可能性のある関数で、バイトスライスまたは文字列内で特定のバイト(文字)が最初に出現するインデックスを返します。C言語のstrchr
やPythonのstr.find()
に似た機能です。
技術的詳細
このコミットの技術的な核心は、Go言語の変数宣言ルールと、それが特定のコンパイラ(この場合はPlan 9向けのGoコンパイラ)によってどのように解釈されるかという点にあります。
src/pkg/net/ipsock_plan9.go
ファイル内のparsePlan9Addr
関数は、以下のように定義されています。
func parsePlan9Addr(s string) (ip IP, iport int, err error) {
// ...
i = byteIndex(s, '!') // 修正前
// ...
}
この関数のシグネチャを見ると、ip IP, iport int, err error
という名前付き戻り値が宣言されています。Goでは、名前付き戻り値は関数の先頭でゼロ値で初期化され、関数のスコープ内で通常の変数として利用できます。つまり、iport
という変数は既に存在しています。
修正前のコードでは、byteIndex(s, '!')
の戻り値をi
という変数に代入しています。
- 修正前の
i = byteIndex(s, '!')
は、i
という既存の変数への代入を意図しています。しかし、この関数シグネチャにはi
という名前の戻り値はありません。最も近いのはiport
です。もしi
がiport
のタイプミスであった場合、これは論理的なエラーになります。 - しかし、Goのコンパイラは、関数シグネチャで宣言された名前付き戻り値の変数名を、関数本体内でローカル変数として使用することを許可します。この場合、
iport
という名前付き戻り値があるため、i
というローカル変数を宣言することはできません。
このコミットの変更は、i
がiport
とは別の、新しいローカル変数として扱われることを意図しているように見えます。しかし、Goの言語仕様では、名前付き戻り値と同じ名前のローカル変数を:=
で宣言することはできません。
この矛盾を解決する最も妥当な説明は、Goの初期のバージョン(2012年当時)のコンパイラ、特にPlan 9向けのコンパイラが、この特定のコードパターンを誤って解釈していたか、またはより厳密なルールを適用していたということです。
- シナリオ1(最も可能性が高い): 元のコード
i = byteIndex(s, '!')
が、Plan 9のコンパイラにとって何らかの理由で不正な代入と判断されていた。例えば、i
が未宣言の変数として扱われたか、あるいはスコープ解決のバグがあったか。それを修正するために、明示的に新しいローカル変数i
を宣言するi := byteIndex(s, '!')
という形式に変更した。この変更が、当時のPlan 9コンパイラではエラーにならず、ビルドが通るようになった、という状況です。これは、Goコンパイラの進化の過程で、特定のプラットフォームでの挙動が異なっていたり、バグがあったりした可能性を示唆しています。
この修正により、parsePlan9Addr
関数はPlan 9環境で正しくビルドされるようになり、Go言語のクロスプラットフォーム互換性が維持されました。
コアとなるコードの変更箇所
変更はsrc/pkg/net/ipsock_plan9.go
ファイルの一箇所のみです。
--- a/src/pkg/net/ipsock_plan9.go
+++ b/src/pkg/net/ipsock_plan9.go
@@ -23,7 +23,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
// parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).\n func parsePlan9Addr(s string) (ip IP, iport int, err error) {\n \taddr := IPv4zero // address contains port only\n-\ti = byteIndex(s, \'!\')\n+\ti := byteIndex(s, \'!\')\n \tif i >= 0 {\n \t\taddr = ParseIP(s[:i])\n \t\tif addr == nil {\n```
## コアとなるコードの解説
変更された行は、`parsePlan9Addr`関数内の以下の部分です。
- **修正前**: `i = byteIndex(s, '!')`
- **修正後**: `i := byteIndex(s, '!')`
この変更の目的は、Plan 9環境でのビルドエラーを解消することです。
`parsePlan9Addr`関数のシグネチャは以下の通りです。
`func parsePlan9Addr(s string) (ip IP, iport int, err error)`
ここで、`iport int`という名前付き戻り値が宣言されています。Goでは、名前付き戻り値は関数のスコープ内で通常の変数として扱われます。つまり、`iport`という変数は既に存在しています。
問題の行では、`byteIndex(s, '!')`の戻り値を`i`という変数に代入しています。
- 修正前の`i = byteIndex(s, '!')`は、`i`という既存の変数への代入を意図しています。しかし、この関数シグネチャには`i`という名前の戻り値はありません。最も近いのは`iport`です。もし`i`が`iport`のタイプミスであった場合、これは論理的なエラーになります。
- しかし、Goのコンパイラは、関数シグネチャで宣言された名前付き戻り値の変数名を、関数本体内でローカル変数として使用することを許可します。この場合、`iport`という名前付き戻り値があるため、`i`というローカル変数を宣言することはできません。
このコミットの変更は、`i`が`iport`とは別の、新しいローカル変数として扱われることを意図しているように見えます。しかし、Goの言語仕様では、名前付き戻り値と同じ名前のローカル変数を`:=`で宣言することはできません。
この矛盾を解決する最も妥当な説明は、**Goの初期のバージョン(2012年当時)のコンパイラ、特にPlan 9向けのコンパイラが、この特定のコードパターンを誤って解釈していたか、またはより厳密なルールを適用していた**ということです。
- **シナリオ1(最も可能性が高い)**: 元のコード`i = byteIndex(s, '!')`が、Plan 9のコンパイラにとって何らかの理由で不正な代入と判断されていた。例えば、`i`が未宣言の変数として扱われたか、あるいはスコープ解決のバグがあったか。それを修正するために、明示的に新しいローカル変数`i`を宣言する`i := byteIndex(s, '!')`という形式に変更した。この変更が、当時のPlan 9コンパイラではエラーにならず、ビルドが通るようになった、という状況です。これは、Goコンパイラの進化の過程で、特定のプラットフォームでの挙動が異なっていたり、バグがあったりした可能性を示唆しています。
この修正により、`parsePlan9Addr`関数はPlan 9環境で正しくビルドされるようになり、Go言語のクロスプラットフォーム互換性が維持されました。
## 関連リンク
- Go Change-Id: `https://golang.org/cl/5631051`
## 参考にした情報源リンク
- Go言語の変数宣言に関する公式ドキュメントやチュートリアル(当時のGo言語の仕様とコンパイラの挙動を理解するため)
- Plan 9 from Bell Labsに関する情報源(Plan 9の基本的な概念とGo言語との関連性を理解するため)
- Go言語の`net`パッケージに関する公式ドキュメント
- Go言語のコミット履歴と関連する議論(GitHubやGoのメーリングリストなど)
- Go言語のコンパイラの進化に関する情報(特に初期のバージョンにおける挙動の違い)