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

[インデックス 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 (チャネルchvalueを送信)
  • 値の受信(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が不正なコンテキストで使用された場合)が提供されることになります。

関連リンク

参考にした情報源リンク