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

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

このコミットは、Go言語のテストスイートにおけるメソッドの不正な使用例を、既存の test/method.go から新しいテストファイル test/method2.go へ移動させるものです。これにより、テストの目的が明確化され、特定のコンパイルエラー(レシーバーに関するエラー)を意図的にテストするための分離が行われています。

コミット

commit d289e6344f6ed40d5e2e6646e32fc8a685f38c75
Author: Russ Cox <rsc@golang.org>
Date:   Tue Nov 4 09:45:27 2008 -0800

    move invalid method uses to new test

    R=iant
    DELTA=24  (13 added, 10 deleted, 1 changed)
    OCL=18424
    CL=18439

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

https://github.com/golang/go/commit/d289e6344f6ed40d5e2e6646e32fc8a685f38c75

元コミット内容

不正なメソッドの使用例を新しいテストファイルに移動。

変更の背景

このコミットが行われた2008年11月は、Go言語がまだ活発に開発されていた初期段階にあたります。Go言語の設計、特に型システムやメソッドの定義に関するルールが固まりつつある時期でした。

test/method.go は、Go言語におけるメソッドの基本的な動作を検証するためのテストファイルであったと推測されます。しかし、その中に「不正なメソッドの使用」に関するテストケースが含まれていると、本来の「正しいメソッドの動作検証」という目的と混在し、テストの意図が不明瞭になる可能性があります。

このコミットの背景には、以下のような意図があったと考えられます。

  1. テストの分離と明確化: 正しい動作を検証するテストと、コンパイルエラーやランタイムエラーを意図的に引き起こすテストを分離することで、それぞれのテストの目的を明確にし、管理しやすくする。
  2. コンパイラの挙動テストの強化: Goコンパイラが、不正なコードに対して適切にエラーを報告するかどうかを検証するための専用のテストケースを設ける。特に、test/method2.go には ERROR "receiver" というコメントがあり、これはコンパイラが特定のレシーバーの定義に対してエラーを出すことを期待していることを示しています。
  3. テストスイートの整理: 開発が進むにつれてテストケースが増加し、既存のファイルが肥大化するのを防ぐための整理の一環。

前提知識の解説

このコミットを理解するためには、Go言語の以下の基本的な概念を理解しておく必要があります。

  1. Go言語のメソッド:

    • Go言語において、メソッドは特定の型に関連付けられた関数です。
    • メソッドは「レシーバー」と呼ばれる特別な引数を持ちます。レシーバーは、メソッドがどの型の値に対して呼び出されるかを定義します。
    • レシーバーには「値レシーバー」と「ポインタレシーバー」の2種類があります。
      • 値レシーバー: func (t T) MethodName(...) のように定義され、レシーバーの型の値のコピーに対してメソッドが呼び出されます。
      • ポインタレシーバー: func (t *T) MethodName(...) のように定義され、レシーバーの型のポインタに対してメソッドが呼び出されます。これにより、メソッド内でレシーバーの値を変更できます。
    • Goの仕様では、ポインタ型自体に直接メソッドを定義することはできません。例えば、type P *int と定義した場合、func (p P) MethodName() のようなメソッド定義は不正とされます。メソッドは基底の型(この場合は int)または構造体型に定義されるのが一般的です。このコミットで移動されたコードは、まさにこの「ポインタ型への直接のメソッド定義」という不正なケースを扱っています。
  2. Go言語のテストフレームワーク (go test):

    • Go言語には標準でテストフレームワークが組み込まれており、go test コマンドで実行されます。
    • テストファイルは通常 _test.go というサフィックスを持ちます。
    • コンパイルエラーを期待するテストでは、// errchk ディレクティブが使用されることがあります。これは、そのファイルがコンパイル時に特定のエラーを発生させることを期待していることをコンパイラに伝えるためのものです。// ERROR "..." コメントは、期待されるエラーメッセージのパターンを示します。
  3. golden.out ファイル:

    • Go言語のテストスイートでは、test/golden.out のようなファイルが使用されることがあります。これは、テストの実行結果(標準出力やエラー出力など)の「ゴールデンマスター」または「期待される出力」を記録するファイルです。
    • テスト実行時に生成された出力が golden.out の内容と一致するかどうかを比較することで、テストの合否を判定します。このコミットでは、新しいテストファイル method2.go の追加に伴い、golden.out にそのテストの出力(特にエラーメッセージ)が追加されています。

技術的詳細

このコミットの技術的な核心は、Go言語の型システムにおけるメソッドのレシーバーに関する厳格なルールをテストすることにあります。

test/method.go から削除され、test/method2.go に移動されたコードは、以下のパターンを含んでいます。

type P *int
type P1 *int

func (p P) val() int { return 5 }
func (p *P1) val() int { return 6 }

Go言語の仕様では、メソッドのレシーバーは、基底の型(int, string など)または構造体型(struct)である必要があります。ポインタ型(*int*T)を直接レシーバーの型として使用することはできません。

  • type P *intint へのポインタ型 *int のエイリアスです。
  • func (p P) val() int は、P 型(つまり *int 型)を値レシーバーとしてメソッドを定義しようとしています。
  • func (p *P1) val() int は、P1 型(つまり *int 型)へのポインタをポインタレシーバーとしてメソッドを定義しようとしています。これは **int となり、これもGoのメソッドレシーバーとしては不正です。

test/method2.go では、これらの不正なメソッド定義に対して // ERROR "receiver" というコメントが付けられています。これは、Goコンパイラがこれらの行で「レシーバーが不正である」というコンパイルエラーを正しく報告することを期待していることを意味します。

test/golden.out の変更は、test/method2.go が実行された際に、6g ./method2.go (Goコンパイラ 6g によるコンパイル) が予期せず成功した場合に BUG: errchk: command succeeded unexpectedly というエラーを報告するように更新されています。これは、method2.go がコンパイルエラーを出すべきであり、もしコンパイルが成功してしまったらそれはバグである、というテストの意図を明確にしています。

また、test/golden.out の別の変更 panic on line 74 PC=xxx から panic on line 77 PC=xxx への変更は、test/method.go からコードが削除されたことにより、他のテストケースの行番号がずれたことによる調整である可能性が高いです。

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

test/golden.out

--- a/test/golden.out
+++ b/test/golden.out
@@ -37,6 +37,9 @@ Faulting address: 0x0
 pc: xxx
 
 
+=========== ./method2.go
+BUG: errchk: command succeeded unexpectedly:  6g ./method2.go
+\
 =========== ./peano.go
 0! = 1
 1! = 1
@@ -121,7 +124,7 @@ BUG: fails incorrectly
 =========== bugs/bug095.go
 found 2, expected 1
 
-panic on line 74 PC=xxx
+panic on line 77 PC=xxx
 BUG wrong result
 
 =========== bugs/bug098.go

test/method.go

--- a/test/method.go
+++ b/test/method.go
@@ -10,8 +10,6 @@ type S string
 type S1 string
 type I int
 type I1 int
-type P *int
-type P1 *int
 type T struct { x int }
 type T1 T
 
@@ -19,8 +17,6 @@ func (s S) val() int { return 1 }
 func (s *S1) val() int { return 2 }
 func (i I) val() int { return 3 }
 func (i *I1) val() int { return 4 }
-func (p P) val() int { return 5 }
-func (p *P1) val() int { return 6 }
 //func (t T) val() int { return 7 }
 func (t *T1) val() int { return 8 }
 
@@ -37,8 +33,6 @@ func main() {
 	var ps *S1;
 	var i I;
 	var pi *I1;
-\tvar p P;
-\tvar pp *P1;
 	var t T;
 	var pt *T1;
 
@@ -46,8 +40,6 @@ func main() {
 	if ps.val() != 2 { panicln("ps.val:", ps.val()) }
 	if i.val() != 3 { panicln("i.val:", i.val()) }
 	if pi.val() != 4 { panicln("pi.val:", pi.val()) }
-\tif p.val() != 5 { panicln("p.val:", p.val()) }
-\tif pp.val() != 6 { panicln("pp.val:", pp.val()) }
 //\tif t.val() != 7 { panicln("t.val:", t.val()) }
 	if pt.val() != 8 { panicln("pt.val:", pt.val()) }
 
@@ -55,8 +47,6 @@ func main() {
 	if val(ps) != 2 { panicln("ps.val:", val(ps)) }
 	if val(i) != 3 { panicln("i.val:", val(i)) }
 	if val(pi) != 4 { panicln("pi.val:", val(pi)) }
-\tif val(p) != 5 { panicln("p.val:", val(p)) }
-\tif val(pp) != 6 { panicln("pp.val:", val(pp)) }
 //\tif val(t) != 7 { panicln("t.val:", val(t)) }
 	if val(pt) != 8 { panicln("pt.val:", val(pt)) }
 

test/method2.go (新規ファイル)

--- /dev/null
+++ b/test/method2.go
@@ -0,0 +1,14 @@
+// errchk $G $D/$F.go
+\
+// 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 main
+\
+type T struct {a int}
+type P *T
+type P1 *T
+\
+func (p P) val() int { return 1 }  // ERROR "receiver"
+func (p *P1) val() int { return 1 }  // ERROR "receiver"

コアとなるコードの解説

  • test/method.go からの削除:

    • type P *inttype P1 *int というポインタ型のエイリアスの定義が削除されました。
    • これらの型をレシーバーとするメソッド func (p P) val() intfunc (p *P1) val() int の定義も削除されました。
    • main 関数内のこれらの型に関連する変数宣言 (var p P; var pp *P1;) と、それらのメソッド呼び出し (p.val(), pp.val(), val(p), val(pp)) も削除されました。
    • これにより、test/method.go は、Go言語の仕様に準拠した正しいメソッドの使用例のみをテストするファイルとなりました。
  • test/method2.go の新規追加:

    • このファイルは、// errchk $G $D/$F.go というディレクティブで始まっています。これは、このファイルがコンパイルエラーを発生させることを期待していることを示します。$G はGoコンパイラ、$D/$F.go は現在のディレクトリとファイル名を指します。
    • type T struct {a int} という構造体型が定義されています。
    • type P *Ttype P1 *T という、構造体 T へのポインタ型のエイリアスが定義されています。
    • func (p P) val() int { return 1 } // ERROR "receiver": ここで、P 型(つまり *T 型)を値レシーバーとしてメソッドを定義しようとしています。Goの仕様ではこれは不正であり、// ERROR "receiver" コメントはコンパイラが「receiver」に関するエラーを出すことを期待しています。
    • func (p *P1) val() int { return 1 } // ERROR "receiver": ここで、P1 型へのポインタ(つまり **T 型)をポインタレシーバーとしてメソッドを定義しようとしています。これもGoの仕様では不正であり、同様に「receiver」に関するエラーが期待されます。
    • この新しいファイルは、Go言語のメソッドレシーバーのルールに違反するコードを意図的に含み、コンパイラがそれらを正しく検出してエラーを報告するかどうかを検証するための専用のテストケースとして機能します。
  • test/golden.out の変更:

    • =========== ./method2.go のセクションが追加され、その下に BUG: errchk: command succeeded unexpectedly: 6g ./method2.go という行が追加されました。これは、method2.go がコンパイルエラーを出すべきであり、もしコンパイルが成功してしまったら、それは errchk テストのバグであるということを示しています。
    • panic on line 74 PC=xxxpanic on line 77 PC=xxx に変更されたのは、test/method.go からコードが削除されたことにより、golden.out に記録されている他のテストのパニック発生行番号がずれたため、その調整が行われたものです。

このコミットは、Go言語の型システムとコンパイラの堅牢性を確保するための、初期段階における重要なテストの整理と強化を示しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Gitのdiff出力
  • 一般的なGo言語のテスト慣習に関する知識