[インデックス 1489] ファイルの概要
このコミットは、Go言語の初期のコンパイラ(gc
)におけるエクスポート(公開)ルールの処理を改善するものです。具体的には、src/cmd/gc/export.c
と src/cmd/gc/go.y
の2つのファイルが変更されています。
src/cmd/gc/export.c
: Goコンパイラのシンボルエクスポートロジックを定義するC言語のソースファイルです。主に、シンボルがエクスポートされるべきか、パッケージプライベートであるべきかを決定し、関連する警告を生成する役割を担います。src/cmd/gc/go.y
: Go言語の文法を定義するYacc(Yet Another Compiler Compiler)の文法ファイルです。コンパイラの構文解析フェーズで使用され、言語構造(この場合は関数宣言)がどのように認識されるかを記述します。
これらの変更は、Go言語のシンボル公開に関する警告メッセージをより詳細にし、特にexport
キーワード(Go言語の初期に存在した、明示的にシンボルを公開するためのキーワード)が使用された場合の挙動を、より一貫性のあるものにすることを目的としています。
コミット
commit 605d0746c5fdedc35b62eb2f3d470cb56e5fcecd
Author: Russ Cox <rsc@golang.org>
Date: Thu Jan 15 16:43:51 2009 -0800
catch export on func.
print names in message.
R=ken
OCL=22891
CL=22891
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/605d0746c5fdedc35b62eb2f3d470cb56e5fcecd
元コミット内容
catch export on func.
print names in message.
R=ken
OCL=22891
CL=22891
変更の背景
このコミットは、Go言語の初期開発段階(2009年1月)に行われたもので、Go言語のシンボル公開(エクスポート)ルールに関するコンパイラの挙動を改善することを目的としています。
Go言語では、シンボル(変数、関数、型など)がパッケージ外に公開される(エクスポートされる)かどうかは、その名前が大文字で始まるかどうかによって決まります。しかし、Go言語の非常に初期のバージョンには、明示的にシンボルをエクスポートするためのexport
キーワードが存在していました。このキーワードは後に廃止され、現在のGo言語には存在しませんが、このコミットが作成された時点ではまだ使用されていました。
変更の背景には、以下の問題意識があったと考えられます。
- 警告メッセージの不親切さ: シンボルがエクスポートルールに違反している場合(例: 大文字で始まるべきなのに小文字で始まる、またはその逆)、コンパイラは警告を発しますが、その警告メッセージにはどのシンボルが問題なのかが明示されていませんでした。これにより、開発者は警告の原因を特定しにくかったと考えられます。
export
キーワード使用時の挙動の不整合:export
キーワードが明示的に使用された場合と、名前の大文字・小文字によって自動的にエクスポートが決定される場合(autoexport
ロジック)との間で、コンパイラの挙動に微妙な不整合があった可能性があります。特に、export
キーワードが使われているにもかかわらず、名前が小文字で始まるシンボルがどのように扱われるべきか、という点に課題があったと推測されます。このコミットは、export
キーワードが使われた関数に対してもautoexport
ロジックを適用し、一貫した警告とエクスポート挙動を実現しようとしています。- 関数のエクスポート処理の抜け: 関数宣言(
xfndcl
)がautoexport
の対象から漏れているケースがあった可能性があり、これを修正することで、すべての関数が適切にエクスポートルールに則って処理されるようにしています。
これらの改善により、コンパイラの診断メッセージがより分かりやすくなり、Go言語のエクスポートルールがより堅牢かつ予測可能な形で適用されるようになりました。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびコンパイラに関する前提知識が必要です。
-
Go言語のシンボル公開(エクスポート)ルール:
- Go言語において、パッケージ内のシンボル(変数、関数、型、メソッドなど)がパッケージ外からアクセス可能(エクスポートされる)かどうかは、そのシンボルの名前が大文字で始まるかどうかによって決まります。
- 名前が大文字で始まるシンボルはエクスポートされ、パッケージ外から参照可能です。
- 名前が小文字で始まるシンボルはパッケージプライベートであり、そのシンボルが定義されているパッケージ内からのみアクセス可能です。
- このルールは、Go言語の設計思想である「明示的なものは少なく、暗黙的なものは多く」を反映しており、アクセス修飾子(
public
,private
など)を記述する必要がないため、コードの簡潔さに貢献しています。
-
Go言語の初期の
export
キーワード:- Go言語の非常に初期のバージョン(このコミットが作成された2009年頃)には、現在のGo言語には存在しない
export
というキーワードがありました。これは、シンボルを明示的にエクスポートするために使用されていました。 - 例:
export func MyFunction() {}
- このキーワードは、名前の大文字・小文字によるエクスポートルールが確立された後、冗長であると判断され、Go 1.0のリリース前に廃止されました。このコミットは、その過渡期におけるコンパイラの挙動を扱っています。
- Go言語の非常に初期のバージョン(このコミットが作成された2009年頃)には、現在のGo言語には存在しない
-
Goコンパイラ(
gc
)の構造:- Go言語の公式コンパイラは、当初は
gc
(Go Compiler)と呼ばれていました。これは、C言語で書かれたコンパイラです。 src/cmd/gc/
: このディレクトリには、gc
コンパイラのソースコードが含まれています。export.c
: このファイルは、コンパイラのバックエンドの一部であり、シンボルのエクスポートに関するロジックを実装しています。具体的には、シンボル名に基づいてエクスポートの可否を判断し、必要に応じて警告を発する役割を担います。autoexport
関数などがここに定義されています。go.y
: このファイルは、Yacc(Yet Another Compiler Compiler)形式で書かれたGo言語の文法定義です。Yaccは、文法規則に基づいてパーサー(構文解析器)を生成するためのツールです。go.y
は、Go言語のソースコードを解析し、抽象構文木(AST)を構築する過程で使用されます。xfndcl
(関数宣言)のような文法規則が定義されています。Sym
構造体: コンパイラ内部でシンボル(変数、関数名など)を表すデータ構造です。シンボルの名前や型などの情報を含みます。dcladj
: 宣言調整(declaration adjustment)を意味するコンパイラ内部の変数で、現在の宣言がどのように扱われるべきか(例: エクスポートされるべきか)を示すフラグのような役割を果たします。exportsym
は、シンボルをエクスポート済みとしてマークする関数(または関数ポインタ)です。
- Go言語の公式コンパイラは、当初は
これらの知識を前提として、コミットの変更内容を詳細に見ていきます。
技術的詳細
このコミットは、Goコンパイラのgc
におけるシンボルエクスポートの処理を、特に以下の点で改善しています。
1. 警告メッセージの改善 (src/cmd/gc/export.c
)
以前のバージョンでは、エクスポートルールに違反するシンボルがあった場合、単に「uppercase missing export」(大文字が不足しているエクスポート)や「export missing uppercase」(エクスポートされているのに大文字ではない)といった一般的な警告メッセージが表示されていました。この変更により、警告メッセージに問題のシンボル名が追加されるようになりました。
- 変更前:
warn("uppercase missing export");
- 変更後:
warn("uppercase missing export: %S", s);
- 変更前:
warn("export missing uppercase");
- 変更後:
warn("export missing uppercase: %S", s);
ここで、%S
はSym *s
(シンボルへのポインタ)をフォーマットしてシンボル名を出力するための、コンパイラ内部のフォーマット指定子です。これにより、開発者はどのシンボルが警告の原因であるかを一目で特定できるようになり、デバッグの効率が向上します。
2. export
キーワード使用時の挙動の修正と一貫性 (src/cmd/gc/export.c
)
このコミットの重要な変更点の一つは、export
キーワードが明示的に使用されたシンボル(特に、名前が小文字で始まるもの)の扱いに関するものです。
変更前は、dcladj == exportsym
(export
キーワードが使われていることを示す)かつexportname(s->name)
が偽(名前が小文字で始まる)の場合、コンパイラは警告を発し、その後packagesym(s)
を呼び出してそのシンボルをパッケージプライベートとして扱っていました。これは、明示的にexport
と書かれているにもかかわらず、名前のルールに違反しているためにエクスポートされないという、直感に反する挙動でした。
変更後、このケースでは警告を発した後もexportsym(s)
を呼び出すようになりました。
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -73,12 +73,14 @@ autoexport(Sym *s)
return;
if(exportname(s->name)) {
if(dcladj != exportsym)
- warn("uppercase missing export");
+ warn("uppercase missing export: %S", s);
exportsym(s);
} else {
- if(dcladj == exportsym)
- warn("export missing uppercase");
- packagesym(s);
+ if(dcladj == exportsym) {
+ warn("export missing uppercase: %S", s);
+ exportsym(s); // ここが変更点
+ } else
+ packagesym(s);
}
}
この変更により、export
キーワードが使われたシンボルは、たとえ名前が小文字で始まっていても(警告は出るものの)エクスポートされるようになりました。これは、明示的なexport
の意図を尊重しつつ、Go言語の命名規則への準拠を促すという、より一貫性のある挙動です。
3. 関数宣言へのautoexport
の適用強化 (src/cmd/gc/go.y
)
go.y
ファイルでは、関数宣言(xfndcl
)の処理ロジックが変更されました。
3.1. 通常の関数宣言へのautoexport
の適用
xfndcl
(通常の関数宣言)のルールに、autoexport
の呼び出しが追加されました。
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -186,6 +186,8 @@ xdcl:
}
|\txfndcl
{
+\t\tif($1 != N && $1->nname != N && $1->type->thistuple == 0)\n+\t\t\tautoexport($1->nname->sym);\n \t\t$$ = N;\
}
|\tLEXPORT { dcladj = exportsym; stksize = initstksize; } common_dcl
この変更により、明示的なexport
キーワードなしで宣言された関数も、その名前(大文字・小文字)に基づいてautoexport
ロジックによって適切にエクスポートされるかどうかが判断されるようになりました。$1->type->thistuple == 0
は、それが通常の関数であり、メソッドではないことを確認するための条件です。
3.2. export
キーワード付き関数宣言の処理の一貫性
LEXPORT xfndcl
(export
キーワード付きの関数宣言)のルールも変更されました。以前は直接exportsym
を呼び出していましたが、autoexport
を介するように変更されました。
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -205,8 +207,11 @@ xdcl:
}
|\tLEXPORT xfndcl
{
-\t\tif($2 != N && $2->nname != N)\n-\t\t\texportsym($2->nname->sym);\n+\t\tif($2 != N && $2->nname != N) {\n+\t\t\tdcladj = exportsym;\n+\t\t\tautoexport($2->nname->sym);\n+\t\t\tdcladj = nil;\n+\t\t}\n \t\t$$ = N;\
}
|\tLPACKAGE { warn("package is gone"); } xfndcl
この変更により、export
キーワードが使われた関数も、autoexport
ロジックを通過するようになりました。これにより、export.c
で導入された「export
キーワードが使われているが名前が小文字のシンボルもエクスポートされるが警告が出る」という挙動が、関数に対しても一貫して適用されるようになります。dcladj
を一時的にexportsym
に設定し、autoexport
を呼び出した後にnil
に戻すことで、このコンテキストをautoexport
関数に伝えています。
これらの技術的詳細は、Go言語の初期のコンパイラが、言語仕様の進化と同時に、その実装も洗練されていった過程を示しています。
コアとなるコードの変更箇所
src/cmd/gc/export.c
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -73,12 +73,14 @@ autoexport(Sym *s)
return;
if(exportname(s->name)) {
if(dcladj != exportsym)
- warn("uppercase missing export");
+ warn("uppercase missing export: %S", s);
exportsym(s);
} else {
- if(dcladj == exportsym)
- warn("export missing uppercase");
- packagesym(s);
+ if(dcladj == exportsym) {
+ warn("export missing uppercase: %S", s);
+ exportsym(s);
+ } else
+ packagesym(s);
}
}
src/cmd/gc/go.y
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -186,6 +186,8 @@ xdcl:
}
|\txfndcl
{
+\t\tif($1 != N && $1->nname != N && $1->type->thistuple == 0)\n+\t\t\tautoexport($1->nname->sym);\n \t\t$$ = N;\
}
|\tLEXPORT { dcladj = exportsym; stksize = initstksize; } common_dcl
@@ -205,8 +207,11 @@ xdcl:
}
|\tLEXPORT xfndcl
{
-\t\tif($2 != N && $2->nname != N)\n-\t\t\texportsym($2->nname->sym);\n+\t\tif($2 != N && $2->nname != N) {\n+\t\t\tdcladj = exportsym;\n+\t\t\tautoexport($2->nname->sym);\n+\t\t\tdcladj = nil;\n+\t\t}\n \t\t$$ = N;\
}
|\tLPACKAGE { warn("package is gone"); } xfndcl
コアとなるコードの解説
src/cmd/gc/export.c
の変更点
このファイルでは、autoexport
関数内の警告メッセージが改善され、export
キーワードが使用された場合の挙動が修正されています。
-
警告メッセージの改善:
warn("uppercase missing export");
がwarn("uppercase missing export: %S", s);
に変更されました。warn("export missing uppercase");
がwarn("export missing uppercase: %S", s);
に変更されました。- これにより、コンパイラがエクスポートルールに関する警告を発する際に、どのシンボルが問題であるかを具体的に示すことができるようになり、開発者にとっての利便性が向上しました。
%S
は、Goコンパイラ内部でシンボル(Sym *s
)の名前を出力するためのフォーマット指定子です。
-
export
キーワード使用時の挙動修正:else
ブロック内のif(dcladj == exportsym)
の条件分岐が変更されました。- 変更前は、
export
キーワードが使われている(dcladj == exportsym
)にもかかわらず、名前が小文字で始まる(exportname(s->name)
が偽)シンボルに対して警告を発した後、packagesym(s)
を呼び出してそのシンボルをパッケージプライベートとして扱っていました。 - 変更後、このケースでも警告を発しますが、その後は
exportsym(s)
を呼び出すようになりました。これは、明示的にexport
と指定されたシンボルは、たとえ命名規則に違反していても(警告は出すが)エクスポートするという、より「意図を尊重する」挙動に変わったことを意味します。
src/cmd/gc/go.y
の変更点
このファイルでは、Go言語の文法定義が変更され、関数宣言がautoexport
ロジックによって適切に処理されるように修正されました。
-
通常の関数宣言(
xfndcl
)へのautoexport
の適用:xfndcl
の文法規則に対応するアクションブロック内に、以下のコードが追加されました。if($1 != N && $1->nname != N && $1->type->thistuple == 0) autoexport($1->nname->sym);
- これは、通常の関数宣言(
$1
は関数ノード、$1->nname
は関数名ノード、$1->type->thistuple == 0
はそれがメソッドではないことを意味します)に対して、autoexport
関数を呼び出すことを保証します。これにより、関数名の大文字・小文字に基づいて、その関数がエクスポートされるべきかどうかが自動的に判断されるようになります。
-
export
キーワード付き関数宣言(LEXPORT xfndcl
)の処理の一貫性:LEXPORT xfndcl
の文法規則に対応するアクションブロックが変更されました。- 変更前は、
export
キーワード付きの関数宣言に対して直接exportsym
を呼び出していました。 - 変更後、
dcladj
を一時的にexportsym
に設定し、autoexport
を呼び出し、その後dcladj
をnil
に戻すという処理に変更されました。if($2 != N && $2->nname != N) { dcladj = exportsym; autoexport($2->nname->sym); dcladj = nil; }
- この変更により、
export
キーワードが使われた関数も、export.c
で修正されたautoexport
ロジックを通過するようになります。これにより、明示的にexport
された関数であっても、名前が小文字で始まる場合には警告が出つつもエクスポートされるという、一貫した挙動が実現されます。dcladj
を一時的に設定することで、autoexport
関数に「これはexport
キーワードによって明示的に指定されたシンボルである」というコンテキストを伝えています。
これらの変更は、Go言語の初期のコンパイラが、言語の設計思想(特にエクスポートルール)をより正確かつ堅牢に実装していく過程の一部を示しています。
関連リンク
- Go言語の公式ドキュメント(現在のエクスポートルールについて):https://go.dev/doc/effective_go#names
- Go言語の初期の歴史に関する情報(
export
キーワードの廃止など):https://go.dev/doc/go1compat (Go 1の互換性に関するドキュメントで、初期の変更点に触れられている可能性があります) - Go言語のコンパイラソースコード(現在のバージョン):https://github.com/golang/go/tree/master/src/cmd/compile (現在のコンパイラは
compile
ディレクトリにあります)
参考にした情報源リンク
- コミット情報:
/home/orange/Project/comemo/commit_data/1489.txt
- GitHub上のコミットページ: https://github.com/golang/go/commit/605d0746c5fdedc35b62eb2f3d470cb56e5fcecd
- Go言語の公式ドキュメント(Effective Go - Names): https://go.dev/doc/effective_go#names
- Go言語の初期の歴史に関する一般的な知識(
export
キーワードの存在と廃止について) - Yaccおよびコンパイラ設計に関する一般的な知識