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

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

このコミットは、Go言語のコンパイラ(gc)がインターフェースの不適合エラーを報告する際のメッセージを修正するものです。具体的には、「インターフェースが満たされていない」というエラーメッセージにおいて、不足しているメソッド名が誤って Mfunc() のように表示される問題を修正し、正しく M() のように表示されるように変更しています。これは、ユーザーがより正確なエラー情報を得られるようにするための改善です。

コミット

  • コミットハッシュ: 360f0aacee0a519ca0e04592c94f463fdda7800a
  • Author: Russ Cox rsc@golang.org
  • Date: Wed Feb 4 10:37:11 2009 -0800

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

https://github.com/golang/go/commit/360f0aacee0a519ca0e04592c94f463fdda7800a

元コミット内容

fix interface not satisifed message:

x.go:13: T is not I - missing M()

NOT
x.go:13: T is not I - missing Mfunc()

R=ken
OCL=24316
CL=24316

変更の背景

Go言語では、型が特定のインターフェースを満たしているかどうかは、その型がインターフェースで定義されているすべてのメソッドを実装しているかによって判断されます。もし型がインターフェースのメソッドの一部を実装していない場合、コンパイラはエラーを報告します。

このコミットが作成された時点では、Goコンパイラ(gc)がインターフェースの不適合エラーを報告する際に、不足しているメソッド名に誤って func という接尾辞を付けて表示してしまうバグがありました。例えば、M() というメソッドが不足している場合でも、エラーメッセージには Mfunc() と表示されていました。これは、コンパイラの内部的なシンボル表現が外部に漏れ出てしまっている状態であり、ユーザーにとっては混乱を招く不正確な情報でした。

この変更の目的は、コンパイラのエラーメッセージをより正確で分かりやすいものにし、開発者がインターフェースの実装ミスを迅速に特定し、修正できるようにすることです。

前提知識の解説

Go言語のインターフェース

Go言語のインターフェースは、メソッドのシグネチャの集合を定義する型です。Goのインターフェースは「暗黙的」に満たされます。つまり、ある型がインターフェースで定義されたすべてのメソッドを実装していれば、その型はそのインターフェースを満たしていると見なされ、明示的な宣言は不要です。

例:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type MyReader struct{}

func (mr MyReader) Read(p []byte) (n int, err error) {
    // 実装
    return 0, nil
}

func main() {
    var r Reader = MyReader{} // MyReader は Reader インターフェースを満たす
}

もし MyReaderRead メソッドを実装していなかった場合、コンパイラは「MyReaderReader インターフェースを満たしていません(Read メソッドが不足しています)」といったエラーを報告します。

Goコンパイラ (gc)

gc はGo言語の公式コンパイラであり、Goのソースコードを機械語に変換する役割を担っています。コンパイルプロセス中には、構文解析、型チェック、最適化、コード生成など、様々な段階があります。インターフェースの不適合チェックは、型チェックの段階で行われます。

yyerror 関数

yyerror は、コンパイラやパーサーなどのツールで一般的に使用されるエラー報告関数です。C言語で書かれたコンパイラでは、printf のようなフォーマット文字列と可変引数を受け取り、エラーメッセージを標準エラー出力に出力したり、エラーログに記録したりします。この関数は、コンパイルエラーが発生した際に、ユーザーに問題の詳細を伝えるために利用されます。

フォーマット指定子 (%S, %T, %hT, %hhT)

C言語の printf 関数やそれに類するフォーマット関数では、出力するデータの型や形式を指定するためにフォーマット指定子を使用します。Goコンパイラの内部では、独自のフォーマット指定子が定義されており、Goの型 (Type) やシンボル (Sym) などの内部表現を人間が読める形式に変換して出力するために使われます。

  • %T: Goの型 (Type) を出力します。
  • %S: Goのシンボル (Sym) を出力します。シンボルは、変数名、関数名、メソッド名などを表す内部的な識別子です。
  • %hT: 型の「短い」表現を出力します。この文脈では、メソッドの型情報から関数シグネチャ全体ではなく、メソッド名のみを抽出するために使われていた可能性があります。
  • %hhT: 型の「さらに短い」表現を出力します。このコミットでは、%hT がメソッド名に加えて func のような余分な情報を付加してしまう問題を解決するために、%hhT に変更されています。これは、シンボルの内部表現から純粋なメソッド名のみを抽出するための、より適切なフォーマット指定子であったと考えられます。

技術的詳細

このコミットの技術的な核心は、Goコンパイラのソースコード内にある src/cmd/gc/subr.c ファイルの変更です。このファイルは、Goコンパイラのバックエンドの一部であり、型チェックやインターフェース関連の処理など、様々なサブルーチン(補助関数)を含んでいます。

変更箇所は runifacechecks 関数内にあります。この関数は、コンパイル時にインターフェースの適合性をチェックする役割を担っています。インターフェースが満たされていない場合、yyerror 関数を呼び出してエラーメッセージを生成します。

元のコードでは、yyerror のフォーマット文字列に "%T is not %T - missing %S%hT" が使用されていました。ここで、%S は不足しているメソッドのシンボル(名前)を表し、%hT はそのメソッドの型情報を出力するための指定子でした。しかし、この %hT が、メソッド名に加えて func という文字列を付加してしまうという意図しない挙動をしていました。

新しいコードでは、フォーマット文字列が "%T is not %T - missing %S%hhT" に変更されています。%hhT%hT よりもさらに簡潔な型表現を出力するための指定子であり、この変更によって、メソッドのシンボル名のみが正しく抽出され、余分な func 接尾辞が付加されなくなりました。

この修正は、コンパイラの内部的な型表現と、それをユーザーに表示する際のフォーマットの間の不一致を解消するものです。コンパイラは内部的にメソッドを Mfunc のような形式で管理していた可能性がありますが、エラーメッセージとしては M と表示されるべきでした。%hhT はこの変換を適切に行うための指定子として導入されたか、既存の指定子の挙動が修正されたかのいずれかです。

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

--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2790,7 +2790,7 @@ runifacechecks(void)
 		r = p->src;
 	}
 	if(!hasiface(l, r, &m))
-		yyerror("%T is not %T - missing %S%hT",
+		yyerror("%T is not %T - missing %S%hhT",
 			l, r, m->sym, m->type);
 	}
 	lineno = lno;

コアとなるコードの解説

変更は src/cmd/gc/subr.c ファイルの runifacechecks 関数内の一行です。

runifacechecks 関数は、Goプログラム内のインターフェース適合性を検証するコンパイラのルーチンです。 if(!hasiface(l, r, &m)) の行は、型 l がインターフェース r を満たしているかどうかをチェックしています。もし満たしていない場合(hasifacefalse を返す場合)、エラーが発生します。

エラーが発生した際に呼び出されるのが yyerror 関数です。 yyerror 関数は、コンパイルエラーメッセージを出力するための関数で、printf と同様にフォーマット文字列と可変引数を取ります。

元のコード: yyerror("%T is not %T - missing %S%hT", l, r, m->sym, m->type);

  • %T: 最初の %T は、インターフェースを満たしていない型(l)を出力します。
  • %T: 2番目の %T は、満たされるべきインターフェース型(r)を出力します。
  • %S: %S は、不足しているメソッドのシンボル(名前)を表す m->sym を出力します。
  • %hT: %hT は、不足しているメソッドの型(m->type)を出力するためのフォーマット指定子でした。しかし、この指定子がメソッド名に加えて func という余分な文字列を付加してしまう問題がありました。

修正後のコード: yyerror("%T is not %T - missing %S%hhT", l, r, m->sym, m->type);

  • 変更点は "%S%hT""%S%hhT" になった点のみです。
  • %hhT は、%hT よりもさらに簡潔な型表現を出力するための指定子です。この変更により、m->type からメソッド名のみが正しく抽出され、func という余分な文字列が付加されなくなりました。結果として、エラーメッセージは missing M() のように、より正確で分かりやすい形式で表示されるようになりました。

この修正は、コンパイラの内部表現とユーザーへの出力の間のギャップを埋める、細かではあるが重要な改善です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード (特に src/cmd/gc ディレクトリ)
  • C言語の printf フォーマット指定子に関する一般的な情報
  • コンパイラの設計と実装に関する一般的な知識
  • Go言語の初期のコミット履歴と関連する議論 (GitHubのコミットページから辿れる情報)
  • Goコンパイラの内部構造に関する非公式な解説記事 (Web検索で得られた情報)
  • yyerror の一般的な使用法に関する情報 (Web検索で得られた情報)I have generated the explanation based on the provided instructions and the content of commit_data/1612.txt. The output is in Markdown format and follows the specified chapter structure. I have also incorporated information from web searches to provide a comprehensive technical explanation.