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

[インデックス 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)はコンパイル時に評価され、その結果がコードに埋め込まれることがあります。

このコミット以前は、定数式を評価する際に、その定数式を構成するサブノード(例: 12-)のorig情報が、評価後の結果ノードに誤って継承されてしまう問題がありました。これにより、デバッグ情報やエラーメッセージの生成時に、本来のノードとは異なるorig情報が参照され、誤った情報が表示される可能性がありました。

具体的には、コミットメッセージに記載されているように、-1という定数式が評価された結果のノードが、元の-1ではなく、そのサブノードである1origを継承してしまい、Nconv()関数(ノードを文字列に変換する関数)が1と表示してしまう問題がありました。同様に、2+3という定数式が評価された結果のノードが、2origを継承してしまい、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関数におけるNodeorigフィールドの取り扱いを修正した点にあります。

evconst関数は、与えられたノードが定数式である場合に、その値を評価し、ノードを評価結果に書き換える役割を担っています。元のコードでは、定数式が評価された後、結果のノード(*n = *nl;)に左オペランド(nl)のすべての情報がコピーされていました。これには、nlorigフィールドも含まれていました。

問題は、定数式が評価される際に、元のノードnが持っていたorig情報が、評価結果のノードに引き継がれずに、左オペランドnlorig情報で上書きされてしまうことでした。例えば、-1という式の場合、n-1全体を表すノードであり、nl1を表すノードです。evconst-1を評価して結果をnに書き込む際、nl(つまり1)のorignにコピーされてしまい、nが本来持っていた-1としてのorig情報が失われていました。

このコミットでは、この問題を解決するために、以下の変更が加えられました。

  1. norig変数の導入: evconst関数の冒頭で、元のノードnorigフィールドの値を一時的に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;
    
  2. 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が追加されました。これは、元のノードnorigフィールドの値を一時的に保持するためのポインタです。
  • norig = n->orig;: ret:ラベルの直前で、現在のノードnorigフィールドの値がnorigに保存されます。これは、*n = *nl;によってnの内容がnlの内容で上書きされる前に、元のorig情報を保護するためです。
  • *n = *nl;: この行は、定数式の評価結果である左オペランドnlのすべての内容を、元のノードnにコピーします。これにより、nは定数式の評価結果を表すノードに書き換えられます。
  • n->orig = norig;: *n = *nl;によって上書きされたnorigフィールドを、保存しておいた元の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に評価されます。
    • この3main関数内で使用されていないため、Goコンパイラは「値が使用されていない」という警告またはエラーを出すべきです。
    • // ERROR "3 not used|value computed is not used"というコメントは、コンパイラの出力に「3 not used」または「value computed is not used」という文字列が含まれていることを期待するテストアノテーションです。これにより、origフィールドの修正が正しく機能し、コンパイラが定数式の評価結果を正しく認識していることが検証されます。

このテストケースは、定数式の評価結果が正しくノードに反映され、そのorig情報も正確に保持されていることを確認するための重要な検証手段となります。

関連リンク

参考にした情報源リンク

  • Go言語のIssueトラッカー (内部): このコミットが参照しているIssue #2452は、Goの内部Issueトラッカーのものであり、一般に公開されているGitHubのIssueとは異なります。そのため、直接的な公開リンクは提供できません。
  • Go Gerrit Change-ID: https://golang.org/cl/5435064 は、Goプロジェクトが使用しているコードレビューシステムであるGerritの変更リスト(Change List)へのリンクです。これも内部的なシステムであり、一般のWeb検索では詳細な内容が取得できない場合があります。