[インデックス 16492] ファイルの概要
このコミットは、Go言語のCコンパイラ(cmd/cc
)において、ワイド文字列定数で21ビットのルーン(rune)をサポートするための変更を導入します。これは、Unicodeのより広い範囲の文字を正確に表現し、処理するために必要となる重要な更新です。また、以前の暫定的な修正の巻き戻しと、Plan 9コンパイラからのコードの取り込みも含まれています。
コミット
commit 781b2a25190f163af848c04a71adfbbf7ce457e2
Author: Anthony Martin <ality@pbrane.org>
Date: Tue Jun 4 16:30:55 2013 -0700
cmd/cc: support 21-bit runes in wide string constants
Changeset 7557a627e9b5 added a temporary stop-gap to silence
a print format warning for %S. This has been reverted.
None of this code is original. It was copied from the latest
Plan 9 compilers.
R=golang-dev, r, rsc
CC=golang-dev
https://golang.org/cl/8630044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/781b2a25190f163af848c04a71adfbbf7ce457e2
元コミット内容
cmd/cc: support 21-bit runes in wide string constants
このコミットは、ワイド文字列定数における21ビットルーンのサポートを追加します。以前の変更セット 7557a627e9b5
で %S
の出力フォーマット警告を抑制するための暫定的な修正が追加されましたが、これはこのコミットで元に戻されています。このコードは独自のものではなく、最新のPlan 9コンパイラからコピーされたものです。
変更の背景
Go言語はUnicodeをネイティブにサポートしており、文字列はUTF-8でエンコードされたバイト列として扱われます。Goの「ルーン(rune)」は、Unicodeのコードポイントを表すint32
型のエイリアスです。UnicodeのコードポイントはU+0000からU+10FFFFまでの範囲にあり、最大で21ビットを必要とします。
従来のC言語のワイド文字(wchar_t
)は、システムによってサイズが異なり、通常は16ビットまたは32ビットです。16ビットのワイド文字(ushort
など)では、U+FFFFを超えるUnicodeコードポイント(サロゲートペアなど)を単一の文字として直接表現することができませんでした。GoのCコンパイラがこれらのワイド文字列定数を正しく処理するためには、21ビットのルーンを適切に扱えるように内部表現と処理ロジックを更新する必要がありました。
この変更のもう一つの背景は、GoのツールチェインがPlan 9の思想とコードベースに深く根ざしていることです。Plan 9のコンパイラは、その設計思想と実装において、Goの初期のコンパイラに大きな影響を与えました。このコミットは、Plan 9コンパイラの最新の改善を取り込むことで、GoのCコンパイラの堅牢性とUnicodeサポートを強化することを目的としています。
前提知識の解説
- ルーン (Rune): Go言語におけるUnicodeコードポイントを表す型です。
int32
のエイリアスであり、単一のUnicode文字を表現します。UTF-8エンコーディングでは1バイトから4バイトの可変長ですが、ルーン自体は固定長のコードポイントです。 - ワイド文字列定数: C言語において、通常の文字列リテラル(
"abc"
)がchar
の配列であるのに対し、ワイド文字列リテラル(L"abc"
)はwchar_t
の配列として扱われます。GoのCコンパイラでは、Goのルーンの概念と結びついて、より広い文字セットを扱うための内部表現として使用されます。 - Plan 9 コンパイラ: ベル研究所で開発されたオペレーティングシステムPlan 9のCコンパイラ群です。Go言語の初期のコンパイラ(
gc
)は、Plan 9のコンパイラ(8c
,6c
,5c
など)のコードベースをフォークして開発されました。そのため、GoのツールチェインにはPlan 9の設計思想やコードが色濃く残っています。 - Bison (Yacc): GNU Bisonは、Yacc(Yet Another Compiler Compiler)互換のパーサジェネレータです。文法定義ファイル(
.y
または.yy
)から、C言語などのソースコード(パーサ)を生成します。このコミットでは、Bisonのバージョンアップに伴う生成コードの変更がy.tab.c
とy.tab.h
に現れています。 ushort
: 通常16ビットの符号なし整数型です。多くのシステムでwchar_t
が16ビットの場合、これにマッピングされます。しかし、Unicodeの全範囲をカバーするには不十分です。- 21ビットルーン: UnicodeのコードポイントはU+0000からU+10FFFFまで存在し、最大で21ビットが必要です。例えば、絵文字や一部の漢字などは16ビットでは表現できません。
技術的詳細
このコミットの主要な技術的変更点は、GoのCコンパイラが内部的にワイド文字列を表現するために使用する型を、従来のushort
から、21ビットルーンを適切に格納できるTRune
型へと変更したことです。
-
TRune
型の導入:src/cmd/cc/cc.h
でtypedef Rune TRune;
が追加されました。Rune
はGoの内部でUnicodeコードポイントを表すために使用される型であり、通常はint32
に相当します。これにより、TRune
は少なくとも32ビットの幅を持つことが保証され、21ビットのルーンを問題なく格納できるようになります。enum
定義内でTRUNE = sizeof(TRune)==4? TUINT: TUSHORT,
という行が追加されました。これは、TRune
のサイズが4バイト(32ビット)であればTUINT
(符号なし整数型)として扱い、そうでなければTUSHORT
(符号なし短整数型)として扱うことを示しています。これにより、ターゲットシステムにおけるTRune
の実際のサイズに応じて適切な型が選択されるようになります。
-
ワイド文字列内部表現の変更:
struct Node
内のrstring
フィールドの型がushort*
からTRune*
に変更されました。Node
はコンパイラの抽象構文木(AST)のノードを表す構造体であり、rstring
はワイド文字列定数の値を保持します。この変更により、ASTレベルで21ビットルーンを直接扱えるようになります。
-
パーサとレキサの更新:
src/cmd/cc/cc.y
(Bisonの文法定義ファイル)とsrc/cmd/cc/lex.c
(レキサのソースファイル)において、ワイド文字列定数(lstring
)の処理がushort
からTRune
に切り替わりました。これには、文字列の長さ計算(sizeof(ushort)
からsizeof(TRune)
へ)、メモリ割り当て、および値の格納方法の変更が含まれます。- 特に
lex.c
では、文字定数や文字列定数をスキャンする際に、convvtox
関数に渡す型がTUSHORT
からTRUNE
に変更されています。また、getnsc
関数で空白文字をスキップするロジックにc >= Runeself
という条件が追加されており、これはUnicodeの非ASCII文字(ルーン)を正しく扱うための調整です。
-
出力処理の変更:
src/cmd/cc/pswt.c
のoutlstring
関数は、ワイド文字列をバイト列として出力する役割を担います。この関数もushort *s
からTRune *s
を受け取るように変更され、内部のバッファサイズとバイトオーダー処理がsizeof(TRune)
に基づいて動的に行われるようになりました。これにより、TRune
が16ビットでも32ビットでも正しくバイト列に変換できるようになります。src/cmd/cc/com.c
では、ワイド文字列のパディング処理において、ushort
のゼロ値ではなくTRune
のゼロ値を使用するように変更されています。src/cmd/cc/sub.c
のprtree1
関数では、デバッグ出力のためにワイド文字列をフォーマットする際に、sizeof(TRune) == sizeof(Rune)
の場合にのみ%S
フォーマットを使用し、そうでない場合は汎用的な"..."
を出力するように変更されました。これは、%S
フォーマットが常にRune
型(int32
)を期待する可能性があるため、型が一致しない場合に誤った出力を避けるための安全策と考えられます。
-
Bisonバージョンアップに伴う変更:
src/cmd/cc/y.tab.c
とsrc/cmd/cc/y.tab.h
は、Bisonパーサジェネレータによって自動生成されるファイルです。このコミットでは、Bisonのバージョンが2.3から2.7.12-4996に更新されたため、これらのファイルの内容が大幅に変更されています。これには、パーサの内部構造、エラー処理ロジック、マクロ定義などが含まれます。この変更自体はルーンのサポートとは直接関係ありませんが、ツールチェインの更新の一環として行われました。
これらの変更により、GoのCコンパイラは、Unicodeの全範囲をカバーする21ビットのルーンを含むワイド文字列定数を、より正確かつ効率的に処理できるようになりました。
コアとなるコードの変更箇所
-
src/cmd/cc/cc.h
:--- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -55,6 +55,8 @@ typedef struct Bits Bits; typedef struct Dynimp Dynimp; typedef struct Dynexp Dynexp; +typedef Rune TRune; /* target system type */ + #define BUFSIZ 8192 #define NSYMB 500 #define NHASH 1024 @@ -85,7 +87,7 @@ struct Node double fconst; /* fp constant */ vlong vconst; /* non fp const */ char* cstring; /* character string */ - ushort* rstring; /* rune string */ + TRune* rstring; /* rune string */ Sym* sym; Type* type; @@ -367,6 +369,9 @@ enum TFILE, TOLD, NALLTYPES, + + /* adapt size of Rune to target system's size */ + TRUNE = sizeof(TRune)==4? TUINT: TUSHORT, }; enum { @@ -766,7 +771,7 @@ void gclean(void); void gextern(Sym*, Node*, int32, int32); void ginit(void); int32 outstring(char*, int32); -int32 outlstring(ushort*, int32); +int32 outlstring(TRune*, int32); void sextern(Sym*, Node*, int32, int32); void xcom(Node*); int32 exreg(Type*); @@ -800,7 +805,6 @@ int machcap(Node*); #pragma varargck type "Q" int32 #pragma varargck type "O" int #pragma varargck type "O" uint -#pragma varargck type "S" ushort* #pragma varargck type "T" Type* #pragma varargck type "U" char* #pragma varargck type "|" int
-
src/cmd/cc/cc.y
:--- a/src/cmd/cc/cc.y +++ b/src/cmd/cc/cc.y @@ -891,9 +891,9 @@ lstring: LLSTRING { $$ = new(OLSTRING, Z, Z); - $$->type = typ(TARRAY, types[TUSHORT]); - $$->type->width = $1.l + sizeof(ushort); - $$->rstring = (ushort*)$1.s; + $$->type = typ(TARRAY, types[TRUNE]); + $$->type->width = $1.l + sizeof(TRune); + $$->rstring = (TRune*)$1.s; $$->sym = symstring; $$->etype = TARRAY; $$->class = CSTATIC; @@ -903,16 +903,16 @@ lstring: char *s; int n; - n = $1->type->width - sizeof(ushort); + n = $1->type->width - sizeof(TRune); s = alloc(n+$2.l+MAXALIGN); memcpy(s, $1->rstring, n); memcpy(s+n, $2.s, $2.l); - *(ushort*)(s+n+$2.l) = 0; + *(TRune*)(s+n+$2.l) = 0; $$ = $1; $$->type->width += $2.l; - $$->rstring = (ushort*)s; + $$->rstring = (TRune*)s; } zelist:
-
src/cmd/cc/pswt.c
:--- a/src/cmd/cc/pswt.c +++ b/src/cmd/cc/pswt.c @@ -102,28 +102,29 @@ newcase(void) } int32 -outlstring(ushort *s, int32 n) +outlstring(TRune *s, int32 n) { - char buf[2]; - int c; + char buf[sizeof(TRune)]; + uint c; + int i; int32 r; if(suppress) return nstring; - while(nstring & 1) + while(nstring & (sizeof(TRune)-1)) outstring("", 1); r = nstring; while(n > 0) { c = *s++; if(align(0, types[TCHAR], Aarg1, nil)) { - buf[0] = c>>8; - buf[1] = c; + for(i = 0; i < sizeof(TRune); i++) + buf[i] = c>>(8*(sizeof(TRune) - i - 1)); } else { - buf[0] = c; - buf[1] = c>>8; + for(i = 0; i < sizeof(TRune); i++) + buf[i] = c>>(8*i); } - outstring(buf, 2); - n -= sizeof(ushort); + outstring(buf, sizeof(TRune)); + n -= sizeof(TRune); } return r; }
コアとなるコードの解説
上記の変更箇所は、GoのCコンパイラがワイド文字列定数を処理する方法の根本的な変更を示しています。
-
cc.h
におけるTRune
の定義と利用:typedef Rune TRune;
は、Goの内部でUnicodeコードポイントを表すRune
型を、Cコンパイラの文脈でTRune
としてエイリアスしています。これにより、ルーンのサイズがint32
(4バイト)であることが保証され、Unicodeの21ビットコードポイントを完全に表現できるようになります。struct Node
のrstring
フィールドがushort*
からTRune*
に変更されたことで、抽象構文木(AST)のノードがワイド文字列定数を保持する際に、16ビットのushort
ではなく、より広い範囲のUnicode文字を格納できるTRune
のポインタを持つようになります。これは、コンパイラの内部表現がUnicodeの要件に適合するように更新されたことを意味します。TRUNE = sizeof(TRune)==4? TUINT: TUSHORT,
という定義は、コンパイル時のTRune
の実際のサイズに応じて、コンパイラがその型をTUINT
(符号なし整数)またはTUSHORT
(符号なし短整数)として扱うことを示しています。これにより、異なるアーキテクチャやコンパイル設定においても、TRune
のサイズに合わせた適切な型推論と処理が可能になります。outlstring
関数のシグネチャ変更も、ワイド文字列の出力処理がTRune
型を直接扱うように更新されたことを示しています。
-
cc.y
におけるlstring
ルールの変更:lstring
ルールは、ワイド文字列定数の構文解析とASTノードの生成を担当します。このルール内で、$$->type = typ(TARRAY, types[TUSHORT]);
が$$->type = typ(TARRAY, types[TRUNE]);
に変更されたことで、生成されるワイド文字列定数の型がTRune
の配列として扱われるようになります。- 文字列の幅の計算も
sizeof(ushort)
からsizeof(TRune)
に変更され、メモリ割り当てや文字列の結合時にTRune
のサイズが考慮されるようになりました。これにより、可変長のUTF-8バイト列ではなく、固定長のルーンとしてワイド文字列が正しく処理されます。
-
pswt.c
におけるoutlstring
関数の変更:outlstring
関数は、ワイド文字列をターゲットのバイナリ形式で出力する役割を担います。この関数がTRune *s
を受け取るように変更されたことで、入力がTRune
のシーケンスであることを前提として処理が行われます。- 内部のバッファ
buf
のサイズがsizeof(TRune)
に、ループ条件がnstring & (sizeof(TRune)-1)
に、そしてoutstring
に渡すサイズがsizeof(TRune)
に変更されたことで、TRune
の実際のサイズ(例えば4バイト)に基づいてバイト列のパディングと出力が行われるようになります。これにより、異なるバイトオーダーのシステムでも、TRune
の各バイトが正しく配置されて出力されるようになります。
これらの変更は、GoのCコンパイラがUnicodeのより広い範囲の文字を、内部的に一貫したTRune
型で扱い、正確にコンパイルするための基盤を強化するものです。
関連リンク
- Go言語のUnicodeサポート: https://go.dev/blog/strings
- Plan 9 from Bell Labs: https://9p.io/plan9/
- GNU Bison: https://www.gnu.org/software/bison/
参考にした情報源リンク
- コミットメッセージと差分情報:
/home/orange/Project/comemo/commit_data/16492.txt
- Go言語の公式ドキュメント(strings, runesに関する記述)
- Bisonのドキュメント(バージョンアップによる変更点に関する一般的な情報)
- Plan 9のCコンパイラに関する一般的な情報
- Unicodeのコードポイントとエンコーディングに関する一般的な情報