[インデックス 19609] ファイルの概要
このコミットは、Go言語の公式仕様書である doc/go_spec.html
の変更に関するものです。具体的には、メソッドのレシーバ宣言に関する文法定義と、その説明文が更新されています。このファイルは、Go言語の構文とセマンティクスを定義する中心的なドキュメントであり、言語の挙動を理解する上で最も権威ある情報源となります。
コミット
commit 187ee2cf2bfd221d8c03daf69a08adb9ede44c84
Author: Robert Griesemer <gri@golang.org>
Date: Tue Jun 24 16:25:09 2014 -0700
spec: receiver declaration is just a parameter declaration
This CL removes the special syntax for method receivers and
makes it just like other parameters. Instead, the crucial
receiver-specific rules (exactly one receiver, receiver type
must be of the form T or *T) are specified verbally instead
of syntactically.
This is a fully backward-compatible (and minor) syntax
relaxation. As a result, the following syntactic restrictions
(which are completely irrelevant) and which were only in place
for receivers are removed:
a) receiver types cannot be parenthesized
b) receiver parameter lists cannot have a trailing comma
The result of this CL is a simplication of the spec and the
implementation, with no impact on existing (or future) code.
Noteworthy:
- gc already permits a trailing comma at the end of a receiver
declaration:
func (recv T,) m() {}
This is technically a bug with the current spec; this CL will
legalize this notation.
- gccgo produces a misleading error when a trailing comma is used:
error: method has multiple receivers
(even though there's only one receiver)
- Compilers and type-checkers won't need to report errors anymore
if receiver types are parenthesized.
Fixes #4496.
LGTM=iant, rsc
R=r, rsc, iant, ken
CC=golang-codereviews
https://golang.org/cl/101500044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/187ee2cf2bfd221d8c03daf69a08adb9ede44c84
元コミット内容
上記の「コミット」セクションに記載されている内容が、このコミットの元の内容です。
変更の背景
このコミットの主な背景は、Go言語の仕様を簡素化し、実装を容易にすることにあります。これまで、メソッドのレシーバ宣言は、通常の関数パラメータ宣言とは異なる特別な構文を持っていました。この「特別扱い」は、レシーバに特有の制約(レシーバは1つのみ、型は T
または *T
の形式である必要があるなど)を構文レベルで強制するために導入されていました。
しかし、このコミットの著者であるRobert Griesemer氏(Go言語の共同設計者の一人)は、これらの制約は構文ではなく、言語のセマンティクス(意味論)として記述する方が適切であると判断しました。これにより、レシーバ宣言を通常のパラメータ宣言と同じ構文に統一することが可能になります。
この変更は、既存のコードや将来のコードに影響を与えない「完全に後方互換性のある(かつ軽微な)構文の緩和」として位置づけられています。具体的には、以下の2つの構文上の制限が撤廃されます。
- レシーバの型を括弧で囲むことができない:以前は
func ((T) recv) m() {}
のようにレシーバの型を括弧で囲むことは許可されていませんでした。 - レシーバのパラメータリストに末尾のカンマを付けることができない:以前は
func (recv T,) m() {}
のようにレシーバ宣言の末尾にカンマを付けることは許可されていませんでした。
これらの制限は「完全に無関係」であり、レシーバにのみ適用されていたため、仕様の不必要な複雑さの原因となっていました。この変更により、仕様がより一貫性のあるものになり、コンパイラの実装も簡素化されます。
コミットメッセージでは、既存のコンパイラ(gc
とgccgo
)の挙動にも言及しており、gc
が既に末尾のカンマを許容していたこと(これは当時の仕様ではバグであった)や、gccgo
が誤解を招くエラーメッセージを出していたこと(末尾のカンマがある場合に「複数のレシーバがある」と報告する)が示されています。この変更は、これらのコンパイラの挙動を合法化し、より正確なエラー報告を可能にするという側面も持っています。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびコンピュータサイエンスの基本的な概念を理解しておく必要があります。
-
Go言語のメソッドとレシーバ:
- Go言語では、関数を型に関連付けることで「メソッド」を定義できます。
- メソッドは、そのメソッドが操作する値(またはポインタ)を「レシーバ」として受け取ります。レシーバは、メソッド名の前に括弧で囲んで宣言されます(例:
func (r MyType) MyMethod() {}
)。 - レシーバの型は、
T
(値レシーバ)または*T
(ポインタレシーバ)の形式でなければなりません。ここでT
は型名です。 - レシーバは常に1つだけです。
-
Go言語の仕様 (Go Language Specification):
- Go言語の挙動は、公式の「Go言語仕様」によって厳密に定義されています。これは、言語の構文、セマンティクス、標準ライブラリの動作などを記述した文書です。
- コンパイラやツールは、この仕様に準拠して実装されます。
- 仕様は、EBNF (Extended Backus-Naur Form) という形式文法を用いて構文を定義しています。
-
EBNF (Extended Backus-Naur Form):
- EBNFは、プログラミング言語の構文を記述するためのメタ言語です。
=
は定義、|
は選択、[]
はオプション、{}
は0回以上の繰り返し、()
はグループ化などを意味します。- このコミットでは、
MethodDecl
(メソッド宣言) とReceiver
(レシーバ) のEBNF定義が変更されています。
-
コンパイラの挙動:
- コンパイラは、ソースコードを解析(字句解析、構文解析、意味解析)して、実行可能なコードを生成します。
- 字句解析: ソースコードをトークン(単語のようなもの)に分割します。
- 構文解析: トークンの並びが言語の文法規則に合致するかどうかをチェックし、抽象構文木 (AST) を構築します。この段階で文法エラーが検出されます。
- 意味解析: 構文的に正しいコードが、意味的にも正しいか(型の一致、変数の宣言など)をチェックします。
- このコミットは、構文解析の段階に影響を与え、特定の構文がもはやエラーとして扱われなくなることを意味します。
-
後方互換性 (Backward Compatibility):
- 新しいバージョンの言語やソフトウェアが、古いバージョンで作成されたコードやデータと互換性があることを指します。
- この変更は、既存のGoコードが引き続き正しくコンパイル・実行されるように設計されています。構文の「緩和」であるため、以前はエラーだったものが許容されるようになるだけで、以前正しかったものがエラーになることはありません。
技術的詳細
このコミットの技術的な核心は、Go言語仕様におけるメソッドレシーバの定義を、より汎用的なパラメータ宣言の定義に統合した点にあります。
変更前は、Go言語仕様のEBNFにおいて、MethodDecl
(メソッド宣言)は以下のように定義されていました。
MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
BaseTypeName = identifier .
ここで、Receiver
は (
で始まり )
で終わる特別な構文を持ち、その内部にはオプションの識別子、オプションの *
、そして BaseTypeName
が含まれると明示されていました。この定義は、レシーバの型が T
または *T
の形式であること、およびレシーバが1つだけであることといった制約を構文レベルで表現しようとしていました。また、この構文定義により、レシーバの型を括弧で囲んだり、レシーバ宣言の末尾にカンマを付けたりすることが構文的に禁止されていました。
このコミットによる変更後、MethodDecl
と Receiver
のEBNF定義は以下のようになります。
MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
Receiver = Parameters .
ここで注目すべきは、Receiver
が Parameters
と定義されている点です。Parameters
はGo言語仕様において、通常の関数パラメータリストを定義するために使用されるEBNFルールです。これにより、レシーバ宣言は、通常の関数パラメータ宣言と全く同じ構文規則に従うことになります。
この構文の変更に伴い、レシーバに特有の制約(「レシーバは1つだけであること」「レシーバの型は T
または *T
の形式であること」)は、EBNFによる構文定義から削除され、代わりに仕様書のテキスト記述によって「口頭で(verbally)」指定されるようになりました。
具体的には、仕様書の該当箇所が以下のように変更されています。
変更前:
The receiver type must be of the form <code>T</code> or <code>*T</code> where
<code>T</code> is a type name. The type denoted by <code>T</code> is called
変更後:
The receiver is specified via an extra parameter section preceeding the method
name. That parameter section must declare a single parameter, the receiver.
Its type must be of the form <code>T</code> or <code>*T</code> (possibly using
parentheses) where <code>T</code> is a type name. The type denoted by <code>T</code> is called
このテキストの変更により、レシーバがメソッド名の前に来る「追加のパラメータセクション」として扱われること、そのセクションが「単一のパラメータ、すなわちレシーバ」を宣言しなければならないこと、そしてその型が「T
または *T
の形式であること(括弧を使用することも可能)」が明確に記述されています。
この変更の技術的な影響は以下の通りです。
- 仕様の簡素化: レシーバに特有の複雑なEBNFルールが不要になり、仕様全体がより一貫性のあるものになります。
- コンパイラの実装の簡素化: コンパイラは、レシーバ宣言を解析する際に、通常のパラメータ宣言と同じ構文解析ロジックを再利用できるようになります。これにより、コードベースの重複が減り、メンテナンスが容易になります。
- 構文の緩和:
- レシーバの型を括弧で囲むことの許可: 以前は構文エラーだった
func ((T) recv) m() {}
のような記述が、構文的には有効になります。ただし、意味的には依然としてレシーバの型がT
または*T
であるという制約は残ります。 - レシーバ宣言の末尾カンマの許可:
func (recv T,) m() {}
のような記述が合法化されます。これは、Goの他のパラメータリストや構造体リテラルなどで末尾カンマが許容されているのと一貫性があります。
- レシーバの型を括弧で囲むことの許可: 以前は構文エラーだった
- 既存のコンパイラの挙動の正規化:
gc
(Goの公式コンパイラ)が既にレシーバ宣言の末尾カンマを許容していたのは、この変更前の仕様では「バグ」とされていましたが、このコミットによりその挙動が「合法」となります。gccgo
(GCCベースのGoコンパイラ)が末尾カンマに対して「method has multiple receivers」という誤解を招くエラーを出していた問題も、この変更により解消されることが期待されます。コンパイラはもはやこれを構文エラーとして報告する必要がなくなります。
この変更は、Go言語の設計哲学である「シンプルさ」と「一貫性」を追求した結果と言えます。
コアとなるコードの変更箇所
変更は doc/go_spec.html
ファイルに対して行われています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Version of May 28, 2014",
+ "Subtitle": "Version of June 24, 2014",
"Path": "/ref/spec"
}-->
@@ -2029,13 +2029,14 @@ and associates the method with the receiver's <i>base type</i>.
<pre class=\"ebnf\">\n MethodDecl = \"func\" Receiver MethodName ( Function | Signature ) .\n-Receiver = \"(\" [ identifier ] [ \"*\" ] BaseTypeName \")\" .\n-BaseTypeName = identifier .\n+Receiver = Parameters .\n </pre>\n \n <p>\n-The receiver type must be of the form <code>T</code> or <code>*T</code> where\n-<code>T</code> is a type name. The type denoted by <code>T</code> is called\n+The receiver is specified via an extra parameter section preceeding the method\n+name. That parameter section must declare a single parameter, the receiver.\n+Its type must be of the form <code>T</code> or <code>*T</code> (possibly using\n+parentheses) where <code>T</code> is a type name. The type denoted by <code>T</code> is called\n the receiver <i>base type</i>; it must not be a pointer or interface type and\n it must be declared in the same package as the method.\n The method is said to be <i>bound</i> to the base type and the method name\n```
## コアとなるコードの解説
このコミットにおけるコアとなるコードの変更は、Go言語仕様のEBNF定義と、それに対応するテキスト説明の修正です。
1. **EBNF定義の変更**:
* `Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" .` という行が削除され、`BaseTypeName = identifier .` も削除されました。
* 代わりに `Receiver = Parameters .` という新しい行が追加されました。
* これは、レシーバの構文が、通常の関数パラメータの構文 (`Parameters` ルールで定義される) と全く同じになったことを意味します。これにより、レシーバ宣言の特別な構文が撤廃され、より汎用的なパラメータ宣言のルールに統合されました。
2. **テキスト説明の変更**:
* 変更前は「レシーバの型は `T` または `*T` の形式でなければならない」と簡潔に記述されていました。
* 変更後は、レシーバが「メソッド名の前にある追加のパラメータセクション」として指定されること、そのパラメータセクションが「単一のパラメータ、すなわちレシーバ」を宣言しなければならないこと、そしてその型が「`T` または `*T` の形式であること(括弧を使用することも可能)」がより詳細に記述されています。
* このテキストの変更は、構文的な制約がEBNFから削除された分、意味的な制約を明確に補足するためのものです。特に「括弧を使用することも可能」という記述は、構文緩和の結果を反映しています。
これらの変更により、Go言語の仕様はよりシンプルで一貫性のあるものになり、レシーバの構文に関する曖昧さが解消されました。
## 関連リンク
* Go言語の変更履歴 (Go ChangeLog): [https://golang.org/doc/devel/release.html](https://golang.org/doc/devel/release.html) (このコミットが含まれるGoのバージョンリリース情報が確認できる可能性があります)
* Go言語の公式仕様書: [https://golang.org/ref/spec](https://golang.org/ref/spec) (このコミットによって変更されたドキュメントの最新版)
* Go Gerrit Change-ID: `https://golang.org/cl/101500044` (Goプロジェクトのコードレビューシステムにおけるこの変更のページ)
* 関連するGo Issue: `#4496` (このコミットが修正したとされる問題のトラッキング番号。直接のGitHubリンクは見つかりませんでしたが、GoのIssueトラッカーで検索可能です。)
## 参考にした情報源リンク
* Go言語の公式ドキュメント (特に仕様書): [https://golang.org/doc/](https://golang.org/doc/)
* EBNF (Extended Backus-Naur Form) の一般的な情報源 (例: Wikipediaなど)
* コンパイラ設計に関する一般的な知識 (字句解析、構文解析など)