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

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

このコミットは、Go言語の実験的な型チェッカー (exp/types) およびパーサー (go/parser) パッケージにおけるコメントの修正と、既知のバグに関するTODOコメントの追加を目的としています。主に、複合リテラル(CompositeLit)のキー解決に関するパーサーと型チェッカー間の責任分担についての説明を明確にしています。

コミット

commit 3e8304b377c3aa34f6747c5ada6d6ffc83c1a9a6
Author: Robert Griesemer <gri@golang.org>
Date:   Wed Dec 26 14:04:50 2012 -0800

    exp/types: some comment fixes
    
    R=adonovan, bradfitz
    CC=golang-dev
    https://golang.org/cl/7018046

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

https://github.com/golang/go/commit/3e8304b377c3aa34f6747c5ada6d6ffc83c1a9a6

元コミット内容

exp/types: いくつかのコメント修正

変更の背景

このコミットの背景には、Go言語のコンパイラにおけるパーサーと型チェッカーの役割分担、特に複合リテラル(CompositeLit)のキーの解決に関する課題があります。

Go言語では、struct{Field: value}map[KeyType]ValueType{key: value} のように、複合リテラルを使用して構造体、マップ、配列、スライスを初期化できます。これらのリテラル内で使用される「キー」は、文脈によって異なる意味を持ちます。例えば、構造体リテラルではフィールド名、マップリテラルではマップのキー、配列/スライスリテラルではインデックスとなります。

このコミット以前、パーサーは複合リテラルのキーを適切に解決できないという既知のバグが存在していました。パーサーは、リテラルの型が不明なため、キーが構造体のフィールド名なのか、それともマップのキーや配列のインデックスなのかを判断できません。このため、キーが識別子である場合、パーサーはそれを未解決のままにし、ast.Object を関連付けませんでした。しかし、型チェッカーはパーサーがこの解決を行ったと仮定していたため、エラーメッセージが適切に発行されないという問題がありました。

このコミットは、この問題に対処するため、既存のコメントを修正してこの挙動を明確にし、型チェッカー側にこの解決の責任があることを示すTODOコメントを追加しています。これにより、将来的な修正の指針を提供し、コードの理解を深めることを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語のコンパイラ関連の概念を理解しておく必要があります。

  • exp パッケージ: Go言語の標準ライブラリには、exp というプレフィックスを持つ実験的なパッケージ群が存在します。これらは将来的に標準ライブラリに取り込まれる可能性のある、開発中の機能やプロトタイプを含んでいます。このコミットで触れられている exp/types は、Goの型チェッカーの初期バージョンまたは実験的な実装であったと考えられます。
  • go/parser パッケージ: Go言語のソースコードを解析し、抽象構文木(AST)を生成するためのパッケージです。ソースコードの字句解析(トークン化)と構文解析(AST構築)を担当します。
  • go/ast パッケージ (Abstract Syntax Tree): Go言語のソースコードを解析した結果を表現するデータ構造です。ソースコードの各要素(関数、変数、式など)がノードとして表現され、それらの関係がツリー構造で表されます。コンパイラの型チェックやコード生成のフェーズで利用されます。
  • ast.Expr: ASTにおける「式」を表すインターフェースです。
  • ast.CompositeLit: 複合リテラルを表すASTノードです。Go言語における配列、スライス、マップ、構造体の初期化に使用されます。例: []int{1, 2, 3}, map[string]int{"a": 1}, struct{X int}{X: 1}
  • ast.KeyValueExpr: 複合リテラル内でキーと値のペアを表すASTノードです。例えば、struct{X int}{X: 1}X: 1 や、map[string]int{"a": 1}"a": 1 の部分です。
  • token パッケージ: Go言語の字句解析器が生成するトークン(キーワード、識別子、演算子など)を定義するパッケージです。token.COLON: トークンを表します。
  • 型チェッカー (Type Checker): プログラムの型が正しく使用されているかを検証するコンパイラのフェーズです。変数の型と値の型が一致するか、関数の引数と戻り値の型が正しいかなどをチェックします。
  • パーサー (Parser): ソースコードを読み込み、その構文構造を解析してASTを構築するコンパイラのフェーズです。

技術的詳細

このコミットは、主に3つのファイルにわたるコメントの修正と追加によって、Goコンパイラのパーサーと型チェッカー間の複合リテラルキー解決の責任に関する理解を深めています。

  1. src/pkg/exp/gotype/gotype_test.go の変更:

    • このファイルでは、単なるコメントのタイポ修正が行われています。
    • // Unless there is comment next to the commented out packages,// Unless there is a comment next to the commented out packages, に、
    • // the package does't typecheck due to errors in the shift// the package doesn't typecheck due to errors in the shift に修正されています。
    • これは、テストコード内の説明文の可読性を向上させるための軽微な修正です。
  2. src/pkg/exp/types/expr.go の変更:

    • func (check *checker) rawExpr(...) メソッド内の case *ast.CompositeLit: ブロックに新しいTODOコメントが追加されています。
    • このTODOコメントは、パーサーが複合リテラルのキーを解決できないという既知のバグについて詳細に説明しています。
    • 具体的には、パーサーはリテラルの型を知らないため、キーが構造体のフィールドなのか、マップのキーなのかを判断できないと述べています。
    • その結果、キーが識別子である場合、パーサーはそれを未解決のままにし、ast.Object を関連付けません。
    • 現在の型チェッカーは、パーサーがこの解決を行ったと仮定しているため、適切なエラーメッセージが発行されないという問題が指摘されています。
    • このTODOコメントは、将来的にこの問題を修正するための重要な手がかりとなります。型チェッカーがこの解決の責任を負うべきであることを示唆しています。
  3. src/pkg/go/parser/parser.go の変更:

    • func (p *parser) parseElement(keyOk bool) ast.Expr メソッド内のコメントが修正されています。
    • 変更前: // don't resolve if map key
    • 変更後:
      // The parser cannot resolve a key expression because it does not know
      // what the composite literal type is: if we have an array/slice index
      // or map key, we want to resolve, but if we have a struct field name
      // we cannot. Leave this to type-checking phase.
      
    • この新しいコメントは、パーサーが複合リテラルのキー式を解決できない理由をより詳細に説明しています。パーサーは複合リテラルの型を知らないため、キーが配列/スライスのインデックス、マップのキー、または構造体のフィールド名であるかを区別できません。
    • このため、キーの解決は型チェックフェーズに委ねられるべきであると明記されています。これは exp/types/expr.go に追加されたTODOコメントと整合しています。
    • また、p.resolve(x) の呼び出しに関するコメントも // not a map key から // not a key に変更され、より一般的な表現になっています。これは、キーがマップのキーであるかどうかに限らず、複合リテラルのキー全般の解決がパーサーの責任ではないことを示唆しています。

これらの変更は、Goコンパイラの内部的な動作、特にパーシングと型チェックの間の責任の境界を明確にし、将来的な改善のための道筋を示すものです。

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

diff --git a/src/pkg/exp/gotype/gotype_test.go b/src/pkg/exp/gotype/gotype_test.go
index addce418d3..755336871e 100644
--- a/src/pkg/exp/gotype/gotype_test.go
+++ b/src/pkg/exp/gotype/gotype_test.go
@@ -52,8 +52,8 @@ var tests = []string{
 
 	// directories
 	// Note: Packages that don't typecheck yet are commented out.
-	// Unless there is comment next to the commented out packages,
-	// the package does't typecheck due to errors in the shift
+	// Unless there is a comment next to the commented out packages,
+	// the package doesn't typecheck due to errors in the shift
 	// expression checker.
 	"archive/tar",
 	"archive/zip",
diff --git a/src/pkg/exp/types/expr.go b/src/pkg/exp/types/expr.go
index 1ac0e91db4..6e31323cb6 100644
--- a/src/pkg/exp/types/expr.go
+++ b/src/pkg/exp/types/expr.go
@@ -714,6 +714,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
 		}
 
 	case *ast.CompositeLit:
+		// TODO(gri) Known bug: The parser doesn't resolve composite literal keys
+		//           because it cannot know the type of the literal and therefore
+		//           cannot know if a key is a struct field or not. Consequently,
+		//           if a key is an identifier, it is unresolved and thus has no
+		//           ast.Objects associated with it. At the moment, the respective
+		//           error message is not issued because the type-checker doesn't
+		//           resolve the identifier, and because it assumes that the parser
+		//           did the resolution.
 		typ := hint
 		openArray := false
 		if e.Type != nil {
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 00757e0d75..ad65a7bf21 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -1189,14 +1189,18 @@ func (p *parser) parseElement(keyOk bool) ast.Expr {
 		return p.parseLiteralValue(nil)
 	}
 
-	x := p.checkExpr(p.parseExpr(keyOk)) // don't resolve if map key
+	// The parser cannot resolve a key expression because it does not know
+	// what the composite literal type is: if we have an array/slice index
+	// or map key, we want to resolve, but if we have a struct field name
+	// we cannot. Leave this to type-checking phase.
+	x := p.checkExpr(p.parseExpr(keyOk))
 	if keyOk {
 		if p.tok == token.COLON {
 			colon := p.pos
 			p.next()
 			return &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseElement(false)}
 		}
-		p.resolve(x) // not a map key
+		p.resolve(x) // not a key
 	}
 
 	return x

コアとなるコードの解説

src/pkg/exp/gotype/gotype_test.go

このファイルでは、テストに関するコメントの軽微な修正が行われています。does'tdoesn't に、commenta comment に修正され、英語の文法と可読性が向上しています。これは機能的な変更ではなく、ドキュメンテーションの改善です。

src/pkg/exp/types/expr.go

rawExpr 関数内の ast.CompositeLit を処理する部分に、重要なTODOコメントが追加されました。

		// TODO(gri) Known bug: The parser doesn't resolve composite literal keys
		//           because it cannot know the type of the literal and therefore
		//           cannot know if a key is a struct field or not. Consequently,
		//           if a key is an identifier, it is unresolved and thus has no
		//           ast.Objects associated with it. At the moment, the respective
		//           error message is not issued because the type-checker doesn't
		//           resolve the identifier, and because it assumes that the parser
		//           did the resolution.

このTODOコメントは、Goコンパイラのパーサーにおける既知の制限と、それが型チェッカーに与える影響を明確にしています。

  • パーサーの制限: パーサーは複合リテラルの型(例: struct{...}map[...][]...)を事前に知らないため、リテラル内のキー(例: Field:, key:)が構造体のフィールド名なのか、マップのキーなのか、配列のインデックスなのかを判断できません。
  • 結果: そのため、キーが識別子(変数名など)である場合、パーサーはそれを解決せず、対応する ast.Object(識別子が参照する宣言を表す)を関連付けません。
  • 型チェッカーへの影響: 現在の型チェッカーは、パーサーがこの識別子の解決を行ったと仮定しています。この仮定が誤っているため、本来発行されるべきエラーメッセージ(例: 未定義のフィールド名)が発行されないという問題が発生しています。

このTODOは、この問題を認識し、将来的に型チェッカーがこのキーの解決を担当する必要があることを示唆しています。

src/pkg/go/parser/parser.go

parseElement 関数内のコメントが大幅に修正されました。

-	x := p.checkExpr(p.parseExpr(keyOk)) // don't resolve if map key
+	// The parser cannot resolve a key expression because it does not know
+	// what the composite literal type is: if we have an array/slice index
+	// or map key, we want to resolve, but if we have a struct field name
+	// we cannot. Leave this to type-checking phase.
+	x := p.checkExpr(p.parseExpr(keyOk))
 	if keyOk {
 		if p.tok == token.COLON {
 			colon := p.pos
 			p.next()
 			return &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseElement(false)}
 		}
-		p.resolve(x) // not a map key
+		p.resolve(x) // not a key
 	}
  • 新しいコメントの追加: 以前の簡潔なコメント // don't resolve if map key が、より詳細な説明に置き換えられました。この新しいコメントは、パーサーが複合リテラルのキー式を解決できない理由を明確に述べています。
    • パーサーは複合リテラルの型を知らないため、キーが配列/スライスのインデックス、マップのキー、または構造体のフィールド名であるかを区別できません。
    • このため、キーの解決は「型チェックフェーズに委ねられるべき」であると明示されています。これは exp/types/expr.go のTODOコメントと完全に一致し、パーサーと型チェッカー間の責任分担を明確にしています。
  • p.resolve(x) のコメント修正: // not a map key// not a key に変更されました。これは、キーがマップのキーであるかどうかに限らず、複合リテラルのキー全般の解決がパーサーの責任ではないことをより正確に表現しています。p.resolve(x) は、その式が複合リテラルのキーではない場合にのみ、パーサーが識別子を解決しようとすることを示唆しています。

これらのコード変更は、Goコンパイラの内部ロジックにおけるパーサーと型チェッカーの役割をより正確に反映し、将来的なバグ修正や機能拡張のための基盤を強化するものです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (go.dev)
  • Go言語のソースコード (github.com/golang/go)
  • 抽象構文木 (AST) に関する一般的な情報
  • コンパイラのパーシングと型チェックのフェーズに関する一般的な情報