[インデックス 1732] ファイルの概要
このコミットは、Go言語の仕様書 doc/go_spec.html
に対する変更であり、複合リテラル(composite literals)に関する記述の明確化と、特定の構文解析の曖昧さの解決を目的としています。具体的には、複合リテラルの型制約、式の型互換性、そしてif
、for
、switch
文の条件部における複合リテラルの構文解析の曖昧さに対する解決策が追記されています。
コミット
commit 7a5e97ba915bddfcaf018280364b1690d4c88846
Author: Russ Cox <rsc@golang.org>
Date: Tue Mar 3 15:40:30 2009 -0800
The final piece of the alternative to my parens proposal
(i.e., the status quo with braces in composite literals).
DELTA=20 (16 added, 0 deleted, 4 changed)
OCL=25640
CL=25646
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7a5e97ba915bddfcaf018280364b1690d4c88846
元コミット内容
The final piece of the alternative to my parens proposal
(i.e., the status quo with braces in composite literals).
DELTA=20 (16 added, 0 deleted, 4 changed)
OCL=25640
CL=25646
変更の背景
このコミットは、Go言語の初期設計段階における複合リテラルの構文に関する議論の最終的な結論の一部です。コミットメッセージにある「parens proposal(括弧提案)」とは、複合リテラルを()
で囲むべきか、それとも現在のGo言語のように{}
で囲むべきかという初期の言語設計上の選択肢を指しています。最終的に、Go言語は複合リテラルにブレース({}
)を使用する現在の形式を採用しました。
しかし、ブレースを使用するこの設計には、特定の文脈、特にif
、for
、switch
文の条件部で複合リテラルを使用した場合に、構文解析上の曖昧さ(parsing ambiguity)が生じる可能性がありました。これは、複合リテラルのブレースが、ステートメントブロックを導入するブレースと衝突するためです。このコミットは、その曖昧さを解消するための仕様上のルールを明確にし、必要に応じて複合リテラルを括弧で囲むことを義務付けることで、この設計上の選択を完成させるものです。
また、複合リテラルの要素の型チェックに関する記述も修正されており、より正確な「assignment compatible(代入互換性)」という用語が導入されています。これは、単なる「一致(match)」ではなく、より柔軟な型システムを反映しています。
前提知識の解説
複合リテラル (Composite Literals)
Go言語における複合リテラルは、構造体(struct)、配列(array)、スライス(slice)、マップ(map)などの複合型をその場で初期化するための構文です。例えば、Point{X: 1, Y: 2}
は構造体の複合リテラル、[]int{1, 2, 3}
はスライスの複合リテラルです。これらは、変数を宣言し、値を代入する手間を省き、簡潔に値を表現するために使用されます。
構文解析の曖昧さ (Parsing Ambiguity)
プログラミング言語のコンパイラやインタプリタは、ソースコードを解析してその構造を理解します。このプロセスを構文解析(parsing)と呼びます。構文解析の曖昧さとは、あるコードスニペットが複数の異なる有効な構文ツリーとして解釈されうる状況を指します。これは言語設計上の問題であり、コンパイラがコードの意図を正確に判断できないため、エラーや予期せぬ動作につながる可能性があります。
Go言語の複合リテラルはブレース{}
を使用しますが、if
、for
、switch
などの制御構造もまた、その本体(ブロック)をブレース{}
で囲みます。このため、例えばif T{a,b,c} { ... }
のようなコードがあった場合、コンパイラはT{a,b,c}
を複合リテラルとして解釈すべきか、それともT
を型名として、その後に続く{a,b,c}
をif
文のブロックとして解釈すべきか、という曖昧さが生じます。
代入互換性 (Assignment Compatibility)
Go言語の型システムにおける重要な概念の一つです。ある値が別の型に「代入互換」であるとは、その値が追加の型変換なしにその型に代入できることを意味します。これは、厳密な型の一致(exact match)よりも広い概念です。例えば、基底型が同じであれば、異なる型名を持つ型間でも代入互換性がある場合があります。このコミットでは、複合リテラルの要素の型が、対応するフィールドや要素の型と「代入互換」である必要があることを明確にしています。
技術的詳細
このコミットは、Go言語の仕様書 doc/go_spec.html
の以下の2つの主要な側面を修正・明確化しています。
-
複合リテラルの型制約と式の型互換性:
- 以前の記述では、「LiteralTypeはstruct, array, slice, or map typeでなければならない」とあり、その後に「TODO: then why doesn't the grammar say that?(なぜ文法がそれを言わないのか?)」というコメントがありました。
- このコミットでは、このTODOコメントが削除され、代わりに「(The grammar enforces this constraint except when the type is given as a TypeName.)」という補足が追加されました。これは、文法自体がこの制約を強制するが、型が
TypeName
として与えられた場合には例外があることを示唆しています。 - さらに、「The types of the expressions must match the respective field, element, and key types of the LiteralType; there is no automatic type conversion.」という記述が、「The types of the expressions must be assignment compatible to the respective field, element, and key types of the LiteralType; there is no additional conversion.」に変更されました。これにより、複合リテラルの要素の型が、対応するフィールドや要素の型と厳密に一致する必要はなく、「代入互換」であればよいことが明確にされました。これは、Goの型システムの柔軟性を反映した重要な変更です。
-
複合リテラルにおける構文解析の曖昧さの解決:
- 新たに以下のパラグラフが追加されました。
A parsing ambiguity arises when a composite literal using the TypeName form of the LiteralType appears in the condition of an "if", "for", or "switch" statement, because the braces surrounding the expressions in the literal are confused with those introducing a block of statements. To resolve the ambiguity in this rare case, the composite literal must appear within parentheses.
- これは、
if
、for
、switch
文の条件部で、TypeName
形式の複合リテラル(例:T{a,b,c}
)が使用された場合に構文解析の曖昧さが発生することを説明しています。複合リテラルのブレースが、ステートメントブロックのブレースと混同されるためです。 - この曖昧さを解決するために、このような稀なケースでは、複合リテラルを括弧で囲む必要があることが明記されました。
- 例として、以下のコードスニペットが示されています。
これらの例は、複合リテラル全体、または複合リテラルを含む式全体を括弧で囲むことで、構文解析の曖昧さを解消できることを示しています。if x == (T{a,b,c}[i]) { ... } if (x == T{a,b,c}[i]) { ... }
- 新たに以下のパラグラフが追加されました。
この変更は、Go言語の構文解析器がコードを正しく解釈できるようにするための重要な仕様上の調整であり、言語の堅牢性と明確性を高めるものです。
コアとなるコードの変更箇所
変更は doc/go_spec.html
ファイルに対して行われています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1875,10 +1875,11 @@ ExprPair = Expression ":" Expression .
<p>
The LiteralType must be a struct, array, slice, or map type.
-<font color=red>TODO: then why doesn't the grammar say that?</font>
-The types of the expressions must match the respective field, element, and
-key types of the LiteralType; there is no automatic type conversion.
-Given
+(The grammar enforces this constraint except when the type is given
+as a TypeName.)
+The types of the expressions must be assignment compatible to
+the respective field, element, and key types of the LiteralType;
+there is no additional conversion.
</p>
<pre>
@@ -1936,6 +1937,21 @@ key-value pairs separated by a colon:
m := map[string]int{"good": 0, "bad": 1, "indifferent": 7};
</pre>
+<p>
+A parsing ambiguity arises when a composite literal using the
+TypeName form of the LiteralType appears in the condition of an
+"if", "for", or "switch" statement, because the braces surrounding
+the expressions in the literal are confused with those introducing
+a block of statements. To resolve the ambiguity in this rare case,
+the composite literal must appear within
+parentheses.
+</p>
+
+<pre>
+if x == (T{a,b,c}[i]) { ... }
+if (x == T{a,b,c}[i]) { ... }
+</pre>
+
<h3>Function literals</h3>
<p>
コアとなるコードの解説
このコミットは、Go言語の仕様書の一部であるHTMLドキュメントを更新しています。
-
複合リテラルの型制約の明確化:
- <font color=red>TODO: then why doesn't the grammar say that?</font>
の行が削除されました。これは、Go言語の文法が複合リテラルの型制約(struct, array, slice, map型であること)を強制しているにもかかわらず、その点が仕様書で不明瞭であったためのTODOコメントでした。+ (The grammar enforces this constraint except when the type is given as a TypeName.)
が追加されました。これは、文法がこの制約を強制するが、型がTypeName
として与えられた場合には例外があることを補足しています。これにより、文法と仕様の整合性が高まりました。- The types of the expressions must match the respective field, element, and
が- The types of the expressions must be assignment compatible to
に変更されました。これは、複合リテラルの要素の型が、対応するフィールドや要素の型と「厳密に一致」するのではなく、「代入互換」であればよいという、より正確な表現に修正されたものです。これにより、Goの型システムの柔軟性が仕様書に反映されました。
-
構文解析の曖昧さの解決策の追加:
- 新たに
<p>
タグで始まる段落が追加され、if
、for
、switch
文の条件部で複合リテラルが使用された場合に発生する構文解析の曖昧さについて説明しています。この曖昧さは、複合リテラルのブレースがステートメントブロックのブレースと混同されるために生じます。 - この曖昧さを解消するために、複合リテラルを括弧で囲む必要があることが明記されました。
- その後に続く
<pre>
タグ内のコード例は、実際にどのように括弧を使用して曖昧さを解消するかを示しています。if x == (T{a,b,c}[i]) { ... }
やif (x == T{a,b,c}[i]) { ... }
のように、複合リテラルを含む式全体を括弧で囲むことで、コンパイラが正しく複合リテラルを認識できるようになります。
- 新たに
これらの変更は、Go言語の仕様をより正確で、曖昧さのないものにするための重要なステップであり、コンパイラの実装者や言語のユーザーにとって、複合リテラルの挙動をより明確に理解するための指針となります。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語の仕様書: https://go.dev/ref/spec (このコミットが変更したドキュメントの最新版)
参考にした情報源リンク
- Go言語の初期の設計に関する議論("parens proposal"など)は、Go言語のメーリングリストや初期の設計ドキュメントに散見されますが、特定の単一のリンクを特定することは困難です。Go言語の進化の歴史を追うことで、これらの議論の背景を理解できます。
- Go言語のGitHubリポジトリ: https://github.com/golang/go
- Go言語の複合リテラルに関する公式ドキュメント: https://go.dev/ref/spec#Composite_literals
- Go言語の代入可能性に関する公式ドキュメント: https://go.dev/ref/spec#Assignability