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

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

このコミットは、Go言語の初期のコンパイラ(またはツールチェインの一部)における抽象構文木(AST)に宣言情報を付加するための初期段階の変更を含んでいます。具体的には、usr/gri/pretty/compilation.gousr/gri/pretty/printer.gousr/gri/pretty/typechecker.go の3つのファイルが変更されています。これらのファイル名から推測すると、Go言語のソースコードのコンパイル、整形(pretty-printing)、型チェックに関連するコンポーネントであることがわかります。

コミット

このコミットは、ASTに宣言情報を付加するための段階的な作業の開始を示しています。この目的は、識別子をクリックするとその宣言にジャンプできるようなHTML出力を生成するのを助けることです。また、このコミットは「old new」に戻す前のスナップショットであると述べられています。

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

https://github.com/golang/go/commit/215eb7eb7f808938f1cb0e0e134979b88e2ab5cb

元コミット内容

- steps towards augmenting ast with declaration info
  (will help produce html output where we can click on identifiers and
  get to the declaration)
- snapshot before changing back to old new

R=r
OCL=22159
CL=22159

変更の背景

このコミットの主な背景は、Go言語のツールチェイン、特にドキュメンテーション生成やIDEサポートの改善に向けたものです。コミットメッセージに明記されているように、「識別子をクリックするとその宣言にジャンプできるようなHTML出力を生成するのを助ける」ことが目的です。これは、ソースコードのナビゲーションを容易にし、開発者がコードベースを理解するのを助けるための基本的な機能です。

この機能を実現するためには、コンパイラのフロントエンド、特にパーサーと型チェッカーが、単にコードが正しいかどうかを検証するだけでなく、各識別子がどの宣言(変数、関数、型など)に対応しているかという情報をASTに埋め込む必要があります。これにより、後続のツール(この場合はHTMLドキュメンテーションジェネレーター)が、識別子と宣言の間のリンクを確立し、インタラクティブなナビゲーションを提供できるようになります。

「snapshot before changing back to old new」という記述は、当時のGo言語のコンパイラ開発が活発であり、実験的な変更やリファクタリングが頻繁に行われていたことを示唆しています。このコミットは、特定の大きな変更(おそらくコンパイラの内部構造やビルドシステムに関するもの)を行う前に、現在の作業状態を保存するためのマイルストーンとしての意味合いも持っていたと考えられます。

前提知識の解説

このコミットを理解するためには、以下の概念が重要です。

  1. 抽象構文木 (Abstract Syntax Tree, AST): コンパイラやインタプリタがソースコードを解析する際に生成する、プログラムの構造を木構造で表現したものです。ASTは、ソースコードの構文的な構造を抽象化し、意味解析(型チェックなど)やコード生成の基盤となります。各ノードは、変数宣言、関数呼び出し、演算子などのプログラムの要素を表します。

  2. 型チェッカー (Type Checker): プログラムの型が正しく使用されているかを検証するコンパイラのフェーズです。例えば、整数型の変数に文字列を代入しようとしたり、存在しない関数を呼び出したりするエラーを検出します。型チェッカーは、ASTを走査し、各識別子の型情報を解決し、型規則に違反がないかを確認します。このコミットの文脈では、型チェッカーが識別子の宣言情報をASTに付加する役割を担うよう変更されています。

  3. 宣言情報 (Declaration Information): プログラム内の各識別子(変数名、関数名、型名など)が、どの場所で、どのような種類(変数、関数、型、定数など)として宣言されているかを示す情報です。この情報がASTに付加されることで、ツールは識別子からその定義元へ簡単に辿れるようになります。

  4. Go言語の初期開発: このコミットは2009年1月のものであり、Go言語が一般に公開される前の非常に初期の段階です。当時のGoコンパイラは現在とは異なる構造を持っていた可能性が高く、usr/gri/pretty/ のようなパスは、Robert Griesemer氏(Go言語の共同設計者の一人)が個人的に作業していたリポジトリやブランチの一部であったことを示唆しています。現在の go/ast, go/types, go/parser, go/scanner パッケージの原型となるような機能が、当時のコンパイラ内部で開発されていたと考えられます。

  5. Scanner.ErrorHandler: Go言語の字句解析器(スキャナー)がエラーを報告するためのインターフェースまたは関数型です。コンパイラの各フェーズ(スキャナー、パーサー、型チェッカーなど)は、処理中に発生したエラーをこのハンドラーを通じて報告します。このコミットでは、型チェッカーがエラーハンドラーを受け取るように変更されており、エラー報告の仕組みがより柔軟になったことを示しています。

技術的詳細

このコミットの技術的な変更は、主にGo言語の型チェッカーとASTの連携を強化し、宣言情報をASTに埋め込むための基盤を構築することに焦点を当てています。

usr/gri/pretty/compilation.go の変更

  • Compile 関数のシグネチャ変更:
    --- a/usr/gri/pretty/compilation.go
    +++ b/usr/gri/pretty/compilation.go
    @@ -136,7 +136,7 @@ export func Compile(src_file string, flags *Flags) (*AST.Program, int) {
     	prog := parser.ParseProgram();
    
     	if err.nerrors == 0 {
    -		TypeChecker.CheckProgram(prog);
    +		TypeChecker.CheckProgram(&err, prog);
     	}
    
     	return prog, err.nerrors;
    
    TypeChecker.CheckProgram の呼び出しが TypeChecker.CheckProgram(prog) から TypeChecker.CheckProgram(&err, prog) に変更されています。これは、型チェッカーがエラーハンドリングの責任をより直接的に持つようになったことを示しています。以前はグローバルなエラー状態に依存していたか、エラーを直接パニックさせていた可能性がありますが、この変更により、型チェッカーが Scanner.ErrorHandler インターフェースを介してエラーを報告できるようになり、よりクリーンで柔軟なエラー処理が可能になります。

usr/gri/pretty/printer.go の変更

  • Program メソッドの型アサーション:
    --- a/usr/gri/pretty/printer.go
    +++ b/gri/pretty/printer.go
    @@ -846,7 +846,7 @@ func (P *Printer) Program(p *AST.Program) {
     	P.Expr(p.ident);\
     	P.newlines = 1;\
     	for i := 0; i < p.decls.Len(); i++ {\
    -		P.Declaration(p.decls.At(i), false);\
    +		P.Declaration(p.decls.At(i).(*AST.Decl), false);\
     	}\
     	P.newlines = 1;\
     }
    
    p.decls.At(i) の結果に対して (*AST.Decl) という型アサーションが追加されています。これは、decls が汎用的なリスト(おそらく interface{} のスライス)であり、その要素が AST.Decl 型であることを明示的に指定することで、コンパイラが型安全性を保証し、後続の処理で AST.Decl のメソッドやフィールドにアクセスできるようにするための変更です。これは、AST構造の厳密な型付けを強化する一環と考えられます。

usr/gri/pretty/typechecker.go の変更

このファイルは最も広範な変更を受けており、型チェッカーの内部構造が大幅に拡張されています。

  • State 構造体の拡張:

    type State struct {
    	// setup
    	err Scanner.ErrorHandler;
    
    	// state
    	level int;
    	top_scope *Globals.Scope;
    }
    

    State 構造体に err Scanner.ErrorHandler フィールドが追加されました。これにより、型チェッカーのインスタンスが独自のエラーハンドラーを持つことができるようになり、エラー報告の柔軟性が向上します。

  • Init メソッドの追加:

    func (s *State) Init(err Scanner.ErrorHandler) {
    	s.err = err;
    }
    

    State を初期化するための Init メソッドが追加され、エラーハンドラーをセットアップできるようになりました。

  • ユーティリティ関数の追加: unimplemented(), unreachable(), assert(pred bool) といったパニックを伴うユーティリティ関数が追加されています。これらは開発中のデバッグや、到達不能なコードパスの検出、前提条件の検証に用いられます。

  • Error メソッドの変更:

    --- a/usr/gri/pretty/typechecker.go
    +++ b/usr/gri/pretty/typechecker.go
    @@ -72,19 +99,87 @@ func (s *State) Declare(obj *Globals.Object) {\
     // ----------------------------------------------------------------------------
     // Common productions
    
    -func (s *State) DeclareIdent(kind int) {
    -	obj := Globals.NewObject(0, kind, "");
    -	s.Declare(obj);
    +func (s *State) DeclareIdent(ident *AST.Expr, kind int, typ *AST.Type) {
    +	// ident is either a comma-separated list or a single ident
    +	switch ident.tok {
    +	case Scanner.IDENT:
    +		obj := Globals.NewObject(ident.pos, kind, ident.s);
    +		s.Declare(obj);
    +	case Scanner.COMMA:
    +		s.DeclareIdent(ident.x, kind, typ);
    +		s.DeclareIdent(ident.y, kind, typ);		
    +	default:
    +		unreachable();
    +	}
     }
    

    s.Error メソッドが panicln("error:" + msg) から s.err.Error(pos, msg) に変更されました。これにより、型チェッカーは自身の ErrorHandler を介してエラーを報告するようになり、エラー処理がより統一的かつ制御可能になりました。

  • DeclareIdent メソッドの変更と拡張: 以前は DeclareIdent(kind int) だったものが、DeclareIdent(ident *AST.Expr, kind int, typ *AST.Type) に変更され、大幅に機能が拡張されています。 この新しい DeclareIdent は、単一の識別子だけでなく、カンマ区切りの識別子リスト(例: var a, b inta, b)も処理できるようになりました。これは、ASTノード (ident *AST.Expr) から識別子の位置 (ident.pos) と文字列 (ident.s) を取得し、それらを使って新しい Globals.Object を作成し、現在のスコープに宣言する役割を担います。Globals.Object は、識別子の名前、種類(変数、関数など)、型などの情報を持つオブジェクトであると推測されます。

  • CheckDeclaration メソッドの追加:

    func (s *State) CheckDeclaration(d *AST.Decl) {
    	if d.tok != Scanner.FUNC && d.list != nil {
    		// group of parenthesized declarations
    		for i := 0; i < d.list.Len(); i++ {
    			s.CheckDeclaration(d.list.At(i).(*AST.Decl))
    		}
    		
    	} else {
    		// single declaration
    		switch d.tok {
    		case Scanner.IMPORT:
    			assert(d.ident == nil || d.ident.tok == Scanner.IDENT);
    			if d.ident != nil {
    				s.DeclareIdent(d.ident, d.tok, d.typ);
    			} else {
    			}
    
    		case Scanner.EXPORT:
    			// TODO
    
    		case Scanner.CONST:
    			s.DeclareIdent(d.ident, d.tok, d.typ);
    
    		case Scanner.VAR:
    			s.DeclareIdent(d.ident, d.tok, d.typ);
    
    		case Scanner.TYPE:
    			assert(d.ident.tok == Scanner.IDENT);
    			// types may be forward-declared
    			obj := s.Lookup(d.ident.s);
    			if obj != nil {
    				// TODO check if proper forward-declaration
    
    			} else {
    				s.DeclareIdent(d.ident, d.tok, d.typ);
    			}
    
    		case Scanner.FUNC:
    			assert(d.ident.tok == Scanner.IDENT);
    			if d.typ.key != nil {
    				// method
    				// TODO
    			} else {
    				s.DeclareIdent(d.ident, d.tok, d.typ);
    			}
    
    		default:
    			unreachable();
    		}
    	}
    }
    

    このメソッドは、Go言語の様々な種類の宣言(import, export, const, var, type, func)を処理し、それぞれの宣言に含まれる識別子を現在のスコープに登録する役割を担います。

    • 括弧で囲まれた宣言グループ(例: var (a int; b string))を再帰的に処理します。
    • 各宣言の種類に応じて、DeclareIdent を呼び出し、識別子をスコープに登録します。
    • TYPE 宣言では、前方宣言の可能性を考慮して既存のオブジェクトをルックアップするロジックが含まれています(ただし、TODO コメントがあり、まだ完全ではないことを示唆しています)。
    • FUNC 宣言では、通常の関数とメソッド(d.typ.key != nil で判別)を区別するロジックが含まれています(こちらも TODO コメントがあります)。
    • EXPORT 宣言は TODO となっており、まだ実装されていないことがわかります。
  • CheckProgram メソッドの変更:

    --- a/usr/gri/pretty/typechecker.go
    +++ b/usr/gri/pretty/typechecker.go
    @@ -94,7 +189,10 @@ func (s *State) CheckProgram(p *AST.Program) {
     // ----------------------------------------------------------------------------
    
    -export func CheckProgram(p *AST.Program) {
    +export func CheckProgram(err Scanner.ErrorHandler, p *AST.Program) {
    +\treturn;  // DISABLED FOR NOW
    +\t
     	var s State;\
    +\ts.Init(err);\
     	s.CheckProgram(p);\
     }
    

    CheckProgram のシグネチャが export func CheckProgram(p *AST.Program) から export func CheckProgram(err Scanner.ErrorHandler, p *AST.Program) に変更され、エラーハンドラーを受け取るようになりました。 また、内部の CheckProgram メソッド(非エクスポート)では、プログラムの宣言リスト (p.decls) をループし、新しく追加された s.CheckDeclaration を呼び出すことで、プログラム全体の宣言を処理するロジックが追加されています。 注目すべきは、エクスポートされた CheckProgram 関数の冒頭に return; // DISABLED FOR NOW という行が追加されている点です。これは、この新しい型チェッカーのロジックがまだ開発中であり、一時的に無効化されていることを示しています。これは、Go言語の初期開発における実験的な性質をよく表しています。

これらの変更は、Go言語の型チェッカーが、単なる型検証だけでなく、プログラムのシンボル(識別子)とその宣言の間の関係を構築し、ASTにその情報を付加するための重要なステップであったことを示しています。これにより、後の段階でクロスリファレンスやドキュメンテーション生成などの高度なツール機能が実現可能になります。

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

このコミットのコアとなるコードの変更箇所は、主に usr/gri/pretty/typechecker.go に集中しています。

  1. TypeChecker.CheckProgram のシグネチャ変更とエラーハンドラーの導入: compilation.gotypechecker.go の両方で、CheckProgram 関数が Scanner.ErrorHandler を引数として受け取るように変更されました。これにより、型チェッカーがエラーを報告するメカニズムが改善されました。

    • usr/gri/pretty/compilation.go (変更箇所):
      // ...
      if err.nerrors == 0 {
      -	TypeChecker.CheckProgram(prog);
      +	TypeChecker.CheckProgram(&err, prog);
      }
      // ...
      
    • usr/gri/pretty/typechecker.go (変更箇所):
      // ...
      export func CheckProgram(err Scanner.ErrorHandler, p *AST.Program) {
      	return;  // DISABLED FOR NOW
      	
      	var s State;
      	s.Init(err);
      	s.CheckProgram(p);
      }
      // ...
      
  2. State 構造体への err フィールドの追加と Init メソッド: 型チェッカーの内部状態を管理する State 構造体に Scanner.ErrorHandler が追加され、その初期化を行う Init メソッドが導入されました。

    • usr/gri/pretty/typechecker.go (追加箇所):
      type State struct {
      	// setup
      	err Scanner.ErrorHandler;
      
      	// state
      	level int;
      	top_scope *Globals.Scope;
      }
      
      func (s *State) Init(err Scanner.ErrorHandler) {
      	s.err = err;
      }
      
  3. DeclareIdent メソッドの拡張: 識別子の宣言を処理する DeclareIdent メソッドが、単一の識別子だけでなく、カンマ区切りのリストも処理できるように拡張されました。

    • usr/gri/pretty/typechecker.go (変更箇所):
      // ...
      func (s *State) DeclareIdent(ident *AST.Expr, kind int, typ *AST.Type) {
      	// ident is either a comma-separated list or a single ident
      	switch ident.tok {
      	case Scanner.IDENT:
      		obj := Globals.NewObject(ident.pos, kind, ident.s);
      		s.Declare(obj);
      	case Scanner.COMMA:
      		s.DeclareIdent(ident.x, kind, typ);
      		s.DeclareIdent(ident.y, kind, typ);		
      	default:
      		unreachable();
      	}
      }
      // ...
      
  4. CheckDeclaration メソッドの追加: 様々な種類の宣言(import, const, var, type, func)を処理し、識別子をスコープに登録する主要なロジックがこのメソッドに集約されました。

    • usr/gri/pretty/typechecker.go (追加箇所):
      // ...
      func (s *State) CheckDeclaration(d *AST.Decl) {
      	if d.tok != Scanner.FUNC && d.list != nil {
      		// group of parenthesized declarations
      		for i := 0; i < d.list.Len(); i++ {
      			s.CheckDeclaration(d.list.At(i).(*AST.Decl))
      		}
      		
      	} else {
      		// single declaration
      		switch d.tok {
      		case Scanner.IMPORT:
      			// ...
      		case Scanner.EXPORT:
      			// TODO
      		case Scanner.CONST:
      			// ...
      		case Scanner.VAR:
      			// ...
      		case Scanner.TYPE:
      			// ...
      		case Scanner.FUNC:
      			// ...
      		default:
      			unreachable();
      		}
      	}
      }
      // ...
      
  5. CheckProgram 内部での宣言処理のループ: 非エクスポートの CheckProgram メソッド内で、プログラムのトップレベル宣言をループし、CheckDeclaration を呼び出すことで、全ての宣言が処理されるようになりました。

    • usr/gri/pretty/typechecker.go (変更箇所):
      // ...
      func (s *State) CheckProgram(p *AST.Program) {
      	s.OpenScope();
      	
      	{	s.OpenScope();
      		for i := 0; i < p.decls.Len(); i++ {
      			s.CheckDeclaration(p.decls.At(i).(*AST.Decl));
      		}
      		s.CloseScope();
      	}
      	
      	// ...
      }
      // ...
      

コアとなるコードの解説

これらのコアとなるコードの変更は、Go言語の型チェッカーが、単にコードの型が正しいかを検証するだけでなく、プログラムのシンボル(識別子)とその宣言の間の関係を構築し、ASTにその情報を付加するための重要なステップです。

  • エラーハンドリングの改善: Scanner.ErrorHandler を型チェッカーに直接渡すことで、エラー報告の仕組みがより柔軟でテストしやすくなりました。以前はエラーがパニックを引き起こしていた可能性がありましたが、この変更により、エラーを捕捉し、よりユーザーフレンドリーな方法で報告できるようになります。これは、コンパイラやツールの堅牢性を高める上で不可欠です。

  • DeclareIdent の汎用化: DeclareIdentAST.Expr を受け取るように変更されたことで、型チェッカーはASTノードから直接識別子の詳細(位置、名前)を取得できるようになりました。また、カンマ区切りの宣言リストを再帰的に処理できるようになったことで、var x, y int のようなGo言語の構文を正しく処理し、それぞれの識別子をスコープに登録できるようになります。これは、Go言語の構文規則に合わせた型チェッカーの進化を示しています。

  • CheckDeclaration による宣言処理の一元化: CheckDeclaration メソッドの導入は、型チェッカーがGo言語の様々な種類の宣言(import, const, var, type, func)を体系的に処理するための中心的なロジックを提供します。このメソッドは、各宣言の種類に応じて適切な処理(例えば、識別子のスコープへの登録、前方宣言の解決など)を行い、ASTに宣言情報を付加するための基盤となります。これにより、型チェッカーはプログラム全体のシンボルテーブルを構築し、各識別子がどの宣言に対応するかを正確に追跡できるようになります。

  • ASTへの宣言情報の付加: これらの変更の最終的な目標は、ASTノードに宣言情報を関連付けることです。例えば、変数 x を参照するASTノードは、その x がどこで宣言されたか(ファイル名、行番号、列番号など)へのポインタを持つようになります。これにより、HTMLドキュメンテーションジェネレーターのようなツールが、識別子をクリックしたときにその宣言にジャンプする機能を実現できるようになります。これは、Go言語のIDEサポートやコードナビゲーション機能の基礎を築く上で極めて重要なステップでした。

  • 開発中のスナップショット: return; // DISABLED FOR NOW のコメントは、この機能がまだ開発中であり、一時的に無効化されていることを明確に示しています。これは、Go言語の初期開発が非常に反復的で実験的なプロセスであったことを物語っています。開発者は、大きな変更をコミットする前に、その時点での作業状態を保存し、後で有効化または調整する計画を持っていたと考えられます。

これらの変更は、Go言語のコンパイラが単なるコードの実行可能ファイルを生成するだけでなく、よりリッチな開発ツールエコシステムをサポートするための基盤を構築する上で不可欠なものでした。

関連リンク

Go言語の初期のASTや型チェッカーに関する直接的な公式ドキュメントは、このコミットが作成された2009年時点では非常に限られていた可能性が高いです。しかし、現在のGo言語のツールチェインの設計思想や、go/astgo/typesgo/parsergo/scanner パッケージのドキュメントは、当時の開発の延長線上にあるため、関連情報として役立ちます。

  • Go言語の公式ドキュメント:

    • The Go Programming Language Specification: Go言語の構文とセマンティクスに関する公式仕様。型システムや宣言のルールについて理解を深めるのに役立ちます。
    • Package ast: Go言語のASTを表現するパッケージ。現在のAST構造を理解する上で参考になります。
    • Package types: Go言語の型システムを扱うパッケージ。型チェックの概念を理解する上で参考になります。
    • Package parser: Go言語のソースコードを解析してASTを生成するパッケージ。
    • Package scanner: Go言語のソースコードを字句解析するパッケージ。Scanner.ErrorHandler の概念を理解する上で参考になります。
  • Go言語の初期の設計に関する議論:

    • Go言語の初期の設計に関するブログ記事やメーリングリストのアーカイブは、当時の開発の背景を理解する上で貴重な情報源となる可能性があります。例えば、Go言語の公式ブログやGoogle Groupsのgolang-nutsメーリングリストのアーカイブを検索すると、関連する議論が見つかるかもしれません。

参考にした情報源リンク

  • Go言語の公式ドキュメント (上記「関連リンク」に記載)
  • コミットメッセージとコード差分
  • Go言語のコンパイラとツールに関する一般的な知識

(注: 2009年当時のGo言語の内部実装に関する詳細な公開資料は限られているため、上記の解説は主にコミット内容と現在のGo言語の設計思想からの推測に基づいています。)

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

このコミットは、Go言語の初期のコンパイラ(またはツールチェインの一部)における抽象構文木(AST)に宣言情報を付加するための初期段階の変更を含んでいます。具体的には、usr/gri/pretty/compilation.gousr/gri/pretty/printer.gousr/gri/pretty/typechecker.go の3つのファイルが変更されています。これらのファイル名から推測すると、Go言語のソースコードのコンパイル、整形(pretty-printing)、型チェックに関連するコンポーネントであることがわかります。

コミット

このコミットは、ASTに宣言情報を付加するための段階的な作業の開始を示しています。この目的は、識別子をクリックするとその宣言にジャンプできるようなHTML出力を生成するのを助けることです。また、このコミットは「old new」に戻す前のスナップショットであると述べられています。

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

https://github.com/golang/go/commit/215eb7eb7f808938f1cb0e0e134979b88e2ab5cb

元コミット内容

- steps towards augmenting ast with declaration info
  (will help produce html output where we can click on identifiers and
  get to the declaration)
- snapshot before changing back to old new

R=r
OCL=22159
CL=22159

変更の背景

このコミットの主な背景は、Go言語のツールチェイン、特にドキュメンテーション生成やIDEサポートの改善に向けたものです。コミットメッセージに明記されているように、「識別子をクリックするとその宣言にジャンプできるようなHTML出力を生成するのを助ける」ことが目的です。これは、ソースコードのナビゲーションを容易にし、開発者がコードベースを理解するのを助けるための基本的な機能です。

この機能を実現するためには、コンパイラのフロントエンド、特にパーサーと型チェッカーが、単にコードが正しいかどうかを検証するだけでなく、各識別子がどの宣言(変数、関数、型など)に対応しているかという情報をASTに埋め込む必要があります。これにより、後続のツール(この場合はHTMLドキュメンテーションジェネレーター)が、識別子と宣言の間のリンクを確立し、インタラクティブなナビゲーションを提供できるようになります。

「snapshot before changing back to old new」という記述は、当時のGo言語のコンパイラ開発が活発であり、実験的な変更やリファクタリングが頻繁に行われていたことを示唆しています。このコミットは、特定の大きな変更(おそらくコンパイラの内部構造やビルドシステムに関するもの)を行う前に、現在の作業状態を保存するためのマイルストーンとしての意味合いも持っていたと考えられます。

前提知識の解説

このコミットを理解するためには、以下の概念が重要です。

  1. 抽象構文木 (Abstract Syntax Tree, AST): コンパイラやインタプリタがソースコードを解析する際に生成する、プログラムの構造を木構造で表現したものです。ASTは、ソースコードの構文的な構造を抽象化し、意味解析(型チェックなど)やコード生成の基盤となります。各ノードは、変数宣言、関数呼び出し、演算子などのプログラムの要素を表します。Go言語では、go/ast パッケージがASTの型を定義し、go/parser パッケージがソースコードからASTを生成します。

  2. 型チェッカー (Type Checker): プログラムの型が正しく使用されているかを検証するコンパイラのフェーズです。例えば、整数型の変数に文字列を代入しようとしたり、存在しない関数を呼び出したりするエラーを検出します。型チェッカーは、ASTを走査し、各識別子の型情報を解決し、型規則に違反がないかを確認します。このコミットの文脈では、型チェッカーが識別子の宣言情報をASTに付加する役割を担うよう変更されています。Go言語では、go/types パッケージが型チェックの機能を提供します。

  3. 宣言情報 (Declaration Information): プログラム内の各識別子(変数名、関数名、型名など)が、どの場所で、どのような種類(変数、関数、型、定数など)として宣言されているかを示す情報です。この情報がASTに付加されることで、ツールは識別子からその定義元へ簡単に辿れるようになります。

  4. Go言語の初期開発: このコミットは2009年1月のものであり、Go言語が一般に公開される前の非常に初期の段階です。当時のGoコンパイラは現在とは異なる構造を持っていた可能性が高く、usr/gri/pretty/ のようなパスは、Robert Griesemer氏(Go言語の共同設計者の一人)が個人的に作業していたリポジトリやブランチの一部であったことを示唆しています。現在の go/ast, go/types, go/parser, go/scanner パッケージの原型となるような機能が、当時のコンパイラ内部で開発されていたと考えられます。

  5. Scanner.ErrorHandler: Go言語の字句解析器(スキャナー)がエラーを報告するためのインターフェースまたは関数型です。コンパイラの各フェーズ(スキャナー、パーサー、型チェッカーなど)は、処理中に発生したエラーをこのハンドラーを通じて報告します。このコミットでは、型チェッカーがエラーハンドラーを受け取るように変更されており、エラー報告の仕組みがより柔軟になったことを示しています。現在の go/scanner パッケージには ErrorHandler 型が定義されています。

技術的詳細

このコミットの技術的な変更は、主にGo言語の型チェッカーとASTの連携を強化し、宣言情報をASTに埋め込むための基盤を構築することに焦点を当てています。

usr/gri/pretty/compilation.go の変更

  • Compile 関数のシグネチャ変更:
    --- a/usr/gri/pretty/compilation.go
    +++ b/usr/gri/pretty/compilation.go
    @@ -136,7 +136,7 @@ export func Compile(src_file string, flags *Flags) (*AST.Program, int) {
     	prog := parser.ParseProgram();
    
     	if err.nerrors == 0 {
    -		TypeChecker.CheckProgram(prog);
    +		TypeChecker.CheckProgram(&err, prog);
     	}
    
     	return prog, err.nerrors;
    
    TypeChecker.CheckProgram の呼び出しが TypeChecker.CheckProgram(prog) から TypeChecker.CheckProgram(&err, prog) に変更されています。これは、型チェッカーがエラーハンドリングの責任をより直接的に持つようになったことを示しています。以前はグローバルなエラー状態に依存していたか、エラーを直接パニックさせていた可能性がありますが、この変更により、型チェッカーが Scanner.ErrorHandler インターフェースを介してエラーを報告できるようになり、よりクリーンで柔軟なエラー処理が可能になります。

usr/gri/pretty/printer.go の変更

  • Program メソッドの型アサーション:
    --- a/usr/gri/pretty/printer.go
    +++ b/usr/gri/pretty/printer.go
    @@ -846,7 +846,7 @@ func (P *Printer) Program(p *AST.Program) {
     	P.Expr(p.ident);\
     	P.newlines = 1;\
     	for i := 0; i < p.decls.Len(); i++ {\
    -		P.Declaration(p.decls.At(i), false);\
    +		P.Declaration(p.decls.At(i).(*AST.Decl), false);\
     	}\
     	P.newlines = 1;\
     }
    
    p.decls.At(i) の結果に対して (*AST.Decl) という型アサーションが追加されています。これは、decls が汎用的なリスト(おそらく interface{} のスライス)であり、その要素が AST.Decl 型であることを明示的に指定することで、コンパイラが型安全性を保証し、後続の処理で AST.Decl のメソッドやフィールドにアクセスできるようにするための変更です。これは、AST構造の厳密な型付けを強化する一環と考えられます。

usr/gri/pretty/typechecker.go の変更

このファイルは最も広範な変更を受けており、型チェッカーの内部構造が大幅に拡張されています。

  • State 構造体の拡張:

    type State struct {
    	// setup
    	err Scanner.ErrorHandler;
    
    	// state
    	level int;
    	top_scope *Globals.Scope;
    }
    

    State 構造体に err Scanner.ErrorHandler フィールドが追加されました。これにより、型チェッカーのインスタンスが独自のエラーハンドラーを持つことができるようになり、エラー報告の柔軟性が向上します。

  • Init メソッドの追加:

    func (s *State) Init(err Scanner.ErrorHandler) {
    	s.err = err;
    }
    

    State を初期化するための Init メソッドが追加され、エラーハンドラーをセットアップできるようになりました。

  • ユーティリティ関数の追加: unimplemented(), unreachable(), assert(pred bool) といったパニックを伴うユーティリティ関数が追加されています。これらは開発中のデバッグや、到達不能なコードパスの検出、前提条件の検証に用いられます。

  • Error メソッドの変更:

    --- a/usr/gri/pretty/typechecker.go
    +++ b/usr/gri/pretty/typechecker.go
    @@ -72,19 +99,87 @@ func (s *State) Declare(obj *Globals.Object) {\
     // ----------------------------------------------------------------------------
     // Common productions
    
    -func (s *State) DeclareIdent(kind int) {
    -	obj := Globals.NewObject(0, kind, "");
    -	s.Declare(obj);
    +func (s *State) DeclareIdent(ident *AST.Expr, kind int, typ *AST.Type) {
    +	// ident is either a comma-separated list or a single ident
    +	switch ident.tok {
    +	case Scanner.IDENT:
    +		obj := Globals.NewObject(ident.pos, kind, ident.s);
    +		s.Declare(obj);
    +	case Scanner.COMMA:
    +		s.DeclareIdent(ident.x, kind, typ);
    +		s.DeclareIdent(ident.y, kind, typ);		
    +	default:
    +		unreachable();
    +	}
     }
    

    s.Error メソッドが panicln("error:" + msg) から s.err.Error(pos, msg) に変更されました。これにより、型チェッカーは自身の ErrorHandler を介してエラーを報告するようになり、エラー処理がより統一的かつ制御可能になりました。

  • DeclareIdent メソッドの変更と拡張: 以前は DeclareIdent(kind int) だったものが、DeclareIdent(ident *AST.Expr, kind int, typ *AST.Type) に変更され、大幅に機能が拡張されています。 この新しい DeclareIdent は、単一の識別子だけでなく、カンマ区切りの識別子リスト(例: var a, b inta, b)も処理できるようになりました。これは、ASTノード (ident *AST.Expr) から識別子の位置 (ident.pos) と文字列 (ident.s) を取得し、それらを使って新しい Globals.Object を作成し、現在のスコープに宣言する役割を担います。Globals.Object は、識別子の名前、種類(変数、関数など)、型などの情報を持つオブジェクトであると推測されます。

  • CheckDeclaration メソッドの追加:

    func (s *State) CheckDeclaration(d *AST.Decl) {
    	if d.tok != Scanner.FUNC && d.list != nil {
    		// group of parenthesized declarations
    		for i := 0; i < d.list.Len(); i++ {
    			s.CheckDeclaration(d.list.At(i).(*AST.Decl))
    		}
    		
    	} else {
    		// single declaration
    		switch d.tok {
    		case Scanner.IMPORT:
    			assert(d.ident == nil || d.ident.tok == Scanner.IDENT);
    			if d.ident != nil {
    				s.DeclareIdent(d.ident, d.tok, d.typ);
    			} else {
    			}
    
    		case Scanner.EXPORT:
    			// TODO
    
    		case Scanner.CONST:
    			s.DeclareIdent(d.ident, d.tok, d.typ);
    
    		case Scanner.VAR:
    			s.DeclareIdent(d.ident, d.tok, d.typ);
    
    		case Scanner.TYPE:
    			assert(d.ident.tok == Scanner.IDENT);
    			// types may be forward-declared
    			obj := s.Lookup(d.ident.s);
    			if obj != nil {
    				// TODO check if proper forward-declaration
    
    			} else {
    				s.DeclareIdent(d.ident, d.tok, d.typ);
    			}
    
    		case Scanner.FUNC:
    			assert(d.ident.tok == Scanner.IDENT);
    			if d.typ.key != nil {
    				// method
    				// TODO
    			} else {
    				s.DeclareIdent(d.tok, d.typ); // Changed from s.DeclareIdent(d.ident, d.tok, d.typ);
    			}
    
    		default:
    			unreachable();
    		}
    	}
    }
    

    このメソッドは、Go言語の様々な種類の宣言(import, export, const, var, type, func)を処理し、それぞれの宣言に含まれる識別子を現在のスコープに登録する役割を担います。

    • 括弧で囲まれた宣言グループ(例: var (a int; b string))を再帰的に処理します。
    • 各宣言の種類に応じて、DeclareIdent を呼び出し、識別子をスコープに登録します。
    • TYPE 宣言では、前方宣言の可能性を考慮して既存のオブジェクトをルックアップするロジックが含まれています(ただし、TODO コメントがあり、まだ完全ではないことを示唆しています)。
    • FUNC 宣言では、通常の関数とメソッド(d.typ.key != nil で判別)を区別するロジックが含まれています(こちらも TODO コメントがあります)。
    • EXPORT 宣言は TODO となっており、まだ実装されていないことがわかります。
  • CheckProgram メソッドの変更:

    --- a/usr/gri/pretty/typechecker.go
    +++ b/usr/gri/pretty/typechecker.go
    @@ -94,7 +189,10 @@ func (s *State) CheckProgram(p *AST.Program) {
     // ----------------------------------------------------------------------------
    
    -export func CheckProgram(p *AST.Program) {
    +export func CheckProgram(err Scanner.ErrorHandler, p *AST.Program) {
    +\treturn;  // DISABLED FOR NOW
    +\t
     	var s State;\
    +\ts.Init(err);\
     	s.CheckProgram(p);\
     }
    

    CheckProgram のシグネチャが export func CheckProgram(p *AST.Program) から export func CheckProgram(err Scanner.ErrorHandler, p *AST.Program) に変更され、エラーハンドラーを受け取るようになりました。 また、内部の CheckProgram メソッド(非エクスポート)では、プログラムの宣言リスト (p.decls) をループし、新しく追加された s.CheckDeclaration を呼び出すことで、プログラム全体の宣言を処理するロジックが追加されています。 注目すべきは、エクスポートされた CheckProgram 関数の冒頭に return; // DISABLED FOR NOW という行が追加されている点です。これは、この新しい型チェッカーのロジックがまだ開発中であり、一時的に無効化されていることを示唆しています。これは、Go言語の初期開発における実験的な性質をよく表しています。

これらの変更は、Go言語の型チェッカーが、単なる型検証だけでなく、プログラムのシンボル(識別子)とその宣言の間の関係を構築し、ASTにその情報を付加するための重要なステップであったことを示しています。これにより、後の段階でクロスリファレンスやドキュメンテーション生成などの高度なツール機能が実現可能になります。

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

このコミットのコアとなるコードの変更箇所は、主に usr/gri/pretty/typechecker.go に集中しています。

  1. TypeChecker.CheckProgram のシグネチャ変更とエラーハンドラーの導入: compilation.gotypechecker.go の両方で、CheckProgram 関数が Scanner.ErrorHandler を引数として受け取るように変更されました。これにより、型チェッカーがエラーを報告するメカニズムが改善されました。

    • usr/gri/pretty/compilation.go (変更箇所):
      // ...
      if err.nerrors == 0 {
      -	TypeChecker.CheckProgram(prog);
      +	TypeChecker.CheckProgram(&err, prog);
      }
      // ...
      
    • usr/gri/pretty/typechecker.go (変更箇所):
      // ...
      export func CheckProgram(err Scanner.ErrorHandler, p *AST.Program) {
      	return;  // DISABLED FOR NOW
      	
      	var s State;
      	s.Init(err);
      	s.CheckProgram(p);
      }
      // ...
      
  2. State 構造体への err フィールドの追加と Init メソッド: 型チェッカーの内部状態を管理する State 構造体に Scanner.ErrorHandler が追加され、その初期化を行う Init メソッドが導入されました。

    • usr/gri/pretty/typechecker.go (追加箇所):
      type State struct {
      	// setup
      	err Scanner.ErrorHandler;
      
      	// state
      	level int;
      	top_scope *Globals.Scope;
      }
      
      func (s *State) Init(err Scanner.ErrorHandler) {
      	s.err = err;
      }
      
  3. DeclareIdent メソッドの拡張: 識別子の宣言を処理する DeclareIdent メソッドが、単一の識別子だけでなく、カンマ区切りのリストも処理できるように拡張されました。

    • usr/gri/pretty/typechecker.go (変更箇所):
      // ...
      func (s *State) DeclareIdent(ident *AST.Expr, kind int, typ *AST.Type) {
      	// ident is either a comma-separated list or a single ident
      	switch ident.tok {
      	case Scanner.IDENT:
      		obj := Globals.NewObject(ident.pos, kind, ident.s);
      		s.Declare(obj);
      	case Scanner.COMMA:
      		s.DeclareIdent(ident.x, kind, typ);
      		s.DeclareIdent(ident.y, kind, typ);		
      	default:
      		unreachable();
      	}
      }
      // ...
      
  4. CheckDeclaration メソッドの追加: 様々な種類の宣言(import, const, var, type, func)を処理し、識別子をスコープに登録する主要なロジックがこのメソッドに集約されました。

    • usr/gri/pretty/typechecker.go (追加箇所):
      // ...
      func (s *State) CheckDeclaration(d *AST.Decl) {
      	if d.tok != Scanner.FUNC && d.list != nil {
      		// group of parenthesized declarations
      		for i := 0; i < d.list.Len(); i++ {
      			s.CheckDeclaration(d.list.At(i).(*AST.Decl))
      		}
      		
      	} else {
      		// single declaration
      		switch d.tok {
      		case Scanner.IMPORT:
      			// ...
      		case Scanner.EXPORT:
      			// TODO
      		case Scanner.CONST:
      			// ...
      		case Scanner.VAR:
      			// ...
      		case Scanner.TYPE:
      			// ...
      		case Scanner.FUNC:
      			// ...
      		default:
      			unreachable();
      		}
      	}
      }
      // ...
      
  5. CheckProgram 内部での宣言処理のループ: 非エクスポートの CheckProgram メソッド内で、プログラムのトップレベル宣言をループし、CheckDeclaration を呼び出すことで、全ての宣言が処理されるようになりました。

    • usr/gri/pretty/typechecker.go (変更箇所):
      // ...
      func (s *State) CheckProgram(p *AST.Program) {
      	s.OpenScope();
      	
      	{	s.OpenScope();
      		for i := 0; i < p.decls.Len(); i++ {
      			s.CheckDeclaration(p.decls.At(i).(*AST.Decl));
      		}
      		s.CloseScope();
      	}
      	
      	// ...
      }
      // ...
      

コアとなるコードの解説

これらのコアとなるコードの変更は、Go言語の型チェッカーが、単にコードの型が正しいかを検証するだけでなく、プログラムのシンボル(識別子)とその宣言の間の関係を構築し、ASTにその情報を付加するための重要なステップです。

  • エラーハンドリングの改善: Scanner.ErrorHandler を型チェッカーに直接渡すことで、エラー報告の仕組みがより柔軟でテストしやすくなりました。以前はエラーがパニックを引き起こしていた可能性がありましたが、この変更により、エラーを捕捉し、よりユーザーフレンドリーな方法で報告できるようになります。これは、コンパイラやツールの堅牢性を高める上で不可欠です。

  • DeclareIdent の汎用化: DeclareIdentAST.Expr を受け取るように変更されたことで、型チェッカーはASTノードから直接識別子の詳細(位置、名前)を取得できるようになりました。また、カンマ区切りの宣言リストを再帰的に処理できるようになったことで、var x, y int のようなGo言語の構文を正しく処理し、それぞれの識別子をスコープに登録できるようになります。これは、Go言語の構文規則に合わせた型チェッカーの進化を示しています。

  • CheckDeclaration による宣言処理の一元化: CheckDeclaration メソッドの導入は、型チェッカーがGo言語の様々な種類の宣言(import, const, var, type, func)を体系的に処理するための中心的なロジックを提供します。このメソッドは、各宣言の種類に応じて適切な処理(例えば、識別子のスコープへの登録、前方宣言の解決など)を行い、ASTに宣言情報を付加するための基盤となります。これにより、型チェッカーはプログラム全体のシンボルテーブルを構築し、各識別子がどの宣言に対応するかを正確に追跡できるようになります。

  • ASTへの宣言情報の付加: これらの変更の最終的な目標は、ASTノードに宣言情報を関連付けることです。例えば、変数 x を参照するASTノードは、その x がどこで宣言されたか(ファイル名、行番号、列番号など)へのポインタを持つようになります。これにより、HTMLドキュメンテーションジェネレーターのようなツールが、識別子をクリックしたときにその宣言にジャンプする機能を実現できるようになります。これは、Go言語のIDEサポートやコードナビゲーション機能の基礎を築く上で極めて重要なステップでした。

  • 開発中のスナップショット: return; // DISABLED FOR NOW のコメントは、この機能がまだ開発中であり、一時的に無効化されていることを明確に示しています。これは、Go言語の初期開発が非常に反復的で実験的なプロセスであったことを物語っています。開発者は、大きな変更をコミットする前に、その時点での作業状態を保存し、後で有効化または調整する計画を持っていたと考えられます。

これらの変更は、Go言語のコンパイラが単なるコードの実行可能ファイルを生成するだけでなく、よりリッチな開発ツールエコシステムをサポートするための基盤を構築する上で不可欠なものでした。

関連リンク

Go言語の初期のASTや型チェッカーに関する直接的な公式ドキュメントは、このコミットが作成された2009年時点では非常に限られていた可能性が高いです。しかし、現在のGo言語のツールチェインの設計思想や、go/astgo/typesgo/parsergo/scanner パッケージのドキュメントは、当時の開発の延長線上にあるため、関連情報として役立ちます。

  • Go言語の公式ドキュメント:

    • The Go Programming Language Specification: Go言語の構文とセマンティクスに関する公式仕様。型システムや宣言のルールについて理解を深めるのに役立ちます。
    • Package ast: Go言語のASTを表現するパッケージ。現在のAST構造を理解する上で参考になります。
    • Package types: Go言語の型システムを扱うパッケージ。型チェックの概念を理解する上で参考になります。
    • Package parser: Go言語のソースコードを解析してASTを生成するパッケージ。
    • Package scanner: Go言語のソースコードを字句解析するパッケージ。Scanner.ErrorHandler の概念を理解する上で参考になります。
  • Go言語の初期の設計に関する議論:

    • Go言語の初期の設計に関するブログ記事やメーリングリストのアーカイブは、当時の開発の背景を理解する上で貴重な情報源となる可能性があります。例えば、Go言語の公式ブログやGoogle Groupsのgolang-nutsメーリングリストのアーカイブを検索すると、関連する議論が見つかるかもしれません。

参考にした情報源リンク

  • Go言語の公式ドキュメント (上記「関連リンク」に記載)
  • コミットメッセージとコード差分
  • Go言語のコンパイラとツールに関する一般的な知識
  • Web検索結果: "Go language AST declaration info early development"

(注: 2009年当時のGo言語の内部実装に関する詳細な公開資料は限られているため、上記の解説は主にコミット内容と現在のGo言語の設計思想からの推測に基づいています。)