[インデックス 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である可能性が高いです。)