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

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

このコミットは、Goコンパイラのインライン化におけるバグを修正するものです。具体的には、コンパイラがポインタ型や受信専用チャネル型のような、先頭に単項演算子と解釈されうる記号を持つ型を正しくフォーマットしないことで発生していた問題を解決します。これにより、インライン化の際にコードが誤って解釈されることを防ぎます。

コミット

  • Author: Russ Cox rsc@golang.org
  • Date: Wed Jan 11 13:21:06 2012 -0800
  • Message: gc: fix inlining bug

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

https://github.com/golang/go/commit/836a517f694a4d737f0e58c68700a94483834fb6

元コミット内容

gc: fix inlining bug

Fixes #2682.

R=lvd
CC=golang-dev
https://golang.org/cl/5538043

変更の背景

このコミットは、Goコンパイラ(gc)が特定の型の式を内部的にフォーマットする際に、曖昧さを生む可能性のある表現に対して適切な括弧を追加していなかったバグに対処しています。特に、ポインタ型 (*T) や受信専用チャネル型 (<-chan T) のnil値が、型アサーションや型変換の文脈で正しく表現されていませんでした。

このフォーマットの不備により、コンパイラのインライン化フェーズで、誤って解釈された式がインライン展開され、結果として不正なコードが生成されたり、コンパイルエラーが発生したりする可能性がありました。コミットメッセージにあるFixes #2682は、この特定のバグを追跡していた内部または外部の課題管理システムのエントリを指していると考えられます。test/fixedbugs/bug392というテストケースが追加されており、この問題が実際に発生していたことを示しています。

前提知識の解説

  • Goコンパイラ (gc): Go言語の公式コンパイラです。Goのソースコードを機械語に変換する役割を担います。歴史的に「garbage collector」の略称ですが、Goの文脈ではコンパイラ全体を指すことが一般的です。
  • インライン化 (Inlining): コンパイラ最適化の一種で、関数呼び出しのオーバーヘッドを削減するために、呼び出される関数の本体を呼び出し元に直接埋め込む技術です。これにより、実行速度の向上が期待できますが、コードサイズが増加する可能性もあります。コンパイラは、インライン化の対象となる関数を決定する際に、その関数の複雑さや呼び出し回数などを考慮します。
  • 型システムとnil: Goは静的型付け言語であり、厳格な型システムを持っています。nilは、ポインタ、チャネル、マップ、スライス、インターフェース、関数などの参照型のゼロ値として使用されます。nilは型によって異なる意味を持つため、コンパイラはそれぞれの型に応じたnilの扱いを正確に処理する必要があります。
  • src/cmd/gc/fmt.c: Goコンパイラのソースコードの一部です。このファイルは、コンパイラの内部表現(抽象構文木など)を整形(フォーマット)する役割を担っています。デバッグ出力や、コンパイラの異なるフェーズ間で情報をやり取りする際に、式の文字列表現を生成するために使用されます。
  • 単項演算子と型の曖昧さ: Go言語では、*はポインタのデリファレンス、<-はチャネルからの受信という単項演算子として機能します。しかし、型宣言においても*T(ポインタ型)や<-chan T(受信専用チャネル型)のようにこれらの記号が使われるため、コンパイラがこれらを正しく区別し、必要に応じて括弧で囲むことで曖昧さを解消する必要があります。例えば、*T(nil)*(T(nil))と解釈される可能性があり、これは意図しない動作につながります。正しくは(T)(nil)のように型変換として解釈されるべきです。

技術的詳細

このコミットの技術的な核心は、Goコンパイラのgcが、特定の型の式を内部的にフォーマットする際に、曖昧さを生む可能性のある表現に対して適切な括弧を追加していなかった点にあります。

src/cmd/gc/fmt.c内のexprfmt関数は、コンパイラが内部的に式を文字列として表現する際に使用されます。この関数は、式が特定の型を持つ場合に、その型を明示的に表示するロジックを含んでいます。

以前の実装では、if(isptr[n->type->etype])という条件で、式がポインタ型である場合にのみ、(%T)(%V)という形式で括弧付きの型と値をフォーマットしていました。これは、*がポインタ型を示すと同時に単項演算子でもあるため、曖昧さを避けるために括弧が必要だからです。

しかし、この条件だけでは不十分でした。<-chan Tのような受信専用チャネル型も、その先頭に<-という記号が来るため、単項演算子(チャネルからの受信)と誤解される可能性があります。例えば、(<-chan int)(nil)という型変換が、括弧がない場合に<- (chan int)(nil)のように解釈されてしまうと、構文エラーや意味の誤解釈につながります。

このバグにより、コンパイラのインライン化フェーズやその他の型チェックフェーズで、これらの型が正しく解釈されず、結果として不正なコードが生成されたり、コンパイルエラーが発生したりする可能性がありました。追加されたテストケースtest/fixedbugs/bug392.dir/one.gotwo.goは、特にfunc F2(c chan int) bool { return c == (<-chan int)(nil) }のようなチャネルのnil比較が問題を引き起こすことを示しています。

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

--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -1078,7 +1078,9 @@ exprfmt(Fmt *f, Node *n, int prec)\n     		if(n->val.ctype == CTNIL)\n     		\tn = n->orig; // if this node was a nil decorated with at type, print the original naked nil\n     		if(n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) {\n-    		\tif(isptr[n->type->etype])\n+    		\t// Need parens when type begins with what might\n+    		\t// be misinterpreted as a unary operator: * or <-.\n+    		\tif(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv))\n     		\t\treturn fmtprint(f, "(%T)(%V)", n->type, &n->val);\n     		\telse \n     		\t\treturn fmtprint(f, "%T(%V)", n->type, &n->val);\

コアとなるコードの解説

変更はsrc/cmd/gc/fmt.cファイルのexprfmt関数内で行われています。この関数は、コンパイラが内部的に式を文字列として表現する際に使用されます。

元のコードでは、if(isptr[n->type->etype])という条件で、式がポインタ型である場合にのみ、(%T)(%V)という形式で括弧付きの型と値をフォーマットしていました。これは、*がポインタ型を示すと同時に単項演算子でもあるため、曖昧さを避けるために括弧が必要だからです。

このコミットでは、この条件に|| (n->type->etype == TCHAN && n->type->chan == Crecv)が追加されました。

  • n->type->etype == TCHAN: 式の型がチャネル型であることを示します。
  • n->type->chan == Crecv: そのチャネルが受信専用チャネル(<-chan)であることを示します。

この追加により、受信専用チャネル型も、その先頭に<-という記号が来るため、単項演算子と誤解される可能性を考慮し、ポインタ型と同様に括弧で囲んでフォーマットされるようになりました。

これにより、コンパイラのインライン化フェーズやその他の型チェックフェーズで、これらの型が正しく解釈され、不正なコード生成やコンパイルエラーが防止されます。コメント// Need parens when type begins with what might // be misinterpreted as a unary operator: * or <-.が追加され、この変更の意図が明確に説明されています。

関連リンク

  • Go CL: https://golang.org/cl/5538043
  • (注: Fixes #2682で参照されているGoのIssue 2682は、公開されているGoのIssueトラッカーではこのインライン化のバグとは異なる内容(config.hが見つからないエラー)を指しているため、このコミットが参照しているのは内部的な課題管理システムのエントリである可能性があります。)

参考にした情報源リンク

  • Go言語のコンパイラに関する一般的な知識
  • Go言語の型システムとnilに関する公式ドキュメント
  • Go言語のインライン化に関する一般的な情報源 (例: Goのブログ記事、Stack Overflowの議論など)