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

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

このコミットは、test/complit.go というファイルに対する変更です。ファイル名から推測されるように、このファイルはGo言語の複合リテラル(composite literal)のテストケースを含んでいます。複合リテラルは、構造体、配列、スライス、マップなどの複合データ型を初期化するための構文です。このテストファイルは、Go言語のコンパイラが複合リテラルを正しく解析・処理できることを検証するために使用されます。

コミット

このコミットは、以前にコメントアウトされていたバグが修正されたため、そのコードをアンコメントするものです。具体的には、複合リテラルの末尾にカンマ(trailing comma)が存在する場合の挙動に関するバグが修正され、そのテストケースが再び有効化されました。

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

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

元コミット内容

commit a577ea3176387fa150d7a0b766416a2d67237ef2
Author: Rob Pike <r@golang.org>
Date:   Wed Jan 7 10:35:43 2009 -0800

    uncomment a BUG that is now fixed
    
    R=rsc
    DELTA=3  (1 added, 0 deleted, 2 changed)
    OCL=22195
    CL=22207
---
 test/complit.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/test/complit.go b/test/complit.go
index 82e38f41d1..d9b9488519 100644
--- a/test/complit.go
+++ b/test/complit.go
@@ -38,8 +38,9 @@ func main() {
 	if len(a1) != 3 { panic("a1") }
 	a2 := [10]int{1,2,3};
 	if len(a2) != 10 || cap(a2) != 10 { panic("a2") }\n-\t//a3 := [10]int{1,2,3,};  // BUG: trailing commas not allowed
-\t//if len(a3) != 10 || a2[3] != 0 { panic("a3") }\n+\n+\ta3 := [10]int{1,2,3,};\n+\tif len(a3) != 10 || a2[3] != 0 { panic("a3") }\n \n 	var oai []int;\
 \toai = []int{1,2,3};\

変更の背景

この変更の背景には、Go言語の初期開発段階における複合リテラルの構文解析に関するバグがありました。具体的には、配列やスライスなどの複合リテラルを定義する際に、最後の要素の後に余分なカンマ(trailing comma)を記述すると、コンパイラがエラーを発生させる、または正しく解析できないという問題が存在していました。

多くのプログラミング言語では、リストや配列、オブジェクトなどの要素を複数行にわたって記述する際に、最後の要素の後にカンマを置くことが許容されています。これは、コードの可読性を向上させ、要素の追加や削除の際に差分(diff)が最小限になるようにするためによく用いられる慣習です。

Go言語もこの慣習をサポートする方向で設計されていましたが、初期の実装にはこの「末尾カンマ」の処理に不具合があったため、関連するテストケースが一時的にコメントアウトされていました。このコミットは、そのバグが修正されたことを受けて、コメントアウトされていたテストコードを再度有効化し、Goコンパイラが末尾カンマを正しく処理できるようになったことを確認するために行われました。

前提知識の解説

Go言語の複合リテラル (Composite Literals)

Go言語において、複合リテラルは、構造体、配列、スライス、マップといった複合データ型の値を直接初期化するための構文です。これにより、変数を宣言と同時に初期値を設定することができます。

例:

  • 配列リテラル: [3]int{1, 2, 3} (要素数3のint型配列)
  • スライスリテラル: []int{1, 2, 3} (int型スライス)
  • 構造体リテラル: struct{x, y int}{10, 20} (匿名構造体の初期化)
  • マップリテラル: map[string]int{"apple": 1, "banana": 2} (stringキー、int値のマップ)

複合リテラルは、コードの簡潔さと可読性を高める上で非常に重要な機能です。

末尾カンマ (Trailing Comma)

プログラミング言語における末尾カンマとは、リスト、配列、オブジェクト、引数リストなどの要素の最後に記述される、余分なカンマのことです。

例:

// 末尾カンマなし
myArray := []int{
    1,
    2,
    3
}

// 末尾カンマあり
myArrayWithTrailingComma := []int{
    1,
    2,
    3, // ここにカンマがある
}

末尾カンマが許容される主な理由は以下の通りです。

  1. 可読性の向上: 特に要素が複数行にわたる場合、各行が同じ形式で終わるため、視覚的に整列され、読みやすくなります。
  2. 差分(Diff)の最小化: バージョン管理システム(Gitなど)を使用している場合、新しい要素を追加する際に、既存の最後の行を変更する必要がなくなります。末尾カンマがない場合、新しい要素を追加すると、既存の最後の行にカンマを追加し、新しい行に要素を追加するため、2行が変更されたと認識されます。末尾カンマがあれば、新しい行を追加するだけで済むため、差分が1行分となり、変更履歴がよりクリーンになります。
  3. コード生成の容易さ: 自動コード生成ツールがリストを生成する際に、最後の要素だけ特別扱いする必要がなくなるため、実装が簡素化されます。

Go言語では、複合リテラルや引数リストにおいて末尾カンマが許容されています。このコミットは、その機能が初期段階で正しく実装されていなかったバグを修正したことを示しています。

技術的詳細

このコミットが修正したバグは、Goコンパイラの字句解析(lexical analysis)または構文解析(parsing)の段階で発生していたと考えられます。

Goコンパイラは、ソースコードを読み込み、まず字句解析器(lexer/scanner)がコードをトークン(キーワード、識別子、演算子、リテラルなど)のストリームに分解します。次に、構文解析器(parser)がこのトークンストリームを受け取り、Go言語の文法規則に従って抽象構文木(AST: Abstract Syntax Tree)を構築します。

問題となっていたのは、配列リテラル [10]int{1,2,3,} のように、最後の要素 3 の後にカンマ . が続く場合でした。初期のコンパイラ実装では、この末尾カンマが構文エラーとして扱われるか、あるいは配列の要素数を正しく推論できないなどの問題があったと推測されます。

このコミットは、// BUG: trailing commas not allowed というコメントが削除され、該当するコードがアンコメントされたことから、コンパイラが末尾カンマを正しく認識し、構文的に有効なものとして処理できるようになったことを意味します。これは、構文解析器が複合リテラルの定義において、末尾のカンマをオプションとして扱うように修正されたことを示唆しています。

具体的には、Go言語の文法定義(EBNFなど)において、複合リテラルの要素リストの最後にカンマを許容するルールが追加または修正されたと考えられます。これにより、コンパイラは [10]int{1,2,3,}[10]int{1,2,3} と同じ意味で解釈し、配列の長さが正しく 10 となり、初期化されていない要素(この場合は a3[3])がゼロ値(int型の場合は 0)で埋められることを保証します。

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

変更は test/complit.go ファイルの以下の部分です。

--- a/test/complit.go
+++ b/test/complit.go
@@ -38,8 +38,9 @@ func main() {
 	if len(a1) != 3 { panic("a1") }
 	a2 := [10]int{1,2,3};
 	if len(a2) != 10 || cap(a2) != 10 { panic("a2") }\n-\t//a3 := [10]int{1,2,3,};  // BUG: trailing commas not allowed
-\t//if len(a3) != 10 || a2[3] != 0 { panic("a3") }\n+\n+\ta3 := [10]int{1,2,3,};\
+\tif len(a3) != 10 || a2[3] != 0 { panic("a3") }\n \n 	var oai []int;\
 \toai = []int{1,2,3};\

具体的には、以下の2行がコメントアウトから解除され、有効なGoコードとして扱われるようになりました。

  1. - //a3 := [10]int{1,2,3,}; // BUG: trailing commas not allowed が削除され、 + a3 := [10]int{1,2,3,}; が追加されました。
  2. - //if len(a3) != 10 || a2[3] != 0 { panic("a3") } が削除され、 + if len(a3) != 10 || a2[3] != 0 { panic("a3") } が追加されました。

コアとなるコードの解説

変更されたコードは、Go言語の配列複合リテラルにおける末尾カンマの挙動をテストしています。

a3 := [10]int{1,2,3,};

この行では、a3 という名前のint型配列を宣言し、要素数10で初期化しています。初期値として {1,2,3,} が与えられています。ここで重要なのは、3 の後に末尾カンマが存在することです。Go言語の配列リテラルでは、指定された初期値の数よりも配列の要素数が多い場合、残りの要素はそれぞれの型のゼロ値で初期化されます。int型の場合、ゼロ値は 0 です。したがって、a3[1, 2, 3, 0, 0, 0, 0, 0, 0, 0] と初期化されることが期待されます。

if len(a3) != 10 || a2[3] != 0 { panic("a3") }

この行は、a3 配列が正しく初期化されたかを検証するテストです。

  • len(a3) != 10: a3 の長さが10であることを確認します。末尾カンマがあっても、配列の宣言で指定された要素数([10]int)が優先され、長さが10になることを期待しています。
  • a2[3] != 0: これはタイプミスであり、おそらく a3[3] != 0 の間違いです。もし a3[3] であれば、これは配列の4番目の要素(インデックス3)がゼロ値 0 であることを確認しています。これは、初期値が 1, 2, 3 までしか与えられていないため、残りの要素がゼロ値で埋められるというGoの仕様に合致するかを検証するものです。

このコミットによって、これらのテストが有効化されたということは、Goコンパイラが [10]int{1,2,3,} のような末尾カンマを含む複合リテラルを正しく解析し、期待通りの配列(長さ10で、残りの要素がゼロ値)を生成できるようになったことを示しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Gitのコミットログと差分表示
  • プログラミング言語における末尾カンマの一般的な慣習に関する知識
  • Go言語のコンパイラの動作に関する一般的な知識