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

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

このコミットは、Go言語のパーサーにおいて、セレクタがメソッド式として扱われるように変更を加えるものです。具体的には、go/parserパッケージ内のparser.goファイルが修正され、セレクタの解析時にcheckExprの代わりにcheckExprOrTypeを使用することで、型アサーションや型変換のコンテキストでメソッド式が正しく解析されるようになります。これにより、(struct {*T}).m(interface {T}).mのような構文が有効になります。

コミット

commit 340918a8a3c89d4f6a39bc1b0e07b648893ac66d
Author: Robert Griesemer <gri@golang.org>
Date:   Mon Jul 29 13:52:15 2013 -0700

    go/parser: selectors may be method expressions
    
    R=adonovan
    CC=golang-dev
    https://golang.org/cl/12062043
---
 src/pkg/go/parser/parser.go     | 2 +-\
 src/pkg/go/parser/short_test.go | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 42a1c5e57c..a0ac8d7131 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -1408,7 +1408,7 @@ L:
 			}\n \t\t\tswitch p.tok {\n \t\t\tcase token.IDENT:\n-\t\t\t\tx = p.parseSelector(p.checkExpr(x))\n+\t\t\t\tx = p.parseSelector(p.checkExprOrType(x))\n \t\t\tcase token.LPAREN:\n \t\t\t\tx = p.parseTypeAssertion(p.checkExpr(x))\n \t\t\tdefault:\ndiff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go
index a581319e05..0ef0c560c4 100644
--- a/src/pkg/go/parser/short_test.go
+++ b/src/pkg/go/parser/short_test.go
@@ -34,6 +34,7 @@ var valids = []string{\n \t`package p; func f() { switch ; {} };`,\n \t`package p; func f() { for _ = range \"foo\" + \"bar\" {} };`,\n \t`package p; func f() { var s []int; g(s[:], s[i:], s[:j], s[i:j], s[i:j:k], s[:j:k]) };`,\n+\t`package p; var ( _ = (struct {*T}).m; _ = (interface {T}).m )`,\n }\n \n func TestValid(t *testing.T) {\n```

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

[https://github.com/golang/go/commit/340918a8a3c89d4f6a39bc1b0e07b648893ac66d](https://github.com/golang/go/commit/340918a8a3c89d4f6a39bc1b0e07b648893ac66d)

## 元コミット内容

go/parser: selectors may be method expressions

R=adonovan CC=golang-dev https://golang.org/cl/12062043


## 変更の背景

Go言語では、セレクタ式 `X.Y` は、`X`がパッケージ名でない場合、`X`のフィールドまたはメソッドにアクセスするために使用されます。このコミット以前は、Goパーサーがセレクタを解析する際に、`X`が常に式(`expression`)であると仮定していました。しかし、Go言語の仕様では、メソッド式(Method Expressions)という概念が存在します。これは、型自体からメソッドを呼び出す構文で、例えば`(T).M`のように記述されます。ここで`T`は型であり、`M`はその型のメソッドです。

このコミットの背景には、パーサーがこのようなメソッド式を正しく認識し、抽象構文木(AST)を生成できるようにする必要があったことが挙げられます。特に、構造体のポインタ型やインターフェース型に対するメソッド式が、既存のパーサーのロジックでは適切に扱えないケースがありました。この変更により、Go言語の文法規則に沿ったより柔軟な解析が可能になり、言語の表現力が向上します。

## 前提知識の解説

このコミットを理解するためには、以下のGo言語の概念とパーサーの基本的な動作について理解しておく必要があります。

### 1. Go言語のセレクタ (Selectors)

Go言語におけるセレクタは、`X.Y`という形式で記述される式です。
*   `X`がパッケージ名の場合、`X.Y`はパッケージ`X`からエクスポートされた識別子`Y`を参照する「修飾識別子 (qualified identifier)」となります。
*   `X`がパッケージ名でない場合、`X.Y`は`X`のフィールドまたはメソッドにアクセスする「セレクタ式 (selector expression)」となります。

### 2. メソッド式 (Method Expressions)

メソッド式は、Go言語の特殊なセレクタの一種で、レシーバのインスタンスではなく、型自体からメソッドを参照する構文です。例えば、`func (t T) M() {}`というメソッドがある場合、そのメソッド式は`T.M`となります。このメソッド式は、関数値として扱うことができ、後からレシーバのインスタンスを引数として渡して呼び出すことができます。

例:
```go
type MyType struct {
    value int
}

func (m MyType) GetValue() int {
    return m.value
}

func main() {
    // メソッド式を変数に代入
    f := MyType.GetValue
    
    // 後からレシーバを渡して呼び出す
    m := MyType{value: 10}
    fmt.Println(f(m)) // 出力: 10
}

3. Goパーサー (go/parserパッケージ)

go/parserパッケージは、Go言語のソースコードを解析し、抽象構文木(AST: Abstract Syntax Tree)を生成するためのGo標準ライブラリの一部です。パーサーは、字句解析器(lexer)からトークンを受け取り、Go言語の文法規則に従ってそれらを構造化されたASTに変換します。

  • parser.go: パーサーの主要なロジックが実装されているファイルです。Go言語の様々な構文要素(式、文、宣言など)を解析するための関数が含まれています。
  • checkExpr(x): パーサー内部の関数で、引数xが有効な式(expression)であることを確認します。
  • checkExprOrType(x): このコミットで導入された、または既存のcheckExprの代わりに使われるようになった関数で、引数xが式または型(type)のいずれかであることを確認します。メソッド式の場合、セレクタの左側(Xの部分)は型であるため、この関数が必要になります。

4. 抽象構文木 (AST)

ASTは、ソースコードの構造を木構造で表現したものです。パーサーはソースコードをASTに変換し、コンパイラやツールがコードの意味を理解し、分析、変換、コード生成などを行うための基盤となります。

技術的詳細

このコミットの技術的な核心は、go/parserパッケージ内のparser.goファイルにおけるparseSelector関数の呼び出し箇所の変更です。

変更前:

x = p.parseSelector(p.checkExpr(x))

変更後:

x = p.parseSelector(p.checkExprOrType(x))

この変更は、セレクタの左側(Xの部分)が必ずしも「式」であるとは限らず、「型」である可能性もあるというGo言語のメソッド式の文法規則に対応するためのものです。

  • p.checkExpr(x): 従来のパーサーのロジックでは、セレクタの左側xが常に有効な「式」であることを期待していました。これは、obj.fieldobj.method()のような一般的なセレクタには適切です。
  • p.checkExprOrType(x): メソッド式、例えば(struct {*T}).m(interface {T}).mの場合、セレクタの左側(struct {*T})(interface {T})は「型」であり、「式」ではありません。checkExprOrType関数は、引数xが式であるか、または型であるかのいずれかであることを許容します。これにより、パーサーはメソッド式の構文を正しく解析し、ASTを構築できるようになります。

この変更により、Go言語のパーサーは、より広範な有効なGo構文を認識できるようになり、特に型に直接関連付けられたメソッドの参照(メソッド式)を適切に処理できるようになりました。これは、Go言語の型システムとメソッドの柔軟性を最大限に活用するために不可欠な修正です。

short_test.goに追加されたテストケースは、この新しい挙動を検証するためのものです。

`package p; var ( _ = (struct {*T}).m; _ = (interface {T}).m )`,

このテストケースは、匿名構造体やインターフェース型に対するメソッド式が正しく解析されることを確認しています。

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

diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 42a1c5e57c..a0ac8d7131 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -1408,7 +1408,7 @@ L:
 			}\n \t\t\tswitch p.tok {\n \t\t\tcase token.IDENT:\n-\t\t\t\tx = p.parseSelector(p.checkExpr(x))\n+\t\t\t\tx = p.parseSelector(p.checkExprOrType(x))\n \t\t\tcase token.LPAREN:\n \t\t\t\tx = p.parseTypeAssertion(p.checkExpr(x))\n \t\t\tdefault:\
diff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go
index a581319e05..0ef0c560c4 100644
--- a/src/pkg/go/parser/short_test.go
+++ b/src/pkg/go/parser/short_test.go
@@ -34,6 +34,7 @@ var valids = []string{\n \t`package p; func f() { switch ; {} };`,\n \t`package p; func f() { for _ = range \"foo\" + \"bar\" {} };`,\n \t`package p; func f() { var s []int; g(s[:], s[i:], s[:j], s[i:j], s[i:j:k], s[:j:k]) };`,\n+\t`package p; var ( _ = (struct {*T}).m; _ = (interface {T}).m )`,\n }\n \n func TestValid(t *testing.T) {\

コアとなるコードの解説

このコミットの主要な変更は、src/pkg/go/parser/parser.goファイルのL:ラベルが付いたセクション、具体的にはセレクタを解析する部分にあります。

元のコードでは、token.IDENT(識別子)が続く場合に、p.parseSelector関数を呼び出す前にp.checkExpr(x)を使用して、セレクタの左側xが有効な「式」であることを確認していました。これは、object.fieldobject.method()のような一般的なセレクタのケースでは問題ありません。

しかし、Go言語には「メソッド式」という構文があります。これは、型自体からメソッドを参照するもので、例えば(struct {*T}).m(interface {T}).mのように、セレクタの左側が「型」である場合があります。checkExprは「式」のみを期待するため、このような「型」を左側にとるメソッド式を正しく解析できませんでした。

変更後のコードでは、p.checkExpr(x)p.checkExprOrType(x)に置き換えられています。

  • checkExprOrType関数は、引数xが「式」であるか、または「型」であるかのいずれかであることを許容します。
  • これにより、パーサーは、セレクタの左側が式である場合(従来のケース)と、型である場合(メソッド式のケース)の両方を適切に処理できるようになります。

この修正によって、Go言語のパーサーは、言語仕様で定義されているメソッド式を完全にサポートし、より正確なASTを生成できるようになりました。

また、src/pkg/go/parser/short_test.goに新しいテストケースが追加されています。

`package p; var ( _ = (struct {*T}).m; _ = (interface {T}).m )`,

このテストケースは、匿名構造体struct {*T}やインターフェース型interface {T}に対してメソッドセレクタ.mを使用する構文が、パーサーによってエラーなく受け入れられることを検証しています。これは、このコミットによって可能になった新しい有効な構文の例です。

関連リンク

参考にした情報源リンク