[インデックス 14351] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)において、スライスや配列のインデックスがint
型の最大値を超える場合に警告を発するように修正を加えるものです。特にGOARCH=386
アーキテクチャでのビルド問題を修正することを目的としています。
コミット
commit e3977f0d3a20ec7311b939fd2e60d78f4c6031ef
Author: Ian Lance Taylor <iant@golang.org>
Date: Wed Nov 7 17:34:06 2012 -0800
cmd/gc: warn about slice indexes larger than int in typecheck pass
Fixes GOARCH=386 build.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6810098
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e3977f0d3a20ec7311b939fd2e60d78f4c6031ef
元コミット内容
cmd/gc: warn about slice indexes larger than int in typecheck pass
Fixes GOARCH=386 build.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6810098
変更の背景
この変更の主な背景は、Goコンパイラがスライスや配列のインデックスを処理する際に、そのインデックスがint
型の最大値を超えてしまうケースを適切に検出していなかった点にあります。特にGOARCH=386
(32ビットアーキテクチャ)のような環境では、int
型の範囲がint64
などのより広い型に比べて小さいため、この問題が顕在化しやすくなります。
Go言語では、スライスや配列のインデックスは通常int
型で表現されます。しかし、コンパイル時に定数として与えられたインデックス値が、ターゲットアーキテクチャのint
型で表現できる範囲を超えてしまう場合、コンパイラはそれを不正なインデックスとして認識し、エラーまたは警告を出すべきです。このコミット以前は、このようなケースが適切に処理されず、結果としてGOARCH=386
環境でのビルドが失敗するなどの問題が発生していました。
この修正は、コンパイル時の型チェックフェーズ(typecheck
pass)において、インデックス値がint
型の最大値を超えていないかを明示的にチェックし、超えている場合には適切なエラーメッセージを生成することで、この問題を解決します。これにより、コンパイル時に潜在的なランタイムエラーを防ぎ、より堅牢なコード生成を可能にします。
前提知識の解説
このコミットを理解するためには、以下のGoコンパイラおよびGo言語の基本的な概念を理解しておく必要があります。
- Goコンパイラ (
cmd/gc
): Go言語の公式コンパイラの一つで、Goソースコードを機械語に変換する役割を担います。gc
は"Go compiler"の略です。 - 型チェックフェーズ (
typecheck
pass): コンパイラの重要なフェーズの一つで、ソースコードがGo言語の型システム規則に準拠しているかを確認します。変数や式の型が正しく、互換性があるか、関数呼び出しの引数が正しい型であるかなどを検証します。このフェーズで型に関するエラーが検出されます。 - スライスと配列のインデックス: Go言語におけるスライス(
[]T
)と配列([N]T
)は、要素にアクセスするために整数インデックスを使用します。Goの仕様では、これらのインデックスは通常int
型で表現されます。 int
型: Go言語のint
型は、プラットフォームに依存する符号付き整数型です。32ビットシステム(例:GOARCH=386
)では32ビット幅、64ビットシステムでは64ビット幅を持ちます。そのため、int
型の最大値はアーキテクチャによって異なります。- 32ビットシステム:
int
の最大値は約2 * 10^9 (20億) - 64ビットシステム:
int
の最大値は約9 * 10^18 (900京)
- 32ビットシステム:
GOARCH=386
: Goのビルド環境変数の一つで、ターゲットアーキテクチャをIntel 80386互換の32ビットCPUに設定します。mpcmpfixfix
: Goコンパイラの内部で使用される多倍長整数(mp
は"multi-precision"の略)を比較する関数です。fix
は固定小数点数を意味し、ここではコンパイル時に確定する定数値を扱います。この関数は2つの多倍長整数を比較し、その大小関係を返します。maxintval[TINT]
:TINT
はGoのint
型を表す内部定数です。maxintval[TINT]
は、ターゲットアーキテクチャのint
型が表現できる最大値を示す多倍長整数定数です。yyerror
: Goコンパイラの字句解析器/構文解析器(yacc
/bison
によって生成されることが多い)がエラーメッセージを出力するために使用する関数です。コンパイルエラーが発生した場合に、ユーザーに分かりやすいメッセージを表示します。src/cmd/gc/typecheck.c
: Goコンパイラのソースコードの一部で、型チェックロジックが実装されているC言語のファイルです。
技術的詳細
このコミットは、Goコンパイラのsrc/cmd/gc/typecheck.c
ファイル内の型チェックロジックに修正を加えています。具体的には、配列やスライスのインデックスが定数である場合に、その値がターゲットアーキテクチャのint
型で表現可能な最大値を超えていないかを検証する新しいチェックを追加しています。
Goコンパイラは、ソースコードを抽象構文木(AST)に変換し、そのASTに対して様々な最適化やチェックを行います。型チェックはその重要なステップの一つです。配列やスライスのインデックスアクセス(例: a[i]
)は、AST上では特定のノード(ONAME
やOINDEX
など)として表現されます。
この修正では、インデックスが定数である場合に、その定数値を多倍長整数として扱い、maxintval[TINT]
(ターゲットint
型の最大値)と比較します。比較にはmpcmpfixfix
関数が使用されます。
mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0
n->right->val.u.xval
: インデックスとして使用されている定数ノードの値(多倍長整数形式)。maxintval[TINT]
: ターゲットアーキテクチャのint
型が表現できる最大値。mpcmpfixfix
が0
より大きい値を返す場合、それはインデックス値がmaxintval[TINT]
よりも大きいことを意味します。
この条件が真である場合、つまりインデックスがint
型の最大値を超えている場合、yyerror
関数を呼び出してコンパイルエラーを発生させます。エラーメッセージは「invalid %s index %N (index too large)」となり、%s
には「array」または「slice」が、%N
には不正なインデックス値が挿入されます。
このチェックは、以下の3つの主要な箇所に追加されています。
- 単一の配列/スライスインデックス(
OINDEX
ノード)のチェック。 - スライス式の下限インデックス(
OAS2FUNC
ノードのn->right->left
)のチェック。 - スライス式の上限インデックス(
OAS2FUNC
ノードのn->right->right
)のチェック。
これにより、コンパイル時にインデックスのオーバーフローを早期に検出し、ランタイムでの予期せぬ動作やクラッシュを防ぐことができます。特に32ビットシステムでは、このチェックがより重要になります。
コアとなるコードの変更箇所
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 9b42772393..2d1dbd75f1 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -828,6 +828,10 @@ reswitch:
yyerror("invalid %s index %N (index must be non-negative)", why, n->right);
} else if(isfixedarray(t) && t->bound > 0 && mpgetfix(n->right->val.u.xval) >= t->bound)
yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound);
+ else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) {
+ why = isfixedarray(t) ? "array" : "slice";
+ yyerror("invalid %s index %N (index too large)", why, n->right);
+ }
}
break;
@@ -947,6 +951,8 @@ reswitch:
yyerror("invalid slice index %N (index must be non-negative)", n->right->left);
else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->left->val.u.xval) > tp->bound)
yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->left, tp->bound);
+ else if(mpcmpfixfix(n->right->left->val.u.xval, maxintval[TINT]) > 0)
+ yyerror("invalid slice index %N (index too large)", n->right->left);
}
}
if(n->right->right != N) {
@@ -961,6 +969,8 @@ reswitch:
yyerror("invalid slice index %N (index must be non-negative)", n->right->right);
else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->right->val.u.xval) > tp->bound)
yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->right, tp->bound);
+ else if(mpcmpfixfix(n->right->right->val.u.xval, maxintval[TINT]) > 0)
+ yyerror("invalid slice index %N (index too large)", n->right->right);
}
}
goto ret;
コアとなるコードの解説
追加されたコードは、既存のインデックスチェック(負のインデックスや配列の境界外チェック)の後に、新たな条件分岐として挿入されています。
-
単一インデックスのチェック (行 831-834):
else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) { why = isfixedarray(t) ? "array" : "slice"; yyerror("invalid %s index %N (index too large)", why, n->right); }
n->right
は、インデックスとして使用されているASTノードを指します。n->right->val.u.xval
はそのノードが持つ定数値(多倍長整数形式)です。mpcmpfixfix(..., maxintval[TINT]) > 0
は、インデックス値が現在のアーキテクチャのint
型で表現できる最大値(maxintval[TINT]
)を超えているかをチェックします。why = isfixedarray(t) ? "array" : "slice";
は、エラーメッセージで使用する文字列を、対象が固定長配列かスライスかに応じて「array」または「slice」に設定します。yyerror(...)
は、指定されたフォーマットでエラーメッセージを出力します。
-
スライス式の下限インデックスのチェック (行 951-952):
else if(mpcmpfixfix(n->right->left->val.u.xval, maxintval[TINT]) > 0) yyerror("invalid slice index %N (index too large)", n->right->left);
- スライス式(例:
s[low:high]
)の下限インデックスlow
に対するチェックです。n->right->left
がlow
のASTノードを指します。 - 同様に、
low
の値がint
型の最大値を超えていないかをmpcmpfixfix
で確認し、超えていればエラーを出力します。
- スライス式(例:
-
スライス式の上限インデックスのチェック (行 965-966):
else if(mpcmpfixfix(n->right->right->val.u.xval, maxintval[TINT]) > 0) yyerror("invalid slice index %N (index too large)", n->right->right);
- スライス式の上限インデックス
high
に対するチェックです。n->right->right
がhigh
のASTノードを指します。 high
の値がint
型の最大値を超えていないかをmpcmpfixfix
で確認し、超えていればエラーを出力します。
- スライス式の上限インデックス
これらの変更により、コンパイル時にインデックスのオーバーフローをより厳密にチェックし、特に32ビット環境でのビルドの堅牢性を向上させています。
関連リンク
- Go CL (Change List) へのリンク: https://golang.org/cl/6810098
参考にした情報源リンク
- Go言語の
int
型について: https://go.dev/ref/spec#Numeric_types - Goコンパイラの内部構造に関する一般的な情報 (Goのソースコードや関連ドキュメント):
- Goのソースコードリポジトリ: https://github.com/golang/go
- Goのコンパイラに関するブログ記事やドキュメント (例: "Go compiler internals"などで検索)
mpcmpfixfix
やmaxintval
のようなコンパイラ内部のシンボルに関する情報 (Goのソースコードを直接参照するか、Goコンパイラの開発者向けドキュメントを参照)src/cmd/compile/internal/gc/builtin.go
(Go 1.5以降のコンパイラではGo言語で書かれているため、関連する定数や関数定義が見つかる可能性があります)src/cmd/compile/internal/big/int.go
(多倍長整数演算に関するコード)
yyerror
に関する情報 (Yacc/Bisonのドキュメントや、コンパイラ開発に関する一般的な情報)- Yacc/Bisonのドキュメント: https://www.gnu.org/software/bison/manual/
- コンパイラ設計に関する書籍やオンラインリソース