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

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

このコミットは、test/fixedbugs/issue8074.go という新しいテストファイルを追加します。このファイルは、Goコンパイラまたはランタイムにおける特定のバグ(issue 8074)のテストケースとして機能します。コミットメッセージによると、このバグはコミット時点では再現しなかったものの、将来的な回帰を防ぐために元のテストケースを含める目的で追加されました。

コミット

commit 5b342f78043389e35a6c8c6ee1030ae733570da1
Author: Dave Cheney <dave@cheney.net>
Date:   Sun Jun 22 17:33:00 2014 +1000

    test: add test case for issue 8074.
    
    Fixes #8074.
    
    The issue was not reproduceable by revision
    
    go version devel +e0ad7e329637 Thu Jun 19 22:19:56 2014 -0700 linux/arm
    
    But include the original test case in case the issue reopens itself.
    
    LGTM=dvyukov
    R=golang-codereviews, dvyukov
    CC=golang-codereviews
    https://golang.org/cl/107290043

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

https://github.com/golang/go/commit/5b342f78043389e35a6c8c6ee1030ae733570da1

元コミット内容

test: add test case for issue 8074.

Fixes #8074.

The issue was not reproduceable by revision

go version devel +e0ad7e329637 Thu Jun 19 22:19:56 2014 -0700 linux/arm

But include the original test case in case the issue reopens itself.

LGTM=dvyukov
R=golang-codereviews, dvyukov
CC=golang-codereviews
https://golang.org/cl/107290043

変更の背景

このコミットは、Goコンパイラまたはランタイムにおける潜在的なバグ issue 8074 のテストケースを追加するために行われました。コミットメッセージによると、この問題は「cannot take the address of 1」というエラーに関連していたようです。これは、Go言語において、アドレスを取ることができない値(非アドレス可能値)のアドレスを取ろうとした際に発生するコンパイルエラーの典型的なメッセージです。

コミットが作成された時点では、特定のGoのバージョン(go version devel +e0ad7e329637 Thu Jun 19 22:19:56 2014 -0700 linux/arm)ではこの問題は再現しなかったとされています。しかし、将来的に同様の問題が再発する(回帰する)可能性を考慮し、元の問題を引き起こしたコードスニペットをテストケースとして追加することで、Goプロジェクトの堅牢性を高めることが目的でした。これにより、もし将来の変更でこのバグが再導入された場合でも、自動テストによって早期に検出できるようになります。

前提知識の解説

このコミットの技術的詳細を理解するためには、以下のGo言語の概念とGoのテストに関する知識が必要です。

  1. Go言語のgoキーワードとゴルーチン (Goroutine):

    • goキーワードは、関数呼び出しの前に置くことで、その関数を新しいゴルーチンとして並行実行します。ゴルーチンはGoランタイムによって管理される軽量なスレッドのようなもので、並行処理を実現するためのGoの主要な機能です。
  2. copy組み込み関数:

    • copy(dst, src []Type) int は、ソーススライス src からデスティネーションスライス dst へ要素をコピーするGoの組み込み関数です。コピーされた要素の数を返します。dstsrcはどちらもスライス型である必要があります。
  3. make組み込み関数:

    • make関数は、スライス、マップ、チャネルといった組み込みの参照型を初期化するために使用されます。例えば、make([]byte, 10) は10バイトの容量を持つバイトスライスを作成し、make(map[float64][]byte) はキーがfloat64、値がバイトスライスであるマップを作成します。
  4. マップの要素アクセス:

    • m[key] の形式でマップから値を取得できます。例えば、m[1.0] はマップ m からキー 1.0 に対応する値を取得します。マップから取得された値は、その時点での一時的な値(テンポラリ)として扱われます。
  5. Go言語におけるアドレス可能性 (Addressability):

    • Go言語では、&演算子を使って変数のメモリアドレスを取得できます。しかし、すべての値がアドレス可能であるわけではありません。定数、リテラル、マップのインデックス式の結果、関数呼び出しの戻り値など、メモリ上の固定された場所を持たない一時的な値は「非アドレス可能」です。非アドレス可能値のアドレスを取ろうとすると、コンパイルエラー「cannot take the address of ...」が発生します。
    • copy関数自体は引数としてスライスヘッダ(ポインタ、長さ、容量)を値渡しで受け取りますが、コンパイラが内部的に非アドレス可能値を一時変数に格納し、その一時変数のアドレスを何らかの形で参照しようとした場合に、この問題が発生する可能性があります。
  6. Goのテストディレクトリ構造 (test/fixedbugs):

    • Goプロジェクトのtest/fixedbugsディレクトリは、過去に修正されたバグのテストケースを格納するために使用されます。これにより、修正されたバグが将来の変更によって再導入されないことを保証します。これらのテストは通常、コンパイル時または実行時に特定の振る舞いをチェックします。
  7. // compile ディレクティブ:

    • Goのテストファイルでは、ファイルの先頭に// compileというコメントを記述することで、そのファイルがコンパイル可能であることをテストする特別な指示として扱われます。これは、コンパイルエラーが発生しないことを確認するためのテストです。

技術的詳細

issue 8074 の具体的な問題は、go copy(a, m[1.0]) というコード行に起因していました。この行は、新しいゴルーチン内でcopy関数を呼び出しています。

問題の核心は、m[1.0] の部分にあります。mmap[float64][]byte型のマップであり、m[1.0] はこのマップからキー1.0に対応する[]byte型の値を取得するマップインデックス式です。Go言語の仕様では、マップインデックス式の結果は一時的な値であり、直接アドレスを取ることができません(非アドレス可能)。

通常、copy関数は引数としてスライスを受け取ります。スライスは内部的にポインタ、長さ、容量を持つ構造体であり、copy関数はこれらのスライスヘッダを値渡しで受け取ります。しかし、この特定のケースでは、goキーワードによって新しいゴルーチンが起動される文脈で、コンパイラがm[1.0]という非アドレス可能値を処理する際に、何らかの形でそのアドレスを取ろうとする内部的な操作が発生していた可能性があります。

例えば、コンパイラがcopy関数の引数を評価する際に、m[1.0]の結果を一時的なメモリ領域に格納し、その一時的な領域へのポインタを生成しようとしたが、その一時的な領域がアドレス可能として扱われていなかった、といったシナリオが考えられます。特に、並行処理のコンテキスト(goキーワード)が絡むことで、コンパイラの最適化やコード生成パスが複雑になり、このようなエッジケースで問題が顕在化した可能性があります。

このコミットは、問題が再現しなくなった後でも、この特定のコードパターンが将来的にコンパイルエラーを引き起こさないことを保証するために、テストケースとして追加されました。// compileディレクティブは、このファイルがエラーなくコンパイルできることを確認します。これにより、Goコンパイラの進化に伴って同様のバグが再発した場合でも、自動的に検出されるようになります。

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

このコミットによって追加された新しいファイル test/fixedbugs/issue8074.go の内容は以下の通りです。

// compile

// Copyright 2014 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 8073.
// was "cannot take the address of 1"

package main

func main() {
	a := make([]byte, 10)
	m := make(map[float64][]byte)
	go copy(a, m[1.0])
}

コアとなるコードの解説

  • // compile: この行は、このGoファイルがコンパイル可能であることをテストするための特別な指示です。Goのテストシステムは、このコメントがあるファイルをコンパイルし、コンパイルエラーが発生しないことを確認します。
  • // Copyright 2014 The Go Authors. ...: Goプロジェクトの標準的な著作権表示とライセンス情報です。
  • // issue 8073.: このコメントは、このテストケースがissue 8073に関連していることを示していますが、コミットメッセージではissue 8074と記載されており、番号の不一致があります。これは、関連する別の問題か、単なるタイプミスである可能性があります。しかし、重要なのは次の行です。
  • // was "cannot take the address of 1": このコメントは、このテストケースが以前に「cannot take the address of 1」というコンパイルエラーを引き起こしていたことを明確に示しています。これは、非アドレス可能値のアドレスを取ろうとした際に発生するGoの一般的なエラーメッセージです。
  • package mainfunc main(): 標準的なGoの実行可能プログラムの構造です。
  • a := make([]byte, 10): 10バイトの容量を持つバイトスライス a を作成します。これはcopy関数のデスティネーションスライスとして使用されます。
  • m := make(map[float64][]byte): キーがfloat64型、値が[]byte型の空のマップ m を作成します。
  • go copy(a, m[1.0]): この行がこのテストケースの核心です。
    • go: copy関数の呼び出しを新しいゴルーチンで実行します。
    • copy(a, m[1.0]): a スライスに、マップ m からキー 1.0 に対応する値([]byte型)をコピーしようとします。
    • 問題は m[1.0] の部分にありました。マップインデックス式の結果は非アドレス可能値であるため、以前のGoコンパイラまたはランタイムの特定のバージョンでは、この文脈でそのアドレスを取ろうとする内部的な処理が原因で「cannot take the address of 1」というエラーが発生していました。このテストケースは、このコードが現在ではエラーなくコンパイルできることを保証します。

関連リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/5b342f78043389e35a6c8c6ee1030ae733570da1
  • Go Gerrit Change-ID: https://golang.org/cl/107290043
  • Go Issue #8074 (推定): コミットメッセージに記載されているissue 8074は、Goの公開Issueトラッカーでは直接見つかりませんでしたが、GoのIssueは通常 https://github.com/golang/go/issues/ の形式でアクセスできます。この番号が内部的なものか、あるいは修正後にクローズされたため見つからない可能性があります。

参考にした情報源リンク