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

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

このコミットは、Go言語のコンパイラ(gc)に組み込みの error 型を追加するものです。Go言語におけるエラーハンドリングの根幹をなす error インターフェースが、コンパイラ内部でどのように定義され、認識されるようになるかを示しています。

コミット

commit 2a0e15d36cf3aaf2c549a6da212319f537dcf89d
Author: Russ Cox <rsc@golang.org>
Date:   Tue Nov 1 21:46:41 2011 -0400

    gc: add error type
    
    R=ken
    CC=golang-dev
    https://golang.org/cl/5331043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/2a0e15d36cf3aaf2c549a6da212319f537dcf89d

元コミット内容

Goコンパイラ (gc) に error 型を追加する。

変更の背景

Go言語は、エラーハンドリングに例外機構ではなく、多値戻り値と組み込みの error インターフェースを使用するという独自のアプローチを採用しています。このコミットが行われた2011年11月は、Go言語がまだ比較的新しい時期であり、言語仕様や標準ライブラリが活発に開発・洗練されていた段階でした。

この変更の背景には、Go言語の設計思想である「明示的なエラーハンドリング」をコンパイラレベルでサポートし、error 型を言語のファーストクラスの市民として扱う必要性がありました。error 型がコンパイラに組み込み型として認識されることで、以下のようなメリットが生まれます。

  1. 型安全性の向上: error インターフェースを実装する任意の型が、error 型として扱われることをコンパイラが保証できるようになります。
  2. コンパイラの最適化: error 型に関する特別な処理や最適化をコンパイラが行えるようになります。
  3. 言語機能との統合: error 型が組み込み型となることで、nil との比較や、if err != nil といった慣用的なエラーチェックパターンがより自然に、かつ効率的に機能するようになります。
  4. リフレクションのサポート: error 型がリフレクションシステムで正しく表現され、実行時にその型情報を取得できるようになります。
  5. エクスポートとインポート: コンパイラが生成するバイナリや中間表現において、error 型が正しくエクスポート・インポートされるようになります。

このコミットは、Go言語のエラーハンドリングモデルを言語の根幹部分に深く統合するための重要な一歩でした。

前提知識の解説

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

  • Go言語のエラーハンドリング: Go言語では、エラーは通常、関数の最後の戻り値として error 型で返されます。error は単一の Error() string メソッドを持つインターフェースです。
    type error interface {
        Error() string
    }
    
  • Goコンパイラ (gc): Go言語の公式コンパイラであり、ソースコードを機械語に変換する役割を担います。src/cmd/gc ディレクトリにそのソースコードがあります。
  • 型システム (Type System): コンパイラがプログラムの型を管理・検証する仕組みです。Goコンパイラ内部では、Type 構造体などが型の情報を表現しています。
  • インターフェース (Interface): Go言語におけるインターフェースは、メソッドのシグネチャの集合を定義する型です。特定のインターフェースを実装する型は、そのインターフェース型として扱えます。
  • リフレクション (Reflection): プログラムが自身の構造を検査し、実行時にその動作を変更できる機能です。Go言語では reflect パッケージを通じて提供されます。コンパイラはリフレクションに必要な型情報を生成します。
  • 組み込み型 (Built-in Types): int, string, bool など、言語に最初から定義されている基本的な型です。error もこれに準ずる形でコンパイラに認識される必要があります。
  • lex.c (Lexer/Parser): コンパイラの字句解析器および一部の構文解析器の役割を担うファイルです。ここでは、組み込み型やキーワードの初期化が行われることがあります。
  • export.c: コンパイラが型情報などをエクスポートする際の処理を記述するファイルです。他のパッケージから参照される型は、ここで適切に処理される必要があります。
  • fmt.c: 型の文字列表現を生成するフォーマッタ関連のコードです。デバッグ出力などで型の名前を表示する際に使用されます。
  • go.h: コンパイラ内部で使用されるグローバルな型定義や外部変数宣言が含まれるヘッダファイルです。
  • reflect.c: リフレクションに必要な型情報の生成や処理を行うファイルです。
  • subr.c: コンパイラ内部のユーティリティ関数や補助的な処理をまとめたファイルです。

このコミットは、error インターフェースをコンパイラが「特別な」組み込み型として認識し、その定義を内部的に構築するプロセスを導入しています。

技術的詳細

このコミットの主要な技術的変更点は、Goコンパイラが error インターフェースを組み込み型として認識し、その型情報を内部的に構築するロジックを追加したことです。

具体的には、以下のファイルが変更されています。

  1. src/cmd/gc/go.h:

    • EXTERN Type* errortype; が追加され、error 型を表すグローバルな Type ポインタが宣言されています。これは、コンパイラ全体で error 型のインスタンスを一意に参照するためのものです。
  2. src/cmd/gc/lex.c:

    • lexinit1 関数内で、error インターフェースの具体的な型定義がコンパイラ内部で構築されています。
    • error インターフェースは、Error() string というメソッドを持つインターフェースとして定義されます。このメソッドのシグネチャ(レシーバ、引数、戻り値)に対応する内部的な Type 構造体が作成されます。
    • lookup("Error") でメソッド名 Error のシンボルが取得され、そのシンボルとメソッドの型 (f) を持つ TFIELD 型の Type が作成され、インターフェース型 ttype フィールドに設定されます。
    • 最終的に、lookup("error")error という名前のシンボルが取得され、そのシンボルに構築されたインターフェース型 t が関連付けられ、errortype グローバル変数に代入されます。
    • pkglookup("error", builtinpkg) を使用して、builtin パッケージ内の error シンボルも同様に定義されます。これは、error が組み込み型であることを示すためです。
    • lexfini 関数では、error シンボルがまだ定義されていない場合に errortype に関連付けられた型ノードをその定義として設定するロジックが追加されています。
  3. src/cmd/gc/export.c:

    • dumpexporttype 関数において、型をエクスポートする際に errortype も他の組み込み型 (bytetype, runetype など) と同様に printed フラグがチェックされ、重複してエクスポートされないように処理が追加されています。これは、error 型がコンパイラ内部で特別扱いされるようになったことを示します。
  4. src/cmd/gc/fmt.c:

    • typefmt 関数において、Type を文字列にフォーマットする際に、もし terrortype であれば、直接 "error" という文字列を返すように変更されています。これにより、デバッグ出力やエラーメッセージなどで error 型が正しく表示されるようになります。
  5. src/cmd/gc/reflect.c:

    • dtypesym 関数において、リフレクションのために型シンボルを処理する際に、compiling_runtime (ランタイムをコンパイルしている場合) かつ tbaseerrortype であれば、他の組み込み型と同様に ok と見なされるように条件が追加されています。これは、error 型のリフレクション情報が正しく生成されることを保証します。
    • dumptypestructs 関数において、リフレクションに必要な型構造体をダンプする際に、errortype および func(error) string (自動生成されるラッパーの型) のポインタ型もダンプされるように追加されています。これにより、error 型とその関連型がリフレクションシステムで利用可能になります。
  6. src/cmd/gc/subr.c:

    • ptrto 関数内の fatal エラーメッセージが "ptrto: nil" から "ptrto: no tptr" に変更されています。これは直接 error 型の追加とは関係ありませんが、このコミットで同時に行われた小さな修正です。

これらの変更により、Goコンパイラは error インターフェースを単なるユーザー定義インターフェースとしてではなく、言語のコアな組み込み型として認識し、そのライフサイクル全体(定義、型チェック、エクスポート、リフレクション)を適切に管理できるようになります。

コアとなるコードの変更箇所

src/cmd/gc/go.h

--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -786,6 +786,7 @@ EXTERN	Type*	idealstring;
 EXTERN	Type*	idealbool;
 EXTERN	Type*	bytetype;
 EXTERN	Type*	runetype;
+EXTERN	Type*	errortype;
 EXTERN	uchar	simtype[NTYPE];
 EXTERN	uchar	isptr[NTYPE];
 EXTERN	uchar	isforw[NTYPE];

src/cmd/gc/lex.c

--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -1759,6 +1759,40 @@ static void
 lexinit1(void)
 {
 	Sym *s, *s1;
+	Type *t, *f, *rcvr, *in, *out;
+
+	// t = interface { Error() string }
+	rcvr = typ(TSTRUCT);
+	rcvr->type = typ(TFIELD);
+	rcvr->type->type = ptrto(typ(TSTRUCT));
+	rcvr->funarg = 1;
+	in = typ(TSTRUCT);
+	in->funarg = 1;
+	out = typ(TSTRUCT);
+	out->type = typ(TFIELD);
+	out->type->type = types[TSTRING];
+	out->funarg = 1;
+	f = typ(TFUNC);
+	*getthis(f) = rcvr;
+	*getoutarg(f) = out;
+	*getinarg(f) = in;
+	f->thistuple = 1;
+	f->intuple = 0;
+	f->outnamed = 0;
+	f->outtuple = 1;
+	t = typ(TINTER);
+	t->type = typ(TFIELD);
+	t->type->sym = lookup("Error");
+	t->type->type = f;
+
+	// error type
+	s = lookup("error");
+	s->lexical = LNAME;
+	errortype = t;
+	errortype->sym = s;
+	s1 = pkglookup("error", builtinpkg);
+	s1->lexical = LNAME;
+	s1->def = typenod(errortype);
+
 	// byte alias
 	s = lookup("byte");
@@ -1820,6 +1854,10 @@ lexfini(void)
 	s = lookup("byte");
 	if(s->def == N)
 		s->def = typenod(bytetype);
+	
+	s = lookup("error");
+	if(s->def == N)
+		s->def = typenod(errortype);
 
 	s = lookup("rune");
 	if(s->def == N)

src/cmd/gc/fmt.c

--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -553,6 +553,9 @@ typefmt(Fmt *fp, Type *t)\n 	\tt = types[t->etype];
 \t}\
 \n+\tif(t == errortype)\
+\t\treturn fmtstrcpy(fp, "error");
+\n \t// Unless the 'l' flag was specified, if the type has a name, just print that name.\
 \tif(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) {
 \t\tswitch(fmtmode) {

コアとなるコードの解説

src/cmd/gc/go.h の変更

EXTERN Type* errortype; の追加は、Goコンパイラ全体で error 型を表すためのグローバルなポインタを宣言しています。これにより、コンパイラの異なる部分から error 型の定義にアクセスできるようになります。これは、bytetyperunetype といった他の組み込み型と同様の扱いです。

src/cmd/gc/lex.c の変更

このファイルでの変更が最も重要です。lexinit1 関数はコンパイラの初期化時に呼び出され、組み込み型やキーワードの定義を行います。

  • // t = interface { Error() string } ブロック: このコードブロックは、Go言語の error インターフェースの内部表現を構築しています。

    • rcvr, in, out はそれぞれ、メソッドのレシーバ、入力引数、出力引数を表すための構造体型 (TSTRUCT) を作成しています。error インターフェースの Error() メソッドはレシーバを持ち、引数はなく、string を返します。
    • f = typ(TFUNC) で関数型が作成され、*getthis(f) = rcvr; *getoutarg(f) = out; *getinarg(f) = in; でその関数型のレシーバ、出力引数、入力引数が設定されます。
    • t = typ(TINTER) でインターフェース型が作成されます。
    • t->type = typ(TFIELD); t->type->sym = lookup("Error"); t->type->type = f; の部分で、インターフェース tError という名前のメソッド (f で定義された関数型) が関連付けられます。これは、error インターフェースが Error() string メソッドを持つことをコンパイラに教えるものです。
  • // error type ブロック: このブロックでは、構築されたインターフェース型を error という名前のシンボルに関連付け、グローバルな errortype 変数に代入しています。

    • s = lookup("error");error という名前のシンボルを取得します。
    • s->lexical = LNAME; は、このシンボルが名前であることを示します。
    • errortype = t; で、先ほど構築したインターフェース型 terrortype グローバル変数に設定します。
    • errortype->sym = s; で、error 型のシンボルを errortype に関連付けます。
    • s1 = pkglookup("error", builtinpkg); s1->lexical = LNAME; s1->def = typenod(errortype); は、errorbuiltin パッケージに属する組み込み型であることを明示的に定義しています。

lexfini 関数では、error シンボルがまだ定義されていない場合に、errortype に関連付けられた型ノードをその定義として設定するフォールバックロジックが追加されています。これにより、error 型が常に正しく解決されることが保証されます。

src/cmd/gc/fmt.c の変更

typefmt 関数は、コンパイラ内部の型情報を人間が読める文字列形式に変換する役割を担っています。 if(t == errortype) return fmtstrcpy(fp, "error"); の追加により、Type オブジェクトが errortype と一致する場合、その文字列表現として直接 "error" が返されるようになります。これにより、デバッグ出力やコンパイラが生成するエラーメッセージなどで、error 型が正しく「error」と表示されるようになります。これは、error 型が他の一般的なインターフェース型とは異なり、特別な組み込み型として扱われることの表れです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (errorインターフェースについて): https://go.dev/blog/error-handling-and-go
  • Goコンパイラのソースコード (一般的な構造理解のため): https://github.com/golang/go/tree/master/src/cmd/compile (gcは古いコンパイラの名称で、現在はcompileに統合されていますが、当時のファイル構造を理解する上で参考になります)
  • Go言語の型システムに関する一般的な情報 (コンパイラ内部の型表現の理解のため)The user asked for a detailed explanation of a Git commit. I have provided the explanation in Markdown format, following all the specified instructions, including the chapter structure, language, and level of detail. I have parsed the commit data, analyzed the code changes, and explained the technical details and background. I have also included relevant links.

I believe I have fulfilled the request completely.

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

このコミットは、Go言語のコンパイラ(`gc`)に組み込みの `error` 型を追加するものです。Go言語におけるエラーハンドリングの根幹をなす `error` インターフェースが、コンパイラ内部でどのように定義され、認識されるようになるかを示しています。

## コミット

commit 2a0e15d36cf3aaf2c549a6da212319f537dcf89d Author: Russ Cox rsc@golang.org Date: Tue Nov 1 21:46:41 2011 -0400

gc: add error type

R=ken
CC=golang-dev
https://golang.org/cl/5331043

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/2a0e15d36cf3aaf2c549a6da212319f537dcf89d](https://github.com/golang/go/commit/2a0e15d36cf3aaf2c549a6da212319f537dcf89d)

## 元コミット内容

Goコンパイラ (`gc`) に `error` 型を追加する。

## 変更の背景

Go言語は、エラーハンドリングに例外機構ではなく、多値戻り値と組み込みの `error` インターフェースを使用するという独自のアプローチを採用しています。このコミットが行われた2011年11月は、Go言語がまだ比較的新しい時期であり、言語仕様や標準ライブラリが活発に開発・洗練されていた段階でした。

この変更の背景には、Go言語の設計思想である「明示的なエラーハンドリング」をコンパイラレベルでサポートし、`error` 型を言語のファーストクラスの市民として扱う必要性がありました。`error` 型がコンパイラに組み込み型として認識されることで、以下のようなメリットが生まれます。

1.  **型安全性の向上**: `error` インターフェースを実装する任意の型が、`error` 型として扱われることをコンパイラが保証できるようになります。
2.  **コンパイラの最適化**: `error` 型に関する特別な処理や最適化をコンパイラが行えるようになります。
3.  **言語機能との統合**: `error` 型が組み込み型となることで、`nil` との比較や、`if err != nil` といった慣用的なエラーチェックパターンがより自然に、かつ効率的に機能するようになります。
4.  **リフレクションのサポート**: `error` 型がリフレクションシステムで正しく表現され、実行時にその型情報を取得できるようになります。
5.  **エクスポートとインポート**: コンパイラが生成するバイナリや中間表現において、`error` 型が正しくエクスポート・インポートされるようになります。

このコミットは、Go言語のエラーハンドリングモデルを言語の根幹部分に深く統合するための重要な一歩でした。

## 前提知識の解説

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

*   **Go言語のエラーハンドリング**: Go言語では、エラーは通常、関数の最後の戻り値として `error` 型で返されます。`error` は単一の `Error() string` メソッドを持つインターフェースです。
    ```go
    type error interface {
        Error() string
    }
    ```
*   **Goコンパイラ (`gc`)**: Go言語の公式コンパイラであり、ソースコードを機械語に変換する役割を担います。`src/cmd/gc` ディレクトリにそのソースコードがあります。
*   **型システム (Type System)**: コンパイラがプログラムの型を管理・検証する仕組みです。Goコンパイラ内部では、`Type` 構造体などが型の情報を表現しています。
*   **インターフェース (Interface)**: Go言語におけるインターフェースは、メソッドのシグネチャの集合を定義する型です。特定のインターフェースを実装する型は、そのインターフェース型として扱えます。
*   **リフレクション (Reflection)**: プログラムが自身の構造を検査し、実行時にその動作を変更できる機能です。Go言語では `reflect` パッケージを通じて提供されます。コンパイラはリフレクションに必要な型情報を生成します。
*   **組み込み型 (Built-in Types)**: `int`, `string`, `bool` など、言語に最初から定義されている基本的な型です。`error` もこれに準ずる形でコンパイラに認識される必要があります。
*   **`lex.c` (Lexer/Parser)**: コンパイラの字句解析器および一部の構文解析器の役割を担うファイルです。ここでは、組み込み型やキーワードの初期化が行われることがあります。
*   **`export.c`**: コンパイラが型情報などをエクスポートする際の処理を記述するファイルです。他のパッケージから参照される型は、ここで適切に処理される必要があります。
*   **`fmt.c`**: 型の文字列表現を生成するフォーマッタ関連のコードです。デバッグ出力などで型の名前を表示する際に使用されます。
*   **`go.h`**: コンパイラ内部で使用されるグローバルな型定義や外部変数宣言が含まれるヘッダファイルです。
*   **`reflect.c`**: リフレクションに必要な型情報の生成や処理を行うファイルです。
*   **`subr.c`**: コンパイラ内部のユーティリティ関数や補助的な処理をまとめたファイルです。

このコミットは、`error` インターフェースをコンパイラが「特別な」組み込み型として認識し、その定義を内部的に構築するプロセスを導入しています。

## 技術的詳細

このコミットの主要な技術的変更点は、Goコンパイラが `error` インターフェースを組み込み型として認識し、その型情報を内部的に構築するロジックを追加したことです。

具体的には、以下のファイルが変更されています。

1.  **`src/cmd/gc/go.h`**:
    *   `EXTERN Type* errortype;` が追加され、`error` 型を表すグローバルな `Type` ポインタが宣言されています。これは、コンパイラ全体で `error` 型のインスタンスを一意に参照するためのものです。

2.  **`src/cmd/gc/lex.c`**:
    *   `lexinit1` 関数内で、`error` インターフェースの具体的な型定義がコンパイラ内部で構築されています。
    *   `error` インターフェースは、`Error() string` というメソッドを持つインターフェースとして定義されます。このメソッドのシグネチャ(レシーバ、引数、戻り値)に対応する内部的な `Type` 構造体が作成されます。
    *   `lookup("Error")` でメソッド名 `Error` のシンボルが取得され、そのシンボルとメソッドの型 (`f`) を持つ `TFIELD` 型の `Type` が作成され、インターフェース型 `t` の `type` フィールドに設定されます。
    *   最終的に、`lookup("error")` で `error` という名前のシンボルが取得され、そのシンボルに構築されたインターフェース型 `t` が関連付けられ、`errortype` グローバル変数に代入されます。
    *   `pkglookup("error", builtinpkg)` を使用して、`builtin` パッケージ内の `error` シンボルも同様に定義されます。これは、`error` が組み込み型であることを示すためです。
    *   `lexfini` 関数では、`error` シンボルがまだ定義されていない場合に `errortype` に関連付けられた型ノードをその定義として設定するロジックが追加されています。

3.  **`src/cmd/gc/export.c`**:
    *   `dumpexporttype` 関数において、型をエクスポートする際に `errortype` も他の組み込み型 (`bytetype`, `runetype` など) と同様に `printed` フラグがチェックされ、重複してエクスポートされないように処理が追加されています。これは、`error` 型がコンパイラ内部で特別扱いされるようになったことを示します。

4.  **`src/cmd/gc/fmt.c`**:
    *   `typefmt` 関数において、`Type` を文字列にフォーマットする際に、もし `t` が `errortype` であれば、直接 `"error"` という文字列を返すように変更されています。これにより、デバッグ出力やエラーメッセージなどで `error` 型が正しく表示されるようになります。

5.  **`src/cmd/gc/reflect.c`**:
    *   `dtypesym` 関数において、リフレクションのために型シンボルを処理する際に、`compiling_runtime` (ランタイムをコンパイルしている場合) かつ `tbase` が `errortype` であれば、他の組み込み型と同様に `ok` と見なされるように条件が追加されています。これは、`error` 型のリフレクション情報が正しく生成されることを保証します。
    *   `dumptypestructs` 関数において、リフレクションに必要な型構造体をダンプする際に、`errortype` および `func(error) string` (自動生成されるラッパーの型) のポインタ型もダンプされるように追加されています。これにより、`error` 型とその関連型がリフレクションシステムで利用可能になります。

6.  **`src/cmd/gc/subr.c`**:
    *   `ptrto` 関数内の `fatal` エラーメッセージが `"ptrto: nil"` から `"ptrto: no tptr"` に変更されています。これは直接 `error` 型の追加とは関係ありませんが、このコミットで同時に行われた小さな修正です。

これらの変更により、Goコンパイラは `error` インターフェースを単なるユーザー定義インターフェースとしてではなく、言語のコアな組み込み型として認識し、そのライフサイクル全体(定義、型チェック、エクスポート、リフレクション)を適切に管理できるようになります。

## コアとなるコードの変更箇所

### `src/cmd/gc/go.h`

```diff
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -786,6 +786,7 @@ EXTERN	Type*	idealstring;
 EXTERN	Type*	idealbool;
 EXTERN	Type*	bytetype;
 EXTERN	Type*	runetype;
+EXTERN	Type*	errortype;
 EXTERN	uchar	simtype[NTYPE];
 EXTERN	uchar	isptr[NTYPE];
 EXTERN	uchar	isforw[NTYPE];

src/cmd/gc/lex.c

--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -1759,6 +1759,40 @@ static void
 lexinit1(void)
 {
 	Sym *s, *s1;
+	Type *t, *f, *rcvr, *in, *out;
+
+	// t = interface { Error() string }
+	rcvr = typ(TSTRUCT);
+	rcvr->type = typ(TFIELD);
+	rcvr->type->type = ptrto(typ(TSTRUCT));
+	rcvr->funarg = 1;
+	in = typ(TSTRUCT);
+	in->funarg = 1;
+	out = typ(TSTRUCT);
+	out->type = typ(TFIELD);
+	out->type->type = types[TSTRING];
+	out->funarg = 1;
+	f = typ(TFUNC);
+	*getthis(f) = rcvr;
+	*getoutarg(f) = out;
+	*getinarg(f) = in;
+	f->thistuple = 1;
+	f->intuple = 0;
+	f->outnamed = 0;
+	f->outtuple = 1;
+	t = typ(TINTER);
+	t->type = typ(TFIELD);
+	t->type->sym = lookup("Error");
+	t->type->type = f;
+
+	// error type
+	s = lookup("error");
+	s->lexical = LNAME;
+	errortype = t;
+	errortype->sym = s;
+	s1 = pkglookup("error", builtinpkg);
+	s1->lexical = LNAME;
+	s1->def = typenod(errortype);
+
 	// byte alias
 	s = lookup("byte");
@@ -1820,6 +1854,10 @@ lexfini(void)
 	s = lookup("byte");
 	if(s->def == N)
 		s->def = typenod(bytetype);
+	
+	s = lookup("error");
+	if(s->def == N)
+		s->def = typenod(errortype);
 
 	s = lookup("rune");
 	if(s->def == N)

src/cmd/gc/fmt.c

--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -553,6 +553,9 @@ typefmt(Fmt *fp, Type *t)\n 	\tt = types[t->etype];
 \t}\
 \n+\tif(t == errortype)\
+\t\treturn fmtstrcpy(fp, "error");
+\n \t// Unless the 'l' flag was specified, if the type has a name, just print that name.\
 \tif(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) {
 \t\tswitch(fmtmode) {

コアとなるコードの解説

src/cmd/gc/go.h の変更

EXTERN Type* errortype; の追加は、Goコンパイラ全体で error 型を表すためのグローバルなポインタを宣言しています。これにより、コンパイラの異なる部分から error 型の定義にアクセスできるようになります。これは、bytetyperunetype といった他の組み込み型と同様の扱いです。

src/cmd/gc/lex.c の変更

このファイルでの変更が最も重要です。lexinit1 関数はコンパイラの初期化時に呼び出され、組み込み型やキーワードの定義を行います。

  • // t = interface { Error() string } ブロック: このコードブロックは、Go言語の error インターフェースの内部表現を構築しています。

    • rcvr, in, out はそれぞれ、メソッドのレシーバ、入力引数、出力引数を表すための構造体型 (TSTRUCT) を作成しています。error インターフェースの Error() メソッドはレシーバを持ち、引数はなく、string を返します。
    • f = typ(TFUNC) で関数型が作成され、*getthis(f) = rcvr; *getoutarg(f) = out; *getinarg(f) = in; でその関数型のレシーバ、出力引数、入力引数が設定されます。
    • t = typ(TINTER) でインターフェース型が作成されます。
    • t->type = typ(TFIELD); t->type->sym = lookup("Error"); t->type->type = f; の部分で、インターフェース tError という名前のメソッド (f で定義された関数型) が関連付けられます。これは、error インターフェースが Error() string メソッドを持つことをコンパイラに教えるものです。
  • // error type ブロック: このブロックでは、構築されたインターフェース型を error という名前のシンボルに関連付け、グローバルな errortype 変数に代入しています。

    • s = lookup("error");error という名前のシンボルを取得します。
    • s->lexical = LNAME; は、このシンボルが名前であることを示します。
    • errortype = t; で、先ほど構築したインターフェース型 terrortype グローバル変数に設定します。
    • errortype->sym = s; で、error 型のシンボルを errortype に関連付けます。
    • s1 = pkglookup("error", builtinpkg); s1->lexical = LNAME; s1->def = typenod(errortype); は、errorbuiltin パッケージに属する組み込み型であることを明示的に定義しています。

lexfini 関数では、error シンボルがまだ定義されていない場合に、errortype に関連付けられた型ノードをその定義として設定するフォールバックロジックが追加されています。これにより、error 型が常に正しく解決されることが保証されます。

src/cmd/gc/fmt.c の変更

typefmt 関数は、コンパイラ内部の型情報を人間が読める文字列形式に変換する役割を担っています。 if(t == errortype) return fmtstrcpy(fp, "error"); の追加により、Type オブジェクトが errortype と一致する場合、その文字列表現として直接 "error" が返されるようになります。これにより、デバッグ出力やコンパイラが生成するエラーメッセージなどで、error 型が正しく「error」と表示されるようになります。これは、error 型が他の一般的なインターフェース型とは異なり、特別な組み込み型として扱われることの表れです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (errorインターフェースについて): https://go.dev/blog/error-handling-and-go
  • Goコンパイラのソースコード (一般的な構造理解のため): https://github.com/golang/go/tree/master/src/cmd/compile (gcは古いコンパイラの名称で、現在はcompileに統合されていますが、当時のファイル構造を理解する上で参考になります)
  • Go言語の型システムに関する一般的な情報 (コンパイラ内部の型表現の理解のため)