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

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

このコミットは、Goコンパイラ(gc)におけるswitchステートメントの処理に関する冗長な型チェックを削除するものです。具体的には、switchステートメントの初期化部分(ninit)に対する不要なwalkstmtlist呼び出しが削除され、コンパイラの効率が向上し、関連するバグ(Issue 2576)が修正されました。この変更は、コンパイラの内部処理の最適化と、特定のコードパターンにおける正確な動作の保証を目的としています。

コミット

gc: remove now redundant typecheck of ->ninit on switches.

Fixes #2576.

R=rsc
CC=golang-dev
https://golang.org/cl/5498105

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

https://github.com/golang/go/commit/41806ec26db3316c7ed4857bb3420cb34e61b28c

元コミット内容

Goコンパイラ(gc)において、switchステートメントの初期化部分(->ninit)に対する冗長な型チェックを削除します。これにより、Issue 2576が修正されます。

変更の背景

このコミットは、Goコンパイラがswitchステートメントを処理する際に発生していた、特定の冗長な処理を解消するために行われました。具体的には、switchステートメントの初期化部分(sw->ninit)が、コンパイルプロセスの早い段階で既に適切に型チェックされ、処理されているにもかかわらず、walkswitch関数内で再度walkstmtlistによる型チェックが実行されていました。

この冗長な処理は、コンパイル時間の無駄だけでなく、場合によっては予期せぬコンパイラの動作やバグを引き起こす可能性がありました。コミットメッセージに「Fixes #2576」とあることから、この冗長な型チェックが特定のバグ(Issue 2576)の原因となっていたことが示唆されます。追加されたテストケースtest/fixedbugs/bug391.goは、switch _, _ = t.a, t.a; {}という形式のswitchステートメントの初期化部分が、この問題の影響を受けていたことを示しています。このテストケースは、コンパイラがこのような構文を正しく処理できることを保証するために追加されました。

前提知識の解説

このコミットを理解するためには、Goコンパイラの基本的な構造と、コンパイルプロセスにおけるいくつかの概念を理解する必要があります。

  • Goコンパイラ (gc): 2012年当時、Go言語の主要なコンパイラはcmd/gcとして知られていました。これは、Go言語で書かれたソースコードを機械語に変換する役割を担っています。コンパイラは、字句解析、構文解析、意味解析(型チェックなど)、中間コード生成、最適化、コード生成といった複数のフェーズを経て動作します。

  • 抽象構文木 (AST - Abstract Syntax Tree): コンパイラの構文解析フェーズで、ソースコードは抽象構文木(AST)と呼ばれるツリー構造のデータ表現に変換されます。ASTは、プログラムの構造を抽象的に表現し、コンパイラの後のフェーズ(型チェック、最適化、コード生成など)で利用されます。各ノードは、変数宣言、関数呼び出し、制御構造(ifforswitchなど)といったプログラムの要素に対応します。

  • switch ステートメントの構造: Go言語のswitchステートメントは、他の多くの言語と同様に、複数のcase節を持つ制御構造です。特徴的なのは、switchキーワードの後に初期化ステートメントと条件式をオプションで記述できる点です。

    switch initStmt; tagExpr {
    case val1:
        // ...
    case val2:
        // ...
    default:
        // ...
    }
    

    ここでinitStmtが初期化ステートメント、tagExprが条件式です。

  • ninit フィールド: GoコンパイラのASTノードにおいて、ninitは特定のノード(この場合はswitchステートメントのノード)に関連付けられた初期化ステートメントのリストを指すフィールドです。例えば、switchステートメントのinitStmt部分がこれに該当します。

  • walkstmtlist 関数: Goコンパイラの「ウォーク(walk)」フェーズは、ASTを走査し、各ノードに対して意味解析や変換を行うプロセスです。walkstmtlistは、ステートメントのリストを順番に走査し、それぞれのステートメントに対して型チェックやその他の必要な処理を実行するコンパイラ内部の関数です。この関数は、ASTの特定のサブツリーを処理するために再帰的に呼び出されることがあります。

  • 冗長な型チェック: コンパイルプロセスにおいて、同じコード部分に対して複数回、不必要な型チェックや処理が行われることを指します。これは、コンパイラの設計上の問題や、異なるコンパイルフェーズ間での情報の引き継ぎが不完全な場合に発生し、コンパイル時間の増加や、まれに誤った最適化、あるいはバグの原因となることがあります。

技術的詳細

このコミットの技術的詳細を理解するには、Goコンパイラのswitchステートメント処理ロジックに焦点を当てる必要があります。

Goコンパイラ(cmd/gc)のsrc/cmd/gc/swt.cファイルは、switchステートメントのコンパイルに関連する処理を担っていました。このファイル内のwalkswitch関数は、switchステートメントのASTノードを受け取り、そのノードをウォーク(走査)して、型チェック、コード変換、および最終的なコード生成のための準備を行います。

変更前のwalkswitch関数には、以下の行が含まれていました。

walkstmtlist(sw->ninit);

この行は、switchステートメントの初期化部分(sw->ninit)に含まれるステートメントのリストに対して、walkstmtlist関数を呼び出し、再度型チェックやその他の処理を実行していました。

しかし、コンパイルプロセスの他の部分、特にswitchステートメントのASTノードが構築される段階や、それ以前のウォークフェーズで、sw->ninitに含まれるステートメントは既に適切に型チェックされ、処理が完了していることが判明しました。したがって、walkswitch関数内で再度walkstmtlist(sw->ninit)を呼び出すことは、完全に冗長な処理であり、コンパイラのオーバーヘッドを増大させていました。

この冗長な呼び出しを削除することで、コンパイラはswitchステートメントの初期化部分を二重に処理する必要がなくなり、コンパイル効率が向上します。また、この冗長な処理が引き起こしていた可能性のある特定のバグ(Issue 2576)も同時に修正されます。Issue 2576は、switchステートメントの初期化部分に複数の代入が含まれる場合に、コンパイラが誤った型チェックを行う、または内部エラーを発生させる問題であったと推測されます。

追加されたtest/fixedbugs/bug391.goは、この特定のシナリオを再現し、修正が正しく適用されたことを検証するためのものです。このテストは、switch _, _ = t.a, t.a; {}という構文が、冗長な型チェックが削除された後も正しくコンパイルされることを保証します。

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

diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
index 7764f0dd35..8b1b93c7da 100644
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -792,7 +792,6 @@ walkswitch(Node *sw)
 	 * cases have OGOTO into statements.
 	 * both have inserted OBREAK statements
 	 */
-	walkstmtlist(sw->ninit);
 	if(sw->ntest == N) {
 		sw->ntest = nodbool(1);
 		typecheck(&sw->ntest, Erv);
diff --git a/test/fixedbugs/bug391.go b/test/fixedbugs/bug391.go
new file mode 100644
index 0000000000..81507188b2
--- /dev/null
+++ b/test/fixedbugs/bug391.go
@@ -0,0 +1,14 @@
+// $G $D/$F.go || echo "Issue2576"
+
+// 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 2576
+package bug
+
+type T struct { a int }
+
+func f(t T) {
+        switch _, _ = t.a, t.a; {}
+}
\ No newline at end of file

コアとなるコードの解説

このコミットの主要な変更は、src/cmd/gc/swt.cファイル内のwalkswitch関数から、以下の1行が削除されたことです。

-	walkstmtlist(sw->ninit);

この行の削除は、switchステートメントの初期化部分(sw->ninit)に対するwalkstmtlistの呼び出しが不要になったことを意味します。これは、コンパイルプロセスのより早い段階で、switchステートメントの初期化ステートメントが既に適切に処理され、型チェックが完了しているためです。この冗長な処理を削除することで、コンパイラの効率が向上し、コンパイル時間が短縮されます。

また、この変更と同時に、test/fixedbugs/bug391.goという新しいテストファイルが追加されました。このテストファイルは、Issue 2576で報告された特定のバグシナリオを再現するために作成されました。

func f(t T) {
        switch _, _ = t.a, t.a; {}
}

このテストケースは、switchステートメントの初期化部分で複数の代入が行われるという、以前問題を引き起こしていた可能性のある構文を含んでいます。このテストが追加されたことで、今回の変更がIssue 2576を正しく修正し、将来的に同様の回帰が発生しないことが保証されます。

要するに、このコミットは、Goコンパイラの内部処理を合理化し、特定のswitchステートメントの構文に関するバグを修正することで、コンパイラの堅牢性と効率を向上させています。

関連リンク

  • Gerrit Change-ID: https://golang.org/cl/5498105

参考にした情報源リンク