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

[インデックス 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がインポート競合エラーを報告する際に、どのパッケージから不整合な定義が来たのかを示す情報を追加することです。

  1. Sym構造体へのimportdefフィールドの追加: src/cmd/gc/go.hファイルにおいて、Sym構造体にPkg* importdef;という新しいフィールドが追加されました。このフィールドは、そのシンボルが最初にインポートされた際に、どのパッケージで定義が見つかったかを指すPkg構造体へのポインタを保持します。これにより、シンボルの定義元パッケージを追跡できるようになります。

  2. importvarおよびimporttype関数でのimportdefの更新: src/cmd/gc/export.cファイル内のimportvar関数(変数のインポート処理)とimporttype関数(型のインポート処理)において、シンボルが正常にインポートされた際に、そのシンボルのimportdefフィールドに現在のimportpkg(現在処理中のインポート元パッケージ)を代入する処理が追加されました。これにより、シンボルが最初に定義されたパッケージの情報を記録します。

  3. エラーメッセージの改善: 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フォーマット指定子は、yyerrorPkg構造体のパスを表示するための新しい機能として内部的に追加されたものと推測されます。これにより、開発者はエラーメッセージを見ただけで、どの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検索では公開情報を見つけることができませんでした。)