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

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

このコミットは、Go言語の初期開発段階において、型チェッカーの基盤となる typechecker.go ファイルを新規追加したものです。Go言語の共同開発者の一人であるRobert Griesemer氏によってコミットされました。このファイルは、Goプログラムの型が正しく使用されているかを検証するための基本的なロジック、特にスコープ管理と識別子の解決メカニズムを定義しています。

コミット

  • コミットハッシュ: 003f0ad6afacbed76dfc09788d1117c44cc42222
  • 作者: Robert Griesemer gri@golang.org
  • 日付: Tue Dec 16 18:03:18 2008 -0800
  • コミットメッセージ:
    - added missing file
    
    R=r
    OCL=21384
    CL=21384
    

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

https://github.com/golang/go/commit/003f0ad6afacbed76dfc09788d1117c44cc42222

元コミット内容

- added missing file

R=r
OCL=21384
CL=21384

変更の背景

このコミットは、Go言語がまだ一般に公開される前の、内部開発が進められていた非常に初期の段階で行われました。Go言語のような静的型付け言語において、型チェッカーはコンパイラの最も重要なコンポーネントの一つです。型チェッカーは、プログラムが言語の型システム規則に準拠していることを保証し、コンパイル時に多くの潜在的なエラーを検出することで、プログラムの堅牢性と安全性を高めます。

usr/gri/pretty/typechecker.go というパスは、Robert Griesemer氏の作業ディレクトリ(usr/gri)内で開発されていたコードが、Go言語のメインリポジトリに統合されたことを示唆しています。pretty というディレクトリ名から、この型チェッカーが、コードの「整形」や「美化」に関連するツールの一部として、あるいはそのための情報を提供する目的で開発されていた可能性も考えられますが、主要な目的は型チェック機能の提供です。

OCL (Original Change List) および CL (Change List) は、Google社内で当時広く使われていたバージョン管理システムであるPerforceにおける変更セットの識別子です。これは、このコミットがGitに移行される前のPerforceの変更セット21384に由来することを示しています。

前提知識の解説

型チェッカー (Type Checker)

型チェッカーは、コンパイラのフロントエンドの一部であり、プログラムが型の規則に違反していないかを検証する役割を担います。静的型付け言語(Go、Java、C++など)では、変数の型がコンパイル時に決定され、型チェッカーはその型の一貫性を保証します。これにより、実行時エラー(例: 整数と文字列の加算)を未然に防ぎ、コードの信頼性を向上させます。

スコープ (Scope)

スコープとは、プログラム内で識別子(変数名、関数名、型名など)が有効である、または参照可能である領域を指します。プログラミング言語では、通常、ブロック({})、関数、パッケージなどによってスコープが定義されます。スコープは、名前の衝突を防ぎ、コードのモジュール性を高めるために不可欠な概念です。型チェッカーは、識別子がどのスコープで宣言され、どこから参照可能であるかを追跡するためにスコープ管理を必要とします。

抽象構文木 (AST: Abstract Syntax Tree)

抽象構文木は、ソースコードの構文構造を抽象的に表現した木構造のデータ構造です。コンパイラの字句解析と構文解析のフェーズで生成され、その後の意味解析(型チェックを含む)やコード生成の入力となります。型チェッカーはASTを走査し、各ノード(式、文、宣言など)の型情報を解決・検証します。

Go言語の初期開発

Go言語は、Google社内で2007年後半にRobert Griesemer、Rob Pike、Ken Thompsonによって設計が開始されました。このコミットが行われた2008年12月は、Go言語がまだ一般に公開されておらず、内部で活発に開発が進められていた時期にあたります。この時期のコードは、現在のGo言語の設計とは異なる部分も多く、言語の進化の過程を垣間見ることができます。

技術的詳細

typechecker.go ファイルは、Go言語の型チェッカーの初期実装における主要なコンポーネントを定義しています。

State 構造体

type State struct {
	level int;
	top_scope *Globals.Scope;
}

State 構造体は、型チェック処理全体のコンテキストを保持します。

  • level: スコープのネストレベルを示す可能性があります。
  • top_scope: 現在のトップスコープへのポインタです。スコープチェーンの先頭を指します。

エラーハンドリング

func (s *State) Error(pos int, msg string) {
	panicln("error:" + msg);
}

初期の実装では、エラーが発生した場合に panicln を使用してプログラムを終了させています。これは、開発初期段階における簡潔なエラー報告メカニズムであり、後のバージョンではより洗練されたエラー処理に置き換えられていきます。pos はソースコード上の位置を示すものと思われます。

スコープ管理関数

このファイルの中核は、スコープの開閉、識別子の検索、宣言を行う関数群です。

  • func (s *State) OpenScope():

    func (s *State) OpenScope() {
    	s.top_scope = Globals.NewScope(s.top_scope);
    }
    

    新しいスコープを作成し、それを現在のトップスコープの親として設定することで、スコープチェーンを拡張します。これにより、新しいブロックや関数に入った際に、そのブロック内で有効な新しい名前空間が作成されます。

  • func (s *State) CloseScope():

    func (s *State) CloseScope() {
    	s.top_scope = s.top_scope.parent;
    }
    

    現在のトップスコープをその親スコープに戻すことで、スコープチェーンを短縮します。これは、ブロックや関数を抜ける際に、そのブロック内で宣言された識別子が無効になることをシミュレートします。

  • func (s *State) Lookup(ident string) *Globals.Object:

    func (s *State) Lookup(ident string) *Globals.Object {
    	for scope := s.top_scope; scope != nil; scope = scope.parent {
    		obj := scope.Lookup(ident);
    		if obj != nil {
    			return obj;
    		}
    	}
    	return nil;
    }
    

    指定された識別子 ident を、現在のトップスコープから親スコープへと遡って検索します。これは、変数の参照解決など、識別子の定義を見つけるために使用されます。

  • func (s *State) DeclareInScope(scope *Globals.Scope, obj *Globals.Object):

    func (s *State) DeclareInScope(scope *Globals.Scope, obj *Globals.Object) {
    	if s.level > 0 {
    		panic("cannot declare objects in other packages");
    	}
    	obj.pnolev = s.level;
    	if scope.Lookup(obj.ident) != nil {
    		s.Error(obj.pos, `"` + obj.ident + `" is declared already`);
    		return;  // don't insert it into the scope
    	}
    	scope.Insert(obj);
    }
    

    指定されたスコープ scope にオブジェクト obj を宣言します。

    • s.level > 0 のチェックと panic("cannot declare objects in other packages") は、初期のGo言語がパッケージ間のオブジェクト宣言に対して特定の制約を持っていたか、あるいはその設計がまだ固まっていなかったことを示唆しています。
    • obj.pnolev = s.level; は、オブジェクトが宣言されたスコープレベルを記録しているようです。
    • scope.Lookup(obj.ident) != nil で、同じスコープ内での識別子の再宣言をチェックし、エラーを報告します。
  • func (s *State) Declare(obj *Globals.Object):

    func (s *State) Declare(obj *Globals.Object) {
    	s.DeclareInScope(s.top_scope, obj);
    }
    

    現在のトップスコープにオブジェクトを宣言するためのヘルパー関数です。

CheckProgram 関数

func (s *State) CheckProgram(p *AST.Program) {
	s.OpenScope();

	{s.OpenScope();

		s.CloseScope();
	}

	s.CloseScope();
}

CheckProgram は、プログラム全体の型チェックを開始するためのエントリポイントです。この初期バージョンでは、具体的な型チェックロジックはまだ実装されておらず、単にスコープの開閉のテストケースのような構造になっています。これは、型チェッカーのフレームワークが構築され始めた段階であることを示しています。

export func CheckProgram(p *AST.Program)

export func CheckProgram(p *AST.Program) {
	var s State;
	s.CheckProgram(p);
}

export キーワードは、この関数がパッケージ外から呼び出されることを示しています。これは、型チェッカーがGoコンパイラの他の部分から利用されることを意図していることを意味します。

依存関係

ファイル冒頭の import 文から、この型チェッカーがGoコンパイラの他の初期パッケージに依存していることがわかります。

  • AST "ast": 抽象構文木を扱うパッケージ。
  • Universe "universe": グローバルな識別子や組み込み型を定義するパッケージ。
  • Globals "globals": グローバルなスコープやオブジェクトを扱うパッケージ。
  • Object "object": プログラム内のオブジェクト(変数、関数など)を表現するパッケージ。
  • Type "type": 型情報を扱うパッケージ。

これらのパッケージは、Goコンパイラの初期の内部構造を垣間見せるものであり、現在のGo言語の標準ライブラリとは異なる命名規則や構造を持っています。

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

このコミットでは、usr/gri/pretty/typechecker.go というファイルが新規追加されました。その内容は以下の通りです。

// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package TypeChecker

import (
	AST "ast";
	Universe "universe";
	Globals "globals";
	Object "object";
	Type "type";
)


type State struct {
	level int;
	top_scope *Globals.Scope;
}


// ----------------------------------------------------------------------------
// Support

func (s *State) Error(pos int, msg string) {
	panicln("error:" + msg);
}


// ----------------------------------------------------------------------------
// Scopes

func (s *State) OpenScope() {
	s.top_scope = Globals.NewScope(s.top_scope);
}


func (s *State) CloseScope() {
	s.top_scope = s.top_scope.parent;
}


func (s *State) Lookup(ident string) *Globals.Object {
	for scope := s.top_scope; scope != nil; scope = scope.parent {
		obj := scope.Lookup(ident);
		if obj != nil {
			return obj;
		}
	}
	return nil;
}


func (s *State) DeclareInScope(scope *Globals.Scope, obj *Globals.Object) {
	if s.level > 0 {
		panic("cannot declare objects in other packages");
	}
	obj.pnolev = s.level;
	if scope.Lookup(obj.ident) != nil {
		s.Error(obj.pos, `"` + obj.ident + `\" is declared already`);
		return;  // don't insert it into the scope
	}
	scope.Insert(obj);
}


func (s *State) Declare(obj *Globals.Object) {
	s.DeclareInScope(s.top_scope, obj);
}


// ----------------------------------------------------------------------------
// Common productions

func (s *State) DeclareIdent(kind int) {
	obj := Globals.NewObject(0, kind, "");
	s.Declare(obj);
}


// ----------------------------------------------------------------------------

func (s *State) CheckProgram(p *AST.Program) {
	s.OpenScope();

	{s.OpenScope();

		s.CloseScope();
	}

	s.CloseScope();
}


// ----------------------------------------------------------------------------

export func CheckProgram(p *AST.Program) {
	var s State;
	s.CheckProgram(p);
}

コアとなるコードの解説

この typechecker.go ファイルは、Go言語の型チェッカーの初期プロトタイプであり、その後のGoコンパイラの型チェックフェーズの基礎を築いたものです。

  • 型チェックのコンテキスト管理: State 構造体は、型チェックの進行中に必要な状態(現在のスコープレベルやトップスコープ)をカプセル化します。これにより、型チェック処理が複数の関数にまたがっても一貫した状態を維持できます。
  • 基本的なスコープ管理: OpenScope, CloseScope, Lookup, DeclareInScope, Declare といった関数は、Go言語のプログラムにおける識別子のスコープ規則を実装するための基本的なメカニズムを提供します。これらは、変数がどこで宣言され、どこからアクセスできるかを追跡するために不可欠です。特に Lookup 関数は、スコープチェーンを遡って識別子を解決する典型的な実装を示しています。
  • エラー報告の初期形態: Error 関数は、型エラーが発生した際に panicln を用いてプログラムを終了させるという、非常にシンプルなエラー報告の仕組みを提供しています。これは、機能のプロトタイピング段階でよく見られるアプローチです。
  • プログラムチェックのエントリポイント: CheckProgram 関数は、Goプログラムの抽象構文木(AST)を受け取り、型チェック処理を開始する主要な関数です。この初期バージョンでは、具体的な型検証ロジックはまだ含まれていませんが、スコープの開閉の基本的な流れが示されており、今後の機能拡張のためのプレースホルダーとなっています。
  • パッケージ間の連携: export func CheckProgram は、この型チェッカーがGoコンパイラの他の部分(例えば、パーサーやコードジェネレーター)から呼び出されることを意図していることを明確に示しています。
  • 著作権表示: ファイル冒頭の // Copyright 2009 The Go Authors. All rights reserved. は、このコードがGo言語の公式な開発の一部として作成されたことを示しており、Go言語の歴史における重要なマイルストーンの一つです。

このコミットは、Go言語のコンパイラがどのようにしてその中核的な機能(型チェック)を構築し始めたかを示す貴重なスナップショットであり、言語設計の進化の過程を理解する上で非常に参考になります。

関連リンク

参考にした情報源リンク