[インデックス 1525] ファイルの概要
このコミットは、Go言語のコンパイラ(gc
)とランタイムにおける重要な変更を導入しています。主な目的は、Go言語の初期設計にあったexport
キーワードの削除と、init
関数の命名規則の変更です。これにより、Go言語の可視性ルールが簡素化され、init
関数の内部的な管理が改善されました。
コミット
commit 0183baaf449338f54727814d079c0254c18226f9
Author: Russ Cox <rsc@golang.org>
Date: Tue Jan 20 14:40:00 2009 -0800
* delete export
* rename init functions
R=ken
OCL=23122
CL=23126
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0183baaf449338f54727814d079c0254c18226f9
元コミット内容
* delete export
* rename init functions
R=ken
OCL=23122
CL=23126
変更の背景
このコミットは、Go言語の初期開発段階における重要な設計変更を反映しています。
-
export
キーワードの削除: Go言語の初期の設計では、C++やJavaのような言語に見られるpublic
やprivate
に相当する可視性制御のためにexport
キーワードが検討されていました。しかし、Go言語の設計者たちは、識別子(変数名、関数名など)の先頭文字が大文字であるか小文字であるかによって、その識別子がエクスポートされる(パッケージ外から参照可能になる)かどうかが決まるという、よりシンプルで慣用的なルールを採用することを決定しました。この変更により、冗長なキーワードが不要になり、コードの記述がより簡潔になりました。このコミットは、その設計変更をコンパイラの実装に反映したものです。 -
init
関数の命名規則の変更: Go言語のinit
関数は、パッケージが初期化される際に自動的に実行される特殊な関数です。複数のファイルにinit
関数が存在する場合や、パッケージ内で複数のinit
関数が定義される場合、それらの関数がどのように管理され、呼び出されるかが問題となります。このコミット以前は、init
関数はpkg.<file>_init
のような形式で内部的に名前が変更されていました。しかし、この命名規則では、意図しない名前の衝突や、外部から誤って呼び出される可能性がありました。このコミットでは、init
関数の内部名をpkg.init·filename
のような、よりユニークで、かつ外部から直接呼び出しにくい形式に変更することで、これらの問題を解決しようとしています。特に、·
(中点)文字を使用することで、通常のGoの識別子としては無効な名前となり、外部からの参照を効果的に防いでいます。
前提知識の解説
Go言語の可視性ルール(エクスポートルール)
Go言語では、識別子(変数、関数、型、メソッドなど)の可視性は、その識別子の名前の先頭文字によって決定されます。
- 大文字で始まる識別子: パッケージ外から参照可能です(エクスポートされます)。これは他の言語の
public
に相当します。 - 小文字で始まる識別子: その識別子が定義されているパッケージ内でのみ参照可能です(エクスポートされません)。これは他の言語の
private
やinternal
に相当します。
このシンプルかつ強力なルールは、Go言語の設計哲学である「明示的であることよりも、慣習に従うこと」を体現しています。
Go言語のinit
関数
Go言語のinit
関数は、各パッケージに複数定義できる特殊な関数です。
- 自動実行:
init
関数は、main
関数が実行される前に、そのパッケージがインポートされた際に自動的に実行されます。 - 引数なし、戻り値なし:
init
関数は引数を取らず、戻り値も持ちません。 - 複数定義可能: 1つのパッケージ内に複数の
init
関数を定義できます(異なるファイルに定義することも、同じファイルに複数定義することも可能です)。 - 実行順序: 同じパッケージ内の複数の
init
関数は、ファイル名の辞書順で実行され、各ファイル内のinit
関数は定義された順に実行されます。インポートされたパッケージのinit
関数は、そのパッケージの定数や変数の初期化が完了した後、かつそのパッケージを利用する側のパッケージのinit
関数が実行される前に実行されます。
init
関数は、プログラムの起動時に必要な初期設定(データベース接続、設定ファイルの読み込み、外部ライブラリの初期化など)を行うためによく使用されます。
技術的詳細
このコミットは、Goコンパイラ(gc
)の内部実装に深く関わる変更を含んでいます。
-
export
キーワードの削除:src/cmd/gc/go.y
: これはGoコンパイラのパーサーの定義ファイル(Yacc/Bison形式)です。export
キーワードに関連する文法規則(oexport
,LEXPORT
,export_list_r
など)が完全に削除されています。これにより、コンパイラはexport
キーワードを構文として認識しなくなります。src/cmd/gc/export.c
: このファイルは、シンボルのエクスポートに関するロジックを扱っていました。export
キーワードの削除に伴い、exportsym
やpackagesym
といった関数が、識別子の先頭文字に基づく新しい可視性ルールに合わせて修正されています。特に、importsym
関数の引数からexport
フラグが削除され、シンボルのエクスポート状態はexportname
関数(識別子の先頭文字をチェックする)によって決定されるようになりました。また、dumpexportconst
,dumpexportvar
,dumpexporttype
といった関数から、export
キーワードを出力するロジックが削除されています。src/cmd/gc/go.h
:dcladj
というグローバル変数が削除されています。これは、宣言がエクスポートされるかパッケージスコープであるかを示すフラグとして使用されていましたが、export
キーワードの削除により不要になりました。src/cmd/gc/sysimport.c
とsrc/cmd/gc/unsafeimport.c
: これらのファイルは、Goの組み込み関数やunsafe
パッケージの定義をコンパイラに提供するものです。以前はexport
キーワードがこれらの定義に含まれていましたが、このコミットで削除され、Goの新しい可視性ルールに統一されました。
-
init
関数の命名規則の変更:src/cmd/gc/dcl.c
: このファイルは宣言の処理を担当します。renameinit
関数が変更され、init
関数の内部名がinit_%s
(%s
はファイル名)からinit·%s
に変更されました。また、fninit
関数内で生成される初期化コードのコメントが更新され、init_<file>_done
がinitdone·<file>
に、init_<file>_function
がInit·<file>
に変更されています。特に、main
パッケージのトップレベルのinit
関数は、init_function
から単にinit
という名前に変更され、ランタイムからの呼び出しを簡素化しています。src/runtime/rt0_amd64.s
: これはAMD64アーキテクチャ向けのランタイムの初期化コードです。mainstart
ルーチン内でmain·init_function(SB)
を呼び出していた箇所がmain·init(SB)
に変更されています。これは、main
パッケージのトップレベルのinit
関数の内部名が変更されたことに対応しています。
これらの変更は、Go言語のコンパイラとランタイムの内部動作に深く関わるものであり、Go言語の設計思想である「シンプルさ」と「明瞭さ」を追求した結果と言えます。
コアとなるコードの変更箇所
-
src/cmd/gc/dcl.c
:renameinit
関数:init
関数の内部名をinit_%s
からinit·%s
に変更。fninit
関数: 初期化コード生成ロジック内の内部変数名(init_done
、init_function
)をinitdone·%s
、Init·%s
に変更。main
パッケージのトップレベルinit
関数の名前をinit_function
からinit
に変更。fninit
関数内のループ条件: インポートされたinit
関数を検索する際の条件を、s->name[0] != 'i'
やstrstr(s->name, "init_") == nil
などから、s->name[0] != 'I' || strncmp(s->name, "Init·", 6) != 0
に変更。これは、新しい命名規則Init·filename
に対応するため。
-
src/cmd/gc/export.c
:autoexport
関数:dcladj
の使用を削除し、exportname
関数に基づいて直接exportsym
またはpackagesym
を呼び出すように変更。警告メッセージも削除。dumpexportconst
,dumpexportvar
,dumpexporttype
関数:export
キーワードの出力ロジックを削除。importsym
関数:export
引数を削除し、exportname(ss->sym->name)
の結果に基づいてs->export
を設定するように変更。importconst
,importvar
,importtype
関数:export
引数を削除し、exportname
関数によるチェックを直接行うように変更。
-
src/cmd/gc/go.h
:dcladj
グローバル変数の削除。importconst
,importtype
,importvar
関数のプロトタイプからint
型のexport
引数を削除。
-
src/cmd/gc/go.y
:oexport
型定義の削除。LEXPORT
およびLPACKAGE
に関連する文法規則(xdcl
内のLEXPORT
ブロック、export_list_r
、export
、oexport
など)を完全に削除。hidden_import
ルールにおいて、oexport
が削除されたことに伴い、importvar
,importconst
,importtype
関数の呼び出しから対応する引数を削除。メソッドのインポートに関するコメントも修正。
-
src/cmd/gc/sysimport.c
:sysimport
文字列内のすべてのexport
キーワードを削除。
-
src/cmd/gc/unsafeimport.c
:unsafeimport
文字列内のexport
キーワードを削除。
-
src/runtime/rt0_amd64.s
:mainstart
ルーチン内のCALL main·init_function(SB)
をCALL main·init(SB)
に変更。
コアとなるコードの解説
このコミットの核心は、Go言語の可視性メカニズムと初期化メカニズムの根本的な変更にあります。
export
キーワードの削除
以前のGo言語の設計では、C++やJavaのように明示的なexport
キーワードを使用して、識別子をパッケージ外に公開するかどうかを制御していました。しかし、このコミットにより、そのアプローチは破棄され、Go言語独自の「識別子の先頭文字が大文字か小文字か」というルールに完全に移行しました。
go.y
の変更: パーサーからexport
キーワードの文法が削除されたことで、コンパイラはもはやexport
という単語を特別な意味を持つキーワードとして認識しません。これにより、Goのソースコードからexport
キーワードを削除しても、コンパイルエラーが発生しなくなります。export.c
の変更:export.c
は、シンボルのエクスポートに関するコンパイラの内部ロジックを管理していました。このコミットでは、export
キーワードの有無に依存していたロジックが、exportname
関数(識別子の先頭文字をチェックする)の呼び出しに置き換えられました。例えば、importsym
関数は、インポートされるシンボルがエクスポートされるべきかどうかを、もはや引数で受け取るのではなく、シンボル名自体から判断するようになりました。これにより、コンパイラはGoの新しい可視性ルールに完全に準拠するようになりました。sysimport.c
とunsafeimport.c
の変更: これらのファイルは、Goの標準ライブラリや組み込み機能の定義をコンパイラに提供します。これらの定義からexport
キーワードが削除されたことは、Go言語全体で新しい可視性ルールが徹底されたことを意味します。
この変更は、Go言語の設計哲学である「シンプルさ」と「明瞭さ」を追求した結果です。キーワードを減らし、慣習的な命名規則に依存することで、コードの読みやすさと記述のしやすさが向上しました。
init
関数の命名規則の変更
Goのinit
関数は、パッケージの初期化において重要な役割を果たします。このコミットは、これらの関数の内部的な命名規則を変更することで、その管理を改善しています。
-
dcl.c
の変更:renameinit
関数は、ユーザーが定義したinit
関数をコンパイラが内部的に扱うためのユニークな名前に変更する役割を担っています。以前はinit_ファイル名
という形式でしたが、このコミットによりinit·ファイル名
という形式に変更されました。ここで注目すべきは、·
(中点)文字の使用です。この文字はGoの識別子としては無効であるため、この名前を持つ関数はGoのコードから直接呼び出すことができません。これにより、init
関数が意図せず外部から呼び出されることを防ぎ、その特殊な性質をより明確にしています。fninit
関数は、パッケージの初期化処理をまとめた内部的な関数を生成します。この関数内で使用される内部的なフラグ変数(init_done
)や、初期化処理全体をラップする関数(init_function
)の名前も、同様にinitdone·ファイル名
やInit·ファイル名
に変更されました。特に、main
パッケージのトップレベルの初期化関数は、ランタイムから直接呼び出されるため、init_function
から単にinit
という簡潔な名前に変更されました。fninit
関数内のインポートされたinit
関数を検索するロジックも、新しい命名規則に合わせて更新されました。これにより、コンパイラは他のパッケージからインポートされたinit
関数を正しく識別し、初期化シーケンスに含めることができます。
-
rt0_amd64.s
の変更:rt0_amd64.s
は、Goプログラムが起動する際に最初に実行されるアセンブリコードです。このコードは、main
パッケージの初期化関数を呼び出す責任があります。main·init_function(SB)
からmain·init(SB)
への変更は、main
パッケージのトップレベルの初期化関数の内部名が変更されたことに直接対応しています。これにより、ランタイムは新しい命名規則に従って初期化関数を正しく見つけ、実行できるようになります。
これらの変更は、init
関数の内部的な管理をより堅牢にし、名前の衝突を防ぎ、その特殊な性質を強化することを目的としています。
関連リンク
- Go言語の初期の設計に関する議論やメーリングリストのアーカイブは、Goプロジェクトの公式リポジトリや関連するメーリングリストで確認できる可能性があります。
- Go言語の
init
関数に関する公式ドキュメントやブログ記事は、Goの公式サイトで参照できます。
参考にした情報源リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語のブログ: https://go.dev/blog/
- Go言語の初期の設計に関するメーリングリストアーカイブ (例: golang-nuts): https://groups.google.com/g/golang-nuts
- Go言語のソースコードリポジトリ: https://github.com/golang/go
- Go言語の
init
関数に関する解説記事 (例: A Tour of Go, Effective Goなど) - Go言語の可視性ルールに関する解説記事 (例: A Tour of Go, Effective Goなど)
- Go言語のコンパイラ(gc)の内部構造に関する資料 (より専門的な内容)