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

[インデックス 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.goipsock_linux.goなど)を持つことがあります。

Go言語の変数宣言とスコープ

Go言語には、変数を宣言し初期化する方法がいくつかあります。

  1. varキーワードによる宣言:
    var i int
    i = 10 // 後で値を代入
    
    または
    var i int = 10 // 宣言と同時に初期化
    
  2. 短い変数宣言(:=:
    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ビルドで発生していたという状況です。

考えられるシナリオは以下の通りです。

  1. Goコンパイラのバージョンまたはプラットフォーム固有の挙動: 2012年当時のGoコンパイラ(特にPlan 9向け)が、この特定のケース(名前付き戻り値と短い変数宣言の衝突)に対して、他のプラットフォームのコンパイラよりも厳密なチェックを行っていた可能性があります。
  2. 誤った記述: 開発者が意図せず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です。もしiiportのタイプミスであった場合、これは論理的なエラーになります。
  • しかし、Goのコンパイラは、関数シグネチャで宣言された名前付き戻り値の変数名を、関数本体内でローカル変数として使用することを許可します。この場合、iportという名前付き戻り値があるため、iというローカル変数を宣言することはできません。

このコミットの変更は、iiportとは別の、新しいローカル変数として扱われることを意図しているように見えます。しかし、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.goipsock_linux.goなど)を持つことがあります。

Go言語の変数宣言とスコープ

Go言語には、変数を宣言し初期化する方法がいくつかあります。

  1. varキーワードによる宣言:
    var i int
    i = 10 // 後で値を代入
    
    または
    var i int = 10 // 宣言と同時に初期化
    
  2. 短い変数宣言(:=:
    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です。もしiiportのタイプミスであった場合、これは論理的なエラーになります。
  • しかし、Goのコンパイラは、関数シグネチャで宣言された名前付き戻り値の変数名を、関数本体内でローカル変数として使用することを許可します。この場合、iportという名前付き戻り値があるため、iというローカル変数を宣言することはできません。

このコミットの変更は、iiportとは別の、新しいローカル変数として扱われることを意図しているように見えます。しかし、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言語のコンパイラの進化に関する情報(特に初期のバージョンにおける挙動の違い)