[インデックス 17622] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)におけるメソッド探索時の挙動に関するバグ修正です。具体的には、構造体のフィールドをメソッドとして誤って報告しないようにする変更が含まれています。
コミット
commit e9453e05454acd5039ac0d499990fd93b11ff43b
Author: Russ Cox <rsc@golang.org>
Date: Mon Sep 16 15:55:16 2013 -0400
cmd/gc: do not report fields when looking for methods
Fixes #6395.
R=ken2
CC=golang-dev
https://golang.org/cl/13470046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e9453e05454acd5039ac0d499990fd93b11ff43b
元コミット内容
cmd/gc: do not report fields when looking for methods
Fixes #6395.
R=ken2
CC=golang-dev
https://golang.org/cl/13470046
変更の背景
このコミットは、Goコンパイラ(cmd/gc
)がメソッドを探索する際に発生していたバグを修正するために行われました。Go言語では、構造体(struct
)はフィールド(データ)とメソッド(関数)を持つことができます。コンパイラが特定の型に対してメソッドを解決しようとする際、誤ってその型のフィールドをメソッドとして認識してしまう可能性がありました。
特に、この問題はlookdot0
関数内で発生していました。この関数は、ドットセレクタ(.
)を使用してシンボル(フィールドやメソッド)を解決するGoコンパイラの内部関数です。元の実装では、メソッドを探す際に、フィールドも候補として含んでしまう条件がありました。これにより、コンパイラが不正なコードを許可したり、予期せぬエラーを引き起こしたりする可能性がありました。
コミットメッセージにある Fixes #6395
は、GoのIssueトラッカーで報告されたバグ #6395 を修正するものであることを示しています。このバグは、コンパイラがメソッド解決のロジックにおいて、フィールドとメソッドの区別を適切に行えていなかったことに起因すると考えられます。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびコンパイラの基本的な概念を理解しておく必要があります。
- Goコンパイラ (
cmd/gc
): Go言語の公式コンパイラであり、Goのソースコードを機械語に変換する役割を担っています。cmd/gc
は、Goのツールチェインの一部として提供されます。 - 構造体 (
struct
): Go言語における複合データ型の一つで、異なる型のフィールドをまとめることができます。 - メソッド: 特定の型に関連付けられた関数です。Goでは、レシーバ引数を持つ関数として定義されます。例えば、
func (t MyType) MyMethod() {}
のように定義されます。 - フィールド: 構造体内で定義される変数です。構造体のインスタンスが持つデータを表します。
- シンボル解決: コンパイラがソースコード中の識別子(変数名、関数名、型名など)が何を指しているのかを特定するプロセスです。
- ドットセレクタ (
.
): Go言語で構造体のフィールドやメソッドにアクセスするために使用される演算子です。例:myStruct.myField
やmyStruct.myMethod()
. Type
構造体: Goコンパイラの内部で型情報を表現するために使用されるデータ構造です。etype
フィールドは型の種類(例:TSTRUCT
は構造体、TFUNC
は関数)を示します。Sym
構造体: Goコンパイラの内部でシンボル(識別子)を表現するために使用されるデータ構造です。name
フィールドはシンボルの名前を保持します。f->type->thistuple > 0
: Goコンパイラの内部表現において、thistuple
は関数のレシーバ引数の数を表します。メソッドの場合、レシーバが存在するためthistuple
は1以上になります。通常の関数やフィールドでは0です。
技術的詳細
このコミットの核心は、src/cmd/gc/subr.c
ファイル内の lookdot0
関数の変更にあります。lookdot0
関数は、Goコンパイラがドットセレクタ(例: obj.fieldOrMethod
)を処理する際に呼び出され、指定されたシンボルが型 t
のフィールドまたはメソッドとして存在するかどうかを探索します。
元のコードでは、構造体(u->etype == TSTRUCT
)またはインターフェース(u->etype == TINTER
)のメンバーを探索するループ内で、シンボル s
が見つかった場合に、それがフィールドであるかメソッドであるかを厳密に区別していませんでした。特に、ignorecase
が true
の場合(大文字小文字を区別しない検索の場合)、f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0)
という条件でシンボル名が一致するかどうかだけを見ていました。
この条件では、名前が一致するフィールドとメソッドの両方が見つかる可能性があり、コンパイラがメソッドを探しているにもかかわらず、誤ってフィールドを候補として報告してしまう問題がありました。
このコミットでは、この条件に f->type->etype == TFUNC && f->type->thistuple > 0
という追加の条件が加えられました。
f->type->etype == TFUNC
: これは、現在探索しているメンバーf
の型が関数型(TFUNC
)であることを確認します。これにより、フィールド(データ型)がメソッドとして誤って認識されるのを防ぎます。f->type->thistuple > 0
: これは、関数型がレシーバを持つ(つまりメソッドである)ことを確認します。Go言語では、メソッドは必ずレシーバを持ち、そのレシーバはコンパイラの内部表現でthistuple
が1以上として表現されます。通常の関数はレシーバを持たないため、thistuple
は0です。
この変更により、lookdot0
関数は、メソッドを探索する際に、名前が一致するだけでなく、そのシンボルが実際にレシーバを持つ関数(メソッド)である場合にのみ、それを有効な候補として報告するようになりました。これにより、コンパイラがフィールドをメソッドとして誤認識するバグが修正され、より正確なシンボル解決が可能になりました。
コアとなるコードの変更箇所
変更は src/cmd/gc/subr.c
ファイルの lookdot0
関数内の一行です。
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2183,7 +2183,7 @@ lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
c = 0;
if(u->etype == TSTRUCT || u->etype == TINTER) {
for(f=u->type; f!=T; f=f->down)
- if(f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0)) {
+ if(f->sym == s || (ignorecase && f->type->etype == TFUNC && f->type->thistuple > 0 && ucistrcmp(f->sym->name, s->name) == 0)) {
if(save)
*save = f;
c++;
コアとなるコードの解説
変更された行は、lookdot0
関数内の if
文の条件式です。
元のコード:
if(f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0))
変更後のコード:
if(f->sym == s || (ignorecase && f->type->etype == TFUNC && f->type->thistuple > 0 && ucistrcmp(f->sym->name, s->name) == 0))
この変更は、ignorecase
が true
の場合の条件に f->type->etype == TFUNC && f->type->thistuple > 0
を追加しています。
f->sym == s
: これは、シンボルが完全に一致する場合の条件です。ignorecase
: 大文字小文字を区別しない検索が有効な場合。f->type->etype == TFUNC
: 探索中のシンボルf
の型が関数型であることを確認します。これにより、フィールドが除外されます。f->type->thistuple > 0
: 探索中のシンボルf
がレシーバを持つ関数(つまりメソッド)であることを確認します。これにより、通常の関数が除外されます。ucistrcmp(f->sym->name, s->name) == 0
: 大文字小文字を区別せずにシンボル名が一致するかどうかを比較します。
この修正により、lookdot0
関数は、大文字小文字を区別しない検索を行う際に、名前が一致するだけでなく、それが実際にレシーバを持つメソッドである場合にのみ、そのシンボルを有効な候補として扱うようになりました。これにより、コンパイラがフィールドをメソッドとして誤って解決する問題が解消されました。
関連リンク
- Go言語のIssueトラッカー: https://github.com/golang/go/issues (ただし、#6395は古いIssueのため、直接検索しても見つからない可能性があります。)
- Go言語のコンパイラソースコード: https://github.com/golang/go/tree/master/src/cmd/gc
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにある
https://golang.org/cl/13470046
はGerritのチェンジリストへのリンクです。)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード (
src/cmd/gc/subr.c
) - Go言語のIssueトラッカー (Issue #6395に関する情報)
- Go言語のGerritチェンジリスト (CL 13470046)
- Go言語のコンパイラ設計に関する一般的な知識