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

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

このコミットは、Go言語のgoyaccツールにおけるデバッグ出力に関するバグ修正です。具体的には、yydebugレベルが2以上の場合に発生していたインデックス参照の誤りを修正し、エラーリカバリ時のスタックポップに関するデバッグメッセージをより正確なものにしています。

コミット

commit a67c69cbaee1e419b1950832afb295cc951b202e
Author: Rob Pike <r@golang.org>
Date:   Fri Jan 20 10:22:41 2012 -0800

    goyacc: fix indexing bug when yydebug >= 2
    Fixes #2701.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5556070

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

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

元コミット内容

goyacc: fix indexing bug when yydebug >= 2
Fixes #2701.

変更の背景

この変更は、Go言語のパーサジェネレータであるgoyaccツールにおいて、デバッグレベルが2以上(yydebug >= 2)に設定されている場合に発生していたインデックス参照のバグを修正するために行われました。

元のコードでは、エラーリカバリ処理中にスタックから状態をポップする際のデバッグメッセージにおいて、$$S[$$p-1].yysという形で、存在しない可能性のあるインデックスを参照していました。これは、スタックが既に空に近い状態である場合に、無効なメモリ領域へのアクセスや、誤った値の表示を引き起こす可能性がありました。

この問題は、GoのIssue #2701として報告されており、このコミットはその報告されたバグを修正することを目的としています。デバッグ出力は開発者がパーサの動作を理解し、問題を特定するために非常に重要であるため、その正確性は保証されるべきです。

前提知識の解説

Yacc (Yet Another Compiler Compiler) とパーサジェネレータ

Yaccは、プログラミング言語のコンパイラやインタプリタの構文解析部分(パーサ)を自動生成するためのツールです。BNF(Backus-Naur Form)に似た文法規則を記述した入力ファイル(通常は.y拡張子)を受け取り、その文法を解析するためのC言語のコード(通常はy.tab.c)を生成します。生成されたコードは、入力ストリームを読み込み、文法規則に従ってトークンを解析し、構文木を構築したり、セマンティックアクションを実行したりします。

Goyacc

goyaccは、Go言語で書かれたYaccのGo言語版実装です。Yaccと同様に、文法定義ファイルからGo言語のパーサコードを生成します。Go言語のプロジェクトでカスタムの構文解析器を必要とする場合に利用されます。

yydebug

yydebugは、Yaccやその派生ツール(goyaccを含む)で生成されたパーサのデバッグレベルを制御するための変数です。この変数の値によって、パーサの実行中にどれだけ詳細なデバッグ情報を出力するかが決まります。

  • yydebug = 0: デバッグ出力なし。
  • yydebug = 1: 通常のデバッグ情報(シフト、還元などのアクション)が出力されます。
  • yydebug >= 2: より詳細なデバッグ情報が出力されます。これには、スタックの状態やエラーリカバリの詳細などが含まれることがあります。

このコミットで修正されたバグは、yydebugが2以上のときにのみ顕在化するものでした。これは、より詳細なデバッグ情報を提供しようとした際に、誤ったインデックス参照が発生していたことを示しています。

エラーリカバリ

パーサが入力ストリームで文法エラーを検出した場合、通常はエラーメッセージを出力し、解析を停止します。しかし、より堅牢なパーサでは、エラーを検出した後も解析を続行しようと試みる「エラーリカバリ」メカニズムが実装されています。これは、エラーが発生した箇所をスキップしたり、スタックから状態をポップしたりすることで、後続の有効な入力の解析を試みるものです。

このコミットの変更箇所は、このエラーリカバリ処理中のデバッグ出力に関連しています。

技術的詳細

このコミットは、src/cmd/goyacc/goyacc.goファイル内の$$default:ラベルが付いたセクション、具体的にはエラーリカバリ処理の一部を修正しています。

元のコードでは、yydebug >= 2の場合に以下のデバッグメッセージを出力していました。

fmt.Printf("error recovery pops state %d, uncovers %d\n",
    $$S[$$p].yys, $$S[$$p-1].yys)

ここで問題となるのは、$$S[$$p-1].yysの部分です。$$pは現在のスタックポインタを示しており、エラーリカバリの過程でスタックから要素がポップされる($$p--)前にこの行が実行されます。もし$$pが0、つまりスタックが空の状態であるか、あるいはスタックに1つしか要素がない状態でこのデバッグ出力が実行されると、$$p-1は負のインデックスとなり、$$S配列の範囲外アクセスが発生します。これはGo言語ではランタイムパニックを引き起こす可能性がありますし、そうでなくとも不定な値を出力することになります。

修正後のコードでは、$$S[$$p-1].yysの出力が削除され、より安全で正確なデバッグメッセージに変更されています。

fmt.Printf("error recovery pops state %d\n", $$S[$$p].yys)

これにより、スタックからポップされる現在の状態($$S[$$p].yys)のみが出力されるようになり、インデックス参照のバグが解消されました。この変更は、デバッグメッセージの正確性を保ちつつ、潜在的なランタイムエラーを防ぐための重要な修正です。

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

--- a/src/cmd/goyacc/goyacc.go
+++ b/src/cmd/goyacc/goyacc.go
@@ -3271,10 +3271,9 @@ $$default:
 					}
 				}
 
-				/* the current p has no shift onn "error", pop stack */
+				/* the current p has no shift on "error", pop stack */
 				if $$Debug >= 2 {
-\t\t\t\t\tfmt.Printf("error recovery pops state %d, uncovers %d\n",
-\t\t\t\t\t\t$$S[$$p].yys, $$S[$$p-1].yys)
+\t\t\t\t\tfmt.Printf("error recovery pops state %d\n", $$S[$$p].yys)
 				}
 				$$p--
 			}

コアとなるコードの解説

変更はsrc/cmd/goyacc/goyacc.goファイルの3274行目から3276行目にかけて行われています。

  • 変更前:

    fmt.Printf("error recovery pops state %d, uncovers %d\n",
        $$S[$$p].yys, $$S[$$p-1].yys)
    

    この行では、エラーリカバリ時にスタックから状態をポップする際に、現在の状態($$S[$$p].yys)と、その一つ前の状態($$S[$$p-1].yys)の両方を出力しようとしていました。しかし、$$p-1が有効なインデックスでない場合に問題が発生していました。

  • 変更後:

    fmt.Printf("error recovery pops state %d\n", $$S[$$p].yys)
    

    変更後は、$$S[$$p-1].yysの出力が削除され、スタックからポップされる現在の状態($$S[$$p].yys)のみが出力されるようになりました。これにより、インデックス参照のバグが修正され、デバッグメッセージの正確性と安全性が向上しました。コメントのタイプミス "onn" も "on" に修正されていますが、これは機能的な変更ではありません。

この修正は、goyaccが生成するパーサのデバッグ出力の信頼性を高め、開発者がパーサの挙動をより正確に把握できるようにするために重要です。

関連リンク

参考にした情報源リンク