[インデックス 11193] ファイルの概要
このコミットは、Goコンパイラ(gc
)の構文解析部分におけるバグ修正と、そのバグを再現するためのテストケースの追加を含んでいます。具体的には、src/cmd/gc/go.y
ファイルが修正され、test/fixedbugs/bug394.go
という新しいテストファイルが追加されました。
コミット
- コミットハッシュ:
b16f3a2d507fc718adcee19514a4c7b0081e726c
- 作者: Scott Lawrence bytbox@gmail.com
- コミット日時: Mon Jan 16 18:12:25 2012 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b16f3a2d507fc718adcee19514a4c7b0081e726c
元コミット内容
gc: don't fault on return outside function
Fixes #2598.
R=golang-dev, ality, minux.ma, mpimenov, rsc
CC=golang-dev
https://golang.org/cl/5510043
変更の背景
このコミットは、Goコンパイラ(gc
)が関数外で return
ステートメントに遭遇した際に発生する「フォールト」(異常終了、パニック)を修正するために行われました。
Go言語では、return
ステートメントは関数の内部でのみ使用されるべきものです。しかし、コンパイラがソースコードを解析する際、誤って関数外に記述された return
ステートメントを処理しようとすると、内部的なデータ構造(特に現在の関数を表す curfn
ノード)が期待される状態ではないため、ヌルポインタ参照などのエラーが発生し、コンパイラ自体がクラッシュしてしまう問題がありました。
この問題は、GoのIssue #2598として報告されており、コンパイラがクラッシュする代わりに、適切なエラーメッセージ(例: "non-declaration statement outside function body")を出力して処理を継続するように修正する必要がありました。
前提知識の解説
- Goコンパイラ (
gc
): Go言語の公式コンパイラであり、Goソースコードを機械語に変換する役割を担っています。 go.y
: Goコンパイラのソースコードに含まれるファイルで、Yacc (Yet Another Compiler Compiler) または Bison 形式で記述された文法定義ファイルです。Go言語の構文規則(どのキーワードがどのように組み合わされるかなど)がこのファイルに記述されており、コンパイラの字句解析器と構文解析器の生成に利用されます。return
ステートメント: Go言語において、関数の実行を終了し、呼び出し元に制御を戻すために使用されるキーワードです。必要に応じて値を返すこともできます。- AST (Abstract Syntax Tree - 抽象構文木): ソースコードの構文構造を木構造で表現したものです。コンパイラはソースコードを解析する過程でASTを構築し、これを用いて意味解析やコード生成を行います。
curfn
: Goコンパイラの内部で、現在処理中の関数を表すASTノードへのポインタまたは参照です。コンパイラが関数のスコープ内でコードを解析している間、このcurfn
変数は現在の関数に関する情報(引数、ローカル変数、戻り値など)を保持します。関数外では通常、nil
またはそれに相当する値(Goコンパイラの文脈ではN
)になります。fault
(フォールト): プログラムが予期せぬエラー(例: ヌルポインタ参照、不正なメモリアクセス)により、オペレーティングシステムによって強制的に終了させられること。ここではGoコンパイラがクラッシュすることを指します。
技術的詳細
この修正は、go.y
ファイル内の non_dcl_stmt
(非宣言ステートメント) の構文規則、特に return
ステートメントの処理部分に焦点を当てています。
以前のコードでは、return
ステートメントが引数なしで記述された場合(例: return
)、コンパイラは $$->list
が nil
であることを確認した後、現在の関数 (curfn
) の宣言リスト (curfn->dcl
) を走査しようとしていました。この走査は、関数の戻り値の処理や、特定のクリーンアップ処理のために行われるものでした。
問題は、return
ステートメントが関数の外で記述された場合、curfn
が N
(ヌル) になっているにもかかわらず、curfn->dcl
へのアクセスが試みられていた点です。これにより、ヌルポインタ参照が発生し、コンパイラがフォールト(クラッシュ)していました。
修正では、この curfn->dcl
へのアクセスを試みる前に、curfn
が N
ではないことを確認する条件 && curfn != N
が追加されました。これにより、return
ステートメントが関数外にある場合、curfn
が N
であるため、curfn->dcl
へのアクセスがスキップされ、コンパイラのクラッシュが防止されます。代わりに、構文解析の段階で適切なエラー("non-declaration statement outside function body")が報告されるようになります。
また、この修正を検証するために、test/fixedbugs/bug394.go
という新しいテストファイルが追加されました。このテストファイルは、関数外に return nil
を記述することで、コンパイラが正しくエラーを報告し、クラッシュしないことを確認します。
コアとなるコードの変更箇所
src/cmd/gc/go.y
の変更点:
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -1618,7 +1618,7 @@ non_dcl_stmt:
{\n \t\t$$ = nod(ORETURN, N, N);\n \t\t$$->list = $2;\n-\t\tif($$->list == nil) {\n+\t\tif($$->list == nil && curfn != N) {\n \t\t\tNodeList *l;\n \t\t\t\n \t\t\tfor(l=curfn->dcl; l; l=l->next) {\n```
`test/fixedbugs/bug394.go` の追加:
```diff
--- /dev/null
+++ b/test/fixedbugs/bug394.go
@@ -0,0 +1,10 @@
+// errchk $G $D/$F.go
+
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 2598
+package foo
+
+return nil // ERROR "non-declaration statement outside function body"
コアとなるコードの解説
src/cmd/gc/go.y
の変更は、return
ステートメントの処理ロジックにあります。
変更前のコード:
if($$->list == nil) {
// ... curfn->dcl を使用するロジック ...
}
このコードは、return
ステートメントに引数がない場合($$->list
が nil
)、その後のブロックを実行します。このブロック内では、現在の関数 (curfn
) の宣言リスト (curfn->dcl
) を走査する処理が含まれていました。しかし、return
が関数外にある場合、curfn
は N
(ヌル) であり、curfn->dcl
へのアクセスはパニックを引き起こしました。
変更後のコード:
if($$->list == nil && curfn != N) {
// ... curfn->dcl を使用するロジック ...
}
追加された && curfn != N
という条件は、この問題を解決します。これにより、return
ステートメントに引数がなく、かつ 現在の関数 (curfn
) が有効なノードである(つまり、return
が関数内で使用されている)場合にのみ、curfn->dcl
を使用するブロックが実行されるようになります。return
が関数外にある場合、curfn
は N
であるため、この条件は偽となり、危険なコードブロックはスキップされます。これにより、コンパイラのクラッシュが回避され、代わりに適切な構文エラーが報告されるようになります。
test/fixedbugs/bug394.go
は、この修正が正しく機能することを確認するための回帰テストです。このファイルは、package foo
の直後に return nil
という不正な return
ステートメントを配置しています。// ERROR "non-declaration statement outside function body"
というコメントは、コンパイラがこの行で期待するエラーメッセージを示しており、コンパイラがクラッシュせずにこの特定のエラーを報告することを確認します。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/b16f3a2d507fc718adcee19514a4c7b0081e726c
- Go Issue #2598: このコミットが修正したGoのIssueです。2012年当時のGoのIssueトラッカーは現在とは異なるシステムで運用されていたため、直接的なリンクは提供できませんが、内容は「関数外での
return
ステートメントによるコンパイラのフォールト」に関するものです。 - Go CL 5510043: このコミットに対応するGoのコードレビュー(Change List)です。こちらもIssueと同様に、当時のシステムでのリンクであり、現在の
go.dev/cl
では直接参照できない可能性があります。
参考にした情報源リンク
- Go言語の公式ドキュメント
- Goコンパイラのソースコード
- Yacc/Bisonの一般的な概念に関する情報