[インデックス 18570] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)における配列のインデックスが範囲外であるというエラーメッセージの表示を修正するものです。具体的には、エラーメッセージ内で報告されるインデックスが実際のインデックスよりも1大きいという「オフバイワン」エラーを修正し、正確なインデックス値を表示するように変更されました。これにより、開発者はより正確なエラー情報に基づいて問題を特定しやすくなります。
コミット
commit 8eec4ebd7d6f81ec432710a87902746e560b7a37
Author: Rick Arnold <rickarnoldjr@gmail.com>
Date: Wed Feb 19 11:29:36 2014 -0800
cmd/gc: fix array index out of bounds error message
The error message was previously off by one in all cases.
Fixes #7150.
LGTM=r
R=golang-codereviews, r
CC=golang-codereviews
https://golang.org/cl/65850043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8eec4ebd7d6f81ec432710a87902746e560b7a37
元コミット内容
cmd/gc: fix array index out of bounds error message
The error message was previously off by one in all cases.
Fixes #7150.
LGTM=r
R=golang-codereviews, r
CC=golang-codereviews
https://golang.org/cl/65850043
変更の背景
この変更は、Goコンパイラが配列のインデックスが範囲外である場合に生成するエラーメッセージに存在するバグを修正するために行われました。具体的には、GoのIssue 7150として報告された問題に対応しています。
以前のコンパイラでは、配列のインデックスが宣言された範囲を超えてアクセスされた際に表示されるエラーメッセージにおいて、報告されるインデックス値が実際のインデックス値よりも1大きいという「オフバイワン」エラーが発生していました。例えば、サイズが10の配列に対してインデックス10でアクセスしようとした場合(これは範囲外)、エラーメッセージには「array index 11 out of bounds」のように表示されていました。これは開発者にとって混乱を招き、デバッグを困難にする可能性がありました。
このコミットは、この誤ったエラー報告を修正し、正確なインデックス値をエラーメッセージに含めることで、開発者がより迅速かつ正確に問題を特定できるようにすることを目的としています。
前提知識の解説
1. 配列のインデックスと範囲外アクセス (Array Index Out of Bounds)
プログラミングにおいて、配列は同じ型の要素を連続して格納するデータ構造です。配列の各要素には、0から始まる整数値のインデックス(添字)が割り当てられます。例えば、サイズがNの配列の場合、有効なインデックスは0からN-1までです。
「配列のインデックスが範囲外 (array index out of bounds)」とは、プログラムが配列の有効なインデックス範囲外のインデックスを使用して要素にアクセスしようとすることを指します。これは、プログラムの論理的な誤りであり、未定義の動作を引き起こしたり、プログラムのクラッシュやセキュリティ上の脆弱性につながる可能性があります。コンパイラやランタイムは、このようなエラーを検出し、開発者に警告またはエラーを報告することが一般的です。
2. Goコンパイラ (cmd/gc
)
Go言語の公式コンパイラは、通常 gc
と呼ばれます。これは cmd/gc
ディレクトリにソースコードが格納されており、Goのソースコードを機械語に変換する役割を担っています。コンパイル時には、構文チェック、型チェック、最適化など、様々な処理が行われます。
本コミットで変更されている src/cmd/gc/typecheck.c
は、Goコンパイラの型チェックフェーズに関連するC言語のソースファイルです。型チェックは、プログラムが型システムに準拠しているかを確認するプロセスであり、配列のインデックスが正しい型であるか、そしてそのインデックスが配列の有効な範囲内にあるかどうかの検証も含まれます。
3. エラー報告 (yyerror
)
コンパイラは、ソースコードに問題がある場合、その問題を開発者に報告する必要があります。Goコンパイラでは、エラーメッセージの出力に yyerror
のような関数が使用されます。これは、C言語で書かれたコンパイラやパーサーでよく見られる、Yacc/Bisonなどのツールによって生成されるパーサーの一部として提供されるエラー報告関数に似ています。yyerror
は、指定されたフォーマット文字列と引数を使用してエラーメッセージを標準エラー出力に出力します。
4. オフバイワンエラー (Off-by-one Error)
オフバイワンエラーは、プログラミングにおける一般的な論理エラーの一種です。これは、ループの反復回数、配列のインデックス、範囲の境界などを計算する際に、値が1だけずれてしまうことによって発生します。例えば、0から始まるインデックスを持つ配列を扱う際に、要素数をインデックスとして使用してしまうなどが典型的な例です。本コミットでは、エラーメッセージで報告されるインデックス値が実際のインデックス値から1ずれていたため、オフバイワンエラーに該当します。
技術的詳細
このコミットの技術的な核心は、Goコンパイラの型チェックロジックにおける配列のインデックス範囲チェックのエラーメッセージ生成部分の修正です。
Goコンパイラは、配列リテラル(例: [10]int{0: 0, 5: 25}
のような形式)を処理する際に、指定されたインデックスが配列の宣言された境界内にあるかどうかを検証します。この検証は typecheckcomplit
関数内で行われます。
変更前のコードでは、配列リテラル内のインデックスが配列の境界を超えていると判断された場合、エラーメッセージを生成するために yyerror
関数が呼び出されていました。この際、エラーメッセージに表示されるインデックス値は len
という変数から取得されていました。しかし、この len
変数は、配列リテラル内で指定されたインデックスの「数」または「次の要素が配置されるべき位置」を示しており、実際の範囲外のインデックス値そのものではありませんでした。
例えば、サイズが0の配列 [0]int
に対してインデックス 0
でアクセスしようとした場合、len
は 0
となります。しかし、これは既に範囲外です。もしインデックス 5
でアクセスしようとした場合、len
は 5
となります。この len
の値がそのままエラーメッセージに表示されると、実際のインデックス値とずれてしまうという問題がありました。
具体的には、len
は配列リテラルで指定された最大のインデックス値に1を加えた値、または次に要素が追加されるべき位置を示していました。そのため、エラーメッセージで「array index %d out of bounds」と表示される %d
の部分に len
をそのまま渡すと、実際の範囲外のインデックスよりも1大きい値が表示されてしまっていたのです。
このコミットでは、このオフバイワンエラーを修正するために、yyerror
に渡すインデックス値を len - 1
に変更しました。これにより、エラーメッセージには実際に範囲外となったインデックス値が正確に表示されるようになります。
例えば、[0]int{0: 0}
の場合、len
は 0
ですが、len - 1
は -1
となり、これは別のエラー(インデックスが非負整数でない)として処理されます。
[0]int{5: 25}
の場合、len
は 5
でしたが、修正後は len - 1
で 4
となり、array index 4 out of bounds [0:0]
のように表示されるはずです。
[10]int{2: 10, 15: 30}
の場合、len
は 15
でしたが、修正後は len - 1
で 14
となり、array index 14 out of bounds [0:10]
のように表示されるはずです。
この修正は、コンパイラのエラーメッセージの正確性を向上させ、開発者のデバッグ体験を改善する上で重要な変更です。
コアとなるコードの変更箇所
変更は src/cmd/gc/typecheck.c
ファイルの1箇所と、テストケースの追加です。
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -2503,7 +2503,7 @@ typecheckcomplit(Node **np)\
len = i;\
if(t->bound >= 0 && len > t->bound) {\
setlineno(l);\
-\t\t\t\t\tyyerror(\"array index %d out of bounds [0:%d]\", len, t->bound);\
+\t\t\t\t\tyyerror(\"array index %d out of bounds [0:%d]\", len-1, t->bound);\
\t\t\t\t\tt->bound = -1;\t// no more errors\
\t\t\t\t}\
\t\t\t}
また、この修正を検証するための新しいテストファイル test/fixedbugs/issue7150.go
が追加されました。
// errorcheck
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// issue 7150: array index out of bounds error off by one
package main
func main() {
_ = [0]int{-1: 50} // ERROR "array index must be non-negative integer constant"
_ = [0]int{0: 0} // ERROR "array index 0 out of bounds \[0:0\]"
_ = [0]int{5: 25} // ERROR "array index 5 out of bounds \[0:0\]"
_ = [10]int{2: 10, 15: 30} // ERROR "array index 15 out of bounds \[0:10\]"
_ = [10]int{5: 5, 1: 1, 12: 12} // ERROR "array index 12 out of bounds \[0:10\]"
}
コアとなるコードの解説
変更された行は src/cmd/gc/typecheck.c
の以下の部分です。
yyerror("array index %d out of bounds [0:%d]", len-1, t->bound);
yyerror
: Goコンパイラがエラーメッセージを出力するために使用する関数です。"array index %d out of bounds [0:%d]"
: エラーメッセージのフォーマット文字列です。- 最初の
%d
は、範囲外となった配列のインデックスを表示するためのプレースホルダーです。 [0:%d]
の2番目の%d
は、配列の有効な最大インデックス(t->bound
)を表示するためのプレースホルダーです。
- 最初の
len-1
: これがこのコミットの核心的な変更点です。以前はlen
が直接渡されていましたが、len
は配列リテラルで指定された最大のインデックス値に1を加えた値、または次に要素が追加されるべき位置を示していました。そのため、実際の範囲外のインデックス値よりも1大きい値が表示されていました。len-1
とすることで、実際に範囲外となったインデックス値が正確にエラーメッセージに表示されるようになります。t->bound
: 配列の境界(サイズ-1)を示します。これは配列の有効な最大インデックスです。
この変更により、例えば [10]int{15: 30}
のようなコードがあった場合、以前は「array index 16 out of bounds [0:10]」のようなメッセージが表示されていたのが、修正後は「array index 15 out of bounds [0:10]」のように、より正確なインデックス値が報告されるようになります。
追加されたテストファイル test/fixedbugs/issue7150.go
は、この修正が正しく機能することを確認するためのものです。// ERROR "..."
コメントは、その行でコンパイラが特定のパターンに一致するエラーメッセージを出力することを期待していることを示します。これにより、修正後のコンパイラが期待通りの正確なエラーメッセージを生成するかどうかが自動的に検証されます。
関連リンク
- Go Issue 7150: https://github.com/golang/go/issues/7150
- Go CL 65850043: https://golang.org/cl/65850043
参考にした情報源リンク
- googlesource.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHOgsEc-H_FkH-2qabG2whqLEuPWxfsuViwakjaM7YUf-VBCrb4hiSq43FxbML4AlROyMTCvzQZGIjfivFSMah6TW2p_ob_nDXnVNnqDgA7XPrOyyEDgam3aPMV_AsfxxKj91Di0Ciu5-hPBr8epnK0eV0AAPaBPNfDGCfF)