[インデックス 17486] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)におけるチャネルへのsend
操作が値として使用された場合に表示されていた特定のヒントメッセージを削除するものです。このメッセージはGo 1以前のユーザーがGo 1に移行する際に役立つものでしたが、Go 1以降では混乱を招くようになったため削除されました。
コミット
commit e1ac15743b026f843401b95d34c2c0f54847e15d
Author: Russ Cox <rsc@golang.org>
Date: Fri Sep 6 15:47:52 2013 -0400
cmd/gc: remove "send used as value" hint
This message was helpful for pre-Go 1 users updating to Go 1.
That time is past. Now the message is confusing because it
depends on knowing what pre-Go 1 looked like.
Update #4697.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13335051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e1ac15743b026f843401b95d34c2c0f54847e15d
元コミット内容
このコミットは、Goコンパイラ(cmd/gc
)から「send statement %N used as value; use select for non-blocking send」というヒントメッセージを削除することを目的としています。コミットメッセージによると、このメッセージはGo 1リリース以前のユーザーがGo 1にコードを更新する際に役立つものでしたが、Go 1のリリースから時間が経ち、このメッセージがGo 1以前の言語仕様を知らないユーザーにとっては混乱を招くようになったため、削除されました。この変更は、Go issue #4697 に関連しています。
変更の背景
Go言語は、その初期段階から継続的に進化してきました。Go 1は、言語の安定性と互換性を保証するための重要なマイルストーンでした。Go 1以前のバージョンでは、チャネル操作、特にsend
ステートメントのセマンティクスが現在とは異なっていました。
具体的には、Go 1以前では、チャネルへのsend
操作(例: ch <- val
)は、その操作自体が値を返すかのように扱われることがありました。しかし、Go 1以降では、send
ステートメントは値を返さないステートメントとして厳密に定義されました。この変更により、非ブロッキングなsend
操作を行いたい場合は、select
ステートメントを使用する必要があることが明確になりました。
Go 1への移行期には、古いコードベースを持つ開発者が新しいGo 1のセマンティクスに適合させる必要がありました。この際、コンパイラが「send statement %N used as value; use select for non-blocking send」というヒントメッセージを出すことで、開発者は自身のコードがGo 1の新しいルールに違反していることを認識し、select
ステートメントへの移行を促されました。
しかし、Go 1が広く普及し、Go 1以前の言語仕様を知らない新しい開発者が増えるにつれて、このヒントメッセージはかえって混乱を招くようになりました。なぜなら、彼らにとってはsend
ステートメントが値を返すという概念自体が存在せず、このメッセージが何を意味するのか理解できなかったためです。このコミットは、このような状況の変化に対応し、コンパイラのメッセージをより現代のGo言語の慣習に合わせるために行われました。
前提知識の解説
Go言語のチャネルとsend
操作
Go言語のチャネルは、ゴルーチン間で値を安全にやり取りするための強力な同期プリミティブです。チャネルは型付けされており、特定の型の値のみを送受信できます。
- チャネルの宣言:
ch := make(chan int)
(int型のチャネルを作成) - 値の送信(send):
ch <- value
(チャネルch
にvalue
を送信) - 値の受信(receive):
value := <-ch
(チャネルch
から値を受信)
チャネルへの送信操作は、デフォルトではブロッキングです。つまり、受信側が値を受け取るまで、送信側のゴルーチンはブロックされます。非ブロッキングな送信を行うには、select
ステートメントとdefault
ケースを組み合わせる必要があります。
Goコンパイラ(cmd/gc
)と型チェック
cmd/gc
は、Go言語の公式コンパイラの一部であり、Goソースコードを機械語に変換する役割を担っています。コンパイルプロセスには、字句解析、構文解析、型チェック、最適化、コード生成などが含まれます。
型チェックは、プログラムが言語の型システム規則に従っていることを検証する重要なフェーズです。これにより、型に関するエラー(例: 整数型に文字列を代入しようとする)が実行時ではなくコンパイル時に検出されます。
コンパイラの内部表現とフラグ(top
, Erv
)
Goコンパイラの内部では、ソースコードは抽象構文木(AST)として表現されます。型チェックの過程で、ASTの各ノード(式やステートメントなど)に対して様々な属性やフラグが設定されます。
top
: これは、コンパイラが現在処理しているASTノードの「期待されるコンテキスト」を示すビットマスクです。例えば、式が値として使用されることを期待されているか、ステートメントとして使用されることを期待されているか、といった情報を含みます。Erv
(Expression Result Value):top
フラグの一部で、式が値を生成することを期待されていることを示します。例えば、x = y + z
のような代入文の右辺のy + z
は値を生成するため、Erv
が期待されます。
yyerror
yyerror
は、Goコンパイラ内で使用されるエラー報告関数です。コンパイル時にエラーが検出された場合、この関数が呼び出され、指定されたエラーメッセージと関連情報(ファイル名、行番号など)が出力されます。
技術的詳細
このコミットで変更されたファイルは src/cmd/gc/typecheck.c
です。このファイルはGoコンパイラの型チェックフェーズのC言語実装の一部です。
変更箇所は、typecheck
関数の内部にあるOSEND
ケース、つまりチャネルへの送信操作の型チェックロジックです。
元のコードでは、OSEND
(送信ステートメント)が処理される際に、if(top & Erv)
という条件がチェックされていました。
top
は現在のASTノードのコンテキストを示すフラグです。Erv
は、そのノードが値を生成する式として期待されていることを示すフラグです。
この条件if(top & Erv)
は、「もしsend
ステートメントが、値を期待されるコンテキストで使用されている場合」を意味します。Go 1以前のセマンティクスでは、このような状況が発生し得たため、コンパイラはyyerror
を呼び出して「send statement %N used as value; use select for non-blocking send」というエラーメッセージを出力していました。これは、send
ステートメントが値を返さないことを明確にし、非ブロッキングな操作にはselect
を使うべきだと開発者に促すためのものでした。
しかし、Go 1以降では、send
ステートメントは常に値を返さないステートメントとして扱われるようになりました。したがって、send
ステートメントがErv
コンテキストで現れることは、もはや有効なGoプログラムではあり得ません。このため、このチェックとそれに伴うヒントメッセージは不要となり、削除されました。
変更後、ok |= Etop | Erv;
の行は ok |= Etop;
に変更されています。これは、OSEND
操作がErv
(値を生成する式)として扱われるべきではないことを反映しています。Etop
は、トップレベルのステートメントとして扱われることを示すフラグです。
コアとなるコードの変更箇所
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -928,11 +928,7 @@ reswitch:
goto ret;
case OSEND:
- if(top & Erv) {
- yyerror("send statement %N used as value; use select for non-blocking send", n);
- goto error;
- }
- ok |= Etop | Erv;
+ ok |= Etop;
l = typecheck(&n->left, Erv);
typecheck(&n->right, Erv);
defaultlit(&n->left, T);
コアとなるコードの解説
上記の差分は、src/cmd/gc/typecheck.c
ファイル内のtypecheck
関数のOSEND
ケース(チャネル送信操作の型チェックロジック)に対する変更を示しています。
-
削除されたコード:
if(top & Erv) { yyerror("send statement %N used as value; use select for non-blocking send", n); goto error; }
この
if
ブロックは、send
ステートメントが値を期待されるコンテキスト(top & Erv
)で使用された場合に、コンパイルエラー(yyerror
)を発生させ、特定のヒントメッセージを出力していました。このメッセージは、Go 1以前の言語仕様からGo 1への移行を支援するためのものでした。Go 1以降ではsend
ステートメントは値を返さないため、このチェックは不要になりました。 -
変更された行:
- ok |= Etop | Erv; + ok |= Etop;
この行は、
OSEND
操作が型チェック後にどのようなコンテキストで有効であるかを示すok
フラグを設定しています。- 元のコードでは、
ok
フラグにEtop
(トップレベルのステートメント)とErv
(値を生成する式)の両方が設定されていました。これは、Go 1以前のセマンティクスがsend
操作を値として扱う可能性があったことを示唆しています。 - 変更後、
Erv
フラグが削除され、ok
フラグにはEtop
のみが設定されるようになりました。これは、send
ステートメントがGo 1以降では純粋にステートメントであり、値を生成する式ではないという現在のGo言語のセマンティクスを正確に反映しています。
- 元のコードでは、
この変更により、コンパイラはGo 1以前の互換性のための特別な処理を削除し、現在のGo言語のセマンティクスに完全に準拠するようになりました。これにより、コンパイラのコードベースが簡素化され、Go 1以降のユーザーにとってはより明確なエラーメッセージ(もしsend
が不正なコンテキストで使用された場合)が提供されることになります。
関連リンク
- Go issue #4697: https://github.com/golang/go/issues/4697
- Go CL 13335051: https://golang.org/cl/13335051
参考にした情報源リンク
- Go 1 Release Notes: https://go.dev/doc/go1
- Go Language Specification (Channels): https://go.dev/ref/spec#Channel_types
- Go Language Specification (Select statements): https://go.dev/ref/spec#Select_statements
- Go compiler source code (typecheck.c): https://github.com/golang/go/blob/master/src/cmd/compile/internal/gc/typecheck.go (Note: The file path has changed since 2013, but the concepts remain similar.)
- Understanding Go's Type System (General concept): https://go.dev/blog/go-type-system
- Go compiler internals (General overview): https://go.dev/blog/go-compiler-internals
- Go issue tracker: https://github.com/golang/go/issues
- Go Code Review Comments: https://go.dev/doc/effective_go#commentary (For understanding Go's development practices)
- Go Wiki: https://go.dev/wiki/ (General Go information)