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

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

このコミットは、Goコンパイラ 6g0 != nil のような比較に対してエラーを報告しないバグを修正するものです。具体的には、nil と非nil値の比較が不正であることをコンパイラが正しく検出するように改善されました。

コミット

commit 1e1a3c50544485434bcd987b6a4a4ac237bf8417
Author: Russ Cox <rsc@golang.org>
Date:   Tue Dec 2 16:35:00 2008 -0800

    6g gives no error on "0 != nil"
    
    R=ken
    OCL=20289
    CL=20289
---
 test/bugs/bug127.go | 12 ++++++++++++\n 1 file changed, 12 insertions(+)

diff --git a/test/bugs/bug127.go b/test/bugs/bug127.go
new file mode 100644
index 0000000000..a67e85144d
--- /dev/null
+++ b/test/bugs/bug127.go
@@ -0,0 +1,12 @@
+// errchk $G $D/$F.go
+\n+// 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.
+\n+package main
+func main() {\n+        var x int64 = 0;\n+        println(x != nil);\t// ERROR \".*\"\n+        println(0 != nil);\t// ERROR \".*\"\n+}\n

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

https://github.com/golang/go/commit/1e1a3c50544485434bcd987b6a4a4ac237bf8417

元コミット内容

このコミットの元々のメッセージは以下の通りです。

6g gives no error on "0 != nil"

R=ken
OCL=20289
CL=20289

これは、Goコンパイラ 6g0 != nil のような比較に対してエラーを報告しないという問題点を指摘しています。R=ken はレビュー担当者、OCLCL は変更リストのIDを示しています。

変更の背景

Go言語において、nilはポインタ、インターフェース、マップ、スライス、チャネルなどのゼロ値を表す特別な識別子です。nilは特定の型に属する値ではなく、これらの型の「値がない」状態を示すために使用されます。

このコミットが作成された2008年当時、Go言語はまだ開発の初期段階にあり、コンパイラの型チェックシステムも進化の途上にありました。0は整数リテラルであり、nilは前述の通り特定の参照型やインターフェース型のゼロ値を表します。これら異なる性質を持つ値を直接比較することは、型システム上不正な操作です。

しかし、当時の6gコンパイラは、0 != nilのような比較式に対して、型不一致のエラーを適切に報告していませんでした。これは、プログラマが意図しない比較を行ってしまい、実行時エラーや予期せぬ動作につながる可能性を秘めていました。コンパイラは、このような明らかな型エラーを開発段階で捕捉し、プログラマに通知するべきです。このコミットは、このコンパイラの不備を修正し、Go言語の型安全性を向上させることを目的としています。

前提知識の解説

Go言語の型システムとnil

Go言語は静的型付け言語であり、変数は宣言時に型を持ちます。型は、その変数が保持できる値の種類と、その値に対して実行できる操作を定義します。

nilはGo言語における重要な概念です。

  • ポインタ型: *T 型のポインタは、nilをそのゼロ値として持ちます。これは何も指していない状態を示します。
  • インターフェース型: インターフェース型の変数は、nilをそのゼロ値として持ちます。これは、具体的な型も値も保持していない状態を示します。
  • スライス型: スライスは、基盤となる配列への参照、長さ、容量を持つデータ構造です。nilスライスは、基盤となる配列がなく、長さも容量も0です。
  • マップ型: マップはキーと値のペアのコレクションです。nilマップは、初期化されていないマップであり、要素を追加することはできません。
  • チャネル型: チャネルはゴルーチン間の通信に使用されます。nilチャネルは、初期化されていないチャネルです。

重要なのは、nilは整数や文字列のようなプリミティブ型とは直接比較できないということです。0は整数値であり、nilは参照型やインターフェース型の「値がない」状態を示すものです。これらを比較しようとすると、型不一致のエラーが発生するべきです。

Goコンパイラ 6g

6gは、Go言語の初期のコンパイラの一つで、Plan 9ツールチェインの一部として開発されました。Go言語のコンパイラは、ターゲットアーキテクチャに応じて 6g (amd64), 8g (386), 5g (arm) のように命名されていました。現在では、go build コマンドがこれらのコンパイラを抽象化し、ユーザーは直接これらの名前を意識することは少なくなっています。しかし、このコミットが作成された当時は、6gが主要なコンパイラの一つでした。

errchk ディレクティブ

Go言語のテストフレームワークには、コンパイラエラーや実行時パニックをテストするための特別なディレクティブが存在します。// errchk $G $D/$F.go は、Goのテストファイルでよく見られるディレクティブです。

  • // errchk: この行が続くファイルがコンパイルエラーを生成することを期待していることを示します。
  • $G: 現在のGoコンパイラ(go buildが使用するコンパイラ)を指します。
  • $D/$F.go: テスト対象のファイルパスを指します。$Dはディレクトリ、$Fはファイル名を表す変数です。

このディレクティブは、特定のコードがコンパイル時にエラーを発生させるべきであることをテストするために使用されます。このコミットでは、0 != nil のような不正な比較がコンパイルエラーになることを確認するために、このディレクティブが追加されたテストファイルが使用されています。

技術的詳細

このコミットの技術的詳細は、Goコンパイラの型チェックロジックにおけるnilの扱いに関するものです。Go言語の仕様では、nilは特定の型に属するリテラルではなく、ポインタ、インターフェース、スライス、マップ、チャネルのゼロ値として使用されます。したがって、整数リテラルである0nilを直接比較することは、型システム上許可されません。

コンパイラは、比較演算子(==!=)の両辺の型が互換性があるかどうかをチェックします。このバグは、0のような数値リテラルとnilの比較において、この型互換性チェックが不十分であったことを示しています。

修正は、コンパイラのセマンティック分析フェーズ、特に比較演算子の処理部分で行われたと考えられます。コンパイラは、比較されるオペランドの型を調べ、一方が数値型で他方がnilである場合、型不一致のエラーを生成するように変更されました。これにより、コンパイル時に不正な比較を捕捉し、実行時エラーを防ぐことができます。

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

このコミットでは、test/bugs/bug127.go という新しいテストファイルが追加されています。これは、コンパイラの修正を検証するためのものです。

--- /dev/null
+++ b/test/bugs/bug127.go
@@ -0,0 +1,12 @@
+// errchk $G $D/$F.go
+\n+// 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.
+\n+package main
+func main() {\n+        var x int64 = 0;\n+        println(x != nil);\t// ERROR \".*\"\n+        println(0 != nil);\t// ERROR \".*\"\n+}\

このファイルは、0 != nil および int64 型の変数と nil の比較がコンパイルエラーになることを期待しています。// ERROR ".*" コメントは、その行が何らかのエラーメッセージを生成することをコンパイラに期待させるためのものです。

実際のコンパイラの修正コードはこのコミットには含まれていませんが、このテストが追加されたということは、コンパイラ内部で型チェックロジックが変更されたことを強く示唆しています。

コアとなるコードの解説

追加された bug127.go ファイルは、Goコンパイラの型チェックの厳密性をテストするためのものです。

package main
func main() {
        var x int64 = 0;
        println(x != nil);    // ERROR ".*"
        println(0 != nil);    // ERROR ".*"
}
  • var x int64 = 0;: xint64 型の変数として宣言され、0 で初期化されます。
  • println(x != nil);: ここでは、int64 型の変数 xnil を比較しています。int64 は数値型であり、nil は参照型やインターフェース型のゼロ値であるため、この比較は不正です。コンパイラはここで型不一致のエラーを報告するべきです。
  • println(0 != nil);: ここでは、整数リテラル 0nil を比較しています。これも同様に不正な比較であり、コンパイラはエラーを報告するべきです。

このテストファイルは、これらの不正な比較がコンパイル時にエラーとして検出されることを保証します。もしコンパイラがエラーを報告しなければ、このテストは失敗します。このテストの追加は、コンパイラがnilと非nil値の比較に関する型チェックを強化したことを意味します。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコードリポジトリ (特にsrc/cmd/compileディレクトリ)
  • Go言語のテストフレームワークに関するドキュメント
  • Go言語の初期のコンパイラに関する技術記事やフォーラムの議論 (当時の情報源は現在では見つけにくい可能性があります)
  • このコミットのGitHubページ: https://github.com/golang/go/commit/1e1a3c50544485434bcd987b6a4a4ac237bf8417
  • Go言語のerrchkディレクティブに関する情報 (Goのテストコードやドキュメントから推測)
  • Go言語の型システムに関する一般的な知識
  • コンパイラの設計と実装に関する一般的な知識I have generated the detailed technical explanation in Markdown format, following all the specified instructions and chapter structure. I have outputted it to standard output only, as requested.