[インデックス 11223] ファイルの概要
このコミットは、Goコンパイラ(gc
)において、文字列とrune配列間の変換(string
/arrayrune
conversions)の内部表現の処理、特にその「出力(printing)」に関するバグを修正するものです。具体的には、コンパイラの内部でこれらの変換が正しく扱われず、デバッグ出力やエラーメッセージ、あるいは最終的なコード生成の段階で問題を引き起こす可能性があった点を改善しています。
コミット
Goコンパイラ(gc
)が、文字列とrune配列間の型変換(string
/arrayrune
conversions)を適切に処理し、その内部表現を正しく出力できるように修正しました。これにより、関連するバグ(Issue #2714)が解決されます。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/18ee75ec88593d96796089038b93ed66596ae4d9
元コミット内容
commit 18ee75ec88593d96796089038b93ed66596ae4d9
Author: Luuk van Dijk <lvd@golang.org>
Date: Wed Jan 18 09:52:16 2012 +0100
gc: handle printing of string/arrayrune conversions
Fixes #2714.
R=rsc
CC=golang-dev
https://golang.org/cl/5540066
変更の背景
この変更の背景には、Goコンパイラ(gc
)が特定の型変換、特にバイト配列から文字列への変換([]byte
to string
)やrune配列から文字列への変換([]rune
to string
)、あるいはその逆の変換を、コンパイラの内部処理において適切に「出力(printing)」できていなかったという問題があります。
Go言語では、文字列はUTF-8バイトのシーケンスとして扱われ、runeはUnicodeコードポイントを表す整数型です。string
と[]byte
、string
と[]rune
の間には、それぞれ異なるセマンティクスを持つ変換が存在します。例えば、string(b []byte)
はバイトスライスを文字列に変換し、[]byte(s string)
は文字列をバイトスライスに変換します。同様に、string(r []rune)
はruneスライスを文字列に変換し、[]rune(s string)
は文字列をruneスライスに変換します。
コンパイラは、ソースコードを抽象構文木(AST)として内部的に表現し、そのASTを様々な段階で処理します。この「出力(printing)」とは、コンパイラがASTノードや中間表現をデバッグ目的で表示したり、エラーメッセージを生成したり、あるいは最終的なバイナリコードを生成する過程で、これらの変換操作を正しく表現する必要があることを指します。
Issue #2714は、これらの変換がコンパイラの内部で正しく認識されず、その結果として、例えばデバッグ出力が不正確になったり、コンパイラが予期せぬ動作をしたり、あるいは最適化の段階で問題が生じたりするバグを報告していたと考えられます。このコミットは、これらの変換操作がコンパイラの内部で適切に扱われ、その「出力」が正確に行われるようにするための修正です。
前提知識の解説
このコミットを理解するためには、Goコンパイラ(gc
)の基本的な構造と、その内部で型変換がどのように扱われるかについての知識が必要です。
- Goコンパイラ (
gc
): Go言語の公式コンパイラです。ソースコードを解析し、抽象構文木(AST)を構築し、型チェック、最適化、コード生成などの段階を経て、実行可能なバイナリを生成します。 src/cmd/gc/fmt.c
: Goコンパイラのソースコードの一部で、主にコンパイラの内部データ構造(ASTノードなど)をフォーマットし、デバッグ出力やエラーメッセージのために文字列として「出力」する役割を担っています。C言語で書かれていますが、これはGoコンパイラの初期の設計に由来します。- AST (Abstract Syntax Tree): 抽象構文木。ソースコードの構造を木構造で表現したものです。コンパイラはASTを操作することで、プログラムの意味を理解し、変換を行います。
Node
: ASTの各要素(式、文、宣言など)を表すデータ構造です。各Node
は、その種類を示すop
(オペレーションコード)フィールドを持っています。- オペレーションコード (Opcode): コンパイラが内部的に使用する、ASTノードの種類や操作を表す定数です。例えば、
OADD
は加算、OCALLFUNC
は関数呼び出しを表します。このコミットで追加されているOARRAYRUNESTR
、OSTRARRAYBYTE
、OSTRARRAYRUNE
などもこれに該当します。OARRAYBYTESTR
: バイト配列から文字列への変換([]byte
->string
)OARRAYRUNESTR
: Rune配列から文字列への変換([]rune
->string
)OSTRARRAYBYTE
: 文字列からバイト配列への変換(string
->[]byte
)OSTRARRAYRUNE
: 文字列からRune配列への変換(string
->[]rune
)ORUNESTR
: Runeから文字列への変換(rune
->string
)
opprec
配列:fmt.c
内で定義されている配列で、各オペレーションコード(op
)に対応する演算子の優先順位(precedence)を定義していると考えられます。コンパイラがASTを文字列として出力する際に、括弧を適切に配置するために使用されます。exprfmt
関数:fmt.c
内で定義されている関数で、ASTの式ノード(Node *n
)をフォーマットし、指定された出力ストリーム(Fmt *f
)に書き出す役割を担っています。この関数は、式の種類(n->op
)に応じて異なる処理を行います。nodedump
関数:fmt.c
内で定義されている関数で、ASTノードのデバッグ情報を出力するために使用されます。
技術的詳細
このコミットは、Goコンパイラが文字列とrune配列間の型変換を内部的に処理し、その表現を「出力」する際の不足を補うものです。具体的には、以下の2つの主要な変更が行われています。
-
演算子優先順位の定義 (
opprec
配列への追加): Goコンパイラは、ASTを文字列として出力する際に、演算子の優先順位を考慮して不要な括弧を省略したり、必要な括弧を挿入したりします。opprec
配列は、各内部オペレーションコード(op
)に対応する優先順位の値を保持しています。このコミットでは、新たに導入された、または以前は適切に扱われていなかった以下の変換操作のオペレーションコードがopprec
配列に追加されました。OARRAYRUNESTR
OSTRARRAYBYTE
OSTRARRAYRUNE
これらのオペレーションコードに8
という優先順位が割り当てられています。これは、既存のOARRAYBYTESTR
やORUNESTR
と同じ優先順位であり、これらの変換操作がコンパイラの内部で他の型変換や関数呼び出しなどと同様に、特定の優先順位を持つ式として扱われるべきであることを示しています。これにより、コンパイラがこれらの変換を含む式を正しく整形して出力できるようになります。
-
式フォーマット処理の拡張 (
exprfmt
関数への追加):exprfmt
関数は、ASTの式ノードを人間が読める形式の文字列に変換する役割を担っています。この関数内のswitch
文は、ノードのオペレーションコード(n->op
)に基づいて、異なる種類の式を処理します。 このコミットでは、既存のOCONVIFACE
(インターフェース変換)、OCONVNOP
(no-op変換)、OARRAYBYTESTR
(バイト配列から文字列への変換)、ORUNESTR
(runeから文字列への変換)などのケースに加えて、以下の変換操作が追加されました。OARRAYRUNESTR
OSTRARRAYBYTE
OSTRARRAYRUNE
これらのケースが追加されたことで、exprfmt
関数はこれらの変換操作を含む式を認識し、既存の変換と同様に(%T)(%N)
という形式で出力するようになります。ここで%T
は変換先の型を、%N
は変換元の式を表します。これは、コンパイラがこれらの型変換を明示的に表示する必要がある場合に、その構造を正確に反映するためのものです。
これらの変更により、Goコンパイラは、文字列とrune配列間の変換を、その内部表現の段階から正しく認識し、デバッグ出力、エラーメッセージ、あるいはコンパイラの他の段階での処理において、これらの変換を正確に表現できるようになります。これにより、Issue #2714で報告されたような、これらの変換に関連するバグが修正されます。
コアとなるコードの変更箇所
--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -943,6 +943,7 @@ static int opprec[] = {
[OAPPEND] = 8,
[OARRAYBYTESTR] = 8,
[OARRAYLIT] = 8,
+\t[OARRAYRUNESTR] = 8,
[OCALLFUNC] = 8,
[OCALLINTER] = 8,
[OCALLMETH] = 8,
@@ -969,6 +970,8 @@ static int opprec[] = {
[OPRINT] = 8,
[ORECV] = 8,
[ORUNESTR] = 8,
+\t[OSTRARRAYBYTE] = 8,
+\t[OSTRARRAYRUNE] = 8,
[OSTRUCTLIT] = 8,
[OTARRAY] = 8,
[OTCHAN] = 8,
@@ -1214,7 +1217,9 @@ exprfmt(Fmt *f, Node *n, int prec)\n \tcase OCONVIFACE:\n \tcase OCONVNOP:\n \tcase OARRAYBYTESTR:\n+\tcase OARRAYRUNESTR:\n \tcase OSTRARRAYBYTE:\n+\tcase OSTRARRAYRUNE:\n \tcase ORUNESTR:\n \t\tif(n->type == T || n->type->sym == S)\n \t\t\treturn fmtprint(f, \"(%T)(%N)\", n->type, n->left);\n@@ -1372,7 +1377,8 @@ nodedump(Fmt *fp, Node *n)\n \t\t\tindent(fp);\n \t\t}\n \t}\n-\t\tfmtprint(fp, \"[%p]\", n);\n+\n+//\tfmtprint(fp, \"[%p]\", n);\n \n \tswitch(n->op) {\n \tdefault:\n```
## コアとなるコードの解説
このコミットの主要な変更は、`src/cmd/gc/fmt.c`ファイル内の2つのセクションに集中しています。
1. **`opprec` 配列への追加**:
```c
[OARRAYLIT] = 8,
+\t[OARRAYRUNESTR] = 8,
[OCALLFUNC] = 8,
```
```c
[ORUNESTR] = 8,
+\t[OSTRARRAYBYTE] = 8,
+\t[OSTRARRAYRUNE] = 8,
[OSTRUCTLIT] = 8,
```
`opprec`配列は、Goコンパイラの内部で定義されている様々な操作(オペレーションコード)の優先順位を保持しています。この配列に、`OARRAYRUNESTR`(rune配列から文字列への変換)、`OSTRARRAYBYTE`(文字列からバイト配列への変換)、`OSTRARRAYRUNE`(文字列からrune配列への変換)という3つの新しいオペレーションコードが追加され、それぞれに優先順位`8`が割り当てられています。
この変更は、コンパイラがこれらの型変換を、他の既存の型変換(例: `OARRAYBYTESTR`)や関数呼び出し(例: `OCALLFUNC`)と同様に、特定の優先順位を持つ式として認識し、ASTを文字列として出力する際に、括弧の配置などを正しく行うために必要です。
2. **`exprfmt` 関数内の `switch` 文への追加**:
```c
case OARRAYBYTESTR:
+\tcase OARRAYRUNESTR:
\tcase OSTRARRAYBYTE:
+\tcase OSTRARRAYRUNE:
case ORUNESTR:
```
`exprfmt`関数は、GoコンパイラがASTの式ノードを人間が読める形式の文字列に変換する際に使用されます。この関数内の`switch`文は、ノードのオペレーションコード(`n->op`)に基づいて、異なる種類の式を処理します。
この変更により、`OARRAYRUNESTR`、`OSTRARRAYBYTE`、`OSTRARRAYRUNE`の各ケースが既存の`OCONVIFACE`(インターフェース変換)、`OCONVNOP`(no-op変換)、`OARRAYBYTESTR`(バイト配列から文字列への変換)、`ORUNESTR`(runeから文字列への変換)などと同じブロックに追加されました。
このブロック内のコードは、`if(n->type == T || n->type->sym == S)`という条件に基づいて、変換先の型と変換元の式を`(%T)(%N)`という形式で出力します。これは、これらの型変換がコンパイラの内部でどのように表現され、デバッグ出力やエラーメッセージなどでどのように表示されるべきかを定義しています。この追加により、コンパイラはこれらの特定の型変換を正しく認識し、その構造を正確に反映した文字列を生成できるようになります。
3. **`nodedump` 関数内のコメントアウト**:
```c
// fmtprint(fp, "[%p]", n);
```
この行は、`nodedump`関数内でノードのアドレスをデバッグ出力するためのコメントアウトされた行です。これは機能的な変更ではなく、おそらく開発中のデバッグコードが残っていたものを整理したものです。このコミットの主要な目的とは直接関係ありません。
これらの変更は、Goコンパイラが文字列とrune配列間の型変換を、その内部表現の段階から正しく認識し、デバッグ出力やエラーメッセージ、あるいはコンパイラの他の段階での処理において、これらの変換を正確に表現できるようにするために不可欠です。
## 関連リンク
* GitHubコミットページ: [https://github.com/golang/go/commit/18ee75ec88593d96796089038b93ed66596ae4d9](https://github.com/golang/go/commit/18ee75ec88593d96796089038b93ed66596ae4d9)
* Go Change List (Gerrit): [https://golang.org/cl/5540066](https://golang.org/cl/5540066)
## 参考にした情報源リンク
* `/home/orange/Project/comemo/commit_data/11223.txt` (コミットデータ)
* Google Web Search (golang/go #2714, golang.org/cl/5540066)