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

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

このコミットは、Goコンパイラ(gc)における定数エクスポートのバグ修正に関するものです。具体的には、文字定数 '\' (バックスラッシュ) と '\'' (シングルクォート) が正しくエクスポートされない問題を解決し、これによりWindowsビルドが修正されました。

コミット

commit 2ab9bb6aafe2720c4ea2b30fffc827e9f8883f53
Author: Russ Cox <rsc@golang.org>
Date:   Thu Dec 8 22:43:31 2011 -0500

    gc: fix export of '\'' and '\\' constants
    
    Fixes Windows build.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/5472046
---
 src/cmd/gc/fmt.c | 2 +--
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c
index 35acb5b84b..9733095019 100644
--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -363,7 +363,7 @@ Vconv(Fmt *fp)
 		return fmtprint(fp, "%B", v->u.xval);
 	case CTRUNE:
 		x = mpgetfix(v->u.xval);
-		if(' ' <= x && x < 0x80)
+		if(' ' <= x && x < 0x80 && x != '\\' && x != '\'')
 			return fmtprint(fp, "'%c'", (int)x);
 		if(0 <= x && x < (1<<16))
 			return fmtprint(fp, "'\\u%04ux'", (int)x);

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

https://github.com/golang/go/commit/2ab9bb6aafe2720c4ea2b30fffc827e9f8883f53

元コミット内容

gc: fix export of '\'' and '\\' constants

Fixes Windows build.

R=ken2
CC=golang-dev
https://golang.org/cl/5472046

変更の背景

このコミットの主な背景は、Goコンパイラ(gc)が特定の文字定数、特にバックスラッシュ (\) とシングルクォート (') を含む定数をエクスポートする際に発生していたバグを修正することです。コミットメッセージに「Fixes Windows build.」と明記されていることから、この問題がWindows環境でのGoプログラムのビルドに影響を与えていたことがわかります。

Go言語では、ソースコード内で定義された定数や型情報は、他のパッケージから利用できるように「エクスポート」されます。このエクスポートプロセス中に、特定の特殊文字が正しく処理されないと、コンパイラが生成する中間ファイルや最終的なバイナリに誤った情報が埋め込まれ、結果としてビルドエラーや予期せぬ動作を引き起こす可能性があります。

特に、バックスラッシュはエスケープシーケンスの開始文字として、シングルクォートは文字リテラルの区切り文字として、プログラミング言語において特別な意味を持ちます。これらの文字自体を定数として扱う場合、コンパイラはそれらを適切にエスケープまたは処理して、曖昧さなく表現する必要があります。このコミットは、gcがこれらの文字をエクスポートする際の処理ロジックに不備があったことを示しており、その修正がWindows環境でのビルドの安定性向上に寄与しました。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

  1. Goコンパイラ (gc): Go言語の公式コンパイラは、通常 gc と呼ばれます。これはGoのソースコードを機械語に変換する役割を担っています。gc は、コンパイルの過程で、構文解析、型チェック、最適化、コード生成など、様々なフェーズを実行します。このコミットで変更されている src/cmd/gc/fmt.c は、gc の一部であり、コンパイラ内部でのフォーマットや出力に関する処理を担当しています。

  2. 文字定数 (Rune Literals): Go言語において、単一の文字は「rune」(ルーン)として表現されます。これはUnicodeコードポイントに対応する整数値です。文字定数はシングルクォートで囲んで表現され、例えば 'a' は文字 a を表します。特殊な文字(改行、タブ、バックスラッシュなど)は、バックスラッシュを用いたエスケープシーケンスで表現されます。例えば、'\n' は改行、'\\' はバックスラッシュ自体、'\'' はシングルクォート自体を表します。

  3. エクスポート (Exporting): Go言語では、パッケージ内の識別子(変数、定数、関数、型など)が外部のパッケージから参照可能にするために「エクスポート」されます。エクスポートされる識別子は、大文字で始まります。コンパイラは、エクスポートされた情報を他のパッケージが利用できるように、特定の中間形式(例: go.a ファイル内のエクスポートデータ)で出力します。このコミットは、このエクスポートされるデータの中に文字定数が含まれる場合に、その表現が正しく行われない問題に対処しています。

  4. fmt.c の役割: src/cmd/gc/fmt.c は、Goコンパイラ gc のソースコードの一部です。このファイルは、コンパイラが内部で使用する様々な値や構造体を文字列としてフォーマットする機能を提供しています。特に、デバッグ出力、エラーメッセージ、そして他のコンパイルフェーズや外部パッケージへのエクスポートデータの一部として、値を人間が読める形式や機械が解析できる形式に変換する役割を担っています。このコミットで修正されている Vconv 関数は、様々な型の値を特定のフォーマットで変換・出力するための汎用的なメカニズムの一部です。

  5. ASCII文字とエスケープシーケンス: ASCII文字は0から127までの整数値で表現される文字セットです。Goの文字定数では、通常の表示可能なASCII文字はそのまま 'c' のように表現できますが、表示できない文字や、シングルクォート、バックスラッシュなどの特殊な意味を持つ文字は、'\n''\\' のようにバックスラッシュを使ったエスケープシーケンスで表現する必要があります。コンパイラがこれらの文字をエクスポートする際、その文字が通常の表示可能なASCII文字として扱われるべきか、それともエスケープシーケンスとして表現されるべきかを正しく判断する必要があります。

技術的詳細

このコミットの技術的な核心は、src/cmd/gc/fmt.c ファイル内の Vconv 関数における文字定数の処理ロジックの変更です。

Vconv 関数は、Goコンパイラ内部で様々な値を文字列に変換する汎用的な関数です。case CTRUNE: のブロックは、処理対象の v が文字定数(rune)である場合に実行されます。

元のコードでは、文字 x' ' (スペース) 以上かつ 0x80 (ASCII範囲の終わり) 未満の場合、つまり表示可能なASCII文字の範囲内であれば、単純に fmtprint(fp, "'%c'", (int)x); を使って 'c' の形式で出力していました。

しかし、このロジックには問題がありました。バックスラッシュ (\) とシングルクォート (') は、それぞれASCII値が 0x5c0x27 であり、この ' ' <= x && x < 0x80 の条件を満たします。もしこれらの文字がこの条件に合致して '\'''' のように直接出力されてしまうと、Goのソースコードやエクスポートデータとして解釈される際に、エスケープシーケンスや文字列の区切り文字として誤って解釈される可能性があります。

例えば、文字定数 '\'' (シングルクォート文字自体) をエクスポートしようとした場合、元のロジックでは ''' と出力されてしまいます。これはGoの構文としては不正であり、コンパイルエラーや予期せぬ動作の原因となります。同様に、'\\' (バックスラッシュ文字自体) をエクスポートしようとした場合、'\' と出力され、これもGoの構文としては不正です。

この修正は、この問題を解決するために、表示可能なASCII文字の条件に加えて、x がバックスラッシュ ('\\') でもシングルクォート ('\'') でもないことを追加でチェックするように変更しました。

変更後の条件 if(' ' <= x && x < 0x80 && x != '\\' && x != '\'') は、以下の意味を持ちます。

  • ' ' <= x && x < 0x80: 文字 x が表示可能なASCII文字の範囲内であること。
  • x != '\\': 文字 x がバックスラッシュではないこと。
  • x != '\'': 文字 x がシングルクォートではないこと。

この新しい条件により、バックスラッシュとシングルクォートは、たとえ表示可能なASCII文字の範囲内であっても、この if ブロックの対象から除外されます。これにより、これらの特殊文字は、その後の if(0 <= x && x < (1<<16)) ブロック(またはそれ以降のより汎用的な処理)で、'\\uXXXX' のようなUnicodeエスケープシーケンスとして、あるいは他の適切なエスケープ形式で正しく出力されるようになります。これにより、Goコンパイラが生成するエクスポートデータがGoの構文規則に準拠し、Windowsを含む様々な環境でのビルドが安定するようになりました。

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

--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -363,7 +363,7 @@ Vconv(Fmt *fp)
 		return fmtprint(fp, "%B", v->u.xval);
 	case CTRUNE:
 		x = mpgetfix(v->u.xval);
-		if(' ' <= x && x < 0x80)
+		if(' ' <= x && x < 0x80 && x != '\\' && x != '\'')
 			return fmtprint(fp, "'%c'", (int)x);
 		if(0 <= x && x < (1<<16))
 			return fmtprint(fp, "'\\u%04ux'", (int)x);

コアとなるコードの解説

このコミットのコアとなる変更は、src/cmd/gc/fmt.c ファイル内の Vconv 関数における if 文の条件式です。

変更前:

if(' ' <= x && x < 0x80)

この条件は、文字 x がASCIIの表示可能文字(スペースからDELの手前まで)の範囲内にある場合に真となります。この場合、fmtprint(fp, "'%c'", (int)x); を使用して、文字を 'c' の形式で直接出力していました。

変更後:

if(' ' <= x && x < 0x80 && x != '\\' && x != '\'')

変更後の条件式には、x != '\\'x != '\'' という2つの追加の条件が加わっています。

  • x != '\\': x がバックスラッシュ文字(ASCII値 0x5C)ではないことを確認します。
  • x != '\'': x がシングルクォート文字(ASCII値 0x27)ではないことを確認します。

この変更の意図は、バックスラッシュとシングルクォートという2つの特殊文字が、たとえASCIIの表示可能文字の範囲内であっても、'%c' 形式で直接出力されるのを防ぐことです。これらの文字はGoの文字リテラルやエスケープシーケンスにおいて特別な意味を持つため、直接出力されると構文エラーや誤解釈の原因となります。

例えば、Go言語でシングルクォート文字自体を表すには '\'' と記述します。もしコンパイラがこの文字をエクスポートする際に、単に ''' と出力してしまうと、これはGoの構文としては不正な文字リテラルと解釈されてしまいます。同様に、バックスラッシュ文字自体を表す '\'' も、単に '\' と出力されると問題が生じます。

この修正により、バックスラッシュとシングルクォートは、この if ブロックの対象から外れ、その後の if(0 <= x && x < (1<<16)) ブロック(またはそれ以降の処理)で、'\\u%04ux' のようなUnicodeエスケープシーケンスとして、あるいは他の適切なエスケープ形式で出力されるようになります。これにより、Goコンパイラが生成するコードやエクスポートデータがGoの言語仕様に厳密に準拠し、特にWindowsのような環境でのビルドの堅牢性が向上しました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Go言語の仕様、特にリテラルに関するセクション)
  • Goコンパイラ (gc) のソースコード (特に src/cmd/gc/ ディレクトリ内のファイル構造と役割)
  • ASCIIコード表 (特殊文字のASCII値確認のため)
  • Go言語におけるエスケープシーケンスに関する情報