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

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

このコミットは、Go言語のコンパイラの一つであるgccgoにおけるコメントの字句解析(lexing)の不具合を修正するためのテストケースを追加するものです。具体的には、Go言語の仕様では許可されていない「ネストされたブロックコメント」をgccgoが誤って解釈してしまう問題に対応しています。このテストの追加により、gccgoがGo言語のコメント仕様に厳密に従い、不正なコメント構造に対して正しくエラーを報告するようになることを保証します。

コミット

commit f2b59a3483e3d7638b55f4e01a20edac75890759
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Jul 8 14:09:35 2014 -0700

    test: add test for gccgo comment lexing failure
    
    http://gcc.gnu.org/PR61746
    
    http://code.google.com/p/gofrontend/issues/detail?id=35
    
    LGTM=crawshaw
    R=golang-codereviews, crawshaw
    CC=golang-codereviews
    https://golang.org/cl/111980043

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

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

元コミット内容

test: add test for gccgo comment lexing failure
    
http://gcc.gnu.org/PR61746
    
http://code.google.com/p/gofrontend/issues/detail?id=35
    
LGTM=crawshaw
R=golang-codereviews, crawshaw
CC=golang-codereviews
https://golang.org/cl/111980043

変更の背景

Go言語の仕様では、ブロックコメント(/* ... */)はネスト(入れ子)をサポートしていません。つまり、/*で開始されたコメントは、最初に出現する*/で閉じられます。このルールは、コメント内にさらに/*が出現しても、それはコメントの内容の一部として扱われ、新たなコメントの開始とはみなされません。

しかし、gccgo(Go言語のフロントエンドをGCCに統合したコンパイラ)の字句解析器(lexer)には、このネストされたコメントの処理に関するバグが存在しました。具体的には、/* /* nested */ */のような構造があった場合、gccgoのlexerがGo言語の仕様に反して、内側の/*を新たなコメントの開始と誤解釈し、結果として字句解析エラーや予期せぬ動作を引き起こす可能性がありました。

このコミットは、このgccgoのバグを修正し、Go言語のコメント仕様に準拠させるためのテストケースを追加するものです。これにより、gccgoがネストされたコメント構造を正しくエラーとして検出し、コンパイル時に適切なフィードバックを返すことを保証します。参照されているバグトラッカーのリンク(http://gcc.gnu.org/PR61746http://code.google.com/p/gofrontend/issues/detail?id=35)は、この問題が報告され、追跡されていたことを示しています。

前提知識の解説

字句解析(Lexical Analysis / Lexing)

字句解析とは、コンパイラの最初の段階であり、ソースコードをトークン(token)と呼ばれる意味のある最小単位の並びに変換するプロセスです。例えば、int x = 10;というコードは、int(キーワード)、x(識別子)、=(演算子)、10(リテラル)、;(区切り文字)といったトークンに分解されます。字句解析器(lexerまたはscanner)は、この処理を担当します。コメントは通常、字句解析の段階で無視され、トークンストリームには含まれません。

Go言語のコメント

Go言語には2種類のコメントがあります。

  1. 行コメント(Line Comments): //から行末までがコメントになります。
    // これは行コメントです
    
  2. ブロックコメント(Block Comments): /*で始まり、*/で終わる複数行にわたるコメントです。
    /*
    これは
    ブロックコメントです
    */
    
    Go言語の仕様では、ブロックコメントはネストしません。これは、/**/のペアが単純にマッチングされることを意味します。例えば、/* comment /* nested */ */というコードがあった場合、Goのlexerは最初の/*と最初の*/をペアとみなし、残りの */はコメントの外にある不正な構文として扱います。

gccgo

gccgoは、Go言語のプログラムをコンパイルするためのコンパイラフロントエンドの一つです。これは、GNU Compiler Collection (GCC) の一部として開発されており、Go言語のコードをGCCの内部表現に変換し、GCCのバックエンドを利用して様々なアーキテクチャ向けの実行ファイルを生成します。Go言語の公式コンパイラであるgcとは異なる実装ですが、Go言語の仕様に準拠することが求められます。

技術的詳細

このコミットが対処している問題は、gccgoの字句解析器がGo言語のブロックコメントの非ネスト規則を正しく適用できていなかった点にあります。

Go言語の字句解析器は、/*を検出すると、その後の文字を読み進め、*/が出現するまでをコメントとして扱います。この際、コメント内部に別の/*が出現しても、それは特別な意味を持たず、単なるコメントの内容の一部として扱われます。コメントの終了は、最初の*/によってのみ決定されます。

しかし、バグのあるgccgoのlexerは、コメント内部の/*を、新たなコメントの開始と誤って解釈していました。これにより、コメントの終了位置が誤って認識され、結果としてソースコードの残りの部分がコメントとして扱われたり、構文エラーが誤って報告されたりする可能性がありました。

追加されたテストケースは、この特定のシナリオを再現し、gccgoがGo言語の仕様通りに、ネストされたように見えるコメント構造を正しくエラーとして処理することを確認します。つまり、/* // comment */のようなコードは、Go言語の仕様では最初の/**/がペアになり、 // commentはコメントの内容の一部として扱われます。このテストは、gccgoがこの挙動を正しく模倣し、不正な*/が残らないことを検証します。

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

このコミットでは、test/fixedbugs/bug486.goという新しいテストファイルが追加されています。

--- /dev/null
+++ b/test/fixedbugs/bug486.go
@@ -0,0 +1,14 @@
+// compile
+
+// Copyright 2014 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.
+
+// The gccgo lexer had a bug handling nested comments.
+// http://gcc.gnu.org/PR61746
+// http://code.google.com/p/gofrontend/issues/detail?id=35
+
+package main
+
+/*// comment
+*/

コアとなるコードの解説

追加されたbug486.goファイルは非常にシンプルですが、gccgoのコメント字句解析のバグをピンポイントで狙ったものです。

// compile

// Copyright 2014 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.

// The gccgo lexer had a bug handling nested comments.
// http://gcc.gnu.org/PR61746
// http://code.google.com/p/gofrontend/issues/detail?id=35

package main

/*// comment
*/
  • // compile ディレクティブは、このファイルがコンパイル可能であるべきことをテストフレームワークに示します。このテストの目的は、コンパイルが成功するかどうかではなく、特定の構文エラーが正しく検出されるかどうかを確認することです。

  • package main は、通常のGoプログラムのエントリポイントを示します。

  • 最も重要な部分は以下のブロックコメントです。

    /*// comment
    */
    

    Go言語の仕様によれば、このコメントは最初の/*で始まり、最初の*/で終わります。したがって、// commentという部分はコメントの内容の一部として扱われます。この構造自体はGo言語の文法として有効です。

    しかし、もしgccgoのlexerがバグを抱えていた場合、/*の直後にある//を誤って行コメントの開始と解釈したり、あるいは/**/のペアリングを誤ったりする可能性がありました。このテストは、gccgoがこのコードを正しく字句解析し、Go言語の仕様に準拠した振る舞いをすること(つまり、このコードがコンパイルエラーにならないこと、または特定の期待されるエラーを出すこと)を検証します。

    このテストが追加された時点では、gccgoはこのコードを正しく処理できていなかったため、このテストはバグ修正後にパスするようになることを意図しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • GCC Bugzilla
  • GofrontendプロジェクトのIssueトラッカー
  • Stack OverflowなどのプログラミングQ&AサイトでのGoコメントに関する議論
  • GitHubのgolang/goリポジトリの関連Issue