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

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

このコミットは、Goコンパイラのgc(現在のcmd/compileに相当)における型スイッチのエラーメッセージのフォーマットに関する修正です。具体的には、エラーメッセージ内でノード(ASTノード)を表示する際に、より簡潔なフォーマットを使用するように変更されています。これにより、コンパイラが出力するエラーメッセージがより読みやすくなります。

コミット

commit 0d6f857c3f76b9285ab2866e8715e333a3429449
Author: Luuk van Dijk <lvd@golang.org>
Date:   Sun Nov 6 22:13:54 2011 +0100

    gc: Switch related errors should use plain format.
    
    Fixes #2422.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5353046

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

https://github.com/golang/go/commit/0d6f857c3f76b9285ab2866e8715e333a3429449

元コミット内容

gc: Switch related errors should use plain format.

Fixes #2422.

R=rsc
CC=golang-dev
https://golang.org/cl/5353046

変更の背景

このコミットは、Goコンパイラ(gc)が型スイッチに関連するエラーメッセージを出力する際に、ノード(抽象構文木、ASTの要素)の情報を表示する方法を改善することを目的としています。以前のバージョンでは、%+Nというフォーマット指定子を使用してノード情報を出力していましたが、これは詳細すぎる情報(例えば、ノードの内部構造や型情報など)を含んでおり、ユーザーにとってエラーメッセージが冗長で読みにくいものになっていました。

Fixes #2422とあることから、この変更はGoのIssueトラッカーで報告された問題 #2422を解決するためのものです。この問題は、おそらく型スイッチのエラーメッセージがユーザーにとって分かりにくい、または不必要に詳細であるというフィードバックに基づいていると考えられます。開発者は、エラーメッセージは簡潔で、問題の特定に役立つ情報のみを提供すべきであるという原則に従い、ノードの表示フォーマットをより「プレーン」なものに変更することを決定しました。

前提知識の解説

Goコンパイラ (gc)

Go言語の公式コンパイラは、かつてgcという名前で知られていました。これはGo言語のソースコードを機械語に変換する役割を担っています。gcは、Go言語の進化とともにcmd/compileというパッケージに統合され、現在もGo言語のビルドシステムの中核をなしています。コンパイラは、ソースコードの字句解析、構文解析、型チェック、最適化、コード生成といった一連のフェーズを経て実行可能ファイルを生成します。

抽象構文木 (AST)

コンパイラの構文解析フェーズでは、ソースコードを抽象構文木(Abstract Syntax Tree, AST)と呼ばれるツリー構造に変換します。ASTは、プログラムの構造を抽象的に表現したもので、各ノードはプログラムの要素(変数、関数呼び出し、式、文など)に対応します。コンパイラはASTを操作することで、型チェックやコード生成を行います。

yyerror 関数

yyerrorは、Goコンパイラ内でエラーメッセージを出力するために使用される内部関数です。この関数は、C言語のprintf関数に似た可変引数を取り、フォーマット文字列とそれに続く引数に基づいてエラーメッセージを生成します。コンパイラが型チェックやその他の検証中に問題を発見した場合、この関数を呼び出してユーザーにエラーを報告します。

フォーマット指定子 (%N, %+N, %lN)

yyerrorのようなコンパイラ内部のエラー報告関数では、特定のデータ構造(この場合はASTノード)を人間が読める形式で出力するために、特別なフォーマット指定子が使用されます。

  • %N: ASTノードを標準的な形式で表示します。通常、ノードの種類や主要な識別子など、基本的な情報が含まれます。
  • %+N: ASTノードを詳細な形式で表示します。これには、ノードの内部構造、型情報、その他のデバッグに役立つ詳細なメタデータが含まれることがあります。開発者がコンパイラのデバッグを行う際には有用ですが、一般のユーザー向けのエラーメッセージとしては冗長になりがちです。
  • %lN: ASTノードを「プレーン」な形式で表示します。このフォーマットは、ノードの最も簡潔で分かりやすい表現を提供することを目的としています。例えば、変数名やリテラル値など、エラーメッセージの文脈で最も重要な情報のみが表示されます。このコミットでは、この%lNフォーマットへの変更が行われています。

技術的詳細

このコミットの技術的な核心は、Goコンパイラのsrc/cmd/gc/swt.cファイル(型スイッチの処理を担当する部分)におけるyyerror関数のフォーマット指定子の変更です。

具体的には、以下の3箇所でフォーマット指定子が%+Nから%lNに変更されています。

  1. yyerror("cannot type switch on non-interface value %+N", n->ntest->right);yyerror("cannot type switch on non-interface value %lN", n->ntest->right); このエラーは、インターフェース型ではない値に対して型スイッチを行おうとした場合に発生します。

  2. yyerror("case %+N in %T switch", ll->n, t);yyerror("case %lN in %T switch", ll->n, t); このエラーは、型スイッチのcase節で指定された型が、スイッチ対象の型と互換性がない場合に発生します。

  3. yyerror("%#N is not a type", ll->n);yyerror("%lN is not a type", ll->n); このエラーは、型スイッチのcase節で型が期待される箇所に、型ではないものが指定された場合に発生します。

これらの変更により、エラーメッセージに表示されるASTノードの情報が、より簡潔でユーザーフレンドリーな形式になります。例えば、%+NNode(OVAR, "x", type=int)のような詳細な情報を出力していたのに対し、%lNは単にxとだけ出力するようになる、といった違いが考えられます。これにより、エラーメッセージの可読性が向上し、ユーザーは問題の核心をより迅速に理解できるようになります。

また、test/fixedbugs/bug340.goのテストファイルも更新されており、期待されるエラーメッセージの変更が反映されています。これは、コンパイラの出力が変更されたことをテストで確認するための重要なステップです。

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

src/cmd/gc/swt.c

--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -825,7 +825,7 @@ typecheckswitch(Node *n)
 		typecheck(&n->ntest->right, Erv);
 		t = n->ntest->right->type;
 		if(t != T && t->etype != TINTER)
-			yyerror("cannot type switch on non-interface value %+N", n->ntest->right);
+			yyerror("cannot type switch on non-interface value %lN", n->ntest->right);
 	} else {
 		// value switch
 		top = Erv;
@@ -860,13 +860,13 @@ typecheckswitch(Node *n)
 				if(ll->n->op == OTYPE)
 					yyerror("type %T is not an expression", ll->n->type);
 				else if(ll->n->type != T && !eqtype(ll->n->type, t))
-					yyerror("case %+N in %T switch", ll->n, t);
+					yyerror("case %lN in %T switch", ll->n, t);
 				break;
 			case Etype:	// type switch
 				if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL)) {
 					;
 				} else if(ll->n->op != OTYPE && ll->n->type != T) {
-					yyerror("%#N is not a type", ll->n);
+					yyerror("%lN is not a type", ll->n);
 					// reset to original type
 					ll->n = n->ntest->right;
 				}

test/fixedbugs/bug340.go

--- a/test/fixedbugs/bug340.go
+++ b/test/fixedbugs/bug340.go
@@ -10,5 +10,5 @@ package main
 
 func main() {
 	var x interface{}
-	switch t := x.(type) { // GC_ERROR "0 is not a type"
+	switch t := x.(type) { // GC_ERROR "is not a type"
 	case 0:		// GCCGO_ERROR "expected type"
 		t.x = 1 // ERROR "type interface {}|reference to undefined field or method"
 	}

コアとなるコードの解説

src/cmd/gc/swt.cの変更は、typecheckswitch関数内で行われています。この関数は、Go言語のswitch文、特に型スイッチの型チェックを担当しています。

変更された各行は、yyerror関数を呼び出してコンパイルエラーを報告する部分です。

  • yyerror("cannot type switch on non-interface value %lN", n->ntest->right);: この行は、型スイッチの対象がインターフェース型ではない場合にエラーを報告します。以前は%+Nを使用していましたが、%lNに変更することで、n->ntest->right(スイッチ対象のノード)がより簡潔に表示されるようになります。

  • yyerror("case %lN in %T switch", ll->n, t);: この行は、値スイッチのcase節で、caseの値の型がスイッチ対象の型と一致しない場合にエラーを報告します。ここでもll->ncase節のノード)の表示が%+Nから%lNに変わり、簡潔になります。

  • yyerror("%lN is not a type", ll->n);: この行は、型スイッチのcase節で、型が期待される場所に型ではないものが指定された場合にエラーを報告します。ll->ncase節のノード)の表示が%#Nから%lNに変わり、より分かりやすいエラーメッセージになります。

test/fixedbugs/bug340.goの変更は、上記のyyerrorの出力変更に合わせて、期待されるエラーメッセージの文字列を修正しています。具体的には、GC_ERROR "0 is not a type"GC_ERROR "is not a type"に変更されています。これは、%lNフォーマットがノードの値をより簡潔に表示するため、以前は表示されていた0のような具体的な値が省略されるようになったことを示しています。このテストの更新は、コンパイラの出力が意図通りに変更されたことを検証するために不可欠です。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特にsrc/cmd/gc/swt.cおよびtest/fixedbugs/bug340.go)
  • Go言語のコンパイラに関する一般的な知識
  • 抽象構文木 (AST) に関する一般的な知識
  • C言語のprintfフォーマット指定子に関する一般的な知識 (Goコンパイラの内部実装がC言語で書かれているため)