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

[インデックス 1334] ファイルの概要

このコミットは、Go言語のコンパイラが、インポートされたパッケージ名と同じ名前のローカル変数が宣言された場合に、適切にエラーを報告することを確認するためのテストケースを追加するものです。具体的には、syscallパッケージをインポートした状態で、関数内でsyscallという名前のローカル変数を宣言するコードが、コンパイルエラーとなるべきであることを示しています。

コミット

commit 889d9b6ffdae0c5f457d9c3ed1fc1798fc255b3a
Author: Russ Cox <rsc@golang.org>
Date:   Thu Dec 11 18:19:07 2008 -0800

    bug129
    
    package foo
    import "syscall"
    func f() {
            syscall := 1
    }
    
    R=ken
    OCL=21036
    CL=21036
---
 test/bugs/bug129.go | 11 +++++++++++
 test/golden.out     |  4 ++++
 2 files changed, 15 insertions(+)

diff --git a/test/bugs/bug129.go b/test/bugs/bug129.go
new file mode 100644
index 0000000000..f388dca88c
--- /dev/null
+++ test/bugs/bug129.go
@@ -0,0 +1,11 @@
+// $G $D/$F.go || echo BUG129
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package foo
+import "syscall"
+func f() {
+\tsyscall := 1
+}
diff --git a/test/golden.out b/test/golden.out
index 5ed3174070..30badd3b14 100644
--- a/test/golden.out
+++ test/golden.out
@@ -164,6 +164,10 @@ BUG: errchk: command succeeded unexpectedly:  6g bugs/bug123.go
 =========== bugs/bug125.go
 BUG: errchk: command succeeded unexpectedly:  6g bugs/bug125.go
 
+=========== bugs/bug129.go
+bugs/bug129.go:6: syscall is package, not var
+BUG129
+
 =========== fixedbugs/bug016.go
 fixedbugs/bug016.go:7: overflow converting constant to uint
 

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/889d9b6ffdae0c5f457d9c3ed1fc1798fc255b3a

元コミット内容

bug129

package foo
import "syscall"
func f() {
        syscall := 1
}

R=ken
OCL=21036
CL=21036

変更の背景

このコミットは、Go言語のコンパイラが持つべき重要なセマンティックチェック、すなわち「名前のシャドーイング(shadowing)」に関するバグ(bug129)に対応するためのものです。Go言語では、パッケージ名、関数名、変数名など、様々な識別子が存在します。これらの識別子は、それぞれ特定のスコープ(有効範囲)を持ちます。

問題となっていたのは、インポートされたパッケージ名と同じ名前のローカル変数を関数内で宣言した場合に、コンパイラがそれを適切にエラーとして検出しない可能性があったことです。このような状況は「シャドーイング」と呼ばれ、より狭いスコープ(この場合は関数内のローカルスコープ)で宣言された識別子が、より広いスコープ(この場合はパッケージスコープ)で宣言された同じ名前の識別子を「隠してしまう」現象を指します。

シャドーイング自体は、Go言語において常に禁止されているわけではありませんが、インポートされたパッケージ名をローカル変数でシャドーイングすることは、コードの可読性を著しく損ない、意図しない動作を引き起こす可能性があるため、通常はコンパイルエラーとして扱われるべきです。このコミットは、Goコンパイラがこの特定のケース(syscallパッケージ名とローカル変数syscallの衝突)を正しく検出し、開発者に警告またはエラーを出すことを保証するために追加されました。これにより、Goプログラムの堅牢性と予測可能性が向上します。

前提知識の解説

Go言語のパッケージとインポート

Go言語のプログラムは「パッケージ」という単位で構成されます。パッケージは関連する機能の集合であり、コードのモジュール化と再利用を促進します。

  • パッケージ宣言: 各Goソースファイルはpackage <name>で始まり、そのファイルがどのパッケージに属するかを示します。mainパッケージは実行可能なプログラムのエントリポイントです。
  • インポート: 別のパッケージの機能を利用するには、import "<path/to/package>"構文を使用します。これにより、インポートされたパッケージの公開された識別子(関数、変数、型など)を、そのパッケージ名(またはエイリアス)をプレフィックスとして使用して参照できるようになります。例えば、import "fmt"とすると、fmt.Println()のようにfmtを付けて関数を呼び出します。

変数のスコープとシャドーイング

  • スコープ: プログラミングにおいて、スコープとは識別子(変数、関数、型など)が参照可能であるプログラムの領域を指します。Go言語には、グローバルスコープ(パッケージレベル)、ファイルスコープ、関数スコープ、ブロックスコープなどがあります。
  • シャドーイング: シャドーイングは、あるスコープで宣言された識別子と同じ名前の識別子が、その内側のスコープで宣言される現象です。内側のスコープでは、外側のスコープの同じ名前の識別子は「隠され」、内側の識別子が優先されます。
    • 例:
      var x = 10 // パッケージスコープ
      func main() {
          x := 20 // 関数スコープで新しいxを宣言。パッケージスコープのxをシャドーイング
          fmt.Println(x) // 20が出力される
      }
      
    • シャドーイングは、意図的に使用されることもありますが、特にインポートされたパッケージ名をシャドーイングすると、混乱を招き、バグの原因となる可能性が高いため、Goコンパイラはこのようなケースをエラーとして扱います。

Goコンパイラの役割(エラー検出)

Goコンパイラは、ソースコードを機械語に変換するだけでなく、Go言語の仕様に準拠しているかどうかの静的解析も行います。これには、構文エラー、型エラー、未定義の識別子の使用、そして今回のような特定のセマンティックエラー(例えば、インポートされたパッケージ名のシャドーイング)の検出が含まれます。コンパイラがこれらのエラーを早期に検出することで、開発者は実行時エラーを回避し、より堅牢なコードを記述できます。

技術的詳細

このコミットが対処している技術的な問題は、Goコンパイラの名前解決ロジックにおける特定のケースの不備、またはそのテストカバレッジの不足です。

Go言語では、識別子の名前解決は、最も内側のスコープから外側へと順に行われます。

  1. ブロックスコープ: if文、forループ、switch文、関数本体などのブロック内で宣言されたローカル変数。
  2. 関数スコープ: 関数のパラメータや、関数本体のトップレベルで宣言された変数。
  3. パッケージスコープ: パッケージレベルで宣言された変数、定数、型、関数。これには、インポートされたパッケージ名も含まれます。

通常、import "syscall"と記述すると、syscallという識別子はパッケージスコープで利用可能になります。このコミットで示されているコードは、func f() { syscall := 1 }のように、関数内でsyscallという名前の新しいローカル変数を宣言しています。この場合、名前解決のルールに従えば、関数内のsyscallはローカル変数として解釈され、パッケージスコープのsyscallパッケージは隠される(シャドーイングされる)ことになります。

しかし、Go言語の設計思想として、インポートされたパッケージ名をローカル変数でシャドーイングすることは、コードの意図を不明瞭にし、誤解を招く可能性が高いため、コンパイラによってエラーとして扱われるべきです。このコミットは、まさにその挙動をテストし、コンパイラが「syscallはパッケージであり、変数ではない」というエラーメッセージを正しく出力することを確認しています。

これは、コンパイラが単に名前の衝突を検出するだけでなく、その衝突がGo言語のセマンティックルール(特に、インポートされたパッケージ名の保護)に違反しているかどうかを判断する能力を持っていることを示しています。このようなチェックは、大規模なプロジェクトにおいて、異なる開発者が同じ名前を誤って使用することによる衝突を防ぎ、コードベースの一貫性と保守性を維持するために不可欠です。

test/golden.outファイルは、Goのテストフレームワークの一部であり、特定のテストケース(この場合はbug129.go)を実行した際に期待される標準出力や標準エラー出力を記録するために使用されます。このコミットでは、bug129.goがコンパイルされたときに「bugs/bug129.go:6: syscall is package, not var」というエラーメッセージと「BUG129」という出力が期待されることを示しており、コンパイラがこのシャドーイングの問題を正しく報告していることを検証しています。

コアとなるコードの変更箇所

このコミットでは、主に以下の2つのファイルが変更されています。

  1. test/bugs/bug129.go:

    • 新規作成されたテストファイルです。
    • インポートされたsyscallパッケージ名をローカル変数syscallでシャドーイングするGoコードが含まれています。
    • ファイルの先頭には// $G $D/$F.go || echo BUG129という行があり、これはGoのテストスクリプトがこのファイルをコンパイルし、エラーが発生した場合はBUG129という文字列を出力することを期待していることを示しています。
  2. test/golden.out:

    • 既存のファイルに追記されています。
    • test/bugs/bug129.goのテスト実行時に期待される出力(コンパイルエラーメッセージとBUG129)が追加されています。

コアとなるコードの解説

test/bugs/bug129.go

// $G $D/$F.go || echo BUG129

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package foo
import "syscall"
func f() {
	syscall := 1
}
  • package foo: このファイルがfooパッケージに属することを示します。
  • import "syscall": 標準ライブラリのsyscallパッケージをインポートしています。これにより、syscallという識別子がこのパッケージスコープで利用可能になります。
  • func f() { syscall := 1 }:
    • fという名前の関数を定義しています。
    • 関数本体内でsyscall := 1という行があります。これは、syscallという名前の新しいローカル変数を宣言し、値1を代入しています。
    • この行が、インポートされたsyscallパッケージ名をシャドーイングする原因となります。Goコンパイラは、このようなシャドーイングをエラーとして検出するべきです。

test/golden.out

=========== bugs/bug129.go
bugs/bug129.go:6: syscall is package, not var
BUG129
  • =========== bugs/bug129.go: これは、bug129.goのテスト結果のセクションの開始を示します。
  • bugs/bug129.go:6: syscall is package, not var:
    • これは、Goコンパイラがbug129.goの6行目(syscall := 1の行)で検出したエラーメッセージです。
    • メッセージは、「syscallはパッケージであり、変数ではない」と明確に述べており、ローカル変数としてsyscallを宣言しようとしたことが、インポートされたパッケージ名との衝突により不正であることを示しています。
  • BUG129: これは、bug129.goファイルの先頭のコメント// $G $D/$F.go || echo BUG129によって期待される出力です。コンパイルがエラーで終了した場合に、この文字列が出力されることを検証しています。

これらの変更は、Goコンパイラがインポートされたパッケージ名のシャドーイングを正しくエラーとして扱い、その挙動がテストスイートによって保証されることを示しています。

関連リンク

参考にした情報源リンク