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

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

コミット

commit 02370f6760ca41a9a2061a1398e6a7af64bf541a
Author: Andrew Wilkins <axwalk@gmail.com>
Date:   Sun Jan 6 18:08:58 2013 -0800

    go/types: Set Signature.Recv for imported types

    R=golang-dev, bradfitz, gri
    CC=golang-dev
    https://golang.org/cl/7065046

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

https://github.com/golang/go/commit/02370f6760ca41a9a2061a1398e6a7af64bf541a

元コミット内容

このコミットは、Go言語のgo/typesパッケージにおいて、インポートされた型(imported types)のメソッドシグネチャ(Signature)にレシーバ情報(Recv)が正しく設定されるように修正するものです。具体的には、gcimporter.goファイル内のparseFunc関数とparseMethodDecl関数が変更され、メソッドのレシーバ情報がSignature構造体に適切に紐付けられるようになります。

変更の背景

Go言語のコンパイラやツール群は、プログラムの型情報を正確に解析し、利用する必要があります。go/typesパッケージは、Goプログラムの型システムを表現し、型チェックを行うための基盤を提供します。このパッケージは、ソースコードから直接型情報を構築するだけでなく、コンパイル済みのパッケージから型情報をインポートする機能も持っています。

このコミットが行われた2013年当時、Go言語の型システムはまだ進化の途上にありました。特に、外部パッケージからインポートされる型やそのメソッドに関する情報が、内部で完全に再現されていないケースが存在した可能性があります。メソッドはGo言語の重要な特徴であり、レシーバ(メソッドが紐付けられる型)の情報は、そのメソッドがどの型に属し、どのように呼び出されるかを特定するために不可欠です。

このコミットの背景には、インポートされた型に対するメソッドのレシーバ情報がgo/typesパッケージ内で欠落している、あるいは不正確であるという問題があったと考えられます。これにより、型チェックの誤りや、リフレクション、コード生成ツールなど、型情報に依存する他のツールが正しく機能しないといった問題が発生する可能性がありました。この修正は、go/typesパッケージがインポートされた型についても、そのメソッドのレシーバ情報を完全に保持し、正確な型チェックと分析を可能にすることを目的としています。

前提知識の解説

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

  1. Go言語の型システム: Goは静的型付け言語であり、すべての変数には型があります。型は、値がどのような操作をサポートするかを定義します。
  2. メソッドとレシーバ: Go言語のメソッドは、特定の型に関連付けられた関数です。メソッドの定義には「レシーバ」が含まれ、これはメソッドが操作するインスタンス(値レシーバ)またはポインタ(ポインタレシーバ)を示します。 例: func (r MyType) MyMethod() {} (値レシーバ) 例: func (r *MyType) MyMethod() {} (ポインタレシーバ)
  3. go/typesパッケージ: Go言語の標準ライブラリの一部であり、Goプログラムの型システムを表現するためのAPIを提供します。このパッケージは、コンパイラ、リンター、IDEなどのツールがGoコードの型情報を解析し、操作するために使用されます。
    • types.Signature: 関数のシグネチャ(引数、戻り値、レシーバ)を表す構造体です。メソッドの場合、Recvフィールドにレシーバの型情報が格納されます。
    • types.Object: Goプログラム内の名前付きエンティティ(変数、関数、型など)を表すインターフェースです。
  4. go/astパッケージ: Go言語のソースコードの抽象構文木(AST)を表現するためのAPIを提供します。コンパイラやツールは、まずソースコードをASTにパースし、そのASTを解析して型情報を抽出します。
  5. gcimporter.go: このファイルは、Goコンパイラ(gc)によって生成されたコンパイル済みパッケージの型情報をインポートするロジックを扱います。Goのパッケージは、コンパイルされると、その型情報がバイナリ形式で保存され、他のパッケージがその情報を利用できるようにエクスポートされます。gcimporterは、このバイナリ形式の型情報を読み込み、go/typesパッケージの内部表現に変換する役割を担います。
  6. ast.Scope: 抽象構文木(AST)におけるスコープ(名前の可視範囲)を表します。変数の宣言や参照解決に用いられます。
  7. ast.Fun: go/astパッケージで関数を表す定数です。

技術的詳細

このコミットの技術的詳細な変更点は、src/pkg/go/types/gcimporter.goファイル内のparseFunc関数とparseMethodDecl関数の連携にあります。

以前のparseFunc関数は、関数のシグネチャをパースし、その結果をobj.Typeに直接代入していました。しかし、メソッドの場合、レシーバの情報はシグネチャの一部としてパースされるものの、その情報がSignature構造体のRecvフィールドに明示的に設定されるプロセスが不足していた可能性があります。特に、インポートされた型の場合、この情報が正しく伝達されないことが問題でした。

今回の変更では、以下の点が改善されています。

  1. parseFuncの戻り値の変更:

    • 変更前: func (p *gcParser) parseFunc(scope *ast.Scope, name string) (戻り値なし)
    • 変更後: func (p *gcParser) parseFunc(scope *ast.Scope, name string) *Signature (パースしたSignatureを返す) この変更により、parseFuncがパースしたSignatureオブジェクトを呼び出し元に返すことができるようになりました。
  2. parseMethodDeclでのSignature.Recvの設定: parseMethodDecl関数は、メソッドの宣言をパースする役割を担っています。メソッドはレシーバを持つため、この関数内でレシーバ情報が取得されます(recv変数)。 変更前は、p.parseFunc(scope, name)が呼び出されていましたが、parseFuncSignatureを返さないため、パースされたシグネチャオブジェクトに直接アクセスしてRecvフィールドを設定することができませんでした。 変更後、parseFunc*Signatureを返すようになったため、sig := p.parseFunc(scope, name)としてそのシグネチャを受け取り、sig.Recv = recvという行が追加されました。これにより、メソッドのレシーバ情報(recv)が、パースされたシグネチャオブジェクト(sig)のRecvフィールドに明示的に設定されるようになりました。

この修正により、gcimporterが外部パッケージから型情報をインポートする際に、メソッドのレシーバ情報がgo/types.Signature構造体内に正確に保持されるようになります。これは、Go言語の型システムが、インポートされた型に対しても完全かつ正確な情報を提供するために不可欠な変更です。

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

--- a/src/pkg/go/types/gcimporter.go
+++ b/src/pkg/go/types/gcimporter.go
@@ -766,9 +766,10 @@ func (p *gcParser) parseVarDecl() {
 // Func = Signature [ Body ] .
 // Body = "{" ... "}" .
 //
-func (p *gcParser) parseFunc(scope *ast.Scope, name string) {
+func (p *gcParser) parseFunc(scope *ast.Scope, name string) *Signature {
 	obj := p.declare(scope, ast.Fun, name)
-	obj.Type = p.parseSignature()
+	sig := p.parseSignature()
+	obj.Type = sig
 	if p.tok == '{' {
 		p.next()
 		for i := 1; i > 0; p.next() {
@@ -780,6 +781,7 @@ func (p *gcParser) parseFunc(scope *ast.Scope, name string) {
 			}
 		}
 	}
+	return sig
 }
 
 // MethodDecl = "func" Receiver Name Func .
@@ -809,7 +811,8 @@ func (p *gcParser) parseMethodDecl() {
 
 	// declare method in base type scope
 	name := p.parseName() // unexported method names in imports are qualified with their package.
-	p.parseFunc(scope, name)
+	sig := p.parseFunc(scope, name)
+	sig.Recv = recv
 }
 
 // FuncDecl = "func" ExportedName Func .

コアとなるコードの解説

  • func (p *gcParser) parseFunc(scope *ast.Scope, name string) *Signature の変更:

    • この関数は、Goの関数(またはメソッドの本体部分)のシグネチャをパースする役割を担っています。
    • 変更前は戻り値がありませんでしたが、変更後はパースした*Signatureオブジェクトを返すようになりました。
    • sig := p.parseSignature(): 実際のシグネチャのパース処理を行い、その結果をsig変数に格納します。
    • obj.Type = sig: パースされたシグネチャを、対応するオブジェクト(obj)の型として設定します。
    • return sig: パースされたSignatureオブジェクトを呼び出し元に返します。これにより、呼び出し元(特にparseMethodDecl)がこのシグネチャオブジェクトにアクセスできるようになります。
  • func (p *gcParser) parseMethodDecl() の変更:

    • この関数は、Goのメソッド宣言(レシーバを含む)をパースする役割を担っています。
    • name := p.parseName(): メソッドの名前をパースします。
    • sig := p.parseFunc(scope, name): 変更されたparseFuncを呼び出し、メソッドのシグネチャをパースし、その結果の*Signatureオブジェクトをsig変数に受け取ります。
    • sig.Recv = recv: ここが最も重要な変更点です。parseMethodDecl内で既に取得されているレシーバ情報(recv)を、parseFuncから返されたSignatureオブジェクトのRecvフィールドに明示的に設定しています。これにより、インポートされたメソッドのシグネチャに、そのレシーバ情報が正しく関連付けられることが保証されます。

この一連の変更により、gcimporterがGoのバイナリパッケージから型情報を読み込む際に、メソッドのレシーバ情報がgo/typesパッケージの内部表現に正確にマッピングされるようになり、型チェックやその他の型情報に依存する処理の正確性が向上します。

関連リンク

参考にした情報源リンク

  • https://golang.org/cl/7065046 (Go Change-ID for this commit)
  • Go言語のソースコード(src/pkg/go/types/gcimporter.go
  • Go言語の型システムに関する一般的な知識
  • Go言語のコンパイラとツールの内部構造に関する情報(一般的な知識に基づく)
  • go/typesパッケージのSignature構造体に関する情報# [インデックス 14812] ファイルの概要

コミット

commit 02370f6760ca41a9a2061a1398e6a7af64bf541a
Author: Andrew Wilkins <axwalk@gmail.com>
Date:   Sun Jan 6 18:08:58 2013 -0800

    go/types: Set Signature.Recv for imported types

    R=golang-dev, bradfitz, gri
    CC=golang-dev
    https://golang.org/cl/7065046

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

https://github.com/golang/go/commit/02370f6760ca41a9a2061a1398e6a7af64bf541a

元コミット内容

このコミットは、Go言語のgo/typesパッケージにおいて、インポートされた型(imported types)のメソッドシグネチャ(Signature)にレシーバ情報(Recv)が正しく設定されるように修正するものです。具体的には、gcimporter.goファイル内のparseFunc関数とparseMethodDecl関数が変更され、メソッドのレシーバ情報がSignature構造体に適切に紐付けられるようになります。

変更の背景

Go言語のコンパイラやツール群は、プログラムの型情報を正確に解析し、利用する必要があります。go/typesパッケージは、Goプログラムの型システムを表現し、型チェックを行うための基盤を提供します。このパッケージは、ソースコードから直接型情報を構築するだけでなく、コンパイル済みのパッケージから型情報をインポートする機能も持っています。

このコミットが行われた2013年当時、Go言語の型システムはまだ進化の途上にありました。特に、外部パッケージからインポートされる型やそのメソッドに関する情報が、内部で完全に再現されていないケースが存在した可能性があります。メソッドはGo言語の重要な特徴であり、レシーバ(メソッドが紐付けられる型)の情報は、そのメソッドがどの型に属し、どのように呼び出されるかを特定するために不可欠です。

このコミットの背景には、インポートされた型に対するメソッドのレシーバ情報がgo/typesパッケージ内で欠落している、あるいは不正確であるという問題があったと考えられます。これにより、型チェックの誤りや、リフレクション、コード生成ツールなど、型情報に依存する他のツールが正しく機能しないといった問題が発生する可能性がありました。この修正は、go/typesパッケージがインポートされた型についても、そのメソッドのレシーバ情報を完全に保持し、正確な型チェックと分析を可能にすることを目的としています。

前提知識の解説

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

  1. Go言語の型システム: Goは静的型付け言語であり、すべての変数には型があります。型は、値がどのような操作をサポートするかを定義します。
  2. メソッドとレシーバ: Go言語のメソッドは、特定の型に関連付けられた関数です。メソッドの定義には「レシーバ」が含まれ、これはメソッドが操作するインスタンス(値レシーバ)またはポインタ(ポインタレシーバ)を示します。 例: func (r MyType) MyMethod() {} (値レシーバ) 例: func (r *MyType) MyMethod() {} (ポインタレシーバ)
  3. go/typesパッケージ: Go言語の標準ライブラリの一部であり、Goプログラムの型システムを表現するためのAPIを提供します。このパッケージは、コンパイラ、リンター、IDEなどのツールがGoコードの型情報を解析し、操作するために使用されます。
    • types.Signature: 関数のシグネチャ(引数、戻り値、レシーバ)を表す構造体です。メソッドの場合、Recvフィールドにレシーバの型情報が格納されます。
    • types.Object: Goプログラム内の名前付きエンティティ(変数、関数、型など)を表すインターフェースです。
  4. go/astパッケージ: Go言語のソースコードの抽象構文木(AST)を表現するためのAPIを提供します。コンパイラやツールは、まずソースコードをASTにパースし、そのASTを解析して型情報を抽出します。
  5. gcimporter.go: このファイルは、Goコンパイラ(gc)によって生成されたコンパイル済みパッケージの型情報をインポートするロジックを扱います。Goのパッケージは、コンパイルされると、その型情報がバイナリ形式で保存され、他のパッケージがその情報を利用できるようにエクスポートされます。gcimporterは、このバイナリ形式の型情報を読み込み、go/typesパッケージの内部表現に変換する役割を担います。
  6. ast.Scope: 抽象構文木(AST)におけるスコープ(名前の可視範囲)を表します。変数の宣言や参照解決に用いられます。
  7. ast.Fun: go/astパッケージで関数を表す定数です。

技術的詳細

このコミットの技術的詳細な変更点は、src/pkg/go/types/gcimporter.goファイル内のparseFunc関数とparseMethodDecl関数の連携にあります。

以前のparseFunc関数は、関数のシグネチャをパースし、その結果をobj.Typeに直接代入していました。しかし、メソッドの場合、レシーバの情報はシグネチャの一部としてパースされるものの、その情報がSignature構造体のRecvフィールドに明示的に設定されるプロセスが不足していた可能性があります。特に、インポートされた型の場合、この情報が正しく伝達されないことが問題でした。

今回の変更では、以下の点が改善されています。

  1. parseFuncの戻り値の変更:

    • 変更前: func (p *gcParser) parseFunc(scope *ast.Scope, name string) (戻り値なし)
    • 変更後: func (p *gcParser) parseFunc(scope *ast.Scope, name string) *Signature (パースしたSignatureを返す) この変更により、parseFuncがパースしたSignatureオブジェクトを呼び出し元に返すことができるようになりました。
  2. parseMethodDeclでのSignature.Recvの設定: parseMethodDecl関数は、メソッドの宣言をパースする役割を担っています。メソッドはレシーバを持つため、この関数内でレシーバ情報が取得されます(recv変数)。 変更前は、p.parseFunc(scope, name)が呼び出されていましたが、parseFuncSignatureを返さないため、パースされたシグネチャオブジェクトに直接アクセスしてRecvフィールドを設定することができませんでした。 変更後、parseFunc*Signatureを返すようになったため、sig := p.parseFunc(scope, name)としてそのシグネチャを受け取り、sig.Recv = recvという行が追加されました。これにより、メソッドのレシーバ情報(recv)が、パースされたシグネチャオブジェクト(sig)のRecvフィールドに明示的に設定されるようになりました。

この修正により、gcimporterが外部パッケージから型情報をインポートする際に、メソッドのレシーバ情報がgo/types.Signature構造体内に正確に保持されるようになります。これは、Go言語の型システムが、インポートされた型に対しても完全かつ正確な情報を提供するために不可欠な変更です。

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

--- a/src/pkg/go/types/gcimporter.go
+++ b/src/pkg/go/types/gcimporter.go
@@ -766,9 +766,10 @@ func (p *gcParser) parseVarDecl() {
 // Func = Signature [ Body ] .
 // Body = "{" ... "}" .
 //
-func (p *gcParser) parseFunc(scope *ast.Scope, name string) {
+func (p *gcParser) parseFunc(scope *ast.Scope, name string) *Signature {
 	obj := p.declare(scope, ast.Fun, name)
-	obj.Type = p.parseSignature()
+	sig := p.parseSignature()
+	obj.Type = sig
 	if p.tok == '{' {
 		p.next()
 		for i := 1; i > 0; p.next() {
@@ -780,6 +781,7 @@ func (p *gcParser) parseFunc(scope *ast.Scope, name string) {
 			}
 		}
 	}
+	return sig
 }
 
 // MethodDecl = "func" Receiver Name Func .
@@ -809,7 +811,8 @@ func (p *gcParser) parseMethodDecl() {
 
 	// declare method in base type scope
 	name := p.parseName() // unexported method names in imports are qualified with their package.
-	p.parseFunc(scope, name)
+	sig := p.parseFunc(scope, name)
+	sig.Recv = recv
 }
 
 // FuncDecl = "func" ExportedName Func .

コアとなるコードの解説

  • func (p *gcParser) parseFunc(scope *ast.Scope, name string) *Signature の変更:

    • この関数は、Goの関数(またはメソッドの本体部分)のシグネチャをパースする役割を担っています。
    • 変更前は戻り値がありませんでしたが、変更後はパースした*Signatureオブジェクトを返すようになりました。
    • sig := p.parseSignature(): 実際のシグネチャのパース処理を行い、その結果をsig変数に格納します。
    • obj.Type = sig: パースされたシグネチャを、対応するオブジェクト(obj)の型として設定します。
    • return sig: パースされたSignatureオブジェクトを呼び出し元に返します。これにより、呼び出し元(特にparseMethodDecl)がこのシグネチャオブジェクトにアクセスできるようになります。
  • func (p *gcParser) parseMethodDecl() の変更:

    • この関数は、Goのメソッド宣言(レシーバを含む)をパースする役割を担っています。
    • name := p.parseName(): メソッドの名前をパースします。
    • sig := p.parseFunc(scope, name): 変更されたparseFuncを呼び出し、メソッドのシグネチャをパースし、その結果の*Signatureオブジェクトをsig変数に受け取ります。
    • sig.Recv = recv: ここが最も重要な変更点です。parseMethodDecl内で既に取得されているレシーバ情報(recv)を、parseFuncから返されたSignatureオブジェクトのRecvフィールドに明示的に設定しています。これにより、インポートされたメソッドのシグネチャに、そのレシーバ情報が正しく関連付けられることが保証されます。

この一連の変更により、gcimporterがGoのバイナリパッケージから型情報を読み込む際に、メソッドのレシーバ情報がgo/typesパッケージの内部表現に正確にマッピングされるようになり、型チェックやその他の型情報に依存する処理の正確性が向上します。

関連リンク

参考にした情報源リンク

  • https://golang.org/cl/7065046 (Go Change-ID for this commit)
  • Go言語のソースコード(src/pkg/go/types/gcimporter.go
  • Go言語の型システムに関する一般的な知識
  • Go言語のコンパイラとツールの内部構造に関する情報(一般的な知識に基づく)
  • go/typesパッケージのSignature構造体に関する情報