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

[インデックス 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)、コンパイラは $$->listnil であることを確認した後、現在の関数 (curfn) の宣言リスト (curfn->dcl) を走査しようとしていました。この走査は、関数の戻り値の処理や、特定のクリーンアップ処理のために行われるものでした。

問題は、return ステートメントが関数の外で記述された場合、curfnN (ヌル) になっているにもかかわらず、curfn->dcl へのアクセスが試みられていた点です。これにより、ヌルポインタ参照が発生し、コンパイラがフォールト(クラッシュ)していました。

修正では、この curfn->dcl へのアクセスを試みる前に、curfnN ではないことを確認する条件 && curfn != N が追加されました。これにより、return ステートメントが関数外にある場合、curfnN であるため、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 ステートメントに引数がない場合($$->listnil)、その後のブロックを実行します。このブロック内では、現在の関数 (curfn) の宣言リスト (curfn->dcl) を走査する処理が含まれていました。しかし、return が関数外にある場合、curfnN (ヌル) であり、curfn->dcl へのアクセスはパニックを引き起こしました。

変更後のコード:

if($$->list == nil && curfn != N) {
    // ... curfn->dcl を使用するロジック ...
}

追加された && curfn != N という条件は、この問題を解決します。これにより、return ステートメントに引数がなく、かつ 現在の関数 (curfn) が有効なノードである(つまり、return が関数内で使用されている)場合にのみ、curfn->dcl を使用するブロックが実行されるようになります。return が関数外にある場合、curfnN であるため、この条件は偽となり、危険なコードブロックはスキップされます。これにより、コンパイラのクラッシュが回避され、代わりに適切な構文エラーが報告されるようになります。

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の一般的な概念に関する情報