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

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

このコミットは、Go言語の初期のコンパイラ(gc)におけるエクスポート(公開)ルールの処理を改善するものです。具体的には、src/cmd/gc/export.csrc/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言語には存在しませんが、このコミットが作成された時点ではまだ使用されていました。

変更の背景には、以下の問題意識があったと考えられます。

  1. 警告メッセージの不親切さ: シンボルがエクスポートルールに違反している場合(例: 大文字で始まるべきなのに小文字で始まる、またはその逆)、コンパイラは警告を発しますが、その警告メッセージにはどのシンボルが問題なのかが明示されていませんでした。これにより、開発者は警告の原因を特定しにくかったと考えられます。
  2. exportキーワード使用時の挙動の不整合: exportキーワードが明示的に使用された場合と、名前の大文字・小文字によって自動的にエクスポートが決定される場合(autoexportロジック)との間で、コンパイラの挙動に微妙な不整合があった可能性があります。特に、exportキーワードが使われているにもかかわらず、名前が小文字で始まるシンボルがどのように扱われるべきか、という点に課題があったと推測されます。このコミットは、exportキーワードが使われた関数に対してもautoexportロジックを適用し、一貫した警告とエクスポート挙動を実現しようとしています。
  3. 関数のエクスポート処理の抜け: 関数宣言(xfndcl)がautoexportの対象から漏れているケースがあった可能性があり、これを修正することで、すべての関数が適切にエクスポートルールに則って処理されるようにしています。

これらの改善により、コンパイラの診断メッセージがより分かりやすくなり、Go言語のエクスポートルールがより堅牢かつ予測可能な形で適用されるようになりました。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびコンパイラに関する前提知識が必要です。

  1. Go言語のシンボル公開(エクスポート)ルール:

    • Go言語において、パッケージ内のシンボル(変数、関数、型、メソッドなど)がパッケージ外からアクセス可能(エクスポートされる)かどうかは、そのシンボルの名前が大文字で始まるかどうかによって決まります。
    • 名前が大文字で始まるシンボルはエクスポートされ、パッケージ外から参照可能です。
    • 名前が小文字で始まるシンボルはパッケージプライベートであり、そのシンボルが定義されているパッケージ内からのみアクセス可能です。
    • このルールは、Go言語の設計思想である「明示的なものは少なく、暗黙的なものは多く」を反映しており、アクセス修飾子(public, privateなど)を記述する必要がないため、コードの簡潔さに貢献しています。
  2. Go言語の初期のexportキーワード:

    • Go言語の非常に初期のバージョン(このコミットが作成された2009年頃)には、現在のGo言語には存在しないexportというキーワードがありました。これは、シンボルを明示的にエクスポートするために使用されていました。
    • 例: export func MyFunction() {}
    • このキーワードは、名前の大文字・小文字によるエクスポートルールが確立された後、冗長であると判断され、Go 1.0のリリース前に廃止されました。このコミットは、その過渡期におけるコンパイラの挙動を扱っています。
  3. 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コンパイラの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);

ここで、%SSym *s(シンボルへのポインタ)をフォーマットしてシンボル名を出力するための、コンパイラ内部のフォーマット指定子です。これにより、開発者はどのシンボルが警告の原因であるかを一目で特定できるようになり、デバッグの効率が向上します。

2. exportキーワード使用時の挙動の修正と一貫性 (src/cmd/gc/export.c)

このコミットの重要な変更点の一つは、exportキーワードが明示的に使用されたシンボル(特に、名前が小文字で始まるもの)の扱いに関するものです。

変更前は、dcladj == exportsymexportキーワードが使われていることを示す)かつ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 xfndclexportキーワード付きの関数宣言)のルールも変更されました。以前は直接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を呼び出し、その後dcladjnilに戻すという処理に変更されました。
      if($2 != N && $2->nname != N) {
          dcladj = exportsym;
          autoexport($2->nname->sym);
          dcladj = nil;
      }
      
    • この変更により、exportキーワードが使われた関数も、export.cで修正されたautoexportロジックを通過するようになります。これにより、明示的にexportされた関数であっても、名前が小文字で始まる場合には警告が出つつもエクスポートされるという、一貫した挙動が実現されます。dcladjを一時的に設定することで、autoexport関数に「これはexportキーワードによって明示的に指定されたシンボルである」というコンテキストを伝えています。

これらの変更は、Go言語の初期のコンパイラが、言語の設計思想(特にエクスポートルール)をより正確かつ堅牢に実装していく過程の一部を示しています。

関連リンク

参考にした情報源リンク