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

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

このコミットは、Goコンパイラ(cmd/gc)におけるインライン化のバグ修正に関するものです。具体的には、チャネルからの受信演算子(<-)の優先順位が誤って設定されていたために、コンパイラが不正確なコードを生成し、特に括弧の削除が誤って行われる問題に対処しています。この修正により、インライン化されたコードが正しく動作するようになります。

コミット

commit b6ea905ed9cb1f0db828c0d58609c853eb82b89d
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Wed Aug 1 00:45:26 2012 +0200

    cmd/gc: fix inlining bug with receive operator.
    
    The receive operator was given incorrect precedence
    resulting in incorrect deletion of parentheses.
    
    Fixes #3843.
    
    R=rsc
    CC=golang-dev, remy
    https://golang.org/cl/6442049

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

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

元コミット内容

このコミットの元の内容は、Goコンパイラ(cmd/gc)がチャネルからの受信演算子(<-)をインライン化する際に発生するバグを修正するものです。具体的には、受信演算子の優先順位が誤って解釈され、その結果、コンパイラがコード内の括弧を不適切に削除してしまう問題がありました。この問題は、GoのIssue #3843として報告されていました。

変更の背景

この変更の背景には、Goコンパイラのインライン化最適化におけるバグが存在していました。Go言語では、チャネルからの受信操作(<-ch)は非常に一般的な操作ですが、この操作が他の演算子、特に型アサーション(.(type))と組み合わされた場合に、コンパイラがその優先順位を正しく認識できないという問題がありました。

具体的には、(<-x).(int)のような式において、コンパイラがインライン化を行う際に、<-xという受信操作全体が括弧で囲まれているにもかかわらず、その括弧を不適切に削除してしまうことがありました。これにより、<-x.(int)のように解釈され、これはxがチャネルではなくインターフェース型である場合にのみ有効な式となり、結果としてコンパイルエラーや不正な実行時動作を引き起こす可能性がありました。

この問題は、GoのIssue #3843として報告され、コンパイラのコード生成の正確性に影響を与える重要なバグとして認識されました。このコミットは、そのバグを修正し、Goプログラムの信頼性と安定性を向上させることを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびコンパイラの概念に関する知識が必要です。

  • Go言語のチャネルと受信演算子 (<-):
    • Go言語のチャネルは、ゴルーチン間で値を送受信するための通信メカニズムです。
    • 受信演算子 <- は、チャネルから値を受け取るために使用されます。例えば、value := <-ch はチャネル ch から値を受信し、value に代入します。
  • Go言語の型アサーション (.(type)):
    • 型アサーションは、インターフェース型の値が特定の具象型を保持しているかどうかをチェックし、その具象型の値を取り出すために使用されます。例えば、i.(int) はインターフェース iint 型の値を保持していることをアサートします。
  • 演算子の優先順位 (Operator Precedence):
    • プログラミング言語において、式が評価される順序を決定するルールです。例えば、a + b * c という式では、乗算(*)が加算(+)よりも優先順位が高いため、b * c が先に計算され、その結果が a に加算されます。
    • Go言語の仕様では、受信演算子 <- は単項演算子であり、その優先順位は比較的高く設定されています。型アサーション .(type) は、セレクタ(.)と型名から構成され、これも高い優先順位を持ちます。
  • コンパイラのインライン化 (Inlining):
    • コンパイラ最適化の一種で、関数呼び出しのオーバーヘッドを削減するために、呼び出される関数のコードを呼び出し元の場所に直接挿入する手法です。
    • インライン化はパフォーマンス向上に寄与しますが、コンパイラがインライン化されたコードのセマンティクス(意味)を正しく維持するためには、元のコードの演算子の優先順位や括弧の構造を正確に理解し、再構築する必要があります。
  • Goコンパイラ (cmd/gc):
    • Go言語の公式コンパイラです。ソースコードを解析し、中間表現に変換し、最適化を行い、最終的に実行可能なバイナリコードを生成します。
    • src/cmd/gc/fmt.c は、Goコンパイラのフロントエンドの一部であり、抽象構文木(AST)のノードのフォーマットや、演算子の優先順位に関する情報を管理している可能性があります。コンパイラ内部で式の構造を扱う上で重要なファイルです。

技術的詳細

このバグは、Goコンパイラのcmd/gcが、チャネルからの受信演算子<-の内部的な優先順位を誤って扱っていたことに起因します。Go言語の仕様では、受信演算子<-は単項演算子であり、その優先順位は比較的高く、型アサーション.(type)のような後置演算子よりも先に評価されるべきです。

問題の核心は、コンパイラ内部で演算子の優先順位を定義しているテーブル(opprec配列)において、ORECV(受信演算子を表す内部定数)の優先順位が8と設定されていた点にあります。この8という値は、他の特定の演算子(例えば、OPAREN(括弧)、OPRINTNなど)と同じ優先順位であり、これは受信演算子の実際の結合規則と一致していませんでした。

この誤った優先順位設定により、コンパイラが(<-x).(int)のような式を処理する際に、<-x全体が括弧で囲まれているにもかかわらず、インライン化の過程でこの括弧を不適切に削除してしまうことがありました。コンパイラは、<-x.(int)の間の結合が、<-x.(int)に適用されるかのように誤解釈し、結果として<-x.(int)のような不正な式を生成してしまう可能性がありました。これは、xがチャネル型ではなくインターフェース型である場合にのみ有効な構文であり、チャネル型に対してはコンパイルエラーとなります。

修正は、src/cmd/gc/fmt.c内のopprec配列において、ORECVの優先順位を8から7に変更することによって行われました。この7という優先順位は、OMINUS(単項マイナス)、OADDR(アドレス取得)、OIND(間接参照)といった他の単項演算子と同じであり、これにより受信演算子が正しく結合されるようになりました。この変更により、コンパイラは(<-x).(int)のような式を正しく解析し、インライン化の際にも括弧のセマンティクスを維持できるようになりました。

新しいテストケースtest/fixedbugs/bug448.gopkg1.gopkg2.goが追加され、このバグが修正されたことを検証しています。pkg1.goでは(<-x).(int)という問題の式を含む関数Do()が定義され、pkg2.goではその関数を呼び出しています。これにより、コンパイラがこの特定のケースを正しく処理できるようになったことが確認されます。

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

このコミットにおけるコアとなるコードの変更箇所は、src/cmd/gc/fmt.cファイル内のopprec配列の定義です。

--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -964,7 +964,6 @@ static int opprec[] = {
 	[OPAREN] = 8,
 	[OPRINTN] = 8,
 	[OPRINT] = 8,
-	[ORECV] = 8,
 	[ORUNESTR] = 8,
 	[OSTRARRAYBYTE] = 8,
 	[OSTRARRAYRUNE] = 8,
@@ -996,6 +995,7 @@ static int opprec[] = {
 	[OMINUS] = 7,
 	[OADDR] = 7,
 	[OIND] = 7,
+	[ORECV] = 7,
 
 	[OMUL] = 6,
 	[ODIV] = 6,

この変更では、ORECV(受信演算子)の優先順位が8から7に修正されています。

コアとなるコードの解説

src/cmd/gc/fmt.cファイルは、Goコンパイラ(gc)の内部で、抽象構文木(AST)のノードを処理する際に使用される演算子の優先順位を定義しています。opprec配列は、各演算子(内部的にはOで始まる定数で表現される)に対応する優先順位の整数値を格納しています。この数値が大きいほど優先順位が低い(つまり、結合が弱い)ことを意味します。

変更前は、ORECV(チャネル受信演算子 <-)の優先順位が8に設定されていました。この8という優先順位は、OPAREN(括弧)やOPRINTN(内部的なプリントノード)など、他の特定の演算子と同じグループに属していました。この設定が問題でした。

Go言語の文法において、(<-x).(int)のような式では、<-xという受信操作全体がまず評価され、その結果に対して型アサーション.(int)が適用されるべきです。しかし、ORECVの優先順位が8であったため、コンパイラがインライン化などの最適化を行う際に、<-xの括弧が不適切に削除され、<-x.(int)のように解釈される可能性がありました。これは、xがチャネルではなくインターフェース型である場合にのみ有効な構文であり、チャネル型に対してはコンパイルエラーや不正な動作を引き起こします。

修正では、ORECVの優先順位を7に引き下げました。この7という優先順位は、OMINUS(単項マイナス)、OADDR(アドレス取得)、OIND(間接参照)といった他の単項演算子と同じグループです。これらの単項演算子は、後続の式に強く結合する性質を持っています。ORECVの優先順位を7にすることで、コンパイラは<-xを一つのまとまりとして正しく認識し、その結果に対して後続の型アサーションが適用されるという正しい結合規則を維持できるようになりました。これにより、インライン化の際にも括弧が正しく保持され、コンパイルエラーや実行時エラーが回避されるようになりました。

この修正は、Goコンパイラのコード生成の正確性を保証し、特に複雑な式におけるインライン化の信頼性を向上させる上で非常に重要です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (特に言語仕様の演算子の優先順位に関するセクション)
  • Goコンパイラのソースコード (特にsrc/cmd/gcディレクトリ内のファイル)
  • Go Issue Tracker (Issue #3843の議論)
  • Goのインライン化に関する一般的な情報源