[インデックス 10538] ファイルの概要
このコミットは、Goコンパイラ(gc
)の内部における型宣言処理の変更に関するものです。具体的には、typedcl2
という関数がsrc/cmd/gc/dcl.c
から削除され、その機能がsrc/cmd/gc/export.c
内のimporttype
関数に統合されました。また、src/cmd/gc/go.h
からtypedcl2
の関数宣言が削除されています。
コミット
commit 882368939c3dc0e5a938bf9ca9f203391d88ffe9
Author: Luuk van Dijk <lvd@golang.org>
Date: Tue Nov 29 13:34:08 2011 +0100
gc: move typedcl2 into export.c
R=rsc
CC=golang-dev
https://golang.org/cl/5447043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/882368939c3dc0e5a938bf9ca9f203391d88ffe9
元コミット内容
gc: move typedcl2 into export.c
変更の背景
このコミットは、Goコンパイラ(gc
)の初期開発段階におけるコード整理と機能の適切な配置を目的としています。typedcl2
関数は、主にインポート処理中に型を宣言する役割を担っていました。特に、unsafe.Pointer
のような特殊な型の扱いが含まれていました。
元のdcl.c
ファイルは「宣言(declaration)」に関連するコードを扱う場所でしたが、typedcl2
の機能がインポートされた型に特化しているため、型のエクスポート/インポートを扱うexport.c
に移動することがより論理的であると判断されたと考えられます。これにより、コンパイラのコードベースのモジュール性が向上し、関連する機能が同じファイルに集約されることで、将来的なメンテナンスや理解が容易になります。
特に、unsafe.Pointer
の特殊な処理がtypedcl2
内に存在していたことから、インポート時に型の一貫性を保証しつつ、特定の組み込み型に対する特別な扱いを適切に行うための構造改善が意図されています。
前提知識の解説
このコミットを理解するためには、Goコンパイラ(gc
)の基本的な構造と、型システム、特にインポートメカニズムに関する知識が必要です。
-
Goコンパイラ (
gc
):gc
はGo言語の公式コンパイラであり、Goソースコードを機械語に変換します。- コンパイラは通常、字句解析、構文解析、意味解析、中間コード生成、最適化、コード生成といった複数のフェーズで構成されます。
- このコミットが関連するのは、主に意味解析(型チェック、宣言処理)と、パッケージのインポート/エクスポートに関連する部分です。
-
型システムと型宣言:
- Goは静的型付け言語であり、すべての変数や式には型があります。
- 型宣言は、新しい型を定義したり、既存の型に別名を付けたりするプロセスです。
- コンパイラは、プログラム内で使用されるすべての型が正しく定義され、一貫性があることを保証する必要があります。
-
パッケージのインポート/エクスポート:
- Goのプログラムはパッケージに分割され、他のパッケージの公開された型、関数、変数などをインポートして利用できます。
- コンパイラは、インポートされたパッケージの型情報を読み込み、現在のコンパイルユニットの型システムに統合する必要があります。このプロセスでは、インポートされた型と現在のコンパイルユニットで定義されている型との間に矛盾がないかを確認します。
-
unsafe.Pointer
:unsafe.Pointer
はGoのunsafe
パッケージで提供される特殊な型です。これは、任意の型のポインタを保持できる汎用ポインタであり、Goの型システムをバイパスしてメモリを直接操作することを可能にします。unsafe.Pointer
は非常に強力ですが、誤用するとメモリ安全性を損なう可能性があるため、Goの通常の型システムとは異なる特別な扱いがコンパイラ内部で必要とされます。このコミットのコードにも、unsafe.Pointer
に対する特別な処理が明示的に記述されています。
-
コンパイラ内部のデータ構造:
Node
: 抽象構文木(AST)のノードを表すデータ構造。プログラムの構文要素(変数、型、式など)を表現します。Type
: 型情報を表すデータ構造。型の種類(整数、文字列、構造体、ポインタなど)、サイズ、フィールド情報などを保持します。Sym
: シンボル(識別子)を表すデータ構造。変数名、関数名、型名などを管理し、それらに関連付けられたNode
やType
へのポインタを持ちます。TFORW
: 型が前方参照(forward declaration)されている状態を示す型フラグ。まだ完全な定義が利用できないが、後で解決されることを示すために使用されます。TUNSAFEPTR
:unsafe.Pointer
型を表す内部定数。incannedimport
: コンパイラが「缶詰にされた(canned)」インポート、つまりコンパイラ自体に組み込まれた標準ライブラリのインポートを処理しているかどうかを示すフラグ。
技術的詳細
このコミットの核心は、typedcl2
関数のロジックがimporttype
関数内に移動されたことです。
typedcl2
の役割(移動前):
typedcl2
は、インポート処理中に型宣言を処理するための関数でした。その主な機能は以下の通りです。
-
unsafe.Pointer
の特別扱い:- もしインポート中のパッケージが
unsafe
であり、宣言されているシンボルがPointer
であれば、その型をコンパイラ内部のTUNSAFEPTR
(unsafe.Pointer
を表す型)に強制的に設定します。これは、Goコード内でunsafe.Pointer
を直接定義する方法がないため、コンパイラが特別に供給する必要があるためです。
- もしインポート中のパッケージが
-
前方参照型 (
TFORW
) の解決:pt->etype == TFORW
の場合、つまりpt
が前方参照型である場合、その型定義をt
で上書きします。これは、循環参照などによって型が先に参照され、後で定義されるケースに対応するためのものです。copytype(pt->nod, t)
:pt
のノード(AST上の表現)にt
の型情報をコピーします。declare(n, PEXTERN)
: ノードn
を外部リンケージを持つものとして宣言します。checkwidth(pt)
: 型のサイズやアライメントを計算し、検証します。
-
型の一貫性チェック:
pt->etype != TFORW
の場合、つまりpt
が既に定義されている型である場合、インポートされた型t
が既存の型pt->orig
と一致するかどうかをeqtype
で比較します。- もし一致しない場合、
yyerror
(コンパイラエラー報告関数)を使用して「インポート中に型%Sの定義が不整合です」というエラーを出力します。これは、異なるパッケージ間で同じ名前の型が異なる定義を持っている場合に発生する可能性があります。
importtype
の役割(移動後):
importtype
関数は、パッケージの型をインポートする際に呼び出される関数です。このコミットにより、typedcl2
のすべてのロジックがimporttype
の内部に組み込まれました。
変更後のimporttype
は、pt
(既存の型)とt
(インポートされる型)が有効な場合に、上記のtypedcl2
のロジックをそのまま実行します。これにより、型インポートの処理がexport.c
ファイル内で完結し、コードの凝集度が高まりました。
この変更は、コンパイラの型システムがインポートされた型をどのように処理し、特にunsafe.Pointer
のような特殊な型や前方参照型をどのように解決するかを示す良い例です。コンパイラは、異なるソースから来る型情報の一貫性を保ちつつ、言語のセマンティクス(この場合はunsafe.Pointer
の特殊性)を正確に反映させる必要があります。
コアとなるコードの変更箇所
src/cmd/gc/dcl.c
(削除)
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -673,41 +673,6 @@ typedcl1(Node *n, Node *t, int local)
return nod(ODCLTYPE, n, N);
}
-/*
- * typedcl1 but during imports
- */
-void
-typedcl2(Type *pt, Type *t)
-{
- Node *n;
-
- // override declaration in unsafe.go for Pointer.
- // there is no way in Go code to define unsafe.Pointer
- // so we have to supply it.
- if(incannedimport &&
- strcmp(importpkg->name, "unsafe") == 0 &&
- strcmp(pt->nod->sym->name, "Pointer") == 0) {
- t = types[TUNSAFEPTR];
- }
-
- if(pt->etype == TFORW)
- goto ok;
- if(!eqtype(pt->orig, t))
- yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt->orig, t);
- return;
-
-ok:
- n = pt->nod;
- copytype(pt->nod, t);
- // unzero nod
- pt->nod = n;
-
- pt->sym->lastlineno = parserline();
- declare(n, PEXTERN);
-
- checkwidth(pt);
-}
-
/*
* structs, functions, and methods.
* they don't belong here, but where do they belong?
src/cmd/gc/export.c
(追加・変更)
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -349,8 +349,31 @@ importvar(Sym *s, Type *t, int ctxt)
void
importtype(Type *pt, Type *t)
{
-\tif(pt != T && t != T)\n-\t\ttypedcl2(pt, t);\n+\tNode *n;\n+\n+\tif(pt != T && t != T) {\n+\t\t// override declaration in unsafe.go for Pointer.\n+\t\t// there is no way in Go code to define unsafe.Pointer\n+\t\t// so we have to supply it.\n+\t\tif(incannedimport &&\n+\t\t strcmp(importpkg->name, "unsafe") == 0 &&\n+\t\t strcmp(pt->nod->sym->name, "Pointer") == 0) {\n+\t\t\tt = types[TUNSAFEPTR];\n+\t\t}\n+\n+\t\tif(pt->etype == TFORW) {\n+\t\t\tn = pt->nod;\n+\t\t\tcopytype(pt->nod, t);\n+\t\t\t// unzero nod\n+\t\t\tpt->nod = n;\n+\t\t\t\n+\t\t\tpt->sym->lastlineno = parserline();\n+\t\t\tdeclare(n, PEXTERN);\n+\t\t\t\n+\t\t\tcheckwidth(pt);\n+\t\t} else if(!eqtype(pt->orig, t))\n+\t\t\tyyerror("inconsistent definition for type %S during import\\n\\t%lT\\n\\t%lT", pt->sym, pt->orig, t);\n+\t}\n \n \tif(debug['E'])\n \t\tprint("import type %T %lT\\n", pt, t);
src/cmd/gc/go.h
(削除)
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -964,7 +964,6 @@ Type* tointerface(NodeList *l);\n Type* tostruct(NodeList *l);\n Node* typedcl0(Sym *s);\n Node* typedcl1(Node *n, Node *t, int local);\n-void typedcl2(Type *pt, Type *t);\n Node* typenod(Type *t);\n NodeList* variter(NodeList *vl, Node *t, NodeList *el);\n
コアとなるコードの解説
このコミットの主要な変更は、typedcl2
関数の本体がsrc/cmd/gc/dcl.c
からsrc/cmd/gc/export.c
内のimporttype
関数にそのまま移動されたことです。
src/cmd/gc/dcl.c
からの削除:
typedcl2
関数は、typedcl1
(通常の型宣言)とは異なり、「インポート中」に特化した型宣言処理を行うためのものでした。この関数がdcl.c
から完全に削除されたことで、dcl.c
はより一般的な宣言処理に集中するようになりました。
src/cmd/gc/export.c
への統合:
export.c
は、コンパイラがパッケージの型、変数、関数などをエクスポートおよびインポートする際のロジックを扱うファイルです。
importtype
関数は、インポートされた型t
を、既存の型pt
(通常は前方参照型か、既にシンボルテーブルに存在する型)と関連付ける役割を担っています。
変更前は、importtype
が単にtypedcl2(pt, t)
を呼び出すだけでした。
変更後は、typedcl2
の内部ロジック(unsafe.Pointer
の特別処理、前方参照型の解決、型の一貫性チェック)がimporttype
のif(pt != T && t != T)
ブロック内に直接インライン化されました。
この統合により、型インポートの際に必要なすべての処理がimporttype
関数内で完結するようになり、コードの可読性と保守性が向上しました。特に、インポートされた型が前方参照型である場合の解決ロジックや、unsafe.Pointer
のような特殊な型の処理が、インポート処理の文脈で直接扱われるようになった点が重要です。
src/cmd/gc/go.h
からの宣言削除:
typedcl2
関数が外部から呼び出される必要がなくなったため、その関数プロトタイプ(宣言)がヘッダーファイルgo.h
から削除されました。これは、コンパイラの内部APIを整理し、不要な公開を避けるための標準的なプラクティスです。
全体として、このコミットはGoコンパイラの初期段階における内部構造の洗練と、機能の論理的なグループ化を反映しています。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- Goコンパイラのソースコード(現在のバージョン): https://github.com/golang/go/tree/master/src/cmd/compile (注: このコミットは
src/cmd/gc
時代のものですが、現在のコンパイラはsrc/cmd/compile
にあります) unsafe
パッケージのドキュメント: https://pkg.go.dev/unsafe
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語のコードレビューシステム (Gerrit): https://golang.org/cl/5447043 (コミットメッセージに記載されているCLリンク)
- Goコンパイラの歴史と進化に関する一般的な情報 (Goブログ、カンファレンストークなど)
- コンパイラ設計に関する一般的な知識 (型システム、AST、シンボルテーブルなど)
unsafe.Pointer
に関するGoの公式ドキュメントや解説記事