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

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

src/cmd/gc/walk.c は、Goコンパイラの重要なフェーズである「walk」フェーズの一部を構成するC言語のソースファイルです。このフェーズは、Go言語の抽象構文木(AST)を走査し、高レベルなGoの構文をより低レベルな中間表現(IR)に変換したり、複雑なステートメントを単純な操作に分解したりする役割を担っています。具体的には、チャネル操作(送受信)、セレクタ、型チェックなど、様々な言語構造の「desugaring」(糖衣構文の解消)を行います。

コミット

  • コミットハッシュ: a665e2924c295b5dd935032b88a6c0f5ed66b3c0
  • Author: Ken Thompson ken@golang.org
  • Date: Sat Feb 21 12:41:34 2009 -0800

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

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

元コミット内容

    bug with select :=
    
    R=r
    OCL=25278
    CL=25278

変更の背景

このコミットは、Go言語の初期段階におけるコンパイラのバグ修正を目的としています。特に、select ステートメント内でチャネル受信操作と変数宣言(:=)を組み合わせた際に発生する問題に対処しています。Goのselectステートメントは、複数のチャネル操作を待機し、準備ができた最初の操作を実行するための強力な並行処理プリミティブです。しかし、初期のコンパイラ実装では、この特定のシナリオにおいて、型情報の処理が不完全であったり、適切なコンパイル時チェックが欠けていたりするバグが存在したと考えられます。

前提知識の解説

Goコンパイラの「Walk」フェーズ

Goコンパイラは、ソースコードを機械語に変換する過程で複数のフェーズを経ます。その一つが「Walk」フェーズです。このフェーズの主な目的は以下の通りです。

  1. ASTの走査と変換: 字句解析と構文解析によって生成された抽象構文木(AST)を走査し、より低レベルな中間表現(IR)に変換します。
  2. Desugaring(糖衣構文の解消): Go言語のシンタックスシュガー(例えば、for-rangeループやselectステートメント、チャネル操作など)を、より基本的な操作やランタイム関数呼び出しに展開します。これにより、バックエンドのコード生成が容易になります。
  3. 型チェックとセマンティック分析: 型の一貫性を確認し、意味的なエラーを検出します。

OSELECTORECV

Goコンパイラの内部では、ASTの各ノードが特定の操作を表す「オペレーションコード」(OpCode)を持っています。

  • OSELECT: selectステートメント全体を表すオペレーションコードです。
  • ORECV: チャネルからの受信操作(例: <-ch)を表すオペレーションコードです。

walktype 関数

walktype関数は、Goコンパイラの「walk」フェーズにおいて、特定のノードの型情報を処理するために使用されます。この関数は、式の型を決定し、必要に応じて型変換や型チェックを行う役割を担います。コンパイラがチャネル受信操作のような複雑な式を処理する際、その結果の型が正しく推論され、後続の処理で利用できるようにすることが不可欠です。

技術的詳細

このコミットは、src/cmd/gc/walk.c 内の OSELECT および ORECV の処理に関するバグを修正しています。

元のコードでは、OSELECT のケースで walkselect(n) が呼び出される前に、不要な空行が存在していました。これは直接的なバグの原因ではありませんが、コードの整形や、もしかしたら以前のデバッグコードの残骸かもしれません。

より重要な変更は、selectas 関数内の ORECV 処理に関するものです。selectas 関数は、select ステートメント内でチャネル受信操作の結果を変数に代入するようなケース(例: v := <-ch)を処理していると考えられます。

修正前のコードでは、ORECV ノードの expr->left(これはチャネル自体を表す)の型を処理する walktype(expr->left, Erv) の呼び出しが欠落していました。

Go言語では、チャネルからの受信操作は、そのチャネルの要素型と同じ型の値を返します。例えば、chan int から受信すれば int 型の値が得られます。コンパイラは、この受信操作の結果の型を正確に把握し、その結果が代入される変数の型と一致するかどうかを検証する必要があります。

walktype(expr->left, Erv) の追加は、ORECV ノードの左側(つまりチャネル式)の型を明示的に「walk」し、その型情報をコンパイラの内部状態に適切に反映させることを保証します。これにより、select := のような構文でチャネル受信の結果を変数に代入する際に、コンパイラが正しい型推論と型チェックを実行できるようになり、以前存在したバグが解消されます。この修正により、コンパイラはチャネルの要素型を正しく認識し、代入される変数の型との整合性を検証できるようになります。

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

--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -343,7 +343,6 @@ loop:
 	case OSELECT:
 		if(top != Etop)
 			goto nottop;
-\
 		walkselect(n);
 		goto ret;
 
@@ -1487,6 +1486,7 @@ selectas(Node *name, Node *expr)
 
 	if(expr == N || expr->op != ORECV)
 		goto bad;
+\twalktype(expr->left, Erv);\
 	t = expr->left->type;\
 	if(t == T)\
 		goto bad;\

コアとなるコードの解説

1. OSELECT ケースの変更

-

この変更は、OSELECT のケース(select ステートメントの処理)において、walkselect(n) の呼び出しの直前にあった空行を削除しています。これは機能的な変更ではなく、コードのクリーンアップまたは以前のデバッグコードの残骸の削除と考えられます。

2. selectas 関数内の ORECV 処理の変更

+\twalktype(expr->left, Erv);\

これがこのコミットの主要な修正点です。

  • selectas 関数は、select ステートメント内でチャネル受信操作の結果を変数に代入するシナリオ(例: value := <-ch)を処理します。
  • exprORECV ノード(チャネル受信操作)を表します。
  • expr->left は、受信操作が行われるチャネル自体を表すノードです。
  • walktype(expr->left, Erv) の呼び出しが追加されました。
    • walktype は、指定されたノード(ここではチャネルノード expr->left)の型情報を処理するコンパイラ内部関数です。
    • Erv は、評価結果が値として使用されることを示すコンテキストフラグです。
  • この行が追加されることで、コンパイラはチャネル受信操作の対象となるチャネルの型を明示的に「walk」し、その型情報をコンパイラの内部状態に適切に反映させます。これにより、受信操作の結果の型が正しく推論され、その後の型チェックやコード生成で利用できるようになります。

この修正により、select := のような構文でチャネル受信の結果を変数に代入する際に、コンパイラが正しい型推論と型チェックを実行できるようになり、以前存在したバグが解消されました。

関連リンク

  • Go言語の select ステートメントに関する公式ドキュメント: https://go.dev/ref/spec#Select_statements
  • Goコンパイラの内部構造に関する一般的な情報源(Goコンパイラは進化しており、C言語からGo言語に書き換えられています。このコミットは初期のC言語時代のものです):

参考にした情報源リンク