[インデックス 1311] ファイルの概要
このコミットは、Goコンパイラの宣言処理におけるバグ修正を目的としています。具体的には、シンボル(変数、型、定数など)の再宣言チェックのロジックを改善し、より正確なエラー報告を可能にしています。また、src/lib/syscall
ディレクトリ内のDarwin/amd64向けのシステムコール定義の一部がコメントアウトされていますが、これは直接的なバグ修正とは異なり、関連するクリーンアップまたは一時的な変更である可能性があります。
コミット
bug126
R=r
OCL=20879
CL=20879
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/57bd0da37529dfe56fe27b90dbb9304ddfeea940
元コミット内容
commit 57bd0da37529dfe56fe27b90dbb9304ddfeea940
Author: Ken Thompson <ken@golang.org>
Date: Tue Dec 9 18:31:31 2008 -0800
bug126
R=r
OCL=20879
CL=20879
---
src/cmd/gc/dcl.c | 39 +++++++++++++++++----------------
src/cmd/gc/go.h | 4 ++--
src/lib/syscall/syscall_amd64_darwin.go | 2 +-\n src/lib/syscall/types_amd64_darwin.go | 2 +-\n 4 files changed, 24 insertions(+), 23 deletions(-)
変更の背景
このコミットの主な目的は、Goコンパイラ(gc
)におけるシンボルの再宣言に関するバグ(bug126
)を修正することです。Go言語では、同じスコープ内で同じ名前の変数、型、または定数を複数回宣言することはできません。しかし、初期のコンパイラには、この再宣言の検出ロジックに不備があり、特に異なる種類のシンボル(例えば、変数と型)が同じ名前で宣言された場合に、適切なエラーを報告できない、あるいは誤ったエラーを報告する可能性がありました。
bug126
という簡潔なコミットメッセージは、Goプロジェクトの初期のバグトラッカーにおける特定の課題番号を参照している可能性が高いです。この種のバグは、コンパイラの健全性(soundness)と、プログラマが期待する言語のセマンティクスを保証するために非常に重要です。正確な再宣言チェックは、コードの可読性と保守性を高め、予期せぬ動作を防ぐ上で不可欠です。
src/lib/syscall
ディレクトリ内の変更は、直接的なbug126
の修正とは関連性が低いように見えますが、Goコンパイラの開発初期段階では、様々なクリーンアップやプラットフォーム固有の調整が同時に行われることがよくありました。この変更は、特定のシステムコール定義のコメントアウトであり、おそらくは未使用の定義の削除、または将来の変更に向けた準備の一環であると考えられます。
前提知識の解説
1. Goコンパイラ (gc
) の概要
Go言語の公式コンパイラは、初期にはgc
(Go Compiler)と呼ばれていました。これは、C言語で書かれたコンパイラであり、Goプログラムを機械語に変換する役割を担っています。src/cmd/gc
ディレクトリには、このコンパイラのソースコードが含まれています。
2. シンボルテーブルとスコープ
コンパイラは、プログラム内で宣言された変数、関数、型などの名前(シンボル)を管理するために「シンボルテーブル」を使用します。シンボルテーブルは、各シンボルの名前、型、スコープ(有効範囲)、メモリ上の位置などの情報を格納します。
「スコープ」とは、プログラム内でシンボルが参照可能な領域を指します。Go言語には、ブロックレベルのスコープがあり、{}
で囲まれたブロック内で宣言されたシンボルはそのブロック内でのみ有効です。再宣言チェックは、このスコープの概念に基づいて行われます。つまり、同じスコープ内で同じ名前のシンボルが複数回宣言されていないかを検査します。
3. Sym
構造体
Goコンパイラの内部では、プログラム内の各シンボルを表現するためにSym
(Symbol)構造体が使用されます。この構造体には、シンボルの名前、種類(変数、型、定数など)、そしてそのシンボルがどのスコープ(ブロック)で宣言されたかを示す情報が含まれます。
4. 再宣言(Redeclaration)
再宣言とは、同じスコープ内で同じ名前の識別子(変数名、型名、関数名など)を複数回宣言することです。ほとんどのプログラミング言語では、これはコンパイルエラーとなります。Go言語も例外ではなく、再宣言は許可されていません。再宣言を検出することは、コンパイラの重要な役割の一つです。
5. block
とblockgen
Goコンパイラの内部では、スコープを管理するために「ブロック番号」のような概念が使われます。block
は現在のスコープの識別子を示し、blockgen
は新しいブロックが作成されるたびにインクリメントされるグローバルなカウンタのようなものです。これにより、コンパイラは異なるスコープを区別し、シンボルがどのスコープに属するかを追跡できます。
6. syscall
パッケージ
syscall
パッケージは、Goプログラムからオペレーティングシステムのシステムコールを直接呼び出すための機能を提供します。システムコールは、ファイルI/O、ネットワーク通信、プロセス管理など、OSカーネルが提供する低レベルなサービスを利用するために使用されます。syscall_amd64_darwin.go
やtypes_amd64_darwin.go
は、それぞれAMD64アーキテクチャ上のmacOS(Darwin)システムに特化したシステムコール定義と型定義を含んでいます。
技術的詳細
このコミットの主要な変更は、Goコンパイラのシンボル管理と再宣言チェックのロジックに集中しています。
src/cmd/gc/go.h
の変更
Sym
構造体の定義が変更されています。
ushort tblock;
(type block number) とushort vblock;
(variable block number) が削除され、代わりにushort block;
が導入されました。- これは、型と変数のブロック番号を個別に管理するのではなく、単一の
block
フィールドでシンボルが宣言されたブロックを統一的に管理するように変更されたことを意味します。これにより、シンボル管理の複雑さが軽減され、再宣言チェックのロジックが簡素化されます。
- これは、型と変数のブロック番号を個別に管理するのではなく、単一の
int32 lastlineno;
が追加されました。- このフィールドは、シンボルが最後に宣言されたソースコードの行番号を記録するために使用されます。再宣言エラーが発生した場合、この情報を使って、以前の宣言がどこにあったかを正確に報告できるようになります。これは、デバッグとエラー診断の品質を向上させる上で非常に重要です。
src/cmd/gc/dcl.c
の変更
dcl.c
は、Goコンパイラの宣言処理を担当するファイルです。このファイルでは、Sym
構造体の変更に合わせて、シンボルのブロック管理と再宣言チェックのロジックが大幅に修正されています。
dodcltype
関数: 型の宣言を処理する関数です。以前はs->tblock == block
で型の再宣言をチェックしていましたが、新しいs->block == block
に統一されました。dcopy
関数: シンボル情報をコピーする関数です。tblock
とvblock
のコピーが削除され、block
とlastlineno
のコピーが追加されました。popdcl
関数: スコープを抜ける際に宣言スタックをポップする関数です。d->vblock
からd->block
への変更が行われ、ブロック管理の統一が図られています。markdcl
関数: 新しいスコープに入る際に宣言スタックにマークを付ける関数です。ここでもd->vblock
からd->block
への変更が行われています。redeclare
静的関数の導入:- このコミットの最も重要な変更点の一つは、
redeclare
という新しい静的関数が導入されたことです。この関数は、シンボルが現在のブロックで既に宣言されているかどうかをチェックし、もし再宣言であればエラーを報告します。 if(s->block != block)
: シンボルが現在のブロックでまだ宣言されていない場合、s->block
を現在のブロックに設定し、s->lastlineno
を現在の行番号に設定して、関数を終了します。これは、そのシンボルが現在のブロックで初めて宣言されたことを記録します。yyerror("%s %S redeclared in this block %d", str, s, block);
: シンボルが既に現在のブロックで宣言されている場合、再宣言エラーを報告します。print("\tprevious declaration at %L\\n", s->lastlineno);
: そして、lastlineno
フィールドに記録された情報を使って、以前の宣言がどの行で行われたかを詳細に報告します。これにより、開発者はエラーの原因を特定しやすくなります。
- このコミットの最も重要な変更点の一つは、
addvar
、addtyp
、addconst
関数の変更:- これらの関数は、それぞれ変数、型、定数を宣言する際に呼び出されます。以前は各関数内で個別に再宣言チェックのロジックを持っていましたが、これらのロジックが削除され、新しく導入された
redeclare
関数が呼び出されるようになりました。 - これにより、再宣言チェックのロジックが一元化され、コードの重複が排除され、保守性が向上します。また、
redeclare
関数が提供する詳細なエラーメッセージ(以前の宣言行の表示)が、すべての種類のシンボルに適用されるようになります。
- これらの関数は、それぞれ変数、型、定数を宣言する際に呼び出されます。以前は各関数内で個別に再宣言チェックのロジックを持っていましたが、これらのロジックが削除され、新しく導入された
src/lib/syscall/syscall_amd64_darwin.go
および src/lib/syscall/types_amd64_darwin.go
の変更
これらのファイルでは、特定のシステムコール定数やイベントフラグがコメントアウトされています。
syscall_amd64_darwin.go
:SYS_ENOSYS = 299;
の行がコメントアウトされています。types_amd64_darwin.go
:EV_RECEIPT = 0x40;
の行がコメントアウトされています。
これらの変更は、コンパイラの宣言ロジックとは直接関係ありません。Goの初期開発段階では、OS固有のシステムコール定義が頻繁に調整されていました。これは、特定のシステムコールが不要になった、名前が変更された、あるいは一時的に無効化されたなどの理由によるものと考えられます。
コアとなるコードの変更箇所
src/cmd/gc/dcl.c
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -65,7 +65,7 @@ dodcltype(Type *n)
// if n has been forward declared,
// use the Type* created then
s = n->sym;
- if(s->tblock == block) {
+ if(s->block == block) {
switch(s->otype->etype) {
case TFORWSTRUCT:
case TFORWINTER:
@@ -556,8 +556,8 @@ dcopy(Sym *a, Sym *b)
a->lexical = b->lexical;
a->undef = b->undef;
a->vargen = b->vargen;
- a->vblock = b->vblock;
- a->tblock = b->tblock;
+ a->block = b->block;
+ a->lastlineno = b->lastlineno;
a->local = b->local;
a->offset = b->offset;
}
@@ -602,7 +602,7 @@ popdcl(void)
if(d == S)
fatal("popdcl: no mark");
dclstack = d->link;
- block = d->vblock;
+ block = d->block;
}
void
@@ -630,7 +630,7 @@ markdcl(void)
d = push();
d->name = nil; // used as a mark in fifo
- d->vblock = block;
+ d->block = block;
blockgen++;
block = blockgen;
@@ -698,6 +698,18 @@ 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);
+}
+
void
addvar(Node *n, Type *t, int ctxt)
{
@@ -710,15 +722,6 @@ addvar(Node *n, Type *t, int ctxt)
s = n->sym;
- if(s->vblock == block) {
- if(s->oname != N) {
- yyerror("var %S redeclared in this block"
- "\\n\\tprevious declaration at %L",
- s, s->oname->lineno);
- } else
- yyerror("var %S redeclared in this block", s);
- }
-
if(ctxt == PEXTERN) {
r = externdcl;
gen = 0;
@@ -729,10 +732,10 @@ addvar(Node *n, Type *t, int ctxt)
pushdcl(s);
}
+ redeclare("variable", s);
s->vargen = gen;
s->oname = n;
s->offset = 0;
- s->vblock = block;
s->lexical = LNAME;
n->type = t;
@@ -775,12 +778,9 @@ addtyp(Type *n, int ctxt)
n->vargen = ++typgen;
}
- if(s->tblock == block)
- yyerror("type %S redeclared in this block %d", s, block);
-
+ redeclare("type", s);
s->otype = n;
s->lexical = LATYPE;
- s->tblock = block;
d = dcl();
d->dsym = s;
@@ -831,6 +831,7 @@ addconst(Node *n, Node *e, int ctxt)
pushdcl(s);
}
+ redeclare("constant", s);
s->oconst = e;
s->lexical = LACONST;
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -231,8 +231,7 @@ struct Node
struct Sym
{
- ushort tblock; // blocknumber for type
- ushort vblock; // blocknumber for variable
+ ushort block; // blocknumber to catch redeclaration
uchar undef; // a diagnostic has been generated
uchar export; // marked as export
@@ -252,6 +251,7 @@ struct Sym
vlong offset; // stack location if automatic
int32 lexical;
int32 vargen; // unique variable number
+ int32 lastlineno; // last declaration for diagnostic
Sym* link;
};
#define S ((Sym*)0)
コアとなるコードの解説
このコミットの核となる変更は、Goコンパイラがシンボルの再宣言を検出・報告する方法を根本的に改善した点にあります。
-
Sym
構造体の簡素化と情報強化:- 以前は
tblock
(型用)とvblock
(変数用)という2つのフィールドでシンボルが宣言されたブロックを管理していましたが、これを単一のblock
フィールドに統合しました。これにより、シンボルがどのブロックで宣言されたかを統一的に扱うことができ、コンパイラの内部ロジックが簡素化されます。 lastlineno
フィールドが追加されたことで、再宣言エラーが発生した際に、そのシンボルが以前にどの行で宣言されたかを正確に報告できるようになりました。これは、コンパイラのエラーメッセージの質を大幅に向上させ、開発者が問題を迅速に特定し修正するのに役立ちます。
- 以前は
-
redeclare
関数の導入と再宣言ロジックの一元化:redeclare
という新しい静的関数が導入され、変数、型、定数など、あらゆる種類のシンボルの再宣言チェックを一元的に処理するようになりました。- この関数は、与えられたシンボル
s
が現在のblock
で既に宣言されているかどうかをチェックします。- もし
s->block
が現在のblock
と異なる場合(つまり、そのシンボルが現在のブロックでまだ宣言されていない場合)、s->block
を現在のblock
に設定し、s->lastlineno
を現在の行番号に更新します。これは、そのシンボルが現在のブロックで初めて宣言されたことを記録するものです。 - もし
s->block
が現在のblock
と同じ場合(つまり、そのシンボルが現在のブロックで既に宣言されている場合)、yyerror
関数を使って再宣言エラーを報告します。この際、lastlineno
に保存されていた情報を用いて、以前の宣言がどの行で行われたかを詳細に表示します。
- もし
- この変更により、
addvar
、addtyp
、addconst
といった宣言処理を行う関数から、個別の再宣言チェックロジックが削除され、代わりにredeclare
関数を呼び出すようになりました。これにより、コードの重複が排除され、再宣言チェックのロジックがより堅牢で保守しやすくなりました。
これらの変更は、Goコンパイラの初期段階における重要な改善であり、言語のセマンティクスを正確に強制し、開発者に対してより有用なエラーメッセージを提供するための基盤を築きました。
src/lib/syscall
ディレクトリ内の変更は、Goのシステムコールインターフェースの進化の一部であり、コンパイラの宣言ロジックの変更とは直接的な機能関連性はありません。これらは、特定のOS(Darwin/macOS)におけるシステムコール定義の調整やクリーンアップを示唆しています。
関連リンク
- Go言語の初期のバグトラッカーやメーリングリストのアーカイブに
bug126
に関する詳細情報がある可能性があります。
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/gc
ディレクトリ) - Go言語の公式ドキュメント
- コンパイラ設計に関する一般的な知識 (シンボルテーブル、スコープ、宣言処理)
- Go言語の初期のコミット履歴と関連する議論 (もし公開されているものがあれば)
[インデックス 1311] ファイルの概要
このコミットは、Goコンパイラ(gc
)におけるシンボルの再宣言チェックのロジックを改善し、より正確なエラー報告を可能にすることを目的としています。具体的には、シンボル(変数、型、定数など)がどのスコープで宣言されたかを管理する内部構造を統一し、再宣言エラー発生時に以前の宣言箇所を特定できるようにlastlineno
フィールドを追加しています。また、再宣言チェックのロジックをredeclare
関数に一元化することで、コードの重複を排除し、保守性を向上させています。src/lib/syscall
ディレクトリ内の変更は、Darwin/amd64向けのシステムコール定義の一部がコメントアウトされており、これはコンパイラの宣言ロジックとは直接関連しない、プラットフォーム固有の調整またはクリーンアップと考えられます。
コミット
bug126
R=r
OCL=20879
CL=20879
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/57bd0da37529dfe56fe27b90dbb9304ddfeea940
元コミット内容
commit 57bd0da37529dfe56fe27b90dbb9304ddfeea940
Author: Ken Thompson <ken@golang.org>
Date: Tue Dec 9 18:31:31 2008 -0800
bug126
R=r
OCL=20879
CL=20879
---
src/cmd/gc/dcl.c | 39 +++++++++++++++++----------------
src/cmd/gc/go.h | 4 ++--
src/lib/syscall/syscall_amd64_darwin.go | 2 +-\n src/lib/syscall/types_amd64_darwin.go | 2 +-\n 4 files changed, 24 insertions(+), 23 deletions(-)
変更の背景
このコミットの主要な動機は、Goコンパイラ(gc
)におけるシンボルの再宣言に関する既知のバグ(bug126
)を修正することにあります。Go言語の設計原則の一つとして、同じスコープ内での識別子(変数名、型名、定数名など)の重複宣言は許可されていません。これは、コードの曖昧さを排除し、可読性と予測可能性を保証するために不可欠なルールです。
しかし、Go言語の初期開発段階では、コンパイラのシンボル管理および再宣言検出ロジックに不備が存在していました。特に、異なる種類のシンボル(例えば、変数と型)が同じ名前で宣言された場合や、ネストされたスコープでの再宣言が正しく検出されない、あるいは誤ったエラーメッセージが表示されるといった問題が発生していた可能性があります。bug126
というコミットメッセージは、Goプロジェクトの内部バグトラッキングシステムにおける特定の課題番号を指していると考えられます。
この種のバグは、コンパイラの健全性、すなわち言語仕様に厳密に従ってプログラムを解析し、エラーを正確に報告する能力に直接影響します。正確な再宣言チェックは、プログラマが意図しない名前の衝突を避け、より堅牢でエラーの少ないコードを書く上で極めて重要です。このコミットは、コンパイラがGo言語のスコープ規則と宣言規則をより厳密に強制できるようにするための基盤を強化することを目的としています。
src/lib/syscall
ディレクトリ内の変更は、直接的なbug126
の修正とは関連性が低いと考えられます。Go言語の初期段階では、様々なプラットフォーム(この場合はDarwin/macOS)向けのシステムコールインターフェースが活発に開発・調整されており、特定のシステムコール定義のコメントアウトは、未使用の定義の削除、APIの変更、あるいは一時的な無効化など、当時の開発状況に応じた調整の一環である可能性が高いです。
前提知識の解説
1. Goコンパイラ (gc
) とその役割
Go言語の公式コンパイラは、初期にはgc
(Go Compiler)と呼ばれていました。これは、Goのソースコードを解析し、中間表現を経て、最終的に実行可能な機械語コードに変換する役割を担います。src/cmd/gc
ディレクトリには、このコンパイラのC言語で書かれたソースコードが含まれており、字句解析、構文解析、意味解析、コード生成といったコンパイルの各フェーズを実装しています。
2. シンボルテーブルとスコープ管理
コンパイラは、プログラム内で宣言されたすべての識別子(変数、関数、型、定数など)に関する情報を管理するために「シンボルテーブル」を使用します。シンボルテーブルは、識別子の名前、その型、メモリ上のアドレス、そして最も重要な「スコープ」などの情報を格納するデータ構造です。
「スコープ」とは、プログラムのテキスト内で識別子が有効である領域を指します。Go言語では、ブロックレベルのスコープが採用されており、{}
で囲まれたブロック(関数本体、if
文、for
文など)内で宣言された識別子はそのブロック内でのみ有効です。シンボルテーブルは、これらのスコープの階層構造を反映し、ある識別子がどのスコープに属するか、そしてそのスコープ内で一意であるかを追跡します。
3. Sym
構造体
Goコンパイラの内部では、プログラム内の各シンボルを抽象的に表現するためにSym
(Symbol)構造体が定義されています。この構造体は、シンボルの名前、その種類(OTYPE
フィールドで変数、型、定数などを区別)、そしてそのシンボルが宣言されたスコープを示す情報(このコミットで変更されるblock
フィールドなど)を保持します。コンパイラは、このSym
構造体のインスタンスをシンボルテーブルに格納し、プログラムの解析中に参照します。
4. 再宣言(Redeclaration)の概念
再宣言とは、同じスコープ内で既に宣言されている識別子と同じ名前で、再度識別子を宣言しようとすることです。例えば、Go言語では以下のようなコードは再宣言エラーとなります。
package main
func main() {
var x int = 10
var x string = "hello" // エラー: x は既に宣言されています
}
ほとんどの静的型付け言語と同様に、Go言語も再宣言をコンパイルエラーとして扱います。これは、プログラマの意図しない名前の衝突を防ぎ、コードの明確性を保つために不可欠です。コンパイラは、シンボルテーブルを検索することで再宣言を検出し、エラーを報告します。
5. ブロック番号 (block
) と世代カウンタ (blockgen
)
Goコンパイラの内部では、スコープを効率的に管理するために「ブロック番号」という概念が用いられています。block
は現在の処理対象となっているスコープを一意に識別する番号であり、blockgen
は新しいスコープ(ブロック)が開始されるたびにインクリメントされるグローバルなカウンタです。これにより、コンパイラは異なるスコープを明確に区別し、シンボルがどのスコープに属するかを正確に追跡することができます。シンボルが宣言されると、そのシンボルが属するブロックのblock
番号がSym
構造体内に記録されます。
6. syscall
パッケージとシステムコール
syscall
パッケージは、Goプログラムがオペレーティングシステム(OS)の低レベルな機能、すなわち「システムコール」を直接呼び出すためのインターフェースを提供します。システムコールは、ファイル操作、ネットワーク通信、プロセス管理、メモリ管理など、OSカーネルが提供する基本的なサービスを利用するための唯一の手段です。src/lib/syscall/syscall_amd64_darwin.go
やsrc/lib/syscall/types_amd64_darwin.go
といったファイルは、それぞれAMD64アーキテクチャ上で動作するmacOS(Darwin)に特化したシステムコール番号の定義や、システムコールで使用されるデータ構造の型定義を含んでいます。
技術的詳細
このコミットの技術的な核心は、Goコンパイラのシンボル管理と再宣言チェックのメカニズムを根本的に改善した点にあります。
src/cmd/gc/go.h
における Sym
構造体の変更
Sym
構造体は、Goコンパイラがプログラム内の各シンボル(変数、型、定数など)の情報を保持するために使用する中心的なデータ構造です。このコミットでは、その定義が以下のように変更されました。
-
tblock
とvblock
の削除、block
の導入:- 変更前は、
ushort tblock;
(型が宣言されたブロック番号)とushort vblock;
(変数が宣言されたブロック番号)という2つのフィールドが存在していました。これは、型と変数のブロック情報を個別に管理していたことを意味します。 - 変更後、これら2つのフィールドは削除され、代わりに
ushort block;
という単一のフィールドが導入されました。このblock
フィールドは、シンボルが宣言された現在のスコープのブロック番号を統一的に保持します。この変更により、シンボルが型であるか変数であるかに関わらず、その宣言スコープの管理が簡素化され、コンパイラの内部ロジックの複雑性が軽減されます。これは、シンボル管理の一貫性を高める上で重要な改善です。
- 変更前は、
-
lastlineno
の追加:int32 lastlineno;
という新しいフィールドが追加されました。このフィールドは、そのシンボルが最後に宣言されたソースコードの行番号を記録するために使用されます。- この追加は、再宣言エラーが発生した際の診断情報の質を劇的に向上させます。以前のコンパイラでは、再宣言エラーが発生しても、そのシンボルが以前にどこで宣言されたかを示す情報が不足している場合がありました。
lastlineno
を導入することで、コンパイラは「このシンボルは既にこの行で宣言されています」といった、より具体的で役立つエラーメッセージを出力できるようになります。これにより、開発者はエラーの原因を迅速に特定し、修正することが可能になります。
src/cmd/gc/dcl.c
における宣言処理ロジックの変更
dcl.c
は、Goコンパイラの宣言処理(Declaration)を担当するソースファイルであり、シンボルの登録、スコープの管理、再宣言のチェックなど、コンパイルの重要な部分を担っています。このコミットでは、Sym
構造体の変更に合わせて、このファイルの多くの関数が修正されました。
-
dodcltype
関数の変更:- 型の宣言を処理する
dodcltype
関数では、型の再宣言チェックの条件がs->tblock == block
からs->block == block
に変更されました。これは、Sym
構造体におけるブロック管理の統一化を反映したものです。
- 型の宣言を処理する
-
dcopy
関数の変更:- シンボル情報をコピーする
dcopy
関数では、tblock
とvblock
のコピー処理が削除され、代わりにblock
とlastlineno
のコピー処理が追加されました。これにより、シンボル情報のコピーが新しいSym
構造体の定義に適合するようになりました。
- シンボル情報をコピーする
-
popdcl
およびmarkdcl
関数の変更:- スコープを抜ける際に宣言スタックをポップする
popdcl
関数と、新しいスコープに入る際に宣言スタックにマークを付けるmarkdcl
関数では、スコープのブロック番号を管理するフィールドがd->vblock
からd->block
に変更されました。これは、スコープ管理の内部メカニズムがSym
構造体の変更に合わせて更新されたことを示しています。
- スコープを抜ける際に宣言スタックをポップする
-
redeclare
静的関数の導入:- このコミットの最も重要な変更点の一つは、
redeclare
という新しい静的関数が導入されたことです。この関数は、シンボルの再宣言チェックを一元的に行うためのものです。 - ロジック:
if(s->block != block)
: 引数として渡されたシンボルs
のblock
フィールドが、現在のスコープのblock
番号と異なる場合、これはそのシンボルが現在のブロックでまだ宣言されていないことを意味します。この場合、s->block
を現在のblock
に設定し、s->lastlineno
を現在のソースコードの行番号(lineno
)に設定します。これは、そのシンボルが現在のブロックで初めて宣言されたことを記録するものです。その後、関数はreturn
し、処理を終了します。yyerror("%s %S redeclared in this block %d", str, s, block);
: もしs->block
が現在のblock
と同じ場合、これはそのシンボルが現在のブロックで既に宣言されていることを意味します。この場合、yyerror
関数(コンパイラのエラー報告関数)を呼び出して、再宣言エラーを報告します。エラーメッセージには、シンボルの種類(str
)、シンボル名(%S
)、そして現在のブロック番号(%d
)が含まれます。print("\tprevious declaration at %L\\n", s->lastlineno);
: さらに、lastlineno
フィールドに記録されていた情報を使用して、「以前の宣言は%L行目です」という詳細なメッセージを出力します。これにより、開発者はエラーの原因をより迅速に特定できます。
- このコミットの最も重要な変更点の一つは、
-
addvar
、addtyp
、addconst
関数の変更:- これらの関数は、それぞれ変数、型、定数を宣言する際に呼び出されます。変更前は、これらの関数内で個別に再宣言チェックのロジックが実装されていました。例えば、
addvar
関数にはif(s->vblock == block)
といった条件分岐があり、再宣言を検出していました。 - このコミットでは、これらの個別の再宣言チェックロジックが削除され、代わりに新しく導入された
redeclare
関数が呼び出されるようになりました。 - 例:
addvar
関数内のif(s->vblock == block) { ... }
ブロックが削除され、redeclare("variable", s);
という行が追加されました。 - この変更により、再宣言チェックのロジックが一元化され、コードの重複が排除されました。また、
redeclare
関数が提供する詳細なエラーメッセージ(以前の宣言行の表示)が、変数、型、定数といったすべての種類のシンボルに適用されるようになり、コンパイラのエラー報告の一貫性と有用性が向上しました。
- これらの関数は、それぞれ変数、型、定数を宣言する際に呼び出されます。変更前は、これらの関数内で個別に再宣言チェックのロジックが実装されていました。例えば、
src/lib/syscall/syscall_amd64_darwin.go
および src/lib/syscall/types_amd64_darwin.go
の変更
これらのファイルは、Go言語のsyscall
パッケージの一部であり、特にmacOS(Darwin)のAMD64アーキテクチャ向けのシステムコール定義と型定義を含んでいます。
syscall_amd64_darwin.go
:SYS_ENOSYS = 299;
の行がコメントアウトされました。types_amd64_darwin.go
:EV_RECEIPT = 0x40;
の行がコメントアウトされました。
これらの変更は、Goコンパイラの宣言ロジックの改善とは直接的な機能関連性はありません。Go言語の初期開発段階では、異なるOSプラットフォーム向けのシステムコールインターフェースが頻繁に調整されていました。これらのコメントアウトは、以下のような理由が考えられます。
- 未使用の定義の削除: 特定のシステムコールやフラグが、Goの標準ライブラリやランタイムで不要になったため。
- APIの変更: OS側のシステムコールAPIが変更され、古い定義が一時的に無効化された、あるいは新しい定義に置き換えられる前段階。
- 一時的な無効化: 特定の機能がまだ完全に実装されていない、あるいはテスト中であるため、一時的に無効化された。
これらの変更は、Go言語が様々なプラットフォームで動作するために必要な、継続的な低レベルの調整の一部を示しています。
コアとなるコードの変更箇所
src/cmd/gc/dcl.c
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -65,7 +65,7 @@ dodcltype(Type *n)
// if n has been forward declared,
// use the Type* created then
s = n->sym;
- if(s->tblock == block) {
+ if(s->block == block) {
switch(s->otype->etype) {
case TFORWSTRUCT:
case TFORWINTER:
@@ -556,8 +556,8 @@ dcopy(Sym *a, Sym *b)
a->lexical = b->lexical;
a->undef = b->undef;
a->vargen = b->vargen;
- a->vblock = b->vblock;
- a->tblock = b->tblock;
+ a->block = b->block;
+ a->lastlineno = b->lastlineno;
a->local = b->local;
a->offset = b->offset;
}
@@ -602,7 +602,7 @@ popdcl(void)
if(d == S)
fatal("popdcl: no mark");
dclstack = d->link;
- block = d->vblock;
+ block = d->block;
}
void
@@ -630,7 +630,7 @@ markdcl(void)
d = push();
d->name = nil; // used as a mark in fifo
- d->vblock = block;
+ d->block = block;
blockgen++;
block = blockgen;
@@ -698,6 +698,18 @@ 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);
+}
+
void
addvar(Node *n, Type *t, int ctxt)
{
@@ -710,15 +722,6 @@ addvar(Node *n, Type *t, int ctxt)
s = n->sym;
- if(s->vblock == block) {
- if(s->oname != N) {
- yyerror("var %S redeclared in this block"
- "\\n\\tprevious declaration at %L",
- s, s->oname->lineno);
- } else
- yyerror("var %S redeclared in this block", s);
- }
-
if(ctxt == PEXTERN) {
r = externdcl;
gen = 0;
@@ -729,10 +732,10 @@ addvar(Node *n, Type *t, int ctxt)
pushdcl(s);
}
+ redeclare("variable", s);
s->vargen = gen;
s->oname = n;
s->offset = 0;
- s->vblock = block;
s->lexical = LNAME;
n->type = t;
@@ -775,12 +778,9 @@ addtyp(Type *n, int ctxt)
n->vargen = ++typgen;
}
- if(s->tblock == block)
- yyerror("type %S redeclared in this block %d", s, block);
-
+ redeclare("type", s);
s->otype = n;
s->lexical = LATYPE;
- s->tblock = block;
d = dcl();
d->dsym = s;
@@ -831,6 +831,7 @@ addconst(Node *n, Node *e, int ctxt)
pushdcl(s);
}
+ redeclare("constant", s);
s->oconst = e;
s->lexical = LACONST;
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -231,8 +231,7 @@ struct Node
struct Sym
{
- ushort tblock; // blocknumber for type
- ushort vblock; // blocknumber for variable
+ ushort block; // blocknumber to catch redeclaration
uchar undef; // a diagnostic has been generated
uchar export; // marked as export
@@ -252,6 +251,7 @@ struct Sym
vlong offset; // stack location if automatic
int32 lexical;
int32 vargen; // unique variable number
+ int32 lastlineno; // last declaration for diagnostic
Sym* link;
};
#define S ((Sym*)0)
コアとなるコードの解説
このコミットの最も重要な変更は、Goコンパイラがシンボルの再宣言を検出・報告する方法を根本的に改善した点に集約されます。
-
Sym
構造体の改善:tblock
とvblock
の統合: 以前は型と変数のブロック番号をそれぞれtblock
とvblock
で管理していましたが、これを単一のblock
フィールドに統合しました。これにより、シンボルが型であるか変数であるかに関わらず、その宣言スコープの管理が統一され、コンパイラの内部ロジックが簡素化されます。これは、コードの重複を減らし、保守性を高める上で重要なステップです。lastlineno
の追加:lastlineno
フィールドがSym
構造体に追加されました。このフィールドは、シンボルが最後に宣言されたソースコードの行番号を記録します。この情報は、再宣言エラーが発生した際に、以前の宣言がどこで行われたかを正確に報告するために使用されます。これにより、コンパイラのエラーメッセージがより具体的になり、開発者が問題を迅速に特定し修正するのに役立ちます。
-
redeclare
関数の導入と再宣言ロジックの一元化:- このコミットのハイライトは、
redeclare
という新しい静的関数が導入されたことです。この関数は、変数、型、定数など、あらゆる種類のシンボルの再宣言チェックを一元的に処理する役割を担います。 - 動作原理:
redeclare
関数は、引数として渡されたシンボルs
が現在のスコープ(block
)で既に宣言されているかどうかをチェックします。- もし
s->block
が現在のblock
と異なる場合(つまり、そのシンボルが現在のブロックでまだ宣言されていない場合)、s->block
を現在のblock
に設定し、s->lastlineno
を現在のソースコードの行番号(lineno
)に更新します。これは、そのシンボルが現在のブロックで初めて宣言されたことを記録するものです。 - もし
s->block
が現在のblock
と同じ場合(つまり、そのシンボルが現在のブロックで既に宣言されている場合)、再宣言エラーが発生したと判断し、yyerror
関数を呼び出してエラーを報告します。この際、lastlineno
に保存されていた情報を用いて、「以前の宣言は%L行目です」という詳細なメッセージを出力します。
- 影響:
- 以前は、
addvar
(変数宣言)、addtyp
(型宣言)、addconst
(定数宣言)といった各関数内で、それぞれ個別の再宣言チェックロジックが実装されていました。このコミットでは、これらの個別のロジックが削除され、代わりにredeclare
関数が呼び出されるようになりました。 - これにより、再宣言チェックのロジックが単一の関数に集約され、コードの重複が大幅に削減されました。また、ロジックの変更や改善が必要になった場合でも、
redeclare
関数のみを修正すればよいため、保守性が向上します。 - さらに、
redeclare
関数が提供する詳細なエラーメッセージ(以前の宣言行の表示)が、すべての種類のシンボルに一貫して適用されるようになり、コンパイラのエラー報告の品質が全体的に向上しました。
- 以前は、
- このコミットのハイライトは、
これらの変更は、Goコンパイラの初期段階における重要な改善であり、言語のセマンティクスをより厳密に強制し、開発者に対してより有用なエラーメッセージを提供するための強固な基盤を築きました。
src/lib/syscall
ディレクトリ内の変更は、Goのシステムコールインターフェースの進化の一部であり、コンパイラの宣言ロジックの変更とは直接的な機能関連性はありません。これらは、特定のOS(Darwin/macOS)におけるシステムコール定義の調整やクリーンアップを示唆しています。
関連リンク
- Go言語の初期のコミット履歴: Go言語の公式GitHubリポジトリのコミット履歴を遡ることで、このコミット前後の文脈や、関連する他のバグ修正コミットを見つけることができるかもしれません。
- Go言語のコンパイラ設計に関するドキュメント: Go言語のコンパイラがどのように動作するかについての公式またはコミュニティによるドキュメントは、このコミットの背景にある設計思想を理解するのに役立ちます。
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/gc
ディレクトリのdcl.c
とgo.h
、およびsrc/lib/syscall
ディレクトリの関連ファイル) - コンパイラ設計に関する一般的な知識 (シンボルテーブル、スコープ、宣言処理、エラー報告)
- Go言語の公式ドキュメント (言語仕様、特に宣言とスコープに関するセクション)
- (
bug126
に関する具体的な公開情報は見つかりませんでしたが、これは内部的なバグトラッキングIDである可能性が高いです。)