[インデックス 13786] ファイルの概要
このコミットは、Go言語の実験的な型チェッカーAPIの初期セットを導入するものです。具体的には、src/pkg/exp/types/staging
ディレクトリ以下に5つの新しいファイルが追加され、Goプログラムの型情報を解析・表現するための基盤が構築されています。これらのファイルは、Goの抽象構文木(AST)を走査し、各要素の型を決定し、型の一致性や互換性を検証するためのデータ構造と関数を提供します。
コミット
commit b29d641b3a379b2fb0f88ceed066f043acab7c33
Author: Robert Griesemer <gri@golang.org>
Date: Mon Sep 10 14:54:52 2012 -0700
exp/types/staging: typechecker API
First set of type checker files for review.
The primary concern here is the typechecker
API (types.go).
R=rsc, adonovan, r, rogpeppe
CC=golang-dev
https://golang.org/cl/6490089
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b29d641b3a379b2fb0f88ceed066f043acab7c33
元コミット内容
exp/types/staging: typechecker API
レビューのための型チェッカーファイルの最初のセットです。ここでの主な関心事は、型チェッカーAPI (types.go
) です。
変更の背景
このコミットは、Go言語の公式ツールセットに型チェッカー機能を導入するための初期段階として行われました。Go言語のコンパイラは内部的に型チェックを行いますが、外部ツール(IDE、リンター、静的解析ツールなど)がGoコードの型情報をプログラム的に利用するための標準化されたAPIは存在しませんでした。
このexp/types/staging
パッケージの導入は、Go言語の進化において重要なマイルストーンとなります。これにより、以下のようなメリットが期待されます。
- より高度な開発ツールの実現: 型情報にアクセスできることで、より正確なコード補完、リファクタリング、エラー検出などが可能になります。
- 静的解析の強化: コードの潜在的なバグや非効率な部分を、実行前に型レベルで検出する能力が向上します。
- コンパイラの改善: 型チェッカーAPIが独立した形で提供されることで、コンパイラ内部の型チェックロジックの再利用や、よりモジュール化された開発が可能になります。
- 言語仕様の厳密な実装: 型チェッカーは、Go言語の型システムに関する仕様を厳密に実装し、その振る舞いを検証するためのリファレンスとしても機能します。
特に、コミットメッセージで「types.go
が主な関心事」と述べられているように、型チェッカーの核となるデータ構造とインターフェースの設計がこの段階での最重要課題であったことが伺えます。
前提知識の解説
このコミットの理解には、以下のGo言語およびコンパイラ関連の概念に関する知識が役立ちます。
1. Go言語の型システム
Go言語は静的型付け言語であり、すべての変数、関数、式には型があります。Goの型システムは、以下のような特徴を持ちます。
- 基本型:
int
,string
,bool
,float64
などの組み込み型。 - 複合型:
- 配列 (
[N]T
): 固定長で同じ型の要素を格納。 - スライス (
[]T
): 可変長で同じ型の要素を格納。 - 構造体 (
struct{...}
): 異なる型のフィールドをまとめたもの。 - ポインタ (
*T
): 変数のメモリアドレスを指す。 - 関数型 (
func(...) (...)
): 関数のシグネチャ(引数と戻り値の型)を定義。 - インターフェース型 (
interface{...}
): メソッドのセットを定義し、そのメソッドを実装する任意の型を受け入れる。 - マップ (
map[K]V
): キーと値のペアを格納。 - チャネル (
chan T
): ゴルーチン間の通信に使用。
- 配列 (
- 名前付き型:
type MyInt int
のように、既存の型に新しい名前を付けることができる。これにより、基底型が同じでも異なる型として扱われる。 - 型アサーションと型スイッチ: インターフェース値の基底の具体的な型を動的にチェックするメカニズム。
2. 抽象構文木 (AST: Abstract Syntax Tree)
コンパイラやインタプリタは、ソースコードを直接処理するのではなく、まずその構造を抽象的なツリー形式で表現します。これが抽象構文木(AST)です。Go言語では、標準ライブラリのgo/ast
パッケージがGoソースコードのASTを表現するためのデータ構造を提供します。
ast.Expr
: 式を表すインターフェース。ast.Stmt
: 文を表すインターフェース。ast.Decl
: 宣言を表すインターフェース。ast.File
: 単一のGoソースファイル全体のASTを表す。ast.Package
: 複数のast.File
からなるパッケージ全体のASTを表す。
型チェッカーは、このASTを走査し、各ノード(式、変数、関数呼び出しなど)に適切な型を割り当て、Go言語の型規則に違反がないかを確認します。
3. go/token
パッケージ
go/token
パッケージは、Goソースコード内の位置(ファイル、行、列)を表現するための型と、キーワードや演算子などのトークンを定義します。型チェッカーがエラーメッセージを生成する際など、ソースコードの正確な位置を特定するために利用されます。
4. 型チェッカーの役割
型チェッカーは、コンパイラのフロントエンドの一部であり、以下の主要な役割を担います。
- 型推論: 変数や式の型を自動的に決定します。
- 型検証: 演算子や関数呼び出しの引数など、型が期待される文脈で正しい型が使用されているかを確認します。
- 型変換のチェック: 暗黙的または明示的な型変換が許可されているかを確認します。
- 名前解決: 識別子(変数名、関数名など)がどの宣言に対応するかを解決し、その宣言の型情報を取得します。
このコミットで導入されるexp/types/staging
パッケージは、これらの型チェック機能の基盤となるAPIとデータ構造を提供することを目的としています。
技術的詳細
このコミットでは、Go言語の型チェッカーのコアコンポーネントがsrc/pkg/exp/types/staging
パッケージとして導入されています。主要なファイルとその役割は以下の通りです。
types.go
このファイルは、型チェッカーAPIの心臓部であり、Go言語の様々な型を表現するためのデータ構造と、型チェックのエントリーポイントとなるCheck
関数(ただし、このコミット時点ではコメントアウトされている)を定義しています。
Type
インターフェース: すべてのGoの型が実装する基底インターフェース。BasicKind
:int
,string
,bool
などの基本型の種類を列挙する型。BasicInfo
: 基本型の特性(数値型、文字列型、符号なし整数など)を示すフラグのセット。Basic
構造体: 基本型を表現。Kind
,Info
,Name
を持つ。- 複合型を表す構造体:
Array
: 配列型 ([Len]Elt
)Slice
: スライス型 ([]Elt
)StructField
: 構造体のフィールドStruct
: 構造体型 (struct{...}
)Pointer
: ポインタ型 (*Base
)Signature
: 関数型 (func(...) (...)
)Interface
: インターフェース型 (interface{...}
)Map
: マップ型 (map[Key]Elt
)Chan
: チャネル型 (chan Elt
,<-chan Elt
,chan<- Elt
)
NamedType
構造体:type MyType BaseType
のように宣言された名前付き型を表現。基底型 (Underlying
) と関連するメソッド (Methods
) を持つ。ObjList
:*ast.Object
のリストで、パラメータやメソッドのリストを表現するために使用される。sort.Interface
を実装し、名前でソート可能。implementsType
: すべての具体的な型がType
インターフェースを実装するための埋め込みフィールド。
predicates.go
このファイルは、型の比較や特定の特性を持つ型を識別するためのユーティリティ関数(述語関数)を実装しています。
isNamed(typ Type) bool
: 型が名前付き型であるか(基本型またはNamedType
)を判定。isBoolean(typ Type)
,isInteger(typ Type)
,isFloat(typ Type)
,isComplex(typ Type)
,isNumeric(typ Type)
,isString(typ Type)
,isUntyped(typ Type)
,isOrdered(typ Type)
: 型が特定の基本型の特性を持つかを判定。BasicInfo
フラグを利用。isUnsigned(typ Type)
: 型が符号なし整数型かを判定。isComparable(typ Type) bool
: 型が比較可能であるかを判定。Go言語では、ブール値、数値、文字列、ポインタ、チャネル、インターフェース、構造体(すべてのフィールドが比較可能な場合)、配列(要素が比較可能な場合)が比較可能です。関数、マップ、スライスは比較できません。underlying(typ Type) Type
: 名前付き型の基底型を返す。例えば、type MyInt int
の場合、MyInt
の基底型であるint
を返します。deref(typ Type) Type
: ポインタ型の基底型を返す。ポインタ型でなければ元の型を返す。isIdentical(x, y Type) bool
: 2つの型が同一であるかを再帰的に判定する最も重要な関数。Go言語の型同一性ルールに従って、配列の長さ、スライス・マップ・チャネルの要素型、構造体のフィールド、関数のシグネチャ、インターフェースのメソッドなどを比較します。identicalTypes(a, b ObjList) bool
:ObjList
内のオブジェクトの型が同一であるかを判定するヘルパー関数。
exprstring.go
このファイルは、go/ast
パッケージのast.Expr
(式)ノードを簡略化された文字列形式で表現するためのユーティリティ関数を提供します。デバッグやログ出力に役立ちます。
exprString(expr ast.Expr) string
:ast.Expr
を文字列に変換するメイン関数。writeExpr(buf *bytes.Buffer, expr ast.Expr)
:ast.Expr
の各種類(識別子、リテラル、関数リテラル、複合リテラル、括弧式、セレクタ式、インデックス式、スライス式、型アサーション、関数呼び出し、ポインタ参照、単項演算、二項演算)に応じて、bytes.Buffer
に文字列を書き込む。
typestring.go
このファイルは、types
パッケージで定義されたType
インターフェースを実装する型を、人間が読める文字列形式で表現するためのユーティリティ関数を提供します。
typeString(typ Type) string
:Type
を文字列に変換するメイン関数。writeParams(buf *bytes.Buffer, params ObjList, isVariadic bool)
: 関数のパラメータリストを文字列に変換。writeSignature(buf *bytes.Buffer, sig *Signature)
: 関数のシグネチャを文字列に変換。writeType(buf *bytes.Buffer, typ Type)
:Type
の各種類(基本型、配列、スライス、構造体、ポインタ、タプル、シグネチャ、組み込み関数、インターフェース、マップ、チャネル、名前付き型)に応じて、bytes.Buffer
に文字列を書き込む。
universe.go
このファイルは、Go言語の「ユニバーススコープ」(組み込みの識別子や型が定義されているグローバルスコープ)とunsafe
パッケージのスコープを初期化します。
Universe
: 組み込みの型、定数、関数(len
,cap
,make
,new
,panic
など)が定義される*ast.Scope
。unsafe
:unsafe
パッケージのスコープ。Typ
:BasicKind
に対応する組み込みの*Basic
型の配列。aliases
:byte
とrune
のエイリアス定義。predeclaredConstants
:true
,false
,iota
,nil
などの組み込み定数。predeclaredFunctions
:append
,len
,make
などの組み込み関数。init()
関数: パッケージがロードされる際に、Universe
スコープとunsafe
スコープを初期化し、組み込みの型、定数、関数を登録します。
これらのファイル群は、Go言語の型システムをプログラム的に表現し、解析するための包括的なフレームワークの初期バージョンを構成しています。
コアとなるコードの変更箇所
このコミットでは、以下の5つの新しいファイルが追加されています。
src/pkg/exp/types/staging/exprstring.go
src/pkg/exp/types/staging/predicates.go
src/pkg/exp/types/staging/types.go
src/pkg/exp/types/staging/typestring.go
src/pkg/exp/types/staging/universe.go
これらのファイルはすべて新規追加であり、既存のコードの変更はありません。これは、Goの型チェッカーAPIが完全に新しいモジュールとして導入されたことを意味します。
コアとなるコードの解説
このコミットのコアは、types.go
で定義される型を表すデータ構造と、predicates.go
で定義される型に関する述語関数、特にisIdentical
関数です。
types.go
における型表現
types.go
では、Go言語のあらゆる型を表現するための構造体が定義されています。これらは、GoのASTノード(ast.Expr
など)がソースコードの構文構造を表すのに対し、types
パッケージの構造体は、その構文構造が持つ「意味論的な型」を表します。
例えば、int
という基本型はBasic
構造体で表現され、[]string
というスライス型はSlice
構造体と、その要素型であるstring
を表すBasic
構造体の組み合わせで表現されます。
// All types implement the Type interface.
type Type interface {
aType()
}
// A Basic represents a basic type.
type Basic struct {
implementsType
Kind BasicKind
Info BasicInfo
Name string
}
// A Slice represents a slice type []Elt.
type Slice struct {
implementsType
Elt Type // 要素の型
}
これらの構造体は、型チェッカーがASTを走査する際に、各式の型を決定し、その型情報を保持するために使用されます。
predicates.go
における型同一性判定 (isIdentical
)
isIdentical
関数は、Go言語の型システムにおいて非常に重要な役割を果たします。Goでは、2つの型が「同一である」と見なされるための厳密なルールがあります。この関数は、そのルールをコードで実装したものです。
func isIdentical(x, y Type) bool {
if x == y {
return true
}
switch x := x.(type) {
case *Basic:
// Basic types are singletons except for the rune and byte
// aliases, thus we cannot solely rely on the x == y check
// above.
if y, ok := y.(*Basic); ok {
return x.Kind == y.Kind
}
case *Array:
// Two array types are identical if they have identical element types
// and the same array length.
if y, ok := y.(*Array); ok {
return x.Len == y.Len && isIdentical(x.Elt, y.Elt)
}
// ... 他の型に関する同一性チェック ...
case *NamedType:
// Two named types are identical if their type names originate
// in the same type declaration.
if y, ok := y.(*NamedType); ok {
return x.Obj == y.Obj // 同じ ast.Object から宣言された型であるか
}
}
return false
}
この関数は、再帰的に型の内部構造を比較し、Goの言語仕様で定められた型同一性の規則(例:配列は要素型と長さが同じ、スライスは要素型が同じ、構造体はフィールドの名前・型・タグ・匿名性が同じ、関数はパラメータ・結果の型とvariadic性が同じなど)に基づいてtrue
またはfalse
を返します。特にNamedType
の同一性チェックでは、その型が宣言された元の*ast.Object
が同じであるかを確認することで、異なるパッケージで同じ名前の型が宣言されていても、それらが異なる型として扱われることを保証します。
このisIdentical
関数は、型チェッカーが型の一致性や代入可能性を検証する際の基盤となります。
関連リンク
- Go Gerrit Change-ID: https://golang.org/cl/6490089
参考にした情報源リンク
- Go言語の公式ドキュメント (Go言語の型システム、
go/ast
パッケージ、go/token
パッケージに関する一般的な情報) - コンパイラの理論に関する一般的な知識 (AST、型チェッカーの概念)