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

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

このコミットは、Go言語のコンパイラが nil == nilnil != nil のような比較を無効な操作として正しく検出するかどうかをテストするために追加されたものです。具体的には、test/fixedbugs/issue4283.go という新しいテストファイルが追加され、型が不明な nil 同士の比較がコンパイルエラーとなることを検証しています。

コミット

commit 76937156ae7231468d70f889a82a72c97fc70617
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Dec 4 11:30:46 2012 -0800

    test: add test for invalid nil == nil
    
    R=gri
    CC=golang-dev
    https://golang.org/cl/6868059

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

https://github.com/golang/go/commit/76937156ae7231468d70f889a82a72c97fc70617

元コミット内容

test: add test for invalid nil == nil

R=gri
CC=golang-dev
https://golang.org/cl/6868059

変更の背景

Go言語において、nil は特定の型を持たない普遍的な値ではありません。ポインタ、インターフェース、マップ、スライス、チャネル、関数など、様々な型のゼロ値として存在します。このため、nil は常に特定の型に関連付けられています。

このコミットが追加された背景には、Go言語の nil の性質に起因する混乱がありました。特に、型情報を持たない nilnil を直接比較しようとすると、コンパイラは比較対象の具体的な型を特定できず、結果として無効な操作となります。このテストは、このような無効な比較がコンパイル時に正しくエラーとして扱われることを保証するために導入されました。これは、Go言語の型システムの一貫性と堅牢性を維持するための重要な側面です。

前提知識の解説

Go言語における nil

Go言語における nil は、他の多くの言語の null とは異なり、単一の普遍的な値ではありません。nil は、以下の組み込み型のゼロ値として使用されます。

  • ポインタ (*T): どの値も指していないポインタ。
  • インターフェース (interface{}): 型も値も持たないインターフェース。
  • マップ (map[K]V): 初期化されていないマップ。
  • スライス ([]T): 要素を持たないスライス。
  • チャネル (chan T): 初期化されていないチャネル。
  • 関数 (func): 実装を持たない関数。

重要なのは、nil が常に特定の型に関連付けられているという点です。例えば、var p *int と宣言されたポインタ pnil ですが、その型は *int です。同様に、var s []string と宣言されたスライス snil ですが、その型は []string です。

インターフェースと nil の比較

Go言語のインターフェースは、内部的に「型」と「値」の2つのコンポーネントで構成されています。インターフェースが nil と見なされるのは、その「型」と「値」の両方のコンポーネントが nil である場合のみです。

この特性が混乱を招くことがあります。例えば、*MyStruct 型の nil ポインタをインターフェースに代入した場合、インターフェースの「値」コンポーネントは nil ですが、「型」コンポーネントは *MyStruct となります。この場合、インターフェース自体は nil ではありません。

var p *int // p は nil ポインタ、型は *int、値は nil
var i interface{} // i は nil インターフェース、型は nil、値は nil

fmt.Println(p == nil) // true
fmt.Println(i == nil) // true

i = p // i は nil の *int を保持。型は *int、値は nil。
fmt.Println(i == nil) // false! (i の型コンポーネントが *int であり、nil ではないため)

型が不明な nil 同士の比較

nil は常に型に関連付けられているため、コンパイラは nil == nil のような比較を行う際に、比較対象の nil がどの型に属するのかを特定する必要があります。しかし、文脈から型を推論できない場合、コンパイラはこの比較を無効と判断します。これは、型安全性を保ち、予期せぬ動作を防ぐためのGo言語の設計思想に基づいています。

技術的詳細

Go言語のコンパイラは、比較演算子 (==!=) のオペランドの型チェックを厳密に行います。nil はリテラルとして使用される場合、その文脈から型が推論される必要があります。

例えば、var x *int = nil のように具体的な型が指定されている場合、nil*int 型のゼロ値として扱われます。また、if x == nil のように、比較対象の片方が具体的な型を持つ変数である場合も、nil はその変数の型に合わせて解釈されます。

しかし、return nil == nil のように、両方のオペランドが型情報を持たない nil リテラルである場合、コンパイラはどちらの nil も具体的な型に結びつけることができません。Go言語の仕様では、このような型が不明な nil 同士の比較は許可されていません。これは、nil がポインタ、インターフェース、スライスなど、異なるセマンティクスを持つ複数の型に適用されるため、型が特定できない状態での比較は曖昧であり、プログラマの意図を誤解する可能性があるためです。

コンパイラは、このような状況を検出すると「invalid」エラーを発生させ、コンパイルを停止します。これは、開発者が意図しない nil の比較を行わないようにするための安全機構として機能します。

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

--- /dev/null
+++ b/test/fixedbugs/issue4283.go
@@ -0,0 +1,17 @@
+// errorcheck
+
+// Copyright 2012 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.
+
+// Issue 4283: nil == nil can't be done as the type is unknown.
+
+package p
+
+func F1() bool {
+	return nil == nil	// ERROR "invalid"
+}
+
+func F2() bool {
+	return nil != nil	// ERROR "invalid"
+}

コアとなるコードの解説

追加された test/fixedbugs/issue4283.go ファイルは、Go言語のテストフレームワークの一部として、特定のコンパイルエラーを検証するために設計されています。

  • // errorcheck: このコメントは、Goのテストツールがこのファイルを実行する際に、特定のコンパイルエラーが発生することを期待していることを示します。
  • // Issue 4283: nil == nil can't be done as the type is unknown.: このコメントは、このテストがGoのIssue 4283に関連しており、型が不明な nil 同士の比較ができないことを示しています。
  • func F1() bool { return nil == nil // ERROR "invalid" }:
    • この関数 F1bool を返そうとしています。
    • return nil == nil の行がテストの核心です。ここで、型情報を持たない nil リテラル同士の等価比較が行われています。
    • // ERROR "invalid" コメントは、この行でコンパイラが「invalid」というエラーメッセージを伴うエラーを発生させることを期待していることを示します。これは、前述の通り、コンパイラが nil の型を特定できないためです。
  • func F2() bool { return nil != nil // ERROR "invalid" }:
    • この関数 F2 も同様に bool を返そうとしています。
    • return nil != nil の行では、型情報を持たない nil リテラル同士の不等価比較が行われています。
    • // ERROR "invalid" コメントは、この行でも同様に「invalid」エラーが発生することを期待しています。等価比較と同様に、不等価比較も型が不明な nil 同士では無効です。

このテストファイルは、Goコンパイラが nil の型推論と型チェックのルールを正しく適用し、開発者が型が不明な nil 同士の比較という曖昧で無効な操作を行おうとした場合に、適切なエラーを報告することを保証します。

関連リンク

参考にした情報源リンク