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

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

このコミットは、Go言語の初期開発段階において、特定のコンパイラ挙動(またはバグ)を検証するための新しいテストケースを追加するものです。具体的には、ポインタ型の匿名構造体をレシーバとするメソッドの定義が、当時のコンパイラによってどのように扱われるか、そしてそれが「illegal pointer」というエラーを生成することを確認するための回帰テストが導入されています。

コミット

commit a544938c264bbf1dd83c6b8f3d3232f4d96497b1
Author: Robert Griesemer <gri@golang.org>
Date:   Wed Jun 11 12:07:43 2008 -0700

    - added test case
    
    SVN=122159

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

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

元コミット内容

- added test case

変更の背景

Go言語は、その開発初期から厳格なテスト体制を敷いていました。特にコンパイラやランタイムのような基盤部分においては、発見されたバグが将来のバージョンで再発しないように、バグを再現するテストケースを追加することが一般的なプラクティスでした。

このコミットは、まさにその典型例です。bug046.goという新しいテストファイルが追加されており、その内容は特定の型定義とメソッド宣言が当時のGoコンパイラによってエラーとして扱われることを期待しています。具体的には、type T *struct {}のように、匿名構造体へのポインタを基底型とする新しい型Tを定義し、その型TをレシーバとするメソッドMを宣言しています。このコードは、当時のコンパイラが「illegal pointer」というエラーを出すことが期待されており、このテストケースの追加は、この特定のコンパイラ挙動(おそらくバグ、または意図された制限)を捕捉し、将来の変更でこの挙動が意図せず修正されたり、あるいは修正後に回帰したりしないことを保証するためのものです。

test/golden.outの更新は、この新しいテストケースが生成するであろうエラーメッセージを、コンパイラのテストスイートの期待される出力に組み込むことを意味します。これにより、コンパイラの変更がこの特定のテストケースの出力に影響を与えた場合、テストが失敗し、開発者に通知されるようになります。

前提知識の解説

Go言語の初期開発とテスト駆動開発

Go言語は2007年後半に設計が始まり、2009年にオープンソースとして公開されました。このコミットが行われた2008年6月は、まさに言語が活発に開発され、コンパイラが構築されている最中でした。このような段階では、言語仕様が固まっていない部分や、コンパイラの実装が未熟な部分が多く存在します。そのため、特定のコードパターンがどのようにコンパイルされるか、どのようなエラーを出すべきかといった挙動を、テストケースを通じて明確に定義し、回帰テストを行うことが非常に重要でした。

Go言語のメソッドとレシーバ

Go言語では、関数を型に関連付ける「メソッド」の概念があります。メソッドは、そのメソッドが操作する値(レシーバ)を持つことで定義されます。レシーバには「値レシーバ」と「ポインタレシーバ」の2種類があります。

  • 値レシーバ: func (x T) M() {} のように定義され、メソッドが呼び出される際にレシーバの型の値がコピーされます。
  • ポインタレシーバ: func (x *T) M() {} のように定義され、メソッドが呼び出される際にレシーバの型の値へのポインタが渡されます。これにより、メソッド内でレシーバの値を変更することができます。

このコミットのテストケースでは、func (x T) M () {} と定義されており、T自体がポインタ型(*struct {})であるため、レシーバxは「ポインタへの値」として扱われます。

匿名構造体(Anonymous Struct)

Go言語では、名前を持たない構造体をその場で定義して使用することができます。これを匿名構造体と呼びます。 例: var s struct { X int; Y string } このコミットのテストケースでは、type T *struct {}のように、フィールドを持たない匿名構造体へのポインタを新しい型Tとして定義しています。

test/bugs ディレクトリの役割

Go言語のソースコードリポジトリには、test/ディレクトリ以下に様々なテストスイートが含まれています。その中でもtest/bugsディレクトリは、過去に発見され、修正されたバグを再現するためのテストケースを格納する場所です。これらのテストは、将来のコンパイラやランタイムの変更によって同じバグが再発しないことを保証する「回帰テスト」として機能します。

test/golden.out ファイルの役割

test/golden.outは、Goコンパイラのテストスイートにおいて、特定のテストケースが生成する標準出力や標準エラー出力の「期待される結果」を記録するファイルです。コンパイラのテスト実行時には、各テストケースの実際の出力がgolden.outに記録された期待値と比較されます。もし両者が一致しなければ、テストは失敗とみなされます。これにより、コンパイラの挙動が意図せず変更された場合に、それを検知することができます。

// errchk ディレクティブ

Go言語のテストファイル、特にコンパイラの挙動をテストするファイルでは、特別なコメントディレクティブが使用されることがあります。// errchk $G $D/$F.go は、そのテストファイルがコンパイル時に特定のエラーを生成することを期待していることを示します。$GはGoコンパイラ、$D/$F.goは現在のテストファイルのパスを指します。このディレクティブがある場合、テストランナーはコンパイルが成功するのではなく、指定されたエラーメッセージを伴って失敗することを期待します。

技術的詳細

このコミットで追加されたtest/bugs/bug046.goの核心は、Go言語の型システムとメソッドのレシーバに関する初期の挙動をテストすることにあります。

type T *struct {}

func (x T) M () {}  // not clear why this shouldn't work

ここで定義されているTは、*struct {} 、つまり「フィールドを持たない匿名構造体へのポインタ」のエイリアスです。そして、func (x T) M () {} は、このT型を値レシーバとして持つメソッドMを定義しようとしています。

当時のGoコンパイラは、このような型定義とメソッド宣言の組み合わせに対して「illegal <this> pointer」というエラーを生成していました。このエラーメッセージは、コンパイラがメソッドのレシーバ(thisポインタに相当する概念)を正しく構築または解釈できなかったことを示唆しています。

考えられる原因としては、以下のような点が挙げられます。

  1. 匿名構造体へのポインタの特殊性: 当時のコンパイラが、フィールドを持たない匿名構造体へのポインタ型を、メソッドレシーバとして扱う際の内部的なメカニズムが未成熟であった可能性があります。特に、*struct {} は実質的にゼロサイズの型であり、そのポインタをレシーバとして扱う際に、コンパイラが期待する「インスタンス」の概念と合致しなかったのかもしれません。
  2. レシーバの型推論または内部表現の制限: Goコンパイラは、メソッドのレシーバを内部的に特定の形式で表現します。T*struct {} のエイリアスである場合、コンパイラがTをレシーバとして見たときに、その基底型が匿名構造体へのポインタであるという事実を適切に処理できなかった可能性があります。特に、thisポインタは通常、構造体のインスタンスを指すために使用されますが、*struct {} のような型では、その「インスタンス」の概念が曖昧になるため、コンパイラが混乱したのかもしれません。
  3. 言語仕様の進化: Go言語の仕様は、初期段階から現在に至るまで進化を続けています。この時点では、このような特定の型とレシーバの組み合わせが、言語設計者が意図しない挙動を引き起こしていたか、あるいはまだ明確に定義されていなかった可能性があります。// not clear why this shouldn't workというコメントは、当時の開発者自身もこのエラーの理由について完全に納得していなかったことを示唆しており、これはコンパイラのバグである可能性が高いことを裏付けています。

test/golden.outの変更は、このbug046.goがコンパイルされた際に、bugs/bug046.go:7: illegal <this> pointerというエラーメッセージが期待される出力として追加されたことを示しています。これは、このエラーが当時のコンパイラの「正しい」挙動として記録され、将来のコンパイラの変更がこの挙動を壊さないようにするためのものです。

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

このコミットによる主要なコード変更は以下の2ファイルです。

  1. test/bugs/bug046.go (新規追加)

    --- /dev/null
    +++ b/test/bugs/bug046.go
    @@ -0,0 +1,15 @@
    +// 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 {}
    +
    +func (x T) M () {}  // not clear why this shouldn't work
    +
    +/*
    +bug046.go:7: illegal <this> pointer
    +*/
    
  2. test/golden.out (変更) bug046.goに関連する部分のみ抜粋。

    --- a/test/golden.out
    +++ b/test/golden.out
    @@ -240,6 +240,10 @@ BUG: compilation should succeed
     bugs/bug045.go:13: fatal error: naddr: const <T>{<i><int32>INT32;}\n BUG: known to fail incorrectly
     
     +=========== bugs/bug046.go
     +bugs/bug046.go:7: illegal <this> pointer
     +BUG: known to fail incorrectly
     +
     =========== fixedbugs/bug000.go
    

    また、他の既存のテストケースの期待される出力にも微調整が加えられています。これは、コンパイラの変更が全体的な出力に影響を与えたためか、あるいは単にこのコミットでまとめて更新されたためと考えられます。

コアとなるコードの解説

test/bugs/bug046.go

  • // errchk $G $D/$F.go: この行は、このファイルがGoコンパイラ($G)によってコンパイルされる際に、エラーを生成することを期待していることをテストハーネスに伝えます。もしエラーが生成されなかったり、異なるエラーが生成されたりした場合、テストは失敗します。
  • package main: 実行可能なプログラムであることを示します。
  • type T *struct {}: ここがテストの核心です。Tという新しい型を定義しており、その基底型は「フィールドを持たない匿名構造体へのポインタ」です。
  • func (x T) M () {}: T型を値レシーバとして持つメソッドMを定義しようとしています。この行が、当時のコンパイラによって「illegal <this> pointer」エラーを生成することが期待されています。コメント// not clear why this shouldn't workは、この挙動が直感的ではない、あるいはバグである可能性を示唆しています。
  • /* ... */: コメントブロック内には、このテストケースが期待するエラーメッセージが記述されています。これは、テストの意図を明確にするためのものです。

このファイルは、Goコンパイラが特定の複雑な型定義とメソッドレシーバの組み合わせをどのように処理するかを検証するための、非常に具体的な回帰テストケースとして機能します。

test/golden.out

test/golden.outへの変更は、bug046.goがコンパイルされた際に、以下の出力が期待されることを示しています。

=========== bugs/bug046.go
bugs/bug046.go:7: illegal <this> pointer
BUG: known to fail incorrectly
  • =========== bugs/bug046.go: これは、bug046.goのテスト結果のセクションの開始を示します。
  • bugs/bug046.go:7: illegal <this> pointer: これは、bug046.goの7行目(func (x T) M () {}の行)で「illegal <this> pointer」というエラーが発生することを期待していることを示します。
  • BUG: known to fail incorrectly: この行は、このテストケースが「誤って失敗することが知られているバグ」に関連していることを示唆しています。これは、このテストケースがバグを再現するためのものであり、そのバグがまだ修正されていないか、あるいは修正されたが、その挙動がまだ完全に理解されていないことを示唆している可能性があります。Goの初期のテストスイートでは、このようなコメントがよく見られました。

このgolden.outの更新により、Goコンパイラの開発者は、この特定のコンパイルエラーが将来の変更によって意図せず修正されたり、あるいは異なるエラーに変わったりした場合に、すぐにそれを検知できるようになります。

関連リンク

  • Go言語の公式ウェブサイト: https://go.dev/
  • Go言語のソースコード(GitHub): https://github.com/golang/go
  • Go言語のテストに関するドキュメント(Goのテストの仕組みについてより深く理解するため): https://go.dev/doc/code (Goのテストフレームワークに関する一般的な情報)

参考にした情報源リンク

  • Go言語のソースコードリポジトリ(特にtest/ディレクトリの構造と内容)
  • Go言語の初期のコミット履歴と開発メーリングリスト(公開されている場合)
  • Go言語のメソッドとレシーバに関する公式ドキュメント
  • Go言語の型システムに関する公式ドキュメント
  • Go言語の匿名構造体に関する情報
  • Go言語のテストハーネスにおける// errchkディレクティブの慣習に関する情報(Goのソースコード内の既存のテストファイルから推測)
  • test/golden.outのような「ゴールデンファイル」テストの一般的な概念