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

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

このコミットは、Go言語の実験的な型チェッカーパッケージである src/pkg/exp/types/staging において、式(expression)と文(statement)の型チェック機能の基本的なフレームワークを導入するものです。具体的には、以下のファイルが変更されています。

  • src/pkg/exp/types/staging/conversions.go: 型変換(type conversion)の型チェックロジックを実装しています。
  • src/pkg/exp/types/staging/expr.go: さまざまな種類の式(単項演算、二項演算、セレクタ、インデックス、関数呼び出しなど)の型チェックロジックを実装しています。このファイルが最も多くの変更を含んでいます。
  • src/pkg/exp/types/staging/stmt.go: 文(代入文、if文、for文、switch文など)の型チェックロジックを実装しています。
  • src/pkg/exp/types/staging/stubs.go: 以前の未実装のスタブ関数を削除しています。これは、conversions.goexpr.gostmt.go で実際の型チェックロジックが実装されたため不要になったものです。
  • src/pkg/exp/types/staging/testdata/*.src: 新たに多数のテストデータファイルが追加されています。これらは、定数、型変換、宣言、式、文の型チェックが正しく機能するかを検証するためのものです。

コミット

  • コミットハッシュ: 5224875055d5738f18f194b4d5dbfb5babd33a21
  • 作者: Robert Griesemer gri@golang.org
  • 日付: Sun Oct 7 18:01:43 2012 -0700

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

https://github.com/golang/go/commit/5224875055d5738f18f194b4d5dbfb5babd33a21

元コミット内容

/exp/types/staging: expression and statement type checking

Still lots of pieces missing, but basic framework working.
Lots of tests.

R=rsc
CC=golang-dev
https://golang.org/cl/6594054

変更の背景

このコミットは、Go言語のコンパイラおよびツールチェインの重要なコンポーネントである型チェッカーの開発における大きな一歩を示しています。Go言語は静的型付け言語であり、プログラムの正確性を保証するために厳密な型チェックが必要です。

src/pkg/exp/types/staging パッケージは、Go言語の公式な型チェッカー go/types パッケージの前身となる実験的な実装でした。この時期(2012年)はGo言語がまだ比較的新しく、そのエコシステムが成熟していく過程にありました。型チェッカーは、コンパイラがソースコードを機械語に変換する前に、プログラムがGo言語の型規則に準拠しているかを検証する役割を担います。これにより、多くのプログラミングエラーを早期に発見し、実行時エラーを防ぐことができます。

このコミット以前は、式や文の型チェックが十分に実装されていなかったか、あるいは非常に基本的なレベルに留まっていたと考えられます。この変更により、Goプログラムの大部分を構成する式と文に対する体系的な型チェックの基盤が確立されました。これにより、より複雑なGoプログラムの解析と検証が可能になり、Go言語の安定性と信頼性の向上に貢献しました。

前提知識の解説

このコミットの理解には、以下の概念に関する知識が役立ちます。

  1. 静的型付け言語と型チェック: Goは静的型付け言語であり、変数の型はコンパイル時に決定されます。型チェックは、プログラムが型規則に違反していないか(例: 整数型変数に文字列を代入しようとしていないか)を検証するプロセスです。これにより、実行時エラーを減らし、コードの信頼性を高めます。
  2. 抽象構文木 (AST - Abstract Syntax Tree): コンパイラはソースコードを直接扱うのではなく、まずそれをASTというツリー構造に変換します。ASTはプログラムの構造を抽象的に表現したもので、型チェッカーはこのASTを走査して型情報を収集し、規則を適用します。Go言語では go/ast パッケージがASTの定義を提供します。
  3. トークン (Token): ソースコードはまず字句解析器によってトークン(キーワード、識別子、演算子など)のストリームに分割されます。Go言語では go/token パッケージがこれらのトークンを定義します。型チェッカーはASTを通じてこれらのトークンにアクセスし、演算子の種類などを判断します。
  4. 型システム: Go言語には、基本型(int, string, bool など)、複合型(配列、スライス、マップ、構造体、インターフェース、チャネル)、関数型、ポインタ型など、多様な型があります。型チェッカーはこれらの型の互換性や操作の妥当性を検証します。
  5. 定数伝播 (Constant Propagation): コンパイル時に値が確定する定数(const で宣言されたものやリテラル)は、その値を型チェック時に利用できます。これにより、定数式の結果を事前に計算したり、定数に対する不正な操作(例: ゼロ除算)をコンパイル時に検出したりすることが可能になります。
  6. exp/typesgo/types: exp/types はGo言語の標準ライブラリに go/types として組み込まれる前の実験的な型チェッカーです。go/types は、GoのコンパイラやIDE、リンターなどのツールがGoプログラムの型情報を正確に解析するために使用される重要なパッケージです。このコミットは、その基礎を築くものです。
  7. operand 構造体: 型チェックの過程で、式の結果や型情報を保持するための中間表現です。このコミットでは、operand 構造体が式のモード(定数、変数、値、型式など)や型、値などを管理するために使用されています。

技術的詳細

このコミットは、Go言語の型チェッカーの核心部分である式と文の型チェックロジックを大幅に拡張しています。主要なファイルとその役割は以下の通りです。

src/pkg/exp/types/staging/expr.go

このファイルは、Go言語の式の型チェックの大部分を担っています。

  • checker 構造体: 型チェックのコンテキストを保持する主要な構造体です。エラー報告、型情報の管理などを行います。
  • exprOrType(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool): この関数は、与えられたASTの式 e を型チェックし、その結果を operand 構造体 x に格納します。
    • hint: シフト演算の左オペランドなど、型推論のヒントとして使用されます。
    • iota: 定数宣言の一部である場合に iota の値を提供します。
    • cycleOk: 型式が自身を参照する循環を許容するかどうかを制御します(主に型宣言のチェックで使用)。
    • x.mode: 式の結果が定数 (constant)、変数 (variable)、単なる値 (value)、型式 (typexpr)、または無効 (invalid) であるかを示します。
    • x.typ: 式の型を保持します。
    • x.val: 式が定数である場合、その値を保持します。
  • 単項演算子 (unary): +, -, ^, !, &, <- などの単項演算子の型チェックを処理します。
    • token.AND (&): アドレス取得演算子。オペランドが変数であるかを確認します。
    • token.ARROW (<-): チャネル受信演算子。オペランドが受信可能なチャネルであるかを確認します。
    • 定数オペランドの場合、コンパイル時に値を計算し、結果が型に収まるかを確認します。
  • 二項演算子 (binary): +, -, *, /, %, &, |, ^, <<, >>, &&, ||, ==, !=, <, <=, >, >= などの二項演算子の型チェックを処理します。
    • 型変換 (convertUntyped): 演算子の両側のオペランドがアンタイプド定数である場合、共通の型に変換を試みます。
    • 比較演算子 (comparison): ==, !=, <, <=, >, >= の型チェックを処理します。オペランドが比較可能であるか、順序付け可能であるかを確認します。
    • シフト演算子 (shift): <<, >> の型チェックを処理します。右オペランドが符号なし整数型であるか、または符号なし整数型に変換可能なアンタイプド定数であるかを確認します。左オペランドがアンタイプド定数の場合、コンテキストから型を推論します。
    • ゼロ除算の検出など、コンパイル時エラーのチェックも行います。
  • 各種ASTノードの処理:
    • ast.Ident (識別子): 変数、定数、型、関数などの識別子を解決し、その型とモードを設定します。iota の処理もここで行われます。
    • ast.BasicLit (基本リテラル): 数値、文字列、真偽値リテラルの型と値を設定します。
    • ast.FuncLit (関数リテラル): 関数本体の型チェックを行います。
    • ast.CompositeLit (複合リテラル): 配列、スライス、マップ、構造体リテラルの要素の型チェックを行います。
    • ast.SelectorExpr (セレクタ式): pkg.Namestruct.Field のようなセレクタ式の型チェックを行います。パッケージスコープの識別子や構造体のフィールドアクセスを処理します。
    • ast.IndexExpr (インデックス式): 配列、スライス、マップのインデックスアクセスの型チェックを行います。インデックスが整数型であるかを確認します。
    • ast.SliceExpr (スライス式): スライス操作の型チェックを行います。
    • ast.TypeAssertExpr (型アサーション): インターフェース型に対する型アサーションの型チェックを行います。
    • ast.CallExpr (関数呼び出し): 関数呼び出しの型チェックを行います。引数の型と関数のパラメータの型が一致するか、組み込み関数(builtin)の呼び出しなどを処理します。
    • ast.StarExpr (ポインタ間接参照/ポインタ型): *T のようなポインタ型や、ポインタの指す値へのアクセス (*ptr) の型チェックを行います。
    • ast.ArrayType, ast.StructType, ast.FuncType, ast.InterfaceType, ast.MapType, ast.ChanType: これらの型宣言の型チェックを行い、対応する内部型表現(Array, Struct, Signature など)を構築します。

src/pkg/exp/types/staging/stmt.go

このファイルは、Go言語の文の型チェックを担っています。

  • assignOperand(z, x *operand): オペランド x をオペランド z に代入できるかどうかの型チェックを行います。型変換の適用や、型不一致の場合のエラー報告を行います。
  • assignment(lhs ast.Expr, x *operand, decl bool): 単一の代入文(例: a = b または a := b)の型チェックを行います。
    • decltrue の場合、lhs は宣言の一部であり、lhs の型が x の型から推論される可能性があります。
    • 定数宣言の場合、定数値を設定し、定数でない値が定数に代入されようとした場合にエラーを報告します。
  • assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int): 複数の左辺と右辺を持つ代入文(例: a, b = c, da, b := func_returns_two_values())の型チェックを行います。
    • 左辺と右辺の数の不一致を検出します。
    • 多値返却関数からの代入や、カンマOKイディオム(value, ok := map[key])の処理も行います。
  • stmt(s ast.Stmt): 与えられたASTの文 s を型チェックします。
    • ast.ExprStmt (式文): 関数呼び出しやチャネル受信など、副作用を持つ式が文として使用されているかを確認します。
    • ast.SendStmt (チャネル送信文): チャネルへの送信操作の型チェックを行います。チャネルが送信可能であるか、送信される値がチャネルの要素型に代入可能であるかを確認します。
    • ast.AssignStmt (代入文): 通常の代入 (=) や短い変数宣言 (:=)、複合代入演算子 (+=, -= など) の型チェックを assignNtoMbinary 関数を呼び出して行います。
    • ast.BlockStmt (ブロック文): ブロック内の文リストを再帰的に型チェックします。
    • ast.IfStmt (if文): 条件式が真偽値型であるかを確認し、thenブロックとelseブロックを型チェックします。
    • ast.SwitchStmt (switch文): switch式の型チェックを行い、case節の式がswitch式と比較可能であるかを確認します。
    • ast.ForStmt (for文): 条件式が真偽値型であるかを確認し、ループ本体を型チェックします。
  • 未実装のスタブの削除: stubs.go が削除され、expr.gostmt.go で実際の型チェックロジックが実装されたことで、以前のプレースホルダーが不要になりました。

src/pkg/exp/types/staging/conversions.go

このファイルは、Go言語の型変換の型チェックを担っています。

  • conversion(x *operand, conv *ast.CallExpr, typ Type, iota int): Type(expression) 形式の型変換の型チェックを行います。
    • 変換が単一の引数を取ることを確認します。
    • 引数の式を型チェックし、その結果を変換先の型 typ に変換できるかを確認します。
    • この時点では、まだすべての変換規則が完全に実装されているわけではないことがコメント (TODO(gri) fix this - implement all checks and constant evaluation) から伺えます。

全体として、このコミットはGo言語の型チェッカーの機能的な基盤を構築し、式と文の複雑な型規則を処理するための詳細なロジックを導入しています。多数のテストケースの追加は、この新しい型チェック機能の網羅性と正確性を保証しようとする意図を示しています。

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

このコミットのコアとなる変更は、主に以下の3つの新規ファイルと1つの削除ファイルに集約されます。

  1. src/pkg/exp/types/staging/expr.go (新規追加、793行):

    • func (check *checker) exprOrType(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool): 式と型式の両方を型チェックする主要な関数。GoのASTノード(ast.Ident, ast.BasicLit, ast.CallExpr, ast.UnaryExpr, ast.BinaryExpr, ast.ArrayType, ast.StructType など)ごとに詳細な型チェックロジックが実装されています。
    • func (check *checker) unary(x *operand, op token.Token): 単項演算子の型チェック。
    • func (check *checker) binary(x, y *operand, op token.Token, hint Type): 二項演算子の型チェック。
    • func (check *checker) comparison(x, y *operand, op token.Token): 比較演算子の型チェック。
    • func (check *checker) shift(x, y *operand, op token.Token, hint Type): シフト演算子の型チェック。
    • func (check *checker) isRepresentable(x *operand, typ *Basic): 定数が指定された型で表現可能かどうかのチェック。
    • func (check *checker) convertUntyped(x *operand, target Type): アンタイプド定数をターゲット型に変換するロジック。
  2. src/pkg/exp/types/staging/stmt.go (新規追加、465行):

    • func (check *checker) stmt(s ast.Stmt): 文を型チェックする主要な関数。GoのASTノード(ast.ExprStmt, ast.SendStmt, ast.AssignStmt, ast.IfStmt, ast.SwitchStmt, ast.ForStmt など)ごとに型チェックロジックが実装されています。
    • func (check *checker) assignOperand(z, x *operand): 単一のオペランド代入の型チェック。
    • func (check *checker) assignment(lhs ast.Expr, x *operand, decl bool): 単一の代入文(宣言を含む)の型チェック。
    • func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int): 複数代入の型チェック。
  3. src/pkg/exp/types/staging/conversions.go (新規追加、39行):

    • func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int): 型変換の型チェック。
  4. src/pkg/exp/types/staging/stubs.go (削除、55行):

    • 以前の未実装の型チェック関数のスタブが削除されました。これは、上記の新しいファイルで実際のロジックが実装されたためです。

これらの変更により、Go言語の型チェッカーが式と文の複雑なセマンティクスを理解し、検証するための具体的なアルゴリズムとデータ構造が導入されました。

コアとなるコードの解説

このコミットの核となるのは、Go言語のASTを走査し、各ノードの型情報を決定・検証する checker 構造体とそのメソッド群です。

  • checker 構造体: 型チェックの全体的な状態を管理します。これには、エラー報告のためのメソッド(errorf, invalidOp, invalidAST)、型情報の解決(typ)、識別子の解決(ident)などが含まれます。型チェックは、この checker インスタンスを通じて行われます。

  • operand 構造体: 型チェックの過程で、式の結果を表現するために使用される重要なデータ構造です。

    type operand struct {
        mode operandMode // 式の評価モード (constant, variable, value, typexpr, invalid)
        expr ast.Expr    // 元のAST式
        typ  Type        // 式の型
        val  interface{} // 定数式の場合の値
    }
    

    mode フィールドは、式が定数であるか、変数であるか、単なる値であるか、型を表す式であるか、あるいは無効な式であるかを示します。これにより、Goの型システムにおける異なる種類の式の振る舞いを正確にモデル化できます。例えば、&x のようにアドレスが取れるのは variable モードの式のみです。

  • exprOrType メソッド: このメソッドは、GoのASTノードの型チェックの中心的なディスパッチャです。switch e := e.(type) を用いて、入力された ast.Expr の具体的な型(*ast.Ident, *ast.BasicLit, *ast.BinaryExpr など)に応じて適切な型チェックロジックに処理を委譲します。

    • 識別子 (ast.Ident) の解決: e.Obj を参照して、識別子が指すオブジェクト(定数、型、変数、関数、パッケージ)を特定し、その Kind に応じて operand.modeoperand.typ を設定します。iota の特殊な振る舞いもここで処理されます。
    • 定数式の評価: ast.BasicLit や定数オペランドを持つ演算子(unary, binary)の場合、operand.val にコンパイル時計算された値を格納します。これにより、定数伝播と定数に対するエラーチェック(例: ゼロ除算)が可能になります。
    • 型式の処理: ast.ArrayType, ast.StructType などの型宣言ノードは typexpr モードとして処理され、対応するGoの内部型表現(*Array, *Struct など)が構築されます。
  • unary および binary メソッド: これらのメソッドは、単項および二項演算子の型チェックロジックをカプセル化しています。

    • 型の一致と互換性: 演算子の種類に応じて、オペランドの型が適切であるか(例: 算術演算子は数値型、論理演算子は真偽値型)を検証します。
    • アンタイプド定数の型推論: convertUntyped を使用して、アンタイプド定数(例: 1003.14)が周囲のコンテキスト(他のオペランドの型や代入先の型)に基づいて適切な型に変換されるようにします。
    • コンパイル時エラー: ゼロ除算や型不一致など、コンパイル時に検出できるエラーを報告します。
  • stmt メソッド: 文の型チェックのディスパッチャです。switch s := s.(type) を用いて、各文の種類(*ast.AssignStmt, *ast.IfStmt, *ast.ForStmt など)に応じた型チェックロジックを呼び出します。

    • 代入文 (assignNtoM): Goの複雑な代入規則(単一代入、複数代入、短い変数宣言、多値返却関数の結果の代入、カンマOKイディオム)を処理します。左辺と右辺の型互換性を検証し、必要に応じて型推論を行います。
    • 制御フロー文 (if, for, switch): 条件式が真偽値型であるかを確認し、各ブロック内の文を再帰的に型チェックします。

このコミットは、Go言語の型チェッカーが、言語仕様に定められた複雑な型規則とセマンティクスを、ASTを効率的に走査しながら正確に適用するための強固な基盤を築いたことを示しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Go Language Specification)
  • go/types パッケージのソースコードとドキュメント
  • コンパイラ設計に関する一般的な知識(AST、型チェック、セマンティック分析)
  • GitHubのコミット履歴と関連するGoのIssue/CL (Change List)

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

このコミットは、Go言語の実験的な型チェッカーパッケージである src/pkg/exp/types/staging において、式(expression)と文(statement)の型チェック機能の基本的なフレームワークを導入するものです。具体的には、以下のファイルが変更されています。

  • src/pkg/exp/types/staging/conversions.go: 型変換(type conversion)の型チェックロジックを実装しています。
  • src/pkg/exp/types/staging/expr.go: さまざまな種類の式(単項演算、二項演算、セレクタ、インデックス、関数呼び出しなど)の型チェックロジックを実装しています。このファイルが最も多くの変更を含んでいます。
  • src/pkg/exp/types/staging/stmt.go: 文(代入文、if文、for文、switch文など)の型チェックロジックを実装しています。
  • src/pkg/exp/types/staging/stubs.go: 以前の未実装のスタブ関数を削除しています。これは、conversions.goexpr.gostmt.go で実際の型チェックロジックが実装されたため不要になったものです。
  • src/pkg/exp/types/staging/testdata/*.src: 新たに多数のテストデータファイルが追加されています。これらは、定数、型変換、宣言、式、文の型チェックが正しく機能するかを検証するためのものです。

コミット

  • コミットハッシュ: 5224875055d5738f18f194b4d5dbfb5babd33a21
  • 作者: Robert Griesemer gri@golang.org
  • 日付: Sun Oct 7 18:01:43 2012 -0700

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

https://github.com/golang/go/commit/5224875055d5738f18f194b4d5dbfb5babd33a21

元コミット内容

/exp/types/staging: expression and statement type checking

Still lots of pieces missing, but basic framework working.
Lots of tests.

R=rsc
CC=golang-dev
https://golang.org/cl/6594054

変更の背景

このコミットは、Go言語のコンパイラおよびツールチェインの重要なコンポーネントである型チェッカーの開発における大きな一歩を示しています。Go言語は静的型付け言語であり、プログラムの正確性を保証するために厳密な型チェックが必要です。

src/pkg/exp/types/staging パッケージは、Go言語の公式な型チェッカー go/types パッケージの前身となる実験的な実装でした。この時期(2012年)はGo言語がまだ比較的新しく、そのエコシステムが成熟していく過程にありました。型チェッカーは、コンパイラがソースコードを機械語に変換する前に、プログラムがGo言語の型規則に準拠しているかを検証する役割を担います。これにより、多くのプログラミングエラーを早期に発見し、実行時エラーを防ぐことができます。

このコミット以前は、式や文の型チェックが十分に実装されていなかったか、あるいは非常に基本的なレベルに留まっていたと考えられます。この変更により、Goプログラムの大部分を構成する式と文に対する体系的な型チェックの基盤が確立されました。これにより、より複雑なGoプログラムの解析と検証が可能になり、Go言語の安定性と信頼性の向上に貢献しました。

前提知識の解説

このコミットの理解には、以下の概念に関する知識が役立ちます。

  1. 静的型付け言語と型チェック: Goは静的型付け言語であり、変数の型はコンパイル時に決定されます。型チェックは、プログラムが型規則に違反していないか(例: 整数型変数に文字列を代入しようとしていないか)を検証するプロセスです。これにより、実行時エラーを減らし、コードの信頼性を高めます。
  2. 抽象構文木 (AST - Abstract Syntax Tree): コンパイラはソースコードを直接扱うのではなく、まずそれをASTというツリー構造に変換します。ASTはプログラムの構造を抽象的に表現したもので、型チェッカーはこのASTを走査して型情報を収集し、規則を適用します。Go言語では go/ast パッケージがASTの定義を提供します。
  3. トークン (Token): ソースコードはまず字句解析器によってトークン(キーワード、識別子、演算子など)のストリームに分割されます。Go言語では go/token パッケージがこれらのトークンを定義します。型チェッカーはASTを通じてこれらのトークンにアクセスし、演算子の種類などを判断します。
  4. 型システム: Go言語には、基本型(int, string, bool など)、複合型(配列、スライス、マップ、構造体、インターフェース、チャネル)、関数型、ポインタ型など、多様な型があります。型チェッカーはこれらの型の互換性や操作の妥当性を検証します。
  5. 定数伝播 (Constant Propagation): コンパイル時に値が確定する定数(const で宣言されたものやリテラル)は、その値を型チェック時に利用できます。これにより、定数式の結果を事前に計算したり、定数に対する不正な操作(例: ゼロ除算)をコンパイル時に検出したりすることが可能になります。
  6. exp/typesgo/types: exp/types はGo言語の標準ライブラリに go/types として組み込まれる前の実験的な型チェッカーです。go/types は、GoのコンパイラやIDE、リンターなどのツールがGoプログラムの型情報を正確に解析するために使用される重要なパッケージです。このコミットは、その基礎を築くものです。
  7. operand 構造体: 型チェックの過程で、式の結果や型情報を保持するための中間表現です。このコミットでは、operand 構造体が式のモード(定数、変数、値、型式など)や型、値などを管理するために使用されています。

技術的詳細

このコミットは、Go言語の型チェッカーの核心部分である式と文の型チェックロジックを大幅に拡張しています。主要なファイルとその役割は以下の通りです。

src/pkg/exp/types/staging/expr.go

このファイルは、Go言語の式の型チェックの大部分を担っています。

  • checker 構造体: 型チェックのコンテキストを保持する主要な構造体です。エラー報告、型情報の管理などを行います。
  • exprOrType(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool): この関数は、与えられたASTの式 e を型チェックし、その結果を operand 構造体 x に格納します。
    • hint: シフト演算の左オペランドなど、型推論のヒントとして使用されます。
    • iota: 定数宣言の一部である場合に iota の値を提供します。
    • cycleOk: 型式が自身を参照する循環を許容するかどうかを制御します(主に型宣言のチェックで使用)。
    • x.mode: 式の結果が定数 (constant)、変数 (variable)、単なる値 (value)、型式 (typexpr)、または無効 (invalid) であるかを示します。
    • x.typ: 式の型を保持します。
    • x.val: 式が定数である場合、その値を保持します。
  • 単項演算子 (unary): +, -, ^, !, &, <- などの単項演算子の型チェックを処理します。
    • token.AND (&): アドレス取得演算子。オペランドが変数であるかを確認します。
    • token.ARROW (<-): チャネル受信演算子。オペランドが受信可能なチャネルであるかを確認します。
    • 定数オペランドの場合、コンパイル時に値を計算し、結果が型に収まるかを確認します。
  • 二項演算子 (binary): +, -, *, /, %, &, |, ^, <<, >>, &&, ||, ==, !=, <, <=, >, >= などの二項演算子の型チェックを処理します。
    • 型変換 (convertUntyped): 演算子の両側のオペランドがアンタイプド定数である場合、共通の型に変換を試みます。
    • 比較演算子 (comparison): ==, !=, <, <=, >, >= の型チェックを処理します。オペランドが比較可能であるか、順序付け可能であるかを確認します。
    • シフト演算子 (shift): <<, >> の型チェックを処理します。右オペランドが符号なし整数型であるか、または符号なし整数型に変換可能なアンタイプド定数であるかを確認します。左オペランドがアンタイプド定数の場合、コンテキストから型を推論します。
    • ゼロ除算の検出など、コンパイル時エラーのチェックも行います。
  • 各種ASTノードの処理:
    • ast.Ident (識別子): 変数、定数、型、関数などの識別子を解決し、その型とモードを設定します。iota の特殊な振る舞いもここで行われます。
    • ast.BasicLit (基本リテラル): 数値、文字列、真偽値リテラルの型と値を設定します。
    • ast.FuncLit (関数リテラル): 関数本体の型チェックを行います。
    • ast.CompositeLit (複合リテラル): 配列、スライス、マップ、構造体リテラルの要素の型チェックを行います。
    • ast.SelectorExpr (セレクタ式): pkg.Namestruct.Field のようなセレクタ式の型チェックを行います。パッケージスコープの識別子や構造体のフィールドアクセスを処理します。
    • ast.IndexExpr (インデックス式): 配列、スライス、マップのインデックスアクセスの型チェックを行います。インデックスが整数型であるかを確認します。
    • ast.SliceExpr (スライス式): スライス操作の型チェックを行います。
    • ast.TypeAssertExpr (型アサーション): インターフェース型に対する型アサーションの型チェックを行います。
    • ast.CallExpr (関数呼び出し): 関数呼び出しの型チェックを行います。引数の型と関数のパラメータの型が一致するか、組み込み関数(builtin)の呼び出しなどを処理します。
    • ast.StarExpr (ポインタ間接参照/ポインタ型): *T のようなポインタ型や、ポインタの指す値へのアクセス (*ptr) の型チェックを行います。
    • ast.ArrayType, ast.StructType, ast.FuncType, ast.InterfaceType, ast.MapType, ast.ChanType: これらの型宣言の型チェックを行い、対応する内部型表現(Array, Struct, Signature など)を構築します。

src/pkg/exp/types/staging/stmt.go

このファイルは、Go言語の文の型チェックを担っています。

  • assignOperand(z, x *operand): オペランド x をオペランド z に代入できるかどうかの型チェックを行います。型変換の適用や、型不一致の場合のエラー報告を行います。
  • assignment(lhs ast.Expr, x *operand, decl bool): 単一の代入文(例: a = b または a := b)の型チェックを行います。
    • decltrue の場合、lhs は宣言の一部であり、lhs の型が x の型から推論される可能性があります。
    • 定数宣言の場合、定数値を設定し、定数でない値が定数に代入されようとした場合にエラーを報告します。
  • assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int): 複数の左辺と右辺を持つ代入文(例: a, b = c, da, b := func_returns_two_values())の型チェックを行います。
    • 左辺と右辺の数の不一致を検出します。
    • 多値返却関数からの代入や、カンマOKイディオム(value, ok := map[key])の処理も行います。
  • stmt(s ast.Stmt): 与えられたASTの文 s を型チェックします。
    • ast.ExprStmt (式文): 関数呼び出しやチャネル受信など、副作用を持つ式が文として使用されているかを確認します。
    • ast.SendStmt (チャネル送信文): チャネルへの送信操作の型チェックを行います。チャネルが送信可能であるか、送信される値がチャネルの要素型に代入可能であるかを確認します。
    • ast.AssignStmt (代入文): 通常の代入 (=) や短い変数宣言 (:=)、複合代入演算子 (+=, -= など) の型チェックを assignNtoMbinary 関数を呼び出して行います。
    • ast.BlockStmt (ブロック文): ブロック内の文リストを再帰的に型チェックします。
    • ast.IfStmt (if文): 条件式が真偽値型であるかを確認し、thenブロックとelseブロックを型チェックします。
    • ast.SwitchStmt (switch文): switch式の型チェックを行い、case節の式がswitch式と比較可能であるかを確認します。
    • ast.ForStmt (for文): 条件式が真偽値型であるかを確認し、ループ本体を型チェックします。
  • 未実装のスタブの削除: stubs.go が削除され、expr.gostmt.go で実際の型チェックロジックが実装されたことで、以前のプレースホルダーが不要になりました。

src/pkg/exp/types/staging/conversions.go

このファイルは、Go言語の型変換の型チェックを担っています。

  • conversion(x *operand, conv *ast.CallExpr, typ Type, iota int): Type(expression) 形式の型変換の型チェックを行います。
    • 変換が単一の引数を取ることを確認します。
    • 引数の式を型チェックし、その結果を変換先の型 typ に変換できるかを確認します。
    • この時点では、まだすべての変換規則が完全に実装されているわけではないことがコメント (TODO(gri) fix this - implement all checks and constant evaluation) から伺えます。

全体として、このコミットはGo言語の型チェッカーの機能的な基盤を構築し、式と文の複雑な型規則を処理するための詳細なロジックを導入しています。多数のテストケースの追加は、この新しい型チェック機能の網羅性と正確性を保証しようとする意図を示しています。

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

このコミットのコアとなる変更は、主に以下の3つの新規ファイルと1つの削除ファイルに集約されます。

  1. src/pkg/exp/types/staging/expr.go (新規追加、793行):

    • func (check *checker) exprOrType(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool): 式と型式の両方を型チェックする主要な関数。GoのASTノード(ast.Ident, ast.BasicLit, ast.CallExpr, ast.UnaryExpr, ast.BinaryExpr, ast.ArrayType, ast.StructType など)ごとに詳細な型チェックロジックが実装されています。
    • func (check *checker) unary(x *operand, op token.Token): 単項演算子の型チェック。
    • func (check *checker) binary(x, y *operand, op token.Token, hint Type): 二項演算子の型チェック。
    • func (check *checker) comparison(x, y *operand, op token.Token): 比較演算子の型チェック。
    • func (check *checker) shift(x, y *operand, op token.Token, hint Type): シフト演算子の型チェック。
    • func (check *checker) isRepresentable(x *operand, typ *Basic): 定数が指定された型で表現可能かどうかのチェック。
    • func (check *checker) convertUntyped(x *operand, target Type): アンタイプド定数をターゲット型に変換するロジック。
  2. src/pkg/exp/types/staging/stmt.go (新規追加、465行):

    • func (check *checker) stmt(s ast.Stmt): 文を型チェックする主要な関数。GoのASTノード(ast.ExprStmt, ast.SendStmt, ast.AssignStmt, ast.IfStmt, ast.SwitchStmt, ast.ForStmt など)ごとに型チェックロジックが実装されています。
    • func (check *checker) assignOperand(z, x *operand): 単一のオペランド代入の型チェック。
    • func (check *checker) assignment(lhs ast.Expr, x *operand, decl bool): 単一の代入文(宣言を含む)の型チェック。
    • func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int): 複数代入の型チェック。
  3. src/pkg/exp/types/staging/conversions.go (新規追加、39行):

    • func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int): 型変換の型チェック。
  4. src/pkg/exp/types/staging/stubs.go (削除、55行):

    • 以前の未実装の型チェック関数のスタブが削除されました。これは、上記の新しいファイルで実際のロジックが実装されたためです。

これらの変更により、Go言語の型チェッカーが式と文の複雑なセマンティクスを理解し、検証するための具体的なアルゴリズムとデータ構造が導入されました。

コアとなるコードの解説

このコミットの核となるのは、Go言語のASTを走査し、各ノードの型情報を決定・検証する checker 構造体とそのメソッド群です。

  • checker 構造体: 型チェックの全体的な状態を管理します。これには、エラー報告のためのメソッド(errorf, invalidOp, invalidAST)、型情報の解決(typ)、識別子の解決(ident)などが含まれます。型チェックは、この checker インスタンスを通じて行われます。

  • operand 構造体: 型チェックの過程で、式の結果を表現するために使用される重要なデータ構造です。

    type operand struct {
        mode operandMode // 式の評価モード (constant, variable, value, typexpr, invalid)
        expr ast.Expr    // 元のAST式
        typ  Type        // 式の型
        val  interface{} // 定数式の場合の値
    }
    

    mode フィールドは、式が定数であるか、変数であるか、単なる値であるか、型を表す式であるか、あるいは無効な式であるかを示します。これにより、Goの型システムにおける異なる種類の式の振る舞いを正確にモデル化できます。例えば、&x のようにアドレスが取れるのは variable モードの式のみです。

  • exprOrType メソッド: このメソッドは、GoのASTノードの型チェックの中心的なディスパッチャです。switch e := e.(type) を用いて、入力された ast.Expr の具体的な型(*ast.Ident, *ast.BasicLit, *ast.CallExpr, *ast.UnaryExpr, *ast.BinaryExpr など)に応じて適切な型チェックロジックに処理を委譲します。

    • 識別子 (ast.Ident) の解決: e.Obj を参照して、識別子が指すオブジェクト(定数、型、変数、関数、パッケージ)を特定し、その Kind に応じて operand.modeoperand.typ を設定します。iota の特殊な振る舞いもここで処理されます。
    • 定数式の評価: ast.BasicLit や定数オペランドを持つ演算子(unary, binary)の場合、operand.val にコンパイル時計算された値を格納します。これにより、定数伝播と定数に対するエラーチェック(例: ゼロ除算)が可能になります。
    • 型式の処理: ast.ArrayType, ast.StructType などの型宣言ノードは typexpr モードとして処理され、対応するGoの内部型表現(*Array, *Struct など)が構築されます。
  • unary および binary メソッド: これらのメソッドは、単項および二項演算子の型チェックロジックをカプセル化しています。

    • 型の一致と互換性: 演算子の種類に応じて、オペランドの型が適切であるか(例: 算術演算子は数値型、論理演算子は真偽値型)を検証します。
    • アンタイプド定数の型推論: convertUntyped を使用して、アンタイプド定数(例: 1003.14)が周囲のコンテキスト(他のオペランドの型や代入先の型)に基づいて適切な型に変換されるようにします。
    • コンパイル時エラー: ゼロ除算や型不一致など、コンパイル時に検出できるエラーを報告します。
  • stmt メソッド: 文の型チェックのディスパッチャです。switch s := s.(type) を用いて、各文の種類(*ast.AssignStmt, *ast.IfStmt, *ast.ForStmt など)に応じた型チェックロジックを呼び出します。

    • 代入文 (assignNtoM): Goの複雑な代入規則(単一代入、複数代入、短い変数宣言、多値返却関数の結果の代入、カンマOKイディオム)を処理します。左辺と右辺の型互換性を検証し、必要に応じて型推論を行います。
    • 制御フロー文 (if, for, switch): 条件式が真偽値型であるかを確認し、各ブロック内の文を再帰的に型チェックします。

このコミットは、Go言語の型チェッカーが、言語仕様に定められた複雑な型規則とセマンティクスを、ASTを効率的に走査しながら正確に適用するための強固な基盤を築いたことを示しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Go Language Specification)
  • go/types パッケージのソースコードとドキュメント
  • コンパイラ設計に関する一般的な知識(AST、型チェック、セマンティック分析)
  • GitHubのコミット履歴と関連するGoのIssue/CL (Change List)