[インデックス 10521] ファイルの概要
このコミットは、Goコンパイラのガベージコレクション(GC)に関連する部分、具体的には定数式の評価におけるノードのorig
フィールドの扱いに関するバグ修正です。定数式を評価する際に、サブノードからorig
(元のノード情報)が誤って継承されてしまう問題が修正されました。これにより、-1
のようなノードが1
と表示されたり、2+3
のようなノードが2
と表示されるといった、誤った値が表示される不具合が解消されました。
コミット
commit 60e4a61d307c0e6c32d1933fbf14cf59193349ab
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Mon Nov 28 12:22:15 2011 -0500
gc: don't inherit orig from subnodes in constant expression nodes.
The wrong value made Nconv() show "1" for node "-1", and "2" from
node "2+3".
Fixes #2452.
R=gri, lvd, rsc
CC=golang-dev, remy
https://golang.org/cl/5435064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/60e4a61d307c0e6c32d1933fbf14cf59193349ab
元コミット内容
Goコンパイラのガベージコレクション(GC)部分において、定数式ノードがサブノードからorig
(元のノード情報)を継承しないように修正しました。この誤った値の継承により、Nconv()
関数が-1
というノードに対して1
と表示したり、2+3
というノードに対して2
と表示したりする不具合が発生していました。この修正は、Issue #2452を解決します。
変更の背景
Goコンパイラは、コードを解析し、抽象構文木(AST)を構築します。このASTの各ノードは、元のソースコードにおける位置やその他のメタデータを含むorig
フィールドを持つことがあります。定数式(例: 1 + 2
、-1
)はコンパイル時に評価され、その結果がコードに埋め込まれることがあります。
このコミット以前は、定数式を評価する際に、その定数式を構成するサブノード(例: 1
、2
、-
)のorig
情報が、評価後の結果ノードに誤って継承されてしまう問題がありました。これにより、デバッグ情報やエラーメッセージの生成時に、本来のノードとは異なるorig
情報が参照され、誤った情報が表示される可能性がありました。
具体的には、コミットメッセージに記載されているように、-1
という定数式が評価された結果のノードが、元の-1
ではなく、そのサブノードである1
のorig
を継承してしまい、Nconv()
関数(ノードを文字列に変換する関数)が1
と表示してしまう問題がありました。同様に、2+3
という定数式が評価された結果のノードが、2
のorig
を継承してしまい、Nconv()
が2
と表示してしまう問題も発生していました。
この問題は、コンパイラが生成するエラーメッセージの正確性に影響を与え、開発者が問題を特定するのを困難にする可能性がありました。そのため、このorig
フィールドの誤った継承を修正する必要がありました。
前提知識の解説
- Goコンパイラ (
gc
): Go言語の公式コンパイラです。ソースコードを機械語に変換する役割を担います。 - 抽象構文木 (AST): プログラムのソースコードの抽象的な構文構造を木構造で表現したものです。コンパイラはソースコードを解析し、ASTを構築して、その後の最適化やコード生成を行います。
- ノード (Node): ASTを構成する個々の要素です。変数、定数、演算子、関数呼び出しなどがノードとして表現されます。
Node->orig
フィールド: Goコンパイラの内部構造において、Node
構造体にはorig
というフィールドが存在します。このフィールドは、そのノードがソースコードのどの部分から派生したか、あるいは元のノードが何であったかを示す情報(例えば、元のノードの型や位置情報など)を保持するために使用されます。これは、エラー報告やデバッグ情報の生成において非常に重要です。- 定数式 (Constant Expression): コンパイル時にその値が決定される式のことです。例えば、
1 + 2
はコンパイル時に3
という値に評価されます。 src/cmd/gc/const.c
: Goコンパイラのソースコードの一部で、定数式の評価ロジックが実装されているファイルです。Nconv()
: Goコンパイラの内部関数で、ノードを文字列表現に変換するために使用されます。デバッグ出力やエラーメッセージの生成時に利用されます。
技術的詳細
このコミットの技術的な核心は、src/cmd/gc/const.c
ファイル内のevconst
関数におけるNode
のorig
フィールドの取り扱いを修正した点にあります。
evconst
関数は、与えられたノードが定数式である場合に、その値を評価し、ノードを評価結果に書き換える役割を担っています。元のコードでは、定数式が評価された後、結果のノード(*n = *nl;
)に左オペランド(nl
)のすべての情報がコピーされていました。これには、nl
のorig
フィールドも含まれていました。
問題は、定数式が評価される際に、元のノードn
が持っていたorig
情報が、評価結果のノードに引き継がれずに、左オペランドnl
のorig
情報で上書きされてしまうことでした。例えば、-1
という式の場合、n
は-1
全体を表すノードであり、nl
は1
を表すノードです。evconst
が-1
を評価して結果をn
に書き込む際、nl
(つまり1
)のorig
がn
にコピーされてしまい、n
が本来持っていた-1
としてのorig
情報が失われていました。
このコミットでは、この問題を解決するために、以下の変更が加えられました。
norig
変数の導入:evconst
関数の冒頭で、元のノードn
のorig
フィールドの値を一時的にnorig
という新しい変数に保存するようにしました。Node *nl, *nr, *norig; // norigが追加 // ... ret: norig = n->orig; // n->origを保存 // rewrite n in place. *n = *nl; // restore value of n->orig. n->orig = norig; // 保存しておいたnorigを復元 n->val = v;
orig
フィールドの復元: 定数式の評価が完了し、左オペランドnl
の内容がn
にコピーされた後、一時的に保存しておいたnorig
の値をn->orig
に復元するようにしました。
この変更により、定数式が評価されてノードが書き換えられた後も、元のノードが持っていた正確なorig
情報が保持されるようになりました。これにより、Nconv()
のような関数がノードの情報を表示する際に、正しい元のノード情報に基づいて表示されるようになり、デバッグ情報やエラーメッセージの正確性が向上しました。
コアとなるコードの変更箇所
src/cmd/gc/const.c
--- a/src/cmd/gc/const.c
+++ b/src/cmd/gc/const.c
@@ -424,7 +424,7 @@ isconst(Node *n, int ct)\n void
evconst(Node *n)\n {\n-\tNode *nl, *nr;\n+\tNode *nl, *nr, *norig;\n \tint32 len;\n \tStrlit *str;\n \tint wl, wr, lno, et;\
@@ -842,8 +842,11 @@ unary:\n \t}\n \n ret:\n+\tnorig = n->orig;\n \t// rewrite n in place.\n \t*n = *nl;\n+\t// restore value of n->orig.\n+\tn->orig = norig;\
\tn->val = v;\
\n \t// check range.\
test/fixedbugs/bug379.go
(新規追加ファイル)
// 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 2452.
// Check that the error messages says
// bug378.go:17: 3 not used
// and not
// bug378.go:17: 1 not used
package main
func main() {
1 + 2 // ERROR "3 not used|value computed is not used"
}
コアとなるコードの解説
src/cmd/gc/const.c
の変更
Node *nl, *nr, *norig;
:evconst
関数のローカル変数宣言にnorig
が追加されました。これは、元のノードn
のorig
フィールドの値を一時的に保持するためのポインタです。norig = n->orig;
:ret:
ラベルの直前で、現在のノードn
のorig
フィールドの値がnorig
に保存されます。これは、*n = *nl;
によってn
の内容がnl
の内容で上書きされる前に、元のorig
情報を保護するためです。*n = *nl;
: この行は、定数式の評価結果である左オペランドnl
のすべての内容を、元のノードn
にコピーします。これにより、n
は定数式の評価結果を表すノードに書き換えられます。n->orig = norig;
:*n = *nl;
によって上書きされたn
のorig
フィールドを、保存しておいた元のnorig
の値で復元します。これにより、ノードn
は評価結果の値を持ちつつ、元のソースコードにおける正確なorig
情報を保持することができます。
この変更により、定数式が評価されてノードが書き換えられた後も、元のノードが持っていた正確なorig
情報が保持されるようになり、Nconv()
のような関数がノードの情報を表示する際に、正しい元のノード情報に基づいて表示されるようになります。
test/fixedbugs/bug379.go
の追加
このファイルは、修正されたバグをテストするための新しいテストケースです。
// errchk $G $D/$F.go
: この行は、Goのテストフレームワークがこのファイルをコンパイルし、その出力(エラーメッセージなど)をチェックすることを示しています。// Issue 2452.
: このテストが解決するGoの内部Issue番号を示しています。- コメントブロック:
このコメントは、このテストの目的を明確に示しています。// Check that the error messages says // bug378.go:17: 3 not used // and not // bug378.go:17: 1 not used
1 + 2
という定数式が評価された際に、その結果である3
が使用されていないというエラーメッセージが正しく表示されることを期待しています。バグ修正前は、1
が使用されていないという誤ったメッセージが表示される可能性がありました。 func main() { 1 + 2 // ERROR "3 not used|value computed is not used" }
:1 + 2
は定数式であり、コンパイル時に3
に評価されます。- この
3
はmain
関数内で使用されていないため、Goコンパイラは「値が使用されていない」という警告またはエラーを出すべきです。 // ERROR "3 not used|value computed is not used"
というコメントは、コンパイラの出力に「3 not used」または「value computed is not used」という文字列が含まれていることを期待するテストアノテーションです。これにより、orig
フィールドの修正が正しく機能し、コンパイラが定数式の評価結果を正しく認識していることが検証されます。
このテストケースは、定数式の評価結果が正しくノードに反映され、そのorig
情報も正確に保持されていることを確認するための重要な検証手段となります。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- Goコンパイラのソースコード: https://github.com/golang/go/tree/master/src/cmd/compile (現在のGoコンパイラの主要なソースコードは
src/cmd/compile
以下にあります。このコミット当時のgc
はより広範な意味で使われていました。)
参考にした情報源リンク
- Go言語のIssueトラッカー (内部): このコミットが参照しているIssue #2452は、Goの内部Issueトラッカーのものであり、一般に公開されているGitHubのIssueとは異なります。そのため、直接的な公開リンクは提供できません。
- Go Gerrit Change-ID:
https://golang.org/cl/5435064
は、Goプロジェクトが使用しているコードレビューシステムであるGerritの変更リスト(Change List)へのリンクです。これも内部的なシステムであり、一般のWeb検索では詳細な内容が取得できない場合があります。