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

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

このコミットは、Go言語の初期のコンパイラ(gc)における型と名前の解決に関する問題を修正するものです。具体的には、型が期待される場所で名前が使用された場合に、より適切で明確なエラーメッセージを生成するようにコンパイラの構文解析器を改善しています。

コミット

commit e53d5ad620aeb61021582c72b4779848ff51573b
Author: Russ Cox <rsc@golang.org>
Date:   Tue Dec 16 17:45:28 2008 -0800

    fix type/name thing, again
    
    R=r
    DELTA=8  (7 added, 0 deleted, 1 changed)
    OCL=21379
    CL=21379

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

https://github.com/golang/go/commit/e53d5ad620aeb61021582c72b4779848ff51573b

元コミット内容

fix type/name thing, again

このコミットメッセージは簡潔ですが、「型/名前の問題を再度修正する」という意図が読み取れます。これは、以前にも同様の問題に対する修正が行われたか、あるいはこの問題が複数回にわたって議論・修正の対象となっていたことを示唆しています。コンパイラが識別子を型として解釈すべきか、それとも変数名や関数名として解釈すべきかという、構文解析における曖昧さや誤解釈を解消しようとしていることが推測されます。

変更の背景

Go言語は、その設計初期から静的型付け言語として開発が進められていました。コンパイラはソースコードを解析し、プログラムの構造と意味を理解する過程で、各識別子が何を表しているのか(変数、関数、型など)を正確に判断する必要があります。

このコミットが行われた2008年12月は、Go言語がまだ一般に公開される前の開発初期段階にあたります。この時期は、言語仕様が固まり、コンパイラが安定していく過程であり、構文解析器の挙動やエラーメッセージの質が頻繁に改善されていました。

fix type/name thing, again」というメッセージと、go.y(GoコンパイラのYacc文法定義ファイル)の変更、そしてテスト出力ファイルgolden.outの変更から、以下の背景が考えられます。

  1. 構文解析の曖昧さの解消: Go言語の構文において、ある識別子が型名として使われるべき文脈で、それが型として認識されない、あるいは誤って別のものとして解釈されるケースが存在した可能性があります。
  2. エラーメッセージの改善: 以前のコンパイラは、このような状況で「syntax error near x」のような一般的なエラーメッセージを出力していました。これは、ユーザーにとって問題の根本原因を特定しにくいものでした。開発者は、より具体的で分かりやすいエラーメッセージを提供することで、開発者のデバッグ体験を向上させようとしていました。
  3. コンパイラの堅牢性向上: 構文解析器が特定のパターンをより厳密にチェックし、早期にエラーを検出することで、コンパイラ全体の堅牢性と信頼性を高める狙いがありました。

この変更は、Goコンパイラがコードの意図をより正確に理解し、開発者に対してより有用なフィードバックを提供するための、継続的な改善プロセスの一部であったと言えます。

前提知識の解説

Go言語のコンパイラ gcgo.y

Go言語の公式コンパイラは、歴史的にgc(Go Compiler)と呼ばれていました。このgcは、Go言語のソースコードを機械語に変換する役割を担っています。コンパイルプロセスは、主に以下の段階で構成されます。

  1. 字句解析 (Lexical Analysis): ソースコードをトークン(最小単位の単語)のストリームに変換します。
  2. 構文解析 (Syntactic Analysis): トークンのストリームを解析し、言語の文法規則に従っているかを確認します。この段階で、抽象構文木(AST: Abstract Syntax Tree)が構築されます。
  3. 意味解析 (Semantic Analysis): ASTを走査し、型チェック、名前解決、スコープの確認など、プログラムの意味的な正当性を検証します。
  4. 中間コード生成 (Intermediate Code Generation): ASTから、プラットフォームに依存しない中間表現を生成します。
  5. 最適化 (Optimization): 中間コードを最適化し、実行効率を高めます。
  6. コード生成 (Code Generation): 最適化された中間コードをターゲットアーキテクチャの機械語に変換します。

このコミットで変更されているsrc/cmd/gc/go.yファイルは、Go言語の初期のgcコンパイラにおいて、構文解析の段階で非常に重要な役割を担っていました。.y拡張子は、Yacc(Yet Another Compiler Compiler)またはBisonといったパーサジェネレータツールで使用される文法定義ファイルであることを示しています。

Yaccは、BNF(Backus-Naur Form)に似た形式で文法規則を記述することで、その文法を解析するためのC言語のコード(パーサ)を自動生成するツールです。go.yファイルには、Go言語の構文規則が定義されており、Yaccによって生成されたパーサが、Goのソースコードがこれらの規則に準拠しているかを検証していました。

補足: Goコンパイラは進化しており、Go 1.7以降、gcコンパイラはYaccやBisonのようなツールによって生成されたパーサファイルを使用していません。代わりに、Go言語で手書きされたスキャナとパーサが$GOROOT/src/cmd/compile/internal/syntaxディレクトリ(scanner.goparser.goなど)に実装されています。このコミットは、Go言語の初期のコンパイラ設計における歴史的な側面を示しています。

Go言語の型システムと名前解決

Go言語は静的型付け言語であり、すべての変数、関数パラメータ、戻り値には型が関連付けられています。コンパイル時に型チェックが行われることで、型に関するエラーが実行時ではなく開発段階で検出されます。

名前解決とは、ソースコード中の識別子(変数名、関数名、型名など)が、プログラムのどの実体(メモリ上の場所、関数定義、型定義など)を参照しているのかを特定するプロセスです。これは、構文解析と意味解析の段階で密接に行われます。

例えば、var x intという宣言では、xは変数名、intは型名です。コンパイラは、intが有効な型であることを認識し、xint型の変数として宣言されていることを理解します。もしintの代わりに存在しない型名が使われた場合、コンパイラはエラーを報告する必要があります。

このコミットは、特に「型が期待される文脈で、それが型として認識されない」というシナリオに焦点を当てています。

技術的詳細

このコミットの技術的詳細を理解するためには、go.yファイルにおける文法規則の追加と、それによって生成されるエラーメッセージの変化に注目する必要があります。

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

変更前のgo.yには、latypeというルールが存在していました。これはおそらく、型が期待される文脈(laは"lookahead"や"left-associative"などの略である可能性がありますが、具体的な意味は文脈に依存します)で、予期しないトークンが来た場合に一般的なYYERRORを発生させるものでした。

このコミットでは、新たにnametypeというルールが追加されています。

--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -2038,5 +2038,12 @@ latype:
 	\t\tYYERROR;\n\t}\n\n+nametype:\n+\tLNAME
+\t{\n+\t\tyyerror(\"no type %s\", $1->name);\n+\t\tYYERROR;\n+\t}\n+\n /**/\n \n```

-   **`nametype:`**: 新しく定義された文法ルールです。
-   **`LNAME`**: これは字句解析器(lexer)によって生成されるトークンで、識別子(名前)を表します。つまり、このルールは「名前」が来た場合に適用されます。
-   **`{ yyerror("no type %s", $1->name); YYERROR; }`**: `LNAME`トークンが認識された際に実行されるアクションです。
    -   `yyerror("no type %s", $1->name)`: これはYaccによって提供されるエラー報告関数です。`"no type %s"`というフォーマット文字列と、`$1->name`(`LNAME`トークンが表す名前)を引数として受け取ります。これにより、「`no type [名前]`」という形式の具体的なエラーメッセージが生成されます。
    -   `YYERROR`: これはYaccの特別なマクロで、構文解析器をエラー状態に移行させ、エラー回復処理を開始させます。

この変更は、コンパイラが「型が期待される場所で、しかし実際には型ではない単なる名前(識別子)が来た」という特定の構文エラーパターンを明示的に捕捉し、それに対してより適切なエラーメッセージを出すように設計されたことを示しています。

### `test/golden.out` の変更

`test/golden.out`ファイルは、Goコンパイラのテストスイートの一部であり、特定のテストケースを実行した際の標準出力(特にエラーメッセージ)の期待される結果を記録する「ゴールデンファイル」です。このファイルが変更されたということは、コンパイラの出力、特にエラーメッセージの形式が変わったことを意味します。

```diff
--- a/test/golden.out
+++ b/test/golden.out
@@ -240,7 +240,7 @@ fixedbugs/bug074.go:6: syntax error near string
 fixedbugs/bug074.go:7: x: undefined
 
 =========== fixedbugs/bug081.go
-fixedbugs/bug081.go:5: syntax error near x
+fixedbugs/bug081.go:5: no type x
 
 =========== fixedbugs/bug083.go
 fixedbugs/bug083.dir/bug1.go:5: syntax error near T0
  • fixedbugs/bug081.go:5: の行で、以前は「syntax error near x」というエラーメッセージが出力されていました。
  • このコミット後、同じ行で「no type x」というエラーメッセージが出力されるように変更されています。

この変更は、go.yに追加されたnametypeルールが意図通りに機能し、型が期待される場所でxという名前が使われた場合に、より具体的で分かりやすいエラーメッセージを生成するようになったことを明確に示しています。

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

src/cmd/gc/go.y

 // ... (既存のコード)

 latype:
 	// ... (既存のlatypeルール)
 	{
 		YYERROR;
 	}

+nametype:
+\tLNAME
+\t{
+\t\tyyerror("no type %s", $1->name);
+\t\tYYERROR;
+\t}

 /**/
 // ... (既存のコード)

test/golden.out

--- a/test/golden.out
+++ b/test/golden.out
@@ -240,7 +240,7 @@ fixedbugs/bug074.go:6: syntax error near string
 fixedbugs/bug074.go:7: x: undefined
 
 =========== fixedbugs/bug081.go
-fixedbugs/bug081.go:5: syntax error near x
+fixedbugs/bug081.go:5: no type x
 
 =========== fixedbugs/bug083.go
 fixedbugs/bug083.dir/bug1.go:5: syntax error near T0

コアとなるコードの解説

このコミットの核心は、Goコンパイラの構文解析器が、型が期待される文脈で誤って識別子(名前)が使用された場合に、より具体的で診断に役立つエラーメッセージを生成するように改善された点にあります。

src/cmd/gc/go.yに追加されたnametypeルールは、この目的のために導入されました。

nametype:
	LNAME
	{
		yyerror("no type %s", $1->name);
		YYERROR;
	}
  • nametype:: これは、Go言語の文法において「型」が期待されるが、実際には「名前」(LNAMEトークン)が来た場合に適用される新しい文法規則です。
  • LNAME: 字句解析器が識別子を検出した際に生成するトークンです。例えば、変数名や関数名などがこれに該当します。
  • { yyerror("no type %s", $1->name); YYERROR; }: このブロックは、LNAMEnametypeルールにマッチした場合に実行されるアクションです。
    • yyerror("no type %s", $1->name): コンパイラのエラー報告メカニズムを呼び出し、"no type %s"というフォーマット文字列と、実際に検出された名前($1->name)を使ってエラーメッセージを生成します。これにより、例えばxという名前が型として使われた場合には、「no type x」というメッセージが出力されます。
    • YYERROR: これはYaccの機能で、現在の構文解析の状態をエラー状態に設定し、エラー回復ルーチンをトリガーします。これにより、コンパイラは不正な構文を検出した後も、可能な限り解析を続行しようとします。

この変更により、test/golden.outに示されているように、fixedbugs/bug081.goの5行目で発生していたエラーメッセージが、以前の一般的な「syntax error near x」から、より具体的な「no type x」へと変化しました。

これは、コンパイラが単に「構文が間違っている」と報告するだけでなく、「なぜ構文が間違っているのか、具体的に何が期待され、何が検出されたのか」という情報を提供するようになったことを意味します。この改善は、Go言語の初期開発段階において、コンパイラのエラーメッセージの質を高め、開発者がコードの問題をより迅速に特定し、修正できるようにするための重要なステップでした。

関連リンク

参考にした情報源リンク