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

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

このコミットは、Go言語の初期開発段階において、コンパイラが特定のメソッド呼び出しパターンを正しくコンパイルできないバグを浮き彫りにするための新しいテストケース (test/bugs/bug054.go) を追加するものです。また、既存のテストファイル (test/bugs/bug048.go) を修正し、マップリテラルの構文に関する初期の言語仕様の曖昧さについてコメントを追記しています。全体として、Go言語のコンパイラと文法がまだ成熟していなかった時期の、言語設計上の課題とバグを記録する目的を持っています。

コミット

add a bug: method call doesn't compile

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

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

元コミット内容

add a bug: method call doesn't compile

SVN=123403

変更の背景

Go言語は2007年にGoogleで設計が開始され、2009年にオープンソースとして公開されました。このコミットは2008年6月のものであり、Go言語がまだ活発に開発され、言語仕様やコンパイラの挙動が固まっていない、非常に初期の段階に属します。

この時期には、言語の基本的な機能、特に型システム、メソッドの解決、コンパイル時のエラーハンドリングなどにおいて、まだ多くの未解決の問題やバグが存在していました。このコミットの主な目的は、Goコンパイラが特定の状況下でのメソッド呼び出しを正しく処理できないという既知の、または新たに発見されたバグを再現するためのテストケースを導入することです。具体的には、構造体のフィールドがポインタ型であり、そのポインタを介してメソッドを呼び出す際にコンパイルエラーが発生するという問題が指摘されています。

また、test/bugs/bug048.goの変更は、マップリテラルの構文に関する初期のGo言語の曖昧さを示しています。M(7 : 8)のような記述が、文法上は許容されるものの、意図しない挙動を引き起こす可能性があったり、言語のドキュメントと実際の文法解析の間で一貫性が取れていなかったりしたことを示唆しています。これは、言語設計がまだ流動的であり、細部の仕様が議論され、固められていく過程であったことを反映しています。

前提知識の解説

  • Go言語の初期開発フェーズ: Go言語は、C++やJavaのような既存の言語の欠点を克服し、シンプルで効率的なプログラミングを可能にすることを目指して設計されました。このコミットが行われた2008年は、Go言語がまだGoogle内部で開発されており、その設計思想や機能が日々進化していた時期です。コンパイラもまだ初期段階であり、多くのエッジケースやバグが存在していました。
  • メソッドとレシーバ: Go言語では、関数を特定の型に関連付けることで「メソッド」を定義します。メソッドは、そのメソッドが操作する対象となる「レシーバ」と呼ばれる特別な引数を持ちます。レシーバは値型(T)またはポインタ型(*T)で指定できます。Goのコンパイラは、値型のレシーバを持つメソッドをポインタ型の変数から呼び出したり、ポインタ型のレシーバを持つメソッドを値型の変数から呼び出したりする際に、自動的にポインタのデリファレンスやアドレス取得を行います(メソッド値とメソッド式)。しかし、初期のコンパイラでは、この自動変換のロジックに不備があった可能性があります。
  • 構造体 (Struct): Go言語における構造体は、異なる型のフィールド(データ)をひとまとめにするための複合データ型です。オブジェクト指向言語におけるクラスのデータ部分に相当しますが、Goの構造体はメソッドを直接持つわけではなく、メソッドはレシーバとして構造体を受け取る形で定義されます。
  • インターフェース (Interface): インターフェースは、メソッドのシグネチャ(メソッド名、引数、戻り値の型)の集合を定義する型です。Go言語のインターフェースは、型が特定のインターフェースのすべてのメソッドを実装していれば、そのインターフェースを満たすと見なされる「暗黙的な実装」が特徴です。これにより、柔軟なポリモーフィズムを実現します。
  • コンパイルエラー: ソースコードがGo言語の文法規則や型規則に違反している場合に、Goコンパイラがプログラムを機械語に変換できない状態を指します。コンパイルエラーは、プログラムの実行前に問題を特定し、修正することを可能にします。
  • マップリテラル: Go言語でマップ(キーと値のペアを格納するデータ構造)を初期化するための構文です。map[KeyType]ValueType{key1: value1, key2: value2}のような形式で記述されます。

技術的詳細

このコミットの核心は、Goコンパイラが構造体のフィールドを介したメソッド呼び出しを正しく解決できないというバグを再現することにあります。

test/bugs/bug054.goでは、以下の構造が定義されています。

  1. Elementインターフェース: メソッドを持たない空のインターフェースで、任意の型を受け入れるためのプレースホルダーとして機能します。
  2. Vector構造体: elem *[]Elementというフィールドを持ち、Element型のスライスのポインタを保持します。
  3. VectorAtメソッド: func (v *Vector) At(i int) Elementとして定義されており、Vectorのポインタレシーバを持ち、スライスから指定されたインデックスの要素を返します。
  4. TStruct構造体: fields *Vectorというフィールドを持ち、Vector型のポインタを保持します。
  5. TStructfieldメソッド: func (s *TStruct) field()内で、t := s.fields.At(0)という行があります。

問題は、s.fields.At(0)という呼び出しにありました。ここでs.fields*Vector型(Vector構造体へのポインタ)です。VectorAtメソッドもポインタレシーバ(*Vector)を取ります。Go言語の仕様では、ポインタ型の変数から値レシーバのメソッドを呼び出したり、値型の変数からポインタレシーバのメソッドを呼び出したりする際に、コンパイラが自動的に適切な変換(デリファレンスやアドレス取得)を行います。しかし、このコミットが作成された初期のコンパイラでは、s.fieldsのような「構造体のフィールドがポインタであり、そのポインタを介してメソッドを呼び出す」という特定のシナリオにおいて、この自動変換やメソッド解決のロジックに不備があったと考えられます。コンパイラがs.fields*Vector型であることを認識しつつも、Atメソッドの呼び出しを正しく解決できなかったため、コンパイルエラーが発生していました。このテストケースは、この特定のバグを捕捉し、将来のコンパイラ修正の対象とすることを目指しています。

一方、test/bugs/bug048.goの変更は、マップリテラルの構文に関する初期のGo言語の設計上の課題を浮き彫りにしています。m1 := M(7 : 8);という行に付加されたコメント// BUG: syntax error (grammar but not doc has [a:b]; what should we do?)は、[a:b]という構文が配列やスライスのスライス構文と類似しており、マップリテラル内で使用された場合に曖昧さや混乱を招く可能性があったことを示唆しています。これは、言語の文法がまだ進化の途上にあり、開発者間で特定の構文の解釈や最適な表現について議論が行われていたことを示しています。

test/golden.outの変更は、これらのバグテストの追加に伴い、コンパイラの期待される出力(エラーメッセージなど)が更新されたことを示しています。特に、bug054.goに関する新しいエントリが追加され、他のいくつかの既存のバグテストの出力も、コンパイラの挙動の変更に合わせて調整されています。

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

test/bugs/bug048.go

--- a/test/bugs/bug048.go
+++ b/test/bugs/bug048.go
@@ -9,5 +9,5 @@ package main
 func main() {
  	type M map[int] int;
  	m0 := M(7 , 8);	// parses OK
- 	m1 := M(7 : 8);	// BUG: syntax error
+ 	m1 := M(7 : 8);	// BUG: syntax error  (grammar but not doc has [a:b]; what should we do?)
 }

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

// $G $D/$F.go && $L $F.$A && ./$A.out

// 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 Element interface {
}

type Vector struct {
	elem *[]Element;
}

func (v *Vector) At(i int) Element {
	return v.elem[i];
}

type TStruct struct {
	name string;
	fields *Vector;
}

func (s *TStruct) field() {
	t := s.fields.At(0); // この行がコンパイルエラーを引き起こすことが期待される
}

test/golden.out

このファイルは、Goコンパイラのテストスイートにおける期待される出力(コンパイルエラーメッセージなど)を記録するものです。このコミットでは、bug054.goの追加に伴い、そのテストケースが引き起こすコンパイルエラーがこのファイルに追記されています。また、他のいくつかの既存のバグテストに関する出力も、コンパイラの挙動の微調整に合わせて更新されています。

コアとなるコードの解説

test/bugs/bug054.goは、Go言語のコンパイラにおけるメソッド呼び出しのバグを具体的に示すための最小限の再現コードです。

  • Vector型は*[]Elementというスライスのポインタをフィールドに持ち、Atというポインタレシーバを持つメソッドを定義しています。
  • TStruct型は*VectorというVector型へのポインタをフィールドに持ちます。
  • TStructfieldメソッド内で、s.fields.At(0)という呼び出しが行われています。ここでs.fields*Vector型であり、VectorAtメソッドは*Vectorをレシーバとしています。

当時のGoコンパイラは、s.fieldsがポインタ型であるにもかかわらず、そのポインタを介してVector型のメソッドAtを呼び出す際に、正しく型解決やメソッド解決ができていなかったと考えられます。Go言語の現在の仕様では、このような呼び出しは問題なくコンパイルされますが、初期のコンパイラでは、構造体のフィールドがポインタであり、そのポインタを介してメソッドを呼び出すという特定のシナリオにおいて、コンパイラの内部ロジックが適切に機能していなかったことを示唆しています。このテストケースは、この特定のバグを捕捉し、コンパイラの修正を促すための重要な役割を果たしました。

test/bugs/bug048.goの変更は、M(7 : 8)というマップリテラルの構文に関するコメントの追加です。これは、Go言語の初期段階において、マップリテラルの構文が配列やスライスのスライス構文(例: arr[low:high])と視覚的に類似しており、開発者にとって混乱を招く可能性があったことを示しています。コメント内の「grammar but not doc has [a:b]; what should we do?」という記述は、言語の文法定義と公式ドキュメントの間で一貫性が取れていなかったり、特定の構文の解釈について開発者間で議論があったりしたことを示唆しており、言語設計がまだ流動的であった初期段階の典型的な状況を反映しています。

関連リンク

  • Go言語の初期のコミット履歴は、GoプロジェクトのSubversionリポジトリからGitに移行されたものです。SVN=123403は、元のSubversionリビジョン番号を示しています。このリビジョン番号を直接参照する公開されたGo issueは特定できませんでしたが、Go言語のバグトラッカー(Go issue tracker)には、同様のコンパイラバグに関する多くの議論が初期から存在します。

参考にした情報源リンク