[インデックス 17660] ファイルの概要
このコミットは、Goコンパイラのcmd/gc
ディレクトリ内のswt.c
ファイルに対する変更です。swt.c
は、Go言語のswitch
文の処理、特にケースの重複チェックに関連する部分を扱っていたC言語のソースファイルです。
Goコンパイラは歴史的にC言語で書かれていましたが、後にGo言語自体で書き直されました。したがって、swt.c
は現在のGoコンパイラのソースコードには含まれていませんが、このコミットはGoコンパイラの進化の過程における重要な改善点を示しています。具体的には、switch
文における重複ケースのエラーメッセージを改善し、デバッグの効率を高めることを目的としています。
コミット
commit 36f84809656d92ebe9a7c43e7395239903fcde1a
Author: Russ Cox <rsc@golang.org>
Date: Fri Sep 20 15:15:43 2013 -0400
cmd/gc: print expression in 'duplicate case in switch' error
The line number alone does not help when the line is
case '~', '*', '(', ')', '[', ']', '{', '}', '?', ':', ';', ',', '*', '%', '^', '!', '=', '<', '>', '+', '-', '&', '|':
R=ken2
CC=golang-dev
https://golang.org/cl/13431046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/36f84809656d92ebe9a7c43e7395239903fcde1a
元コミット内容
cmd/gc: print expression in 'duplicate case in switch' error
このコミットは、Goコンパイラ(cmd/gc
)において、switch
文で重複するケースが見つかった際のエラーメッセージに、重複している式(expression)自体を出力するように変更するものです。
コミットメッセージでは、以下のような長いcase
文の例を挙げて、行番号だけでは問題の特定が困難であることを説明しています。
case '~', '*', '(', ')', '[', ']', '{', '}', '?', ':', ';', ',', '*', '%', '^', '!', '=', '<', '>', '+', '-', '&', '|':
変更の背景
Go言語のswitch
文は、複数の値をカンマ区切りで指定できるため、非常に長いcase
リストを持つことがあります。このような状況で重複するケースが存在した場合、従来のコンパイラのエラーメッセージは、重複が発生した行番号と、以前に同じケースが定義された行番号のみを表示していました。
しかし、コミットメッセージの例が示すように、1行に多数のケースが記述されている場合、行番号だけではどの具体的な値が重複しているのかを即座に特定することが困難でした。開発者はエラーメッセージの行番号を頼りにソースコードを確認し、手動で重複箇所を探す必要がありました。これは特に大規模なswitch
文や、複雑な式がケースとして使われている場合に、デバッグの時間を不必要に長くしていました。
この変更は、エラーメッセージに重複している式そのものを表示することで、開発者がエラーの原因をより迅速かつ正確に特定できるようにし、デバッグ体験を向上させることを目的としています。
前提知識の解説
Goコンパイラ (cmd/gc
)
cmd/gc
は、Go言語の公式コンパイラの主要部分を指します。Goのソースコードを解析し、型チェックを行い、最終的に実行可能なバイナリコードを生成する役割を担っています。このコミットが作成された2013年時点では、cmd/gc
はC言語で実装されていました。現在では、Go言語自体で再実装され、cmd/compile/internal/gc
というパスに移動しています。
swt.c
swt.c
は、Goコンパイラのcmd/gc
の一部として存在していたC言語のソースファイルです。その名前が示すように、Go言語のswitch
文の処理、特にケースの解析、重複チェック、および関連するエラー報告ロジックを担当していました。
switch
文 (Go言語)
Go言語のswitch
文には、主に以下の2つの形式があります。
- 式スイッチ (Expression Switch):
switch
キーワードの後に式を記述し、その式の値とcase
句の値を比較します。switch x { case 1: // ... case 2, 3: // 複数の値をカンマ区切りで指定可能 // ... default: // ... }
- 型スイッチ (Type Switch):
インターフェース変数の動的な型に基づいて処理を分岐させます。
switch
キーワードの後に.(type)
という特殊な構文を記述します。switch v := i.(type) { case int: // v は int 型 case string: // v は string 型 default: // v は i と同じインターフェース型 }
このコミットは、これら両方のswitch
文における重複ケースのエラー報告を改善しています。
yyerrorl
と yyerror
これらは、Goコンパイラ(当時はC言語で書かれていた)がエラーメッセージを出力するために使用していた内部関数です。
yyerrorl(lineno, format, ...)
: 指定された行番号(lineno
)に関連付けてエラーメッセージを出力します。yyerror(format, ...)
: 現在のコンパイル位置に関連付けてエラーメッセージを出力します。
これらの関数は、C言語のprintf
のようなフォーマット文字列を受け取り、可変引数でそのフォーマットに合わせた値を受け取ります。このコミットでは、これらのフォーマット文字列に新しいフォーマット指定子を追加することで、エラーメッセージに重複する式や型情報を埋め込んでいます。
技術的詳細
このコミットの技術的な核心は、Goコンパイラがswitch
文の重複ケースを検出した際に生成するエラーメッセージの「情報量」を増やすことにあります。
従来のswt.c
では、重複ケースを検出すると、yyerrorl
またはyyerror
関数を呼び出してエラーを報告していました。この際、エラーメッセージのフォーマット文字列は、単に「duplicate case in switch」といった固定の文字列と、重複したケースが以前に定義された行番号(%L
)のみを含んでいました。
例えば、以下のようなコードがあったとします。
package main
func main() {
x := 1
switch x {
case 1:
println("one")
case 2:
println("two")
case 1: // 重複
println("one again")
}
}
この場合、従来のコンパイラは以下のようなエラーを出力していました(具体的なメッセージはバージョンによって異なる可能性がありますが、内容は類似しています)。
duplicate case in switch
previous case at main.go:7
(例として7行目とする)
このメッセージでは、どの値が重複しているのかが明示されていません。開発者は7行目と、エラーが出た行(例えば10行目)を比較して、手動で1
が重複していることを確認する必要がありました。
このコミットでは、この問題を解決するために、エラーメッセージのフォーマット文字列に新しい「プレースホルダー」を導入しています。
%T
: 型スイッチの重複ケースにおいて、重複している「型」の情報を表示するために使用されます。%N
: 式スイッチの重複ケースにおいて、重複している「式」の情報を表示するために使用されます。
これにより、コンパイラはエラーメッセージを生成する際に、重複している具体的な値や型を動的に挿入できるようになります。例えば、上記の例であれば、新しいエラーメッセージは以下のように改善されます。
duplicate case 1 in switch
previous case at main.go:7
これにより、開発者はエラーメッセージを見ただけで、どの値が重複しているのかを一目で理解できるようになり、デバッグの効率が大幅に向上します。これは、コンパイラのエラーメッセージが単なるエラーの存在を通知するだけでなく、その原因を特定するための具体的な手がかりを提供するという、ユーザーフレンドリーな設計思想に基づいています。
コアとなるコードの変更箇所
src/cmd/gc/swt.c
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -415,7 +415,7 @@ mkcaselist(Node *sw, int arg)
break;
if(!eqtype(c1->node->left->type, c2->node->left->type))
continue;
- yyerrorl(c2->node->lineno, "duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
+ yyerrorl(c2->node->lineno, "duplicate case %T in type switch\n\tprevious case at %L", c2->node->left->type, c1->node->lineno);
}
}
break;
@@ -427,7 +427,7 @@ mkcaselist(Node *sw, int arg)
if(exprcmp(c1, c1->link) != 0)
continue;
setlineno(c1->link->node);
- yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
+ yyerror("duplicate case %N in switch\n\tprevious case at %L", c1->node->left, c1->node->lineno);
}
break;
}
コアとなるコードの解説
このコミットでは、src/cmd/gc/swt.c
ファイル内のmkcaselist
関数が変更されています。この関数は、switch
文のケースリストを処理し、重複するケースを検出する役割を担っています。
変更点は2箇所あり、それぞれ異なる種類のswitch
文のエラーメッセージを改善しています。
-
型スイッチ (Type Switch) の重複ケースに対する変更: 元のコード:
yyerrorl(c2->node->lineno, "duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
変更後のコード:
yyerrorl(c2->node->lineno, "duplicate case %T in type switch\n\tprevious case at %L", c2->node->left->type, c1->node->lineno);
ここでは、
yyerrorl
関数のフォーマット文字列が変更されています。"duplicate case in switch"
が"duplicate case %T in type switch"
に変わりました。- 新しく追加された
%T
は、c2->node->left->type
の値を表示するためのフォーマット指定子です。c2->node->left->type
は、重複している型スイッチのケースの型情報を表します。 - これにより、エラーメッセージは「
duplicate case int in type switch
」のように、重複している具体的な型名を含むようになります。
-
式スイッチ (Expression Switch) の重複ケースに対する変更: 元のコード:
yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
変更後のコード:
yyerror("duplicate case %N in switch\n\tprevious case at %L", c1->node->left, c1->node->lineno);
こちらも
yyerror
関数のフォーマット文字列が変更されています。"duplicate case in switch"
が"duplicate case %N in switch"
に変わりました。- 新しく追加された
%N
は、c1->node->left
の値を表示するためのフォーマット指定子です。c1->node->left
は、重複している式スイッチのケースの式(値)を表します。 - これにより、エラーメッセージは「
duplicate case "hello" in switch
」や「duplicate case 10 in switch
」のように、重複している具体的な式(値)を含むようになります。
これらの変更により、コンパイラが生成するエラーメッセージは、単に重複があることを示すだけでなく、どの値や型が重複しているのかを明示的に示すようになり、開発者のデバッグ作業を大幅に効率化します。
関連リンク
- Go CL 13431046: https://golang.org/cl/13431046
参考にした情報源リンク
- Goコンパイラの概要:
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEE_E3DnpwlWDh_tA9vZLlzltQPoEP3wX5xvv5tq6n4jyG2Wqqzb9WCJrdgM49uL5OnOy7cB6GA2J2x18MP2EjLJdO0ohCWCUCs0B4OCwFuYy_Ys2qVjZfwJwCjMt1cW8Xh1kNcaf0udXStPW_JQoP-K6s=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG-u4w84ooVIxETZr6ZzfljudvQBSm2tf35Zp2ubfzS9KPdby1NwRHW3VHRBASt4wnGcPh74sSSG3aLY7ue2qAPnw9D7U2h1Dh567L9ZdiG_6T4JPWpZLJ3omcyro9yP4a9BP9I
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGujqBdz9LcuDaiNb7C3xQeysp2q_31ui1y7OPsZlRkGuoTTEVkxbBW6LDIJPy8q1Xy2mIth660cswSiIgDh72b3qr2-YPTl2P_JlkPyBBOtHTlrIgpVAJNTICMgj50_j20BnUarB6eGEajAbNm66sBeIQ_CqOl5Gs2i46N_9VI6S7p8twU-A==
- GoコンパイラのC言語からGo言語への移行:
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQELvdUuoYPmnjtjPQL7rqluLddlGv0OwcesS12Yp2AHZlHkXUED288r4mfYclQ4M1cLHujVb7rctrgbNg7xS9lBBRuRKb35NB0kN6szi16ezTRAeaOar9mgCbcVPeuK-tc-88iir0PCfrsJsomVhqY6JXCROAp2mMFV48AZDcS7D7Gur3CsBux8g03YgSkOUQthvid7Wd53oA==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEY_g1FkcUfPWD4GSzq2bjP8vEonZIb9Bmth3igBZ05LYR1kEn7Np_Dc8dV8wIzPR3oChY5WQ0a_Wvm_MCX-WU7osgeK1akkh0nmceSJ4HlJCrJyXIvS_tGS2g3