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

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

このコミットは、Goコンパイラ(cmd/gc)における未定義シンボルへの代入エラー報告の挙動を修正するものです。具体的には、既に未定義であると報告されたシンボルへの代入に対して、重複してエラーメッセージを出力しないように変更されています。これにより、コンパイラのエラー出力がより簡潔になり、ユーザーにとって理解しやすくなります。

コミット

commit f2e94b58a01389cb9dcf51821b96435343594879
Author: Daniel Morsing <daniel.morsing@gmail.com>
Date:   Fri Jan 3 21:03:20 2014 +0100

    cmd/gc: silence assignment errors to undefined symbols
    
    Fixes #6406.
    
    R=golang-codereviews, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/46900043

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

https://github.com/golang/go/commit/f2e94b58a01389cb9dcf51821b96435343594879

元コミット内容

diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 31a2f2c5cb..6f8b6adbbf 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -2680,6 +2680,11 @@ checkassign(Node *n)
 		n->etype = 1;
 		return;
 	}\n+\n+\t// have already complained about n being undefined
+\tif(n->op == ONONAME)\n+\t\treturn;\n+\n \tyyerror("cannot assign to %N", n);\n }\n \ndiff --git a/test/fixedbugs/issue6406.go b/test/fixedbugs/issue6406.go
new file mode 100644
index 0000000000..5491193ef3
--- /dev/null
+++ b/test/fixedbugs/issue6406.go
@@ -0,0 +1,12 @@
+// errorcheck
+\n+// 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.
+\n+package main
+\n+func main() {\n+\ts = "bob" // ERROR "undefined.*s"\n+\t_ = s // ERROR "undefined.*s"\n+}\ndiff --git a/test/typecheck.go b/test/typecheck.go
index a2ad91ff4c..6f1204289a 100644
--- a/test/typecheck.go
+++ b/test/typecheck.go
@@ -14,5 +14,5 @@ func mine(int b) int {\t// ERROR "undefined.*b"\n \n func main() {\n \tmine()\t\t// GCCGO_ERROR "not enough arguments"\n-\tc = mine()\t// ERROR "undefined.*c|not enough arguments" "cannot assign to c"\n+\tc = mine()\t// ERROR "undefined.*c|not enough arguments"\n }\n```

## 変更の背景

この変更は、Goコンパイラが未定義のシンボルに対して複数のエラーメッセージを出力してしまうという問題(Issue 6406)を解決するために行われました。

従来のコンパイラでは、例えば以下のようなコードがあった場合:

```go
func main() {
    s = "bob" // 's' は未定義
    _ = s     // ここでも 's' は未定義
}

s = "bob" の行で「s が未定義」というエラーが報告された後、次の行の _ = s でも再度「s が未定義」というエラーが報告され、さらに s = "bob" の行に対して「s に代入できません」といった、未定義であることとは別の代入エラーが報告されることがありました。これは冗長であり、ユーザーが問題の原因を特定する上で混乱を招く可能性がありました。

このコミットの目的は、一度未定義であると判断されたシンボルに対しては、それ以降の操作(特に代入)に関するエラーメッセージを抑制し、エラー出力をよりクリーンで分かりやすくすることです。

前提知識の解説

このコミットを理解するためには、以下のGoコンパイラの概念とC言語の基本的な知識が必要です。

  • Goコンパイラ (cmd/gc): Go言語の公式コンパイラであり、Goソースコードを機械語に変換する役割を担っています。src/cmd/gc ディレクトリにそのソースコードが存在します。
  • 型チェック (Type Checking): コンパイラの重要なフェーズの一つで、プログラムがGo言語の型システム規則に準拠しているかを確認します。例えば、変数に正しい型の値が代入されているか、関数呼び出しの引数の型が正しいかなどを検証します。このコミットで変更されている typecheck.c は、この型チェック処理の一部を担っています。
  • シンボル (Symbol): プログラム内で定義される変数、関数、型などの名前を指します。コンパイラはこれらのシンボルを管理し、その定義や使用箇所を追跡します。
  • 未定義シンボル (Undefined Symbol): プログラム内で使用されているにもかかわらず、どこにも定義されていないシンボルを指します。これはコンパイルエラーの一般的な原因の一つです。
  • Node 構造体: Goコンパイラの内部では、ソースコードは抽象構文木(AST: Abstract Syntax Tree)として表現されます。このASTの各要素は Node 構造体で表されます。Node には、そのノードが表す操作の種類(op フィールド)や、型情報(type フィールド)、エラー状態(etype フィールド)などが含まれます。
  • ONONAME: Node 構造体の op フィールドが取りうる値の一つで、そのノードが「名前解決に失敗した(未定義の)シンボル」を表すことを示します。
  • yyerror: Goコンパイラ内部で使用されるエラー報告関数です。引数に指定されたフォーマット文字列と値に基づいて、コンパイルエラーメッセージを出力します。
  • test/fixedbugs/issue6406.go: Goプロジェクトのテストスイートの一部で、特定のバグ(この場合はIssue 6406)が修正されたことを検証するためのテストファイルです。// errorcheck ディレクティブは、このファイルがコンパイルエラーを意図的に含んでおり、期待されるエラーメッセージが出力されることを検証するためのものであることを示します。

技術的詳細

このコミットの核心は、src/cmd/gc/typecheck.c 内の checkassign 関数における変更です。

checkassign 関数は、代入操作の型チェックを行う役割を担っています。この関数は、代入の左辺(n)が有効な代入先であるかを検証します。

変更前は、n が未定義のシンボルである場合(つまり、n->op == ONONAME である場合)でも、yyerror("cannot assign to %N", n); という行が実行され、「%N に代入できません」というエラーメッセージが出力されていました。しかし、nONONAME であるということは、既にそのシンボルが未定義であるというエラーが報告されているはずです。

このコミットでは、以下のコードが追加されました。

	// have already complained about n being undefined
	if(n->op == ONONAME)
		return;

このコードは、checkassign 関数が代入の左辺 n を処理する際に、まず n->opONONAME であるかどうかをチェックします。もし ONONAME であれば、それは n が未定義のシンボルであることを意味します。この場合、既にその未定義エラーが報告されているため、checkassign 関数はそれ以上処理を行わずに return します。これにより、重複する「代入できません」というエラーメッセージの出力が抑制されます。

この変更は、コンパイラのエラー報告の質を向上させるためのものです。ユーザーは、同じ問題に対して複数のエラーメッセージを受け取るよりも、最も根本的な原因(この場合はシンボルの未定義)を示す単一のエラーメッセージを受け取る方が、問題を迅速に理解し、修正することができます。

また、test/fixedbugs/issue6406.go という新しいテストファイルが追加されています。このファイルは、s = "bob"_ = s の両方で s が未定義であることを意図的に示し、コンパイラが期待通りに「undefined.*s」というエラーを一度だけ報告し、重複する代入エラーを報告しないことを検証します。

test/typecheck.go の変更は、既存のテストケース c = mine() の期待されるエラーメッセージを更新しています。これは、今回の変更によって「cannot assign to c」というメッセージが抑制されるようになったため、そのテストの期待値もそれに合わせて調整されたものです。

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

src/cmd/gc/typecheck.c

checkassign 関数内に以下のコードが追加されました。

	// have already complained about n being undefined
	if(n->op == ONONAME)
		return;

test/fixedbugs/issue6406.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.

package main

func main() {
	s = "bob" // ERROR "undefined.*s"
	_ = s // ERROR "undefined.*s"
}

test/typecheck.go

既存のテストケースの期待されるエラーメッセージが変更されました。

--- a/test/typecheck.go
+++ b/test/typecheck.go
@@ -14,5 +14,5 @@ func mine(int b) int {\t// ERROR "undefined.*b"\n \n func main() {\n \tmine()\t\t// GCCGO_ERROR "not enough arguments"\n-\tc = mine()\t// ERROR "undefined.*c|not enough arguments" "cannot assign to c"\n+\tc = mine()\t// ERROR "undefined.*c|not enough arguments"\n }\n```

## コアとなるコードの解説

### `src/cmd/gc/typecheck.c` の変更

`checkassign` 関数は、Goコンパイラの型チェックフェーズにおいて、代入式の左辺の妥当性を検証する役割を担っています。

追加されたコードブロック:
```c
	// have already complained about n being undefined
	if(n->op == ONONAME)
		return;

この部分が、今回の修正の肝です。

  1. n->op == ONONAME: これは、代入の左辺を表す Node n の操作タイプが ONONAME であるかをチェックしています。ONONAME は、そのノードが未解決の名前、つまり未定義のシンボルを表すことを意味します。
  2. return;: もし nONONAME であれば、この関数はすぐに処理を終了します。これにより、その後の yyerror("cannot assign to %N", n); という行が実行されなくなります。

このロジックにより、未定義のシンボルに対しては、そのシンボルが未定義であるという最初のエラーメッセージのみが報告され、その後の代入に関する冗長なエラーメッセージは抑制されるようになります。これは、コンパイラのエラーメッセージの品質を向上させ、ユーザーが問題をより効率的にデバッグできるようにするための改善です。

test/fixedbugs/issue6406.go の追加

このテストファイルは、今回の修正が正しく機能することを確認するために作成されました。 // errorcheck ディレクティブは、このGoファイルがコンパイル時に特定のエラーメッセージを生成することを期待していることをコンパイラテストツールに伝えます。

func main() {
	s = "bob" // ERROR "undefined.*s"
	_ = s // ERROR "undefined.*s"
}

このコードでは、変数 s はどこにも定義されていません。

  • s = "bob" の行では、s が未定義であるため、"undefined.*s" というエラーメッセージが期待されます。
  • _ = s の行でも、同様に s が未定義であるため、"undefined.*s" というエラーメッセージが期待されます。

重要なのは、この修正によって、s = "bob" の行で「cannot assign to s」のような重複する代入エラーメッセージが出力されないことを検証している点です。このテストは、コンパイラが未定義シンボルに対するエラー報告を一度に限定し、冗長なメッセージを抑制するようになったことを確認します。

test/typecheck.go の変更

このファイルは既存のテストケースを更新しています。 変更前:

c = mine()	// ERROR "undefined.*c|not enough arguments" "cannot assign to c"

変更後:

c = mine()	// ERROR "undefined.*c|not enough arguments"

この変更は、c = mine() という行で c が未定義である場合に、以前は「cannot assign to c」というエラーメッセージも期待されていたものが、今回の修正によってそのメッセージが抑制されるようになったため、テストの期待値から削除されたことを示しています。これにより、テストが新しいコンパイラの挙動と一致するようになります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Goコンパイラのソースコード (src/cmd/gc)
  • Go言語のIssueトラッカー
  • Go言語のコードレビューシステム (Gerrit)
  • 抽象構文木 (AST) に関する一般的な情報
  • コンパイラの型チェックに関する一般的な情報
  • C言語の基本的な構文と概念I have provided the detailed technical explanation of the commit in Markdown format, following all the specified instructions and chapter structure. I have used the commit data and leveraged web search to provide comprehensive details. The output is to standard output only, as requested.