[インデックス 17510] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)におけるインポート時のシンボル(変数や型)の定義競合エラーメッセージを改善するものです。具体的には、シンボルが異なるパッケージで不整合な形で定義されている場合に表示されるエラーメッセージに、それぞれの定義が見つかったパッケージのパスを追加することで、デバッグの助けとなる詳細情報を提供するようになります。
コミット
commit 85195e2ccfcba41f1d0a4c8a6e1db6b3b6cc9691
Author: Russ Cox <rsc@golang.org>
Date: Mon Sep 9 12:30:53 2013 -0400
cmd/gc: more detail in import conflict error message
Cannot happen when using the go command, but help
people running commands by hand or with other tools.
Fixes #5888.
R=ken2
CC=golang-dev
https://golang.org/cl/13324048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/85195e2ccfcba41f1d0a4c8a6e1db6b3b6cc9691
元コミット内容
Goコンパイラ(cmd/gc
)において、インポート時の競合エラーメッセージをより詳細にする変更です。
この種の競合はgo
コマンドを使用している場合には発生しないはずですが、手動でコマンドを実行しているユーザーや、他のツールを使用しているユーザーを支援することを目的としています。
この変更はIssue #5888を修正します。
変更の背景
Go言語のコンパイラcmd/gc
は、ソースコードをコンパイルする際に、他のパッケージからシンボル(変数、関数、型など)をインポートします。このインポート処理中に、同じ名前のシンボルが異なる定義(例えば、異なる型)を持って複数回現れると、「inconsistent definition(不整合な定義)」というエラーが発生することがありました。
従来のこのエラーメッセージは、どのパッケージでその不整合な定義が見つかったのかという情報が不足しており、特に大規模なプロジェクトや、go
コマンド以外のビルドツールを使用している開発者にとっては、問題の根本原因を特定するのが困難でした。go
コマンドは通常、このような競合が発生しないようにビルドプロセスを管理しますが、手動でのコンパイルや、カスタムビルドシステムを使用する場合には、この問題に直面する可能性がありました。
このコミットの目的は、エラーメッセージに各定義が見つかったパッケージのパスを含めることで、開発者が不整合な定義の発生源を迅速に特定し、問題を解決できるようにすることです。これにより、コンパイラのエラーメッセージがより診断的になり、開発者のデバッグ体験が向上します。
前提知識の解説
cmd/gc
: Go言語の公式コンパイラの一つで、Goのソースコードを機械語に変換する役割を担います。Goのツールチェインの中核をなすコンポーネントです。- シンボル (Symbol): プログラミング言語において、変数名、関数名、型名など、プログラム内の特定のエンティティを識別するために使用される名前のことです。コンパイラはこれらのシンボルを管理し、その定義や参照を解決します。
- インポート (Import): Go言語において、他のパッケージで定義されたシンボルを現在のパッケージで使用するために、そのパッケージを取り込む操作です。
import "path/to/package"
のように記述します。 Sym
構造体:cmd/gc
内部でシンボルに関する情報を保持するために使用される構造体です。シンボルの名前、型、定義元などのメタデータが含まれます。Pkg
構造体:cmd/gc
内部でパッケージに関する情報を保持するために使用される構造体です。パッケージのパス、名前などのメタデータが含まれます。yyerror
:cmd/gc
内でコンパイルエラーを報告するために使用される関数です。C言語のprintf
のようなフォーマット文字列と引数を受け取り、エラーメッセージを出力します。go
コマンド: Go言語のビルド、テスト、実行、依存関係管理などを行うための統合ツールです。通常、Goのプロジェクトをビルドする際にはこのgo
コマンドを使用します。go
コマンドは、依存関係の解決やパッケージの管理を適切に行うため、今回修正されたようなインポート競合を内部的に回避する仕組みを持っています。%Z
フォーマット指定子: このコミットで導入された、yyerror
関数内でPkg
構造体のパスを表示するための新しいフォーマット指定子です。
技術的詳細
このコミットの主要な変更点は、cmd/gc
がインポート競合エラーを報告する際に、どのパッケージから不整合な定義が来たのかを示す情報を追加することです。
-
Sym
構造体へのimportdef
フィールドの追加:src/cmd/gc/go.h
ファイルにおいて、Sym
構造体にPkg* importdef;
という新しいフィールドが追加されました。このフィールドは、そのシンボルが最初にインポートされた際に、どのパッケージで定義が見つかったかを指すPkg
構造体へのポインタを保持します。これにより、シンボルの定義元パッケージを追跡できるようになります。 -
importvar
およびimporttype
関数でのimportdef
の更新:src/cmd/gc/export.c
ファイル内のimportvar
関数(変数のインポート処理)とimporttype
関数(型のインポート処理)において、シンボルが正常にインポートされた際に、そのシンボルのimportdef
フィールドに現在のimportpkg
(現在処理中のインポート元パッケージ)を代入する処理が追加されました。これにより、シンボルが最初に定義されたパッケージの情報を記録します。 -
エラーメッセージの改善:
importvar
およびimporttype
関数内で、不整合な定義が見つかった場合に呼び出されるyyerror
関数のフォーマット文字列が変更されました。- 変更前:
yyerror("inconsistent definition for var %S during import\\n\\t%T\\n\\t%T", s, s->def->type, t);
- 変更後:
yyerror("inconsistent definition for var %S during import\\n\\t%T (in \\\"%Z\\\")\\n\\t%T (in \\\"%Z\\\")", s, s->def->type, s->importdef->path, t, importpkg->path);
この変更により、エラーメッセージに
(in \"<package_path>\")
という形式でパッケージパスが追加されます。- 最初の
%T (in \\\"%Z\\\")
は、既にコンパイラが認識している(以前にインポートされた)シンボルの定義と、それがどのパッケージ(s->importdef->path
)で見つかったかを示します。 - 二番目の
%T (in \\\"%Z\\\")
は、現在インポートしようとしているシンボルの定義と、それがどのパッケージ(importpkg->path
)で見つかったかを示します。
この
%Z
フォーマット指定子は、yyerror
がPkg
構造体のパスを表示するための新しい機能として内部的に追加されたものと推測されます。これにより、開発者はエラーメッセージを見ただけで、どの2つのパッケージ間で定義の不整合が発生しているのかを明確に把握できるようになります。 - 変更前:
コアとなるコードの変更箇所
diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c
index ece02bc3bd..31bcdf8e77 100644
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -481,9 +481,10 @@ importvar(Sym *s, Type *t)
if(s->def != N && s->def->op == ONAME) {
if(eqtype(t, s->def->type))
return;
- yyerror("inconsistent definition for var %S during import\\n\\t%T\\n\\t%T", s, s->def->type, t);
+ yyerror("inconsistent definition for var %S during import\\n\\t%T (in \\\"%Z\\\")\\n\\t%T (in \\\"%Z\\\")", s, s->def->type, s->importdef->path, t, importpkg->path);
}
n = newname(s);
+ s->importdef = importpkg;
n->type = t;
declare(n, PEXTERN);
@@ -509,11 +510,12 @@ importtype(Type *pt, Type *t)
tn = pt->nod;
copytype(pt->nod, t);
pt->nod = n; // unzero nod
+ pt->sym->importdef = importpkg;
pt->sym->lastlineno = parserline();
declare(n, PEXTERN);
checkwidth(pt);
} else if(!eqtype(pt->orig, t))
- yyerror("inconsistent definition for type %S during import\\n\\t%lT\\n\\t%lT", pt->sym, pt, t);
+ yyerror("inconsistent definition for type %S during import\\n\\t%lT (in \\\"%Z\\\")\\n\\t%lT (in \\\"%Z\\\")", pt->sym, pt, pt->sym->importdef->path, t, importpkg->path);
if(debug['E'])
print("import type %T %lT\\n", pt, t);
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 8f0c2dfcf9..51f8fe67f8 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -380,6 +380,7 @@ struct Sym
Sym* link;
int32 npkg; // number of imported packages with this name
uint32 uniqgen;
+ Pkg* importdef; // where imported definition was found
// saved and restored by dcopy
Pkg* pkg;
コアとなるコードの解説
src/cmd/gc/go.h
の変更
@@ -380,6 +380,7 @@ struct Sym
Sym* link;
int32 npkg; // number of imported packages with this name
uint32 uniqgen;
+ Pkg* importdef; // where imported definition was found
// saved and restored by dcopy
Pkg* pkg;
Sym
構造体は、コンパイラがGoプログラム内の各シンボル(変数、関数、型など)に関する情報を格納するために使用されます。この変更では、importdef
という新しいフィールドが追加されています。
Pkg* importdef;
: このポインタは、現在のシンボルが最初にインポートされた際に、その定義が見つかったパッケージ(Pkg
構造体)を指します。これにより、シンボルの「最初の定義元」を追跡できるようになり、競合が発生した際にその情報を提供することが可能になります。
src/cmd/gc/export.c
の変更
importvar
関数内の変更
@@ -481,9 +481,10 @@ importvar(Sym *s, Type *t)
if(s->def != N && s->def->op == ONAME) {
if(eqtype(t, s->def->type))
return;
- yyerror("inconsistent definition for var %S during import\\n\\t%T\\n\\t%T", s, s->def->type, t);
+ yyerror("inconsistent definition for var %S during import\\n\\t%T (in \\\"%Z\\\")\\n\\t%T (in \\\"%Z\\\")", s, s->def->type, s->importdef->path, t, importpkg->path);
}
n = newname(s);
+ s->importdef = importpkg;
n->type = t;
declare(n, PEXTERN);
- エラーメッセージの変更:
yyerror
関数の呼び出しが変更されています。以前は単にシンボルの名前と2つの型情報(既存の定義と新しい定義)を表示していましたが、変更後はそれぞれの型情報に加えて、それがどのパッケージで見つかったかを示すパス情報が追加されています。s->importdef->path
: これは、Sym
構造体の新しいimportdef
フィールドを通じてアクセスされる、シンボルの既存の定義が見つかったパッケージのパスです。importpkg->path
: これは、現在インポートしようとしているシンボルの定義が見つかったパッケージのパスです。%Z
: この新しいフォーマット指定子は、Pkg
構造体のパスを表示するために使用されます。
s->importdef = importpkg;
の追加:newname(s)
の呼び出しの直後にこの行が追加されています。これは、新しいシンボルが作成され、その定義が確立される際に、そのシンボルのimportdef
フィールドに現在のインポート元パッケージ(importpkg
)を設定しています。これにより、このシンボルが将来的に別の場所で不整合な形で再定義されようとした場合に、最初の定義元を特定できるようになります。
importtype
関数内の変更
@@ -509,11 +510,12 @@ importtype(Type *pt, Type *t)
tn = pt->nod;
copytype(pt->nod, t);
pt->nod = n; // unzero nod
+ pt->sym->importdef = importpkg;
pt->sym->lastlineno = parserline();
declare(n, PEXTERN);
checkwidth(pt);
} else if(!eqtype(pt->orig, t))
- yyerror("inconsistent definition for type %S during import\\n\\t%lT\\n\\t%lT", pt->sym, pt, t);
+ yyerror("inconsistent definition for type %S during import\\n\\t%lT (in \\\"%Z\\\")\\n\\t%lT (in \\\"%Z\\\")", pt->sym, pt, pt->sym->importdef->path, t, importpkg->path);
- エラーメッセージの変更:
importvar
と同様に、yyerror
関数のフォーマット文字列が変更され、型の不整合エラーメッセージにパッケージパス情報が追加されています。pt->sym->importdef->path
: 型シンボルの既存の定義が見つかったパッケージのパスです。importpkg->path
: 現在インポートしようとしている型シンボルの定義が見つかったパッケージのパスです。
pt->sym->importdef = importpkg;
の追加: 型のインポート処理においても、型シンボル(pt->sym
)のimportdef
フィールドに現在のインポート元パッケージ(importpkg
)を設定しています。これにより、型の最初の定義元が記録され、将来的な競合検出に役立てられます。
これらの変更により、Goコンパイラはインポート時の定義競合エラーにおいて、より具体的で診断に役立つ情報を提供するようになり、特にgo
コマンドを使用しないビルド環境でのデバッグが容易になります。
関連リンク
参考にした情報源リンク
- コミットメッセージとコードの差分 (
./commit_data/17510.txt
) - Go言語のコンパイラ(
cmd/gc
)の一般的な知識 - Go言語のパッケージインポートメカニズムに関する一般的な知識
- (注: 関連するGo Issue #5888については、Web検索では公開情報を見つけることができませんでした。)