[インデックス 18929] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)における「const initializer is not a constant」(定数初期化子が定数ではありません)という誤ったエラー報告を修正するものです。具体的には、定数初期化の際に、実際には定数であるべき値が誤って非定数と判断され、不必要なエラーメッセージが表示されるバグ(Issue 6403)に対処しています。この修正により、コンパイラはより正確なエラー診断を行うようになり、開発者の混乱を減らします。
コミット
commit 833dae6d26c56bde5fbae27fde0cdc6efa63fefa
Author: Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
Date: Mon Mar 24 20:36:42 2014 +0100
cmd/gc: fix spurious 'const initializer is not a constant' error
Fixes #6403
LGTM=rsc
R=iant, rsc
CC=golang-codereviews
https://golang.org/cl/72840044
---
src/cmd/gc/subr.c | 1 +\
src/cmd/gc/typecheck.c | 5 ++++-\
test/fixedbugs/issue6403.go | 14 ++++++++++++++\
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 01a5c435aa..f9746f0278 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2243,6 +2243,7 @@ adddot(Node *n)
int c, d;
typecheck(&n->left, Etype|Erv);\n+\tn->diag |= n->left->diag;\n t = n->left->type;\
if(t == T)\
goto ret;\
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index ebff0694a0..f6e77acebd 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -3174,7 +3174,10 @@ typecheckdef(Node *n)
goto ret;
}
if(e->type != T && e->op != OLITERAL || !isgoconst(e)) {
-\t\tyyerror("const initializer %N is not a constant", e);\n+\t\tif(!e->diag) {\n+\t\t\tyyerror("const initializer %N is not a constant", e);\n+\t\t\te->diag = 1;\n+\t\t}\n goto ret;
}
t = n->type;
diff --git a/test/fixedbugs/issue6403.go b/test/fixedbugs/issue6403.go
new file mode 100644
index 0000000000..b61e2e225d
--- /dev/null
+++ b/test/fixedbugs/issue6403.go
@@ -0,0 +1,14 @@
+// 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 6403: fix spurious 'const initializer is not a constant' error
+
+package p
+
+import "syscall"
+
+const A int = syscall.X // ERROR "undefined: syscall.X"
+const B int = voidpkg.X // ERROR "undefined: voidpkg"
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/833dae6d26c56bde5fbae27fde0cdc6efa63fefa
元コミット内容
cmd/gc: fix spurious 'const initializer is not a constant' error
Fixes #6403
LGTM=rsc
R=iant, rsc
CC=golang-codereviews
https://golang.org/cl/72840044
変更の背景
このコミットは、Goコンパイラ(cmd/gc
)が特定の状況下で「const initializer is not a constant」(定数初期化子が定数ではありません)という誤ったエラーメッセージを報告するバグ(Issue 6403)を修正するために導入されました。
Go言語では、定数はコンパイル時にその値が決定される必要があります。しかし、このバグが存在する環境では、例えば未定義のシンボルを参照するような、本来であれば別の種類のエラー(例: 「undefined: syscall.X」)が報告されるべきケースで、コンパイラが誤って「定数初期化子が定数ではありません」という、より一般的な、かつこの文脈では不適切なエラーを出力していました。
このような誤ったエラーメッセージは、開発者にとって混乱の原因となります。本来の問題(例: 未定義のシンボル)が不明瞭になり、デバッグ作業が困難になるため、正確なエラー報告はコンパイラのユーザビリティにおいて非常に重要です。このコミットは、コンパイラのエラー診断の精度を向上させ、開発体験を改善することを目的としています。
前提知識の解説
このコミットの理解には、以下のGoコンパイラおよびGo言語の概念に関する知識が役立ちます。
-
Go言語における定数 (Constants): Go言語の定数は、コンパイル時にその値が決定される不変の値を指します。数値、真偽値、文字列などの基本的な型で宣言でき、
const
キーワードを用いて定義されます。定数式は、コンパイル時に評価可能でなければなりません。例えば、const PI = 3.14
は有効ですが、const X = someFunction()
のように実行時に評価される関数呼び出しは定数初期化子としては無効です。 -
Goコンパイラ (
cmd/gc
):cmd/gc
は、Go言語の公式コンパイラの一つであり、Goソースコードを機械語に変換する主要なツールです。コンパイルプロセスには、字句解析、構文解析、型チェック、最適化、コード生成など、複数のフェーズが含まれます。 -
型チェック (Type Checking): 型チェックはコンパイルプロセスの重要なフェーズであり、プログラムが型規則に準拠しているかを確認します。これには、変数の型が正しく使用されているか、関数呼び出しの引数が期待される型と一致しているか、そして定数初期化子が有効な定数式であるかどうかの検証が含まれます。このコミットが修正する問題は、この型チェックフェーズにおける定数式の評価とエラー報告のロジックに関連しています。
-
抽象構文木 (AST) と
Node
構造体: コンパイラはソースコードを解析し、その構造を抽象構文木(AST)として内部的に表現します。ASTの各要素はNode
構造体で表され、プログラムの様々な構成要素(変数、式、ステートメントなど)に対応します。Node
構造体には、そのノードに関する情報(型、操作、子ノードなど)が格納されます。 -
diag
フィールド: Goコンパイラの内部では、Node
構造体にはdiag
というフィールドが含まれることがあります。このフィールドは、そのノードまたはその子ノードに対して既に診断メッセージ(エラーや警告)が報告されたかどうかを示すフラグとして機能します。diag
フラグを使用することで、コンパイラは同じ問題に対して複数の重複したエラーメッセージを出力するのを防ぎ、よりクリーンなエラー報告を実現します。 -
yyerror
関数:yyerror
は、Goコンパイラ内部でエラーメッセージを報告するために使用される関数です。この関数が呼び出されると、指定されたエラーメッセージがコンパイルエラーとして出力されます。
技術的詳細
このコミットの技術的詳細は、Goコンパイラの型チェックフェーズにおけるエラー報告のロジックの改善に焦点を当てています。問題は、ある式が既にエラー(例えば未定義のシンボル)を含んでいるにもかかわらず、その式が定数初期化子として評価される際に、さらに「定数ではない」という誤ったエラーが重複して報告されることでした。
修正は主に2つのファイルで行われています。
-
src/cmd/gc/subr.c
の変更:adddot
関数は、セレクタ(例:a.b
のようなドット演算子)の型チェックを行う際に使用されます。この関数内で、n->diag |= n->left->diag;
という行が追加されました。n
は現在のノード(例:syscall.X
全体)。n->left
はドット演算子の左側の部分(例:syscall
)。n->left->diag
は、左側の部分が既にエラー(例えばsyscall
が未定義、あるいはsyscall.X
のX
が未定義)を持っている場合に設定される診断フラグです。 この変更の目的は、式の左側(n->left
)に既にエラーが報告されている場合、そのエラー情報(diag
フラグ)を親ノード(n
)に伝播させることです。これにより、後続の型チェックフェーズで、このノードが既に問題を持っていることが認識され、重複したエラー報告が抑制される可能性が生まれます。
-
src/cmd/gc/typecheck.c
の変更:typecheckdef
関数は、定数、変数、関数の定義を型チェックする際に使用されます。特に、定数初期化子のチェック部分が修正されました。 変更前は、定数初期化子e
が定数ではないと判断された場合、無条件にyyerror("const initializer %N is not a constant", e);
が呼び出されていました。 変更後は、if(!e->diag)
という条件が追加されました。!e->diag
は、「e
(定数初期化子)に対してまだエラーが報告されていない場合」を意味します。 この条件が真の場合にのみ、yyerror
が呼び出され、「const initializer is not a constant」エラーが報告されます。そして、エラーが報告された後にはe->diag = 1;
が設定され、このノードに対してエラーが報告済みであることをマークします。 このロジックにより、例えばsyscall.X
のような式で、syscall.X
自体が未定義であるために既にadddot
関数などでe->diag
が設定されている場合、typecheckdef
関数は重複して「const initializer is not a constant」エラーを報告しなくなります。これにより、コンパイラはより具体的で関連性の高いエラーメッセージ(この場合は「undefined: syscall.X」)のみを出力するようになります。
これらの変更は、コンパイラのエラー報告メカニズムを洗練させ、単一の根本的な問題に対して複数のエラーメッセージが生成される「エラーの連鎖」を防ぐことに貢献します。
コアとなるコードの変更箇所
src/cmd/gc/subr.c
// adddot関数内
typecheck(&n->left, Etype|Erv);
+n->diag |= n->left->diag; // 追加された行
t = n->left->type;
if(t == T)
goto ret;
src/cmd/gc/typecheck.c
// typecheckdef関数内、定数初期化子のチェック部分
if(e->type != T && e->op != OLITERAL || !isgoconst(e)) {
- yyerror("const initializer %N is not a constant", e); // 変更前
+ if(!e->diag) { // 追加された条件
+ yyerror("const initializer %N is not a constant", e);
+ e->diag = 1; // 追加された行
+ }
goto ret;
}
test/fixedbugs/issue6403.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 6403: fix spurious 'const initializer is not a constant' error
package p
import "syscall"
const A int = syscall.X // ERROR "undefined: syscall.X"
const B int = voidpkg.X // ERROR "undefined: voidpkg"
コアとなるコードの解説
src/cmd/gc/subr.c
の変更
n->diag |= n->left->diag;
この行は、ビットOR演算子 (|=
) を使用して、現在のノードn
の診断フラグdiag
に、その左の子ノードn->left
の診断フラグを結合しています。
- 目的:
n->left
(例えばsyscall
)が既に何らかのエラー(例えば未定義)によってdiag
フラグが設定されている場合、そのエラー状態を親ノードn
(例えばsyscall.X
全体)に伝播させます。これにより、syscall.X
という式全体が既にエラーを含んでいることがコンパイラの他の部分に認識され、後続の処理で重複したエラーメッセージが生成されるのを防ぐための準備となります。
src/cmd/gc/typecheck.c
の変更
if(!e->diag) {
yyerror("const initializer %N is not a constant", e);
e->diag = 1;
}
このブロックは、定数初期化子e
が定数ではないと判断された場合に実行されます。
if(!e->diag)
: この条件は、定数初期化子e
に対してまだエラーが報告されていない場合にのみ、内部のyyerror
呼び出しを実行するようにします。これにより、e
が既に別の理由(例: 未定義のシンボル)でエラーが報告され、e->diag
が設定されている場合、この「const initializer is not a constant」というエラーは出力されません。yyerror("const initializer %N is not a constant", e);
: この行は、定数初期化子が定数ではないというエラーメッセージを報告します。e->diag = 1;
: エラーが報告された後、e
のdiag
フラグを1
に設定します。これにより、このノードに対してエラーが報告済みであることがマークされ、将来的に同じノードに対して重複してエラーが報告されるのを防ぎます。
これらの変更は連携して機能し、コンパイラがより正確で、重複のないエラーメッセージを生成するようにします。特に、未定義のシンボルなど、より具体的なエラーが存在する場合に、一般的な「定数ではない」というエラーが誤って表示されるのを防ぎます。
test/fixedbugs/issue6403.go
の追加
このテストファイルは、修正が正しく機能することを確認するために追加されました。
const A int = syscall.X
とconst B int = voidpkg.X
のようなコードは、syscall.X
やvoidpkg.X
が未定義であるため、本来は「undefined」エラーを発生させるべきです。- このテストの目的は、これらのケースで「const initializer is not a constant」という誤ったエラーが報告されず、代わりに期待される「undefined」エラーのみが報告されることを検証することです。
// ERROR "..."
コメントは、期待されるエラーメッセージを示しており、errorcheck
ディレクティブは、コンパイラがこれらのエラーを正しく検出するかどうかをテストツールが確認するように指示します。
関連リンク
- Go Issue 6403: https://github.com/golang/go/issues/6403 (もし存在すれば)
- Go言語の定数に関する公式ドキュメント: https://go.dev/ref/spec#Constants
参考にした情報源リンク
- GitHubコミットページ: https://github.com/golang/go/commit/833dae6d26c56bde5fbae27fde0cdc6efa63fefa
- Web検索結果 (Go issue 6403 "const initializer is not a constant"): https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHnumjxbJABxYzMau5SS7NpxE2w34PgM6DJ5R-eNCxZWygKhLsvCu2zmHh4MVZtFBXz9ETECSxuZxYV2_eT_fH5r4_JrKTg34yW6TKyTl7VbVm868yidZQdcKBaVArujvjhJC5B8w=
- Web検索結果 (Go issue 6403 "const initializer is not a constant"): https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGj07VO471yb_uYZcEHGFBbSkLk3d7GXJpX2CV0wjdDXH9LGuL0FnnWyYYRBeVQ5gnX-pwC34O5oIHAjMcmDY-07gu-Q3JoHtf7myCJvS07dIXmhbjT_Ah7ubVu2qdzN65Igw=