[インデックス 1312] ファイルの概要
このコミットは、Go言語の初期のコンパイラ(gc
)における2つのマイナーなバグ修正を含んでいます。具体的には、シンボル再宣言のロジックと、any
型(Goの初期段階における特殊な型)の使用制限に関する修正が行われています。これらの修正は、コンパイラの正確性と堅牢性を向上させることを目的としています。
コミット
commit 7dd62cb3bc51222076506132f5d409ec7fa58b38
Author: Ken Thompson <ken@golang.org>
Date: Wed Dec 10 12:38:16 2008 -0800
2 minor bugs
R=r
OCL=20906
CL=20906
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7dd62cb3bc51222076506132f5d409ec7fa58b38
元コミット内容
2 minor bugs
R=r
OCL=20906
CL=20906
変更の背景
このコミットは、Go言語の初期開発段階(2008年12月)に行われたもので、Go言語がまだ一般に公開される前の内部開発フェーズに当たります。当時のGoコンパイラは、C言語で書かれたgc
(Go Compiler)という名称のツールチェーンの一部でした。
変更の背景には、以下の2つの具体的な問題があったと考えられます。
- シンボル再宣言のロジックの誤り:
src/cmd/gc/dcl.c
におけるredeclare
関数は、シンボル(変数、関数など)が現在のスコープブロック内で再宣言された場合にエラーを報告する役割を担っていました。しかし、元の実装では、シンボルが現在のブロックに属していない場合にエラーを報告するという、意図とは逆のロジックになっていました。これにより、本来エラーとすべき再宣言が見過ごされたり、逆に正当な宣言が誤ってエラーとされたりする可能性がありました。 any
型の使用制限の不備:src/cmd/gc/go.y
はGo言語の文法定義ファイルであり、any
型に関する制約が記述されていました。any
型は、Go言語の初期段階における特殊な型であり、後のinterface{}
(空インターフェース)やany
(Go 1.18以降の型パラメータ)の前身、あるいはコンパイラ内部での特殊な用途の型であった可能性があります。この型は通常の使用が制限されるべきでしたが、特定の内部パッケージ(PACKAGE
という名前のパッケージ)では例外的に許可される必要があったと考えられます。元の実装では、この例外処理が考慮されておらず、PACKAGE
パッケージ内でのany
型の使用が不適切に制限されていた可能性があります。
これらのバグは、コンパイラの正確な動作を妨げ、Go言語のセマンティクスを正しく反映できない原因となっていたため、修正が必要でした。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
- Go言語の初期コンパイラ
gc
:- Go言語の最初の公式コンパイラは、C言語で書かれた
gc
(Go Compiler)というツールチェーンでした。これは、Plan 9 Cコンパイラをベースにしていました。 src/cmd/gc
ディレクトリは、このコンパイラのソースコードが置かれていた場所です。
- Go言語の最初の公式コンパイラは、C言語で書かれた
- コンパイラのフェーズ:
- 字句解析 (Lexical Analysis): ソースコードをトークンに分解する。
- 構文解析 (Syntax Analysis): トークン列が文法規則に合致するかをチェックし、抽象構文木 (AST) を構築する。
go.y
のようなYacc/Bisonファイルがこのフェーズで使用される。 - 意味解析 (Semantic Analysis): 型チェック、スコープ解決、シンボル解決などを行う。
dcl.c
のようなファイルがこのフェーズに関わる。 - コード生成 (Code Generation): 実行可能なコードを生成する。
- Yacc/Bison:
go.y
ファイルは、Yacc (Yet Another Compiler Compiler) またはそのGNU版であるBisonで生成されるパーサーの文法定義ファイルです。.y
拡張子はYacc/Bisonの入力ファイルを示します。- 文法規則と、それらの規則がマッチしたときに実行されるC言語のアクションコードが記述されています。
$$
は現在の規則のセマンティック値、$1
などは規則の右辺の各要素のセマンティック値を参照します。yyerror
は、構文解析中にエラーが発生した際に呼び出される関数で、エラーメッセージを出力します。
- シンボルテーブルとスコープ:
- コンパイラは、プログラム内で宣言された変数、関数、型などの「シンボル」に関する情報を「シンボルテーブル」に格納します。
- 「スコープ」は、シンボルが有効なプログラムの領域を指します。Go言語では、ブロック(
{}
で囲まれた領域)ごとにスコープが形成されます。 Sym
構造体はシンボルを表し、block
フィールドはシンボルが宣言されたブロックの識別子、lastlineno
は最後に宣言された行番号を保持していると考えられます。
any
型 (初期Go言語):- Go言語の初期には、現在の
interface{}
(空インターフェース)に相当する概念がany
というキーワードで表現されていた時期があった、あるいはコンパイラ内部で特殊な型として扱われていた可能性があります。 - Go 1.18で導入された
any
キーワードは、interface{}
のエイリアスですが、このコミットの時期とは異なります。このコミットにおけるany
型は、よりプリミティブな、コンパイラ内部の概念であった可能性が高いです。
- Go言語の初期には、現在の
strcmp
関数: C言語の標準ライブラリ関数で、2つの文字列を比較します。strcmp(s1, s2) == 0
は、s1
とs2
が等しいことを意味します。
技術的詳細
src/cmd/gc/dcl.c
の修正
このファイルは、Goコンパイラの宣言(declaration)処理、特にシンボル管理とスコープ解決に関連する部分を扱っています。redeclare
関数は、シンボルが再宣言された場合の処理を担当します。
元のコードのロジックは以下の通りでした。
if(s->block != block) { // シンボルsが現在のブロックとは異なるブロックで宣言されている場合
s->block = block;
s->lastlineno = lineno;
return; // 新しいブロックでの宣言として処理し、関数を終了
}
// ここに到達するのは、s->block == block の場合、つまり同じブロックで再宣言された場合
yyerror("%s %S redeclared in this block %d", str, s, block); // エラーを報告
print("\tprevious declaration at %L\\n", s->lastlineno);
このロジックは、コメントと実際の動作が逆になっていました。if(s->block != block)
の条件が真の場合(異なるブロックでの宣言)、それは再宣言ではなく、新しいスコープでの有効な宣言であるべきです。しかし、その場合はreturn
してしまい、エラー報告は行われません。逆に、if
文を通過してエラー報告が行われるのは、s->block == block
の場合、つまり同じブロックでの再宣言の場合です。これは正しい動作ですが、if
文の条件が混乱を招いていました。
修正後のコードは以下の通りです。
if(s->block == block) { // シンボルsが現在のブロックと同じブロックで宣言されている場合
yyerror("%s %S redeclared in this block", str, s); // エラーを報告
print("\tprevious declaration at %L\\n", s->lastlineno);
}
// ここに到達するのは、s->block == block の場合はエラー報告後、
// s->block != block の場合はif文をスキップして直接ここに来る
s->block = block; // シンボルのブロック情報を現在のブロックに更新
s->lastlineno = lineno; // シンボルの最終宣言行を現在の行に更新
この修正により、ロジックが明確になりました。
s->block == block
の場合のみ、再宣言エラーを報告します。- エラー報告の有無にかかわらず、
s->block
とs->lastlineno
は常に現在のブロックと行番号に更新されます。これは、シンボルが新しいブロックで宣言された場合(s->block != block
)でも、その新しい宣言情報を正しく記録するために必要です。 また、エラーメッセージから%d
(ブロック番号)が削除され、より簡潔になっています。
src/cmd/gc/go.y
の修正
このファイルはGo言語の文法をYacc形式で定義しており、構文解析中に特定のセマンティックアクションを実行します。修正箇所はnametype
という文法規則に関連しています。
元のコードは以下の通りでした。
nametype:
LATYPE
{
if($1->otype != T && $1->otype->etype == TANY) // $1が型であり、その型がTANYである場合
yyerror("the any type is restricted"); // "any typeは制限されています"とエラー
$$ = oldtype($1);
}
;
この規則は、LATYPE
(おそらく型名を表すトークン)が解析されたときに実行されます。$1
はLATYPE
のセマンティック値を参照し、その型情報(otype
、etype
)をチェックしています。TANY
は、コンパイラ内部でany
型を表す定数であると考えられます。
修正後のコードは以下の通りです。
nametype:
LATYPE
{
if($1->otype != T && $1->otype->etype == TANY)
if(strcmp(package, "PACKAGE") != 0) // 現在のパッケージ名が"PACKAGE"でない場合
yyerror("the any type is restricted");
$$ = oldtype($1);
}
;
追加された条件if(strcmp(package, "PACKAGE") != 0)
は、any
型が制限されるのは、現在のパッケージ名が文字列"PACKAGE"
と異なる場合のみであることを意味します。これは、PACKAGE
という名前のパッケージが、any
型を特別に許可される内部的な、あるいはブートストラップ的なパッケージであったことを示唆しています。この修正により、特定の内部パッケージでのany
型の正当な使用が誤ってエラーとなるのを防ぎ、コンパイラの柔軟性と正確性を向上させています。
コアとなるコードの変更箇所
src/cmd/gc/dcl.c
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -701,13 +701,12 @@ testdclstack(void)
static void
redeclare(char *str, Sym *s)
{
- if(s->block != block) {
- s->block = block;
- s->lastlineno = lineno;
- return;
- }
- yyerror("%s %S redeclared in this block %d", str, s, block);
- print("\tprevious declaration at %L\\n\", s->lastlineno);
+ if(s->block == block) {
+ yyerror("%s %S redeclared in this block", str, s);
+ print("\tprevious declaration at %L\\n", s->lastlineno);
+ }
+ s->block = block;
+ s->lastlineno = lineno;
}
void
src/cmd/gc/go.y
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -1087,6 +1087,7 @@ nametype:
LATYPE
{
if($1->otype != T && $1->otype->etype == TANY)
+\t\tif(strcmp(package, "PACKAGE") != 0)
\tyyerror("the any type is restricted");
$$ = oldtype($1);
}
コアとなるコードの解説
src/cmd/gc/dcl.c
の redeclare
関数
この関数は、シンボルs
がstr
という名前で再宣言されたかどうかをチェックし、必要に応じてエラーを報告します。
- 変更前:
if(s->block != block)
: シンボルs
が現在のスコープブロックblock
とは異なるブロックで宣言されている場合、それは再宣言ではなく、新しいスコープでの有効な宣言と見なされ、s->block
とs->lastlineno
を更新して関数を終了していました。- この
if
ブロックを通過した場合(つまりs->block == block
の場合)、同じブロック内での再宣言と判断し、yyerror
でエラーメッセージを出力していました。しかし、このロジックは直感的ではなく、if
文の条件が逆転しているように見えました。
- 変更後:
if(s->block == block)
: シンボルs
が現在のスコープブロックblock
と同じブロックで既に宣言されている場合、これは明確な再宣言エラーです。yyerror
を呼び出してエラーメッセージを出力します。エラーメッセージから不要なブロック番号の表示が削除され、より簡潔になりました。s->block = block; s->lastlineno = lineno;
: この2行はif
ブロックの外に移動しました。これにより、シンボルが同じブロックで再宣言された場合(エラー報告後)でも、新しいブロックで宣言された場合(if
文をスキップ)でも、常にシンボルのブロック情報と最終宣言行が現在の情報に更新されるようになりました。これは、シンボルが有効な宣言として処理された場合に、その最新の宣言情報をシンボルテーブルに反映させるために重要です。
この修正により、シンボル再宣言の検出ロジックがより正確かつ堅牢になり、コンパイラがGo言語のスコープ規則を正しく適用できるようになりました。
src/cmd/gc/go.y
の nametype
規則
この部分は、Go言語の文法定義ファイルにおけるnametype
という規則のアクションブロックです。nametype
は、型名が解析されたときに実行される処理を定義しています。
- 変更前:
if($1->otype != T && $1->otype->etype == TANY)
: 解析された型($1
で参照される)が、コンパイラ内部でTANY
として定義されている特殊な型であるかどうかをチェックしていました。もしそうであれば、yyerror("the any type is restricted")
を呼び出し、any
型の使用が制限されていることを示すエラーを出力していました。
- 変更後:
if($1->otype != T && $1->otype->etype == TANY)
: 既存のany
型チェックはそのままです。if(strcmp(package, "PACKAGE") != 0)
: 新たにこの条件が追加されました。これは、現在のコンパイル対象のパッケージ名が文字列"PACKAGE"
と等しくない場合にのみ、内側のyyerror
が実行されるようにします。package
変数は、現在のコンパイル対象のパッケージ名を表すグローバル変数またはコンテキスト変数であると推測されます。strcmp(package, "PACKAGE") != 0
は、「現在のパッケージ名が"PACKAGE"
ではない」という条件を意味します。
- この二重の
if
文により、any
型がTANY
であり、かつ現在のパッケージが"PACKAGE"
ではない場合にのみ、エラーが報告されるようになりました。
この修正は、any
型が特定の内部パッケージ("PACKAGE"
)でのみ許可されるという、Go言語の初期設計における特殊な要件に対応したものです。これにより、コンパイラは言語のセマンティクスをより正確に強制できるようになりました。
関連リンク
- Go言語の初期開発に関する情報: https://go.dev/doc/history
- Yacc/Bisonのドキュメンテーション(一般的な情報): https://www.gnu.org/software/bison/manual/
- Go言語のコンパイラ構造に関する一般的な情報(現在のGoコンパイラはGoで書かれていますが、初期の
gc
の概念を理解するのに役立つかもしれません): https://go.dev/blog/go1.5compiler
参考にした情報源リンク
- Go言語の公式ドキュメント
- Yacc/Bisonの一般的な使用方法に関する情報
- C言語の標準ライブラリ関数
strcmp
に関する情報 - Go言語のGitHubリポジトリのコミット履歴
- Go言語の初期コンパイラ
gc
に関する歴史的資料(Web検索を通じて得られた情報)
[インデックス 1312] ファイルの概要
このコミットは、Go言語の初期のコンパイラ(gc
)における2つのマイナーなバグ修正を含んでいます。具体的には、シンボル再宣言のロジックと、any
型(Goの初期段階における特殊な型)の使用制限に関する修正が行われています。これらの修正は、コンパイラの正確性と堅牢性を向上させることを目的としています。
コミット
commit 7dd62cb3bc51222076506132f5d409ec7fa58b38
Author: Ken Thompson <ken@golang.org>
Date: Wed Dec 10 12:38:16 2008 -0800
2 minor bugs
R=r
OCL=20906
CL=20906
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7dd62cb3bc51222076506132f5d409ec7fa58b38
元コミット内容
2 minor bugs
R=r
OCL=20906
CL=20906
変更の背景
このコミットは、Go言語の初期開発段階(2008年12月)に行われたもので、Go言語がまだ一般に公開される前の内部開発フェーズに当たります。当時のGoコンパイラは、C言語で書かれたgc
(Go Compiler)という名称のツールチェーンの一部でした。
変更の背景には、以下の2つの具体的な問題があったと考えられます。
- シンボル再宣言のロジックの誤り:
src/cmd/gc/dcl.c
におけるredeclare
関数は、シンボル(変数、関数など)が現在のスコープブロック内で再宣言された場合にエラーを報告する役割を担っていました。しかし、元の実装では、シンボルが現在のブロックに属していない場合にエラーを報告するという、意図とは逆のロジックになっていました。これにより、本来エラーとすべき再宣言が見過ごされたり、逆に正当な宣言が誤ってエラーとされたりする可能性がありました。 any
型の使用制限の不備:src/cmd/gc/go.y
はGo言語の文法定義ファイルであり、any
型に関する制約が記述されていました。any
型は、Go言語の初期段階における特殊な型であり、後のinterface{}
(空インターフェース)やany
(Go 1.18以降の型パラメータ)の前身、あるいはコンパイラ内部での特殊な用途の型であった可能性があります。この型は通常の使用が制限されるべきでしたが、特定の内部パッケージ(PACKAGE
という名前のパッケージ)では例外的に許可される必要があったと考えられます。元の実装では、この例外処理が考慮されておらず、PACKAGE
パッケージ内でのany
型の使用が不適切に制限されていた可能性があります。
これらのバグは、コンパイラの正確な動作を妨げ、Go言語のセマンティクスを正しく反映できない原因となっていたため、修正が必要でした。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
- Go言語の初期コンパイラ
gc
:- Go言語の最初の公式コンパイラは、C言語で書かれた
gc
(Go Compiler)というツールチェーンでした。これは、Plan 9 Cコンパイラをベースにしていました。 src/cmd/gc
ディレクトリは、このコンパイラのソースコードが置かれていた場所です。現在のGoコンパイラはGo言語で書かれており、src/cmd/compile/internal/typecheck/dcl.go
のようなファイルが宣言処理を担当しています。このコミットは、Go言語がまだC言語で実装されていた非常に初期の段階のものであることに注意が必要です。
- Go言語の最初の公式コンパイラは、C言語で書かれた
- コンパイラのフェーズ:
- 字句解析 (Lexical Analysis): ソースコードをトークンに分解する。
- 構文解析 (Syntax Analysis): トークン列が文法規則に合致するかをチェックし、抽象構文木 (AST) を構築する。
go.y
のようなYacc/Bisonファイルがこのフェーズで使用される。 - 意味解析 (Semantic Analysis): 型チェック、スコープ解決、シンボル解決などを行う。
dcl.c
のようなファイルがこのフェーズに関わる。 - コード生成 (Code Generation): 実行可能なコードを生成する。
- Yacc/Bison:
go.y
ファイルは、Yacc (Yet Another Compiler Compiler) またはそのGNU版であるBisonで生成されるパーサーの文法定義ファイルです。.y
拡張子はYacc/Bisonの入力ファイルを示します。- 文法規則と、それらの規則がマッチしたときに実行されるC言語のアクションコードが記述されています。
$$
は現在の規則のセマンティック値、$1
などは規則の右辺の各要素のセマンティック値を参照します。yyerror
は、構文解析中にエラーが発生した際に呼び出される関数で、エラーメッセージを出力します。
- シンボルテーブルとスコープ:
- コンパイラは、プログラム内で宣言された変数、関数、型などの「シンボル」に関する情報を「シンボルテーブル」に格納します。
- 「スコープ」は、シンボルが有効なプログラムの領域を指します。Go言語では、ブロック(
{}
で囲まれた領域)ごとにスコープが形成されます。 Sym
構造体はシンボルを表し、block
フィールドはシンボルが宣言されたブロックの識別子、lastlineno
は最後に宣言された行番号を保持していると考えられます。
any
型 (初期Go言語):- Go言語の初期には、現在の
interface{}
(空インターフェース)に相当する概念がany
というキーワードで表現されていた時期があった、あるいはコンパイラ内部で特殊な型として扱われていた可能性があります。 - Go 1.18で導入された
any
キーワードは、interface{}
のエイリアスですが、このコミットの時期とは異なります。このコミットにおけるany
型は、よりプリミティブな、コンパイラ内部の概念であった可能性が高いです。
- Go言語の初期には、現在の
strcmp
関数: C言語の標準ライブラリ関数で、2つの文字列を比較します。strcmp(s1, s2) == 0
は、s1
とs2
が等しいことを意味します。
技術的詳細
src/cmd/gc/dcl.c
の修正
このファイルは、Goコンパイラの宣言(declaration)処理、特にシンボル管理とスコープ解決に関連する部分を扱っています。redeclare
関数は、シンボルが再宣言された場合の処理を担当します。
元のコードのロジックは以下の通りでした。
if(s->block != block) { // シンボルsが現在のブロックとは異なるブロックで宣言されている場合
s->block = block;
s->lastlineno = lineno;
return; // 新しいブロックでの宣言として処理し、関数を終了
}
// ここに到達するのは、s->block == block の場合、つまり同じブロックで再宣言された場合
yyerror("%s %S redeclared in this block %d", str, s, block); // エラーを報告
print("\tprevious declaration at %L\\n", s->lastlineno);
このロジックは、コメントと実際の動作が逆になっていました。if(s->block != block)
の条件が真の場合(異なるブロックでの宣言)、それは再宣言ではなく、新しいスコープでの有効な宣言であるべきです。しかし、その場合はreturn
してしまい、エラー報告は行われません。逆に、if
文を通過してエラー報告が行われるのは、s->block == block
の場合、つまり同じブロックでの再宣言の場合です。これは正しい動作ですが、if
文の条件が混乱を招いていました。
修正後のコードは以下の通りです。
if(s->block == block) { // シンボルsが現在のブロックと同じブロックで宣言されている場合
yyerror("%s %S redeclared in this block", str, s); // エラーを報告
print("\tprevious declaration at %L\\n", s->lastlineno);
}
// ここに到達するのは、s->block == block の場合はエラー報告後、
// s->block != block の場合はif文をスキップして直接ここに来る
s->block = block; // シンボルのブロック情報を現在のブロックに更新
s->lastlineno = lineno; // シンボルの最終宣言行を現在の行に更新
この修正により、ロジックが明確になりました。
s->block == block
の場合のみ、再宣言エラーを報告します。- エラー報告の有無にかかわらず、
s->block
とs->lastlineno
は常に現在のブロックと行番号に更新されます。これは、シンボルが新しいブロックで宣言された場合(s->block != block
)でも、その新しい宣言情報を正しく記録するために必要です。 また、エラーメッセージから%d
(ブロック番号)が削除され、より簡潔になっています。
src/cmd/gc/go.y
の修正
このファイルはGo言語の文法をYacc形式で定義しており、構文解析中に特定のセマンティックアクションを実行します。修正箇所はnametype
という文法規則に関連しています。
元のコードは以下の通りでした。
nametype:
LATYPE
{
if($1->otype != T && $1->otype->etype == TANY) // $1が型であり、その型がTANYである場合
yyerror("the any type is restricted"); // "any typeは制限されています"とエラー
$$ = oldtype($1);
}
;
この規則は、LATYPE
(おそらく型名を表すトークン)が解析されたときに実行されます。$1
はLATYPE
のセマンティック値を参照し、その型情報(otype
、etype
)をチェックしています。TANY
は、コンパイラ内部でany
型を表す定数であると考えられます。
修正後のコードは以下の通りです。
nametype:
LATYPE
{
if($1->otype != T && $1->otype->etype == TANY)
if(strcmp(package, "PACKAGE") != 0) // 現在のパッケージ名が"PACKAGE"でない場合
yyerror("the any type is restricted");
$$ = oldtype($1);
}
;
追加された条件if(strcmp(package, "PACKAGE") != 0)
は、any
型が制限されるのは、現在のパッケージ名が文字列"PACKAGE"
と異なる場合のみであることを意味します。これは、PACKAGE
という名前のパッケージが、any
型を特別に許可される内部的な、あるいはブートストラップ的なパッケージであったことを示唆しています。この修正により、特定の内部パッケージでのany
型の正当な使用が誤ってエラーとなるのを防ぎ、コンパイラの柔軟性と正確性を向上させています。
コアとなるコードの変更箇所
src/cmd/gc/dcl.c
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -701,13 +701,12 @@ testdclstack(void)
static void
redeclare(char *str, Sym *s)
{
- if(s->block != block) {
- s->block = block;
- s->lastlineno = lineno;
- return;
- }
- yyerror("%s %S redeclared in this block %d", str, s, block);
- print("\tprevious declaration at %L\\n\", s->lastlineno);
+ if(s->block == block) {
+ yyerror("%s %S redeclared in this block", str, s);
+ print("\tprevious declaration at %L\\n", s->lastlineno);
+ }
+ s->block = block;
+ s->lastlineno = lineno;
}
void
src/cmd/gc/go.y
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -1087,6 +1087,7 @@ nametype:
LATYPE
{
if($1->otype != T && $1->otype->etype == TANY)
+\t\tif(strcmp(package, "PACKAGE") != 0)
\tyyerror("the any type is restricted");
$$ = oldtype($1);
}
コアとなるコードの解説
src/cmd/gc/dcl.c
の redeclare
関数
この関数は、シンボルs
がstr
という名前で再宣言されたかどうかをチェックし、必要に応じてエラーを報告します。
- 変更前:
if(s->block != block)
: シンボルs
が現在のスコープブロックblock
とは異なるブロックで宣言されている場合、それは再宣言ではなく、新しいスコープでの有効な宣言と見なされ、s->block
とs->lastlineno
を更新して関数を終了していました。- この
if
ブロックを通過した場合(つまりs->block == block
の場合)、同じブロック内での再宣言と判断し、yyerror
でエラーメッセージを出力していました。しかし、このロジックは直感的ではなく、if
文の条件が逆転しているように見えました。
- 変更後:
if(s->block == block)
: シンボルs
が現在のスコープブロックblock
と同じブロックで既に宣言されている場合、これは明確な再宣言エラーです。yyerror
を呼び出してエラーメッセージを出力します。エラーメッセージから不要なブロック番号の表示が削除され、より簡潔になりました。s->block = block; s->lastlineno = lineno;
: この2行はif
ブロックの外に移動しました。これにより、シンボルが同じブロックで再宣言された場合(エラー報告後)でも、新しいブロックで宣言された場合(if
文をスキップ)でも、常にシンボルのブロック情報と最終宣言行が現在の情報に更新されるようになりました。これは、シンボルが有効な宣言として処理された場合に、その最新の宣言情報をシンボルテーブルに反映させるために重要です。
この修正により、シンボル再宣言の検出ロジックがより正確かつ堅牢になり、コンパイラがGo言語のスコープ規則を正しく適用できるようになりました。
src/cmd/gc/go.y
の nametype
規則
この部分は、Go言語の文法定義ファイルにおけるnametype
という規則のアクションブロックです。nametype
は、型名が解析されたときに実行される処理を定義しています。
- 変更前:
if($1->otype != T && $1->otype->etype == TANY)
: 解析された型($1
で参照される)が、コンパイラ内部でTANY
として定義されている特殊な型であるかどうかをチェックしていました。もしそうであれば、yyerror("the any type is restricted")
を呼び出し、any
型の使用が制限されていることを示すエラーを出力していました。
- 変更後:
if($1->otype != T && $1->otype->etype == TANY)
: 既存のany
型チェックはそのままです。if(strcmp(package, "PACKAGE") != 0)
: 新たにこの条件が追加されました。これは、現在のコンパイル対象のパッケージ名が文字列"PACKAGE"
と等しくない場合にのみ、内側のyyerror
が実行されるようにします。package
変数は、現在のコンパイル対象のパッケージ名を表すグローバル変数またはコンテキスト変数であると推測されます。strcmp(package, "PACKAGE") != 0
は、「現在のパッケージ名が"PACKAGE"
ではない」という条件を意味します。
- この二重の
if
文により、any
型がTANY
であり、かつ現在のパッケージが"PACKAGE"
ではない場合にのみ、エラーが報告されるようになりました。
この修正は、any
型が特定の内部パッケージ("PACKAGE"
)でのみ許可されるという、Go言語の初期設計における特殊な要件に対応したものです。これにより、コンパイラは言語のセマンティクスをより正確に強制できるようになりました。
関連リンク
- Go言語の初期開発に関する情報: https://go.dev/doc/history
- Yacc/Bisonのドキュメンテーション(一般的な情報): https://www.gnu.org/software/bison/manual/
- Go言語のコンパイラ構造に関する一般的な情報(現在のGoコンパイラはGoで書かれていますが、初期の
gc
の概念を理解するのに役立つかもしれません): https://go.dev/blog/go1.5compiler
参考にした情報源リンク
- Go言語の公式ドキュメント
- Yacc/Bisonの一般的な使用方法に関する情報
- C言語の標準ライブラリ関数
strcmp
に関する情報 - Go言語のGitHubリポジトリのコミット履歴
- Web検索結果: "Go compiler src/cmd/gc dcl.c redeclare" (Go言語のコンパイラがC言語からGo言語に移行したことに関する情報)