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

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

このコミットは、Go言語の仕様書(doc/go_spec.html)における複合リテラルのパースに関する曖昧さの記述を修正するものです。具体的には、ifforswitchといったキーワードの直後に複合リテラルが出現する際のパースの競合について、より明確な表現に改訂されています。

コミット

commit a36b5b99cca834aa4968d7aaa81f5e5565f7c461
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Feb 27 08:57:30 2014 -0800

    spec: slightly rephrased wording on parsing ambiguity for composite literals
    
    Fixes #4482.
    
    LGTM=r
    R=r, iant, rsc, ken
    CC=golang-codereviews
    https://golang.org/cl/69020045

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

https://github.com/golang/go/commit/a36b5b99cca834aa4968d7aaa81f5e5565f7c461

元コミット内容

spec: slightly rephrased wording on parsing ambiguity for composite literals

Fixes #4482.

LGTM=r
R=r, iant, rsc, ken
CC=golang-codereviews
https://golang.org/cl/69020045

変更の背景

Go言語では、複合リテラル(Composite Literal)を使用して構造体、配列、スライス、マップなどの複合型の値を簡潔に初期化できます。しかし、特定の文脈、特にifforswitchといった制御フロー文のキーワードの直後に複合リテラルが出現する場合に、パースの曖昧さが発生する可能性がありました。

この曖昧さは、複合リテラルの開始を示す{(左ブレース)が、制御フロー文のブロックの開始を示す{と衝突するために生じます。Goのパーサーは、文脈によっては複合リテラルの開始ブレースを、意図せず制御フロー文のブロック開始ブレースとして解釈してしまう可能性がありました。

この問題はGoのIssue #4482として報告されており、このコミットはその問題を解決するために、Go言語仕様書の該当箇所の記述をより正確かつ明確にする目的で行われました。以前の記述では、この曖昧さが発生する条件が十分に詳細に説明されておらず、開発者が混乱する可能性がありました。この変更は、仕様の記述を改善し、Go言語の構文解析の挙動をより正確に反映させることを目的としています。

前提知識の解説

複合リテラル (Composite Literal)

Go言語における複合リテラルは、構造体、配列、スライス、マップなどの複合型の値をその場で作成し、初期化するための構文です。基本的な形式はTypeName{ElementList}です。

例:

  • 構造体リテラル: Person{Name: "Alice", Age: 30}
  • 配列リテラル: [3]int{1, 2, 3}
  • スライスリテラル: []string{"apple", "banana"}
  • マップリテラル: map[string]int{"one": 1, "two": 2}

複合リテラルは、コードの簡潔性と可読性を高めるために広く使用されます。

Go言語のパース (Parsing)

Goコンパイラは、ソースコードを読み込み、その構文構造を解析して抽象構文木(AST)を構築します。このプロセスをパースと呼びます。パースは、字句解析(トークン化)と構文解析の2つの主要な段階に分かれます。

  • 字句解析 (Lexical Analysis): ソースコードをトークン(キーワード、識別子、演算子、リテラルなど)のストリームに分割します。
  • 構文解析 (Syntactic Analysis): トークンのストリームを文法規則に従って解析し、プログラムの階層的な構造(AST)を構築します。

パースの過程で、同じトークン列が複数の有効な構文構造に解釈されうる場合、パースの曖昧さ(parsing ambiguity)が発生します。Goコンパイラは、このような曖昧さを解決するために特定の規則に従います。

制御フロー文 (if, for, switch)

Go言語の主要な制御フロー文は以下の通りです。

  • if: 条件に基づいてコードブロックを実行します。 if condition { ... }
  • for: ループを定義します。Goにはwhileループがなく、forがその役割も果たします。 for initialization; condition; post { ... } for condition { ... } for { ... }
  • switch: 式の値に基づいて複数のケースの中から一つを実行します。 switch expression { case value: ... default: ... }

これらの文は、その本体を囲むためにブレース{}を使用します。このブレースが複合リテラルのブレースと衝突する可能性が、今回の問題の核心です。

技術的詳細

Go言語のパーサーは、文脈自由文法(Context-Free Grammar)に基づいてソースコードを解析します。しかし、特定の構文構造においては、文脈に依存した解決が必要となる場合があります。今回の複合リテラルのパースの曖昧さは、まさにその一例です。

問題となるシナリオは、ifforswitchといったキーワードの直後に、TypeName{...}形式の複合リテラルが続く場合です。例えば、以下のようなコードを考えます。

type S struct {
    x int
}

func main() {
    if S{x: 1}.x == 1 { // ここで曖昧さが発生する可能性
        // ...
    }
}

この例では、ifキーワードの直後にS{x: 1}という複合リテラルが来ています。パーサーは、S{x: 1}の開始ブレース{を見たときに、それがS型の複合リテラルの開始なのか、それともif文のブロックの開始なのかを判断する必要があります。

Go言語の文法規則では、if文の条件式はブレースで囲まれません。しかし、複合リテラルはブレースで囲まれます。もしパーサーが、S{x: 1}{if文のブロック開始と誤って解釈してしまうと、構文エラーや意図しないパース結果につながります。

この曖昧さを解決するために、Go言語の仕様では、このような状況で複合リテラルを使用する際には、複合リテラル全体を括弧で囲むことを要求しています。

if (S{x: 1}).x == 1 { // 括弧で囲むことで曖昧さを解消
    // ...
}

今回のコミットでは、この曖昧さが発生する条件について、より厳密な記述に修正されました。特に重要な変更点は、曖昧さが発生する条件として「複合リテラルが括弧、角括弧、または波括弧で囲まれていない場合」という条件が追加されたことです。これにより、パーサーが複合リテラルの開始ブレースを制御フロー文のブロック開始と誤解釈する具体的な状況が明確化されました。

この修正は、Go言語のパーサーの挙動をより正確に反映し、開発者がこのような特殊なケースでどのようにコードを記述すべきかを明確に理解できるようにすることを目的としています。

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

--- doc/go_spec.html
+++ doc/go_spec.html
@@ -1,6 +1,6 @@
 <!--{
  	"Title": "The Go Programming Language Specification",
-	"Subtitle": "Version of Feb 25, 2014",
+	"Subtitle": "Version of Feb 27, 2014",
  	"Path": "/ref/spec"
 }-->
 
@@ -2267,8 +2267,6 @@ Similarly, elements that are addresses of composite literals may elide
 the <code>&amp;T</code> when the element type is <code>*T</code>.
 </p>
 
-
-
 <pre>
 [...]Point{{1.5, -3.5}, {0, 0}}   // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
 [][]int{{1, 2, 3}, {4, 5}}        // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
@@ -2278,13 +2276,13 @@ the <code>&amp;T</code> when the element type is <code>*T</code>.
 
 <p>
 A parsing ambiguity arises when a composite literal using the
-TypeName form of the LiteralType appears between the
-<a href="#Keywords">keyword</a> and the opening brace of the block of an
-"if", "for", or "switch" statement, because the braces surrounding
-the expressions in the literal are confused with those introducing
-the block of statements. To resolve the ambiguity in this rare case,
-the composite literal must appear within
-parentheses.
+TypeName form of the LiteralType appears as an operand between the
+<a href="#Keywords">keyword</a> and the opening brace of the block
+of an "if", "for", or "switch" statement, and the composite literal
+is not enclosed in parentheses, square brackets, or curly braces.
+In this rare case, the opening brace of the literal is erroneously parsed
+as the one introducing the block of statements. To resolve the ambiguity,
+the composite literal must appear within parentheses.
 </p>
 
 <pre>

コアとなるコードの解説

このコミットは、Go言語の仕様書であるdoc/go_spec.htmlファイルを変更しています。

  1. 日付の更新:

    -	"Subtitle": "Version of Feb 25, 2014",
    +	"Subtitle": "Version of Feb 27, 2014",
    

    これは単に仕様書のバージョン日付をコミット日に合わせて更新したものです。

  2. パースの曖昧さに関する記述の修正: この部分が本コミットの主要な変更点です。

    変更前:

    A parsing ambiguity arises when a composite literal using the
    TypeName form of the LiteralType appears between the
    <a href="#Keywords">keyword</a> and the opening brace of the block of an
    "if", "for", or "switch" statement, because the braces surrounding
    the expressions in the literal are confused with those introducing
    the block of statements. To resolve the ambiguity in this rare case,
    the composite literal must appear within
    parentheses.
    

    変更前の記述では、「複合リテラルのブレースが、ステートメントブロックのブレースと混同されるため」という理由で曖昧さが発生すると説明されていました。そして、解決策として「複合リテラルを括弧で囲む必要がある」とされていました。

    変更後:

    A parsing ambiguity arises when a composite literal using the
    TypeName form of the LiteralType appears as an operand between the
    <a href="#Keywords">keyword</a> and the opening brace of the block
    of an "if", "for", or "switch" statement, and the composite literal
    is not enclosed in parentheses, square brackets, or curly braces.
    In this rare case, the opening brace of the literal is erroneously parsed
    as the one introducing the block of statements. To resolve the ambiguity,
    the composite literal must appear within parentheses.
    

    変更後の記述では、曖昧さが発生する条件がより詳細かつ厳密に定義されています。

    • appears as an operand between the keyword and the opening brace of the block of an "if", "for", or "switch" statement これは、複合リテラルが制御フロー文のキーワードと、そのブロックの開始ブレースの間に「オペランドとして」出現する場合、という文脈を明確にしています。

    • and the composite literal is not enclosed in parentheses, square brackets, or curly braces. この部分が最も重要な追加です。曖昧さが発生するのは、複合リテラルが既に括弧 ()、角括弧 []、または波括弧 {} のいずれかで囲まれていない場合に限られることを明示しています。これは、例えばスライスリテラルやマップリテラルが既に角括弧や波括弧で囲まれている場合、この曖昧さは発生しないことを示唆しています。

    • In this rare case, the opening brace of the literal is erroneously parsed as the one introducing the block of statements. 曖昧さの具体的なメカニズムとして、「リテラルの開始ブレースが、誤ってステートメントブロックの開始ブレースとしてパースされる」と明確に述べています。

    • To resolve the ambiguity, the composite literal must appear within parentheses. 解決策は変更前と同じく、複合リテラルを括弧で囲むことですが、その必要性がより具体的な条件の下で説明されています。

この修正により、Go言語のパーサーが複合リテラルをどのように解釈するか、そしてどのような場合に開発者が明示的な括弧を使用してパースの曖昧さを解消する必要があるかが、より正確かつ包括的に説明されるようになりました。これは、Go言語の仕様の正確性を高め、開発者の理解を深める上で重要な改善です。

関連リンク

参考にした情報源リンク