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

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

このコミットは、Go言語のコンパイラ(gc)における構造体リテラル(struct literals)の型省略(type elision)機能の削除に関するものです。具体的には、ネストされた構造体リテラルにおいて、フィールドの型が既知であっても、そのネストされたリテラルの型を明示的に記述する必要があるように変更されました。これにより、コードの曖昧さを排除し、明示性を高めることが目的とされています。

コミット

commit 5cb1c82d961a1b2e70b34492e51cc42292913781
Author: Russ Cox <rsc@golang.org>
Date:   Mon Dec 5 14:22:41 2011 -0500

    gc: remove type elision in struct literals
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/5437136

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

https://github.com/golang/go/commit/5cb1c82d961a1b2e70b34492e51cc42292913781

元コミット内容

このコミットの元のメッセージは「gc: remove type elision in struct literals」です。これは、Goコンパイラ(gc)が構造体リテラルにおける型省略の機能を削除したことを意味します。

変更の背景

Go言語は、その設計思想として「明示性(explicitness)」と「簡潔性(simplicity)」を重視しています。初期のGo言語のコンパイラでは、構造体リテラル内でネストされた構造体を初期化する際に、親の構造体のフィールド型からネストされた構造体の型を推論し、その記述を省略できる「型省略(type elision)」の機能が検討または実装されていた可能性があります。

しかし、この機能は、特に複雑なネストやポインタ型が絡む場合に、コードの可読性を損ねたり、曖昧さを生じさせたりする可能性がありました。例えば、next: {} のような記述があった場合、nextフィールドが構造体型なのか、それとも構造体へのポインタ型なのかによって、その意味合いが大きく変わってしまいます。Go言語の設計者たちは、このような潜在的な曖昧さを排除し、コードの意図をより明確にするために、この型省略の機能を削除することを決定しました。コミット内のsrc/cmd/gc/typecheck.cの変更箇所にあるコメント「// No pushtype allowed here. Tried and rejected.」は、この機能が一度試みられたものの、最終的に採用されなかったという経緯を明確に示しています。

前提知識の解説

このコミットの理解には、以下のGo言語およびコンパイラに関する基本的な知識が必要です。

  • Go言語の構造体 (Structs): 複数のフィールドをまとめた複合データ型。
  • 複合リテラル (Composite Literals): Go言語で構造体、配列、スライス、マップなどの複合型を初期化するための構文。構造体リテラルは StructType{field1: value1, field2: value2} の形式で記述されます。
  • ポインタ (Pointers): 変数のメモリアドレスを保持する型。Goでは & 演算子でアドレスを取得し、* 演算子でポインタが指す値にアクセスします。
  • Goコンパイラ (gc): Go言語の公式コンパイラ。src/cmd/gc ディレクトリにはそのソースコードが含まれています。
  • 型チェック (Type Checking): コンパイラのフェーズの一つで、プログラム中の各式の型が言語の規則に適合しているかを確認するプロセス。src/cmd/gc/typecheck.c はこの型チェックを担当する部分のコードです。
  • 型推論 (Type Inference): プログラマが明示的に型を指定しなくても、コンパイラが文脈から型を自動的に判断する機能。このコミットで削除された「型省略」は、ある種の型推論と見なすことができます。
  • 明示性 (Explicitness): コードの意図が明確であり、読み手が推測する必要がないこと。Go言語の設計哲学の重要な側面です。

技術的詳細

このコミットの技術的な核心は、Goコンパイラの型チェックロジックの変更と、それに伴う言語仕様の厳格化です。

  1. src/cmd/gc/typecheck.c の変更:

    • このファイルはGoコンパイラの型チェッカーの一部です。typecheckcomplit 関数は複合リテラルの型チェックを担当しています。
    • 変更前は、pushtype(r, f->type); という行がありました。これは、複合リテラル内のフィールドrに対して、そのフィールドfの型f->typeを推論して適用しようとしていたことを示唆しています。つまり、ネストされた構造体リテラルの型が省略された場合に、親のフィールド型からその型を推論しようとしていたロジックです。
    • 変更後、この行は削除され、代わりに「// No pushtype allowed here. Tried and rejected.」というコメントが追加されました。これは、この型推論/型省略のメカニズムが意図的に削除されたことを明確に示しています。これにより、コンパイラはネストされた構造体リテラルの型を自動的に推論しなくなりました。
  2. test/complit.go の変更:

    • このテストファイルは、複合リテラルの挙動を検証するためのものです。
    • 変更前は、tl := &T{i: 0, next: {i: 1, ...}} のように、nextフィールドがポインタ型 (*T) であるにもかかわらず、ネストされた構造体リテラル T{...} の前にポインタを示す & や型名 T が省略されていました。コンパイラは next の型が *T であることから、{i: 1, ...}&T{i: 1, ...} であると推論していました。
    • 変更後、この記述は tl := &T{i: 0, next: &T{i: 1, ...}} となり、すべてのネストされた構造体リテラルに対して &T が明示的に追加されました。これは、型省略がもはや許可されないことを反映しています。
  3. test/complit1.go の変更:

    • このテストファイルも複合リテラルの挙動を検証します。
    • 変更前は、_ = &T{i: 0, f: 0, s: "", next: {}} // ok という行がありました。ここで next フィールドが構造体型またはポインタ型である場合、{} はその型のゼロ値を持つリテラルとして解釈され、型が省略されても「OK」とされていました。
    • 変更後、この行は _ = &T{i: 0, f: 0, s: "", next: {}} // ERROR "missing type in composite literal" と変更されました。これは、next: {} のように型を完全に省略した記述が、コンパイラエラー「missing type in composite literal」(複合リテラルで型が不足している)を引き起こすようになったことを示しています。これにより、next: T{} または next: &T{} のように、明示的に型を指定する必要があることが強制されます。

これらの変更は、Go言語の構文解析と型チェックの段階で、複合リテラルの解釈をより厳格にし、曖昧な記述を排除することを目的としています。結果として、Goのコードはより読みやすく、予測可能になります。

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

src/cmd/gc/typecheck.c

--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -2183,7 +2183,7 @@ typecheckcomplit(Node **np)
 			s = f->sym;
 			fielddup(newname(s), hash, nhash);
 			r = l->right;
-			pushtype(r, f->type);
+			// No pushtype allowed here.  Tried and rejected.
 			typecheck(&r, Erv);
 			l->right = assignconv(r, f->type, "field value");
 		}

test/complit.go

--- a/test/complit.go
+++ b/test/complit.go
@@ -58,7 +58,7 @@ func main() {
 	var tp *T
 	tp = &T{0, 7.2, "hi", &t}
 
-	tl := &T{i: 0, next: {i: 1, next: {i: 2, next: {i: 3, next: {i: 4}}}}}
+	tl := &T{i: 0, next: &T{i: 1, next: &T{i: 2, next: &T{i: 3, next: &T{i: 4}}}}}
 	teq(tl, 5)
 
 	a1 := []int{1, 2, 3}

test/complit1.go

--- a/test/complit1.go
+++ b/test/complit1.go
@@ -34,6 +34,6 @@ type T struct {
 
 var (
 	_ = &T{0, 0, "", nil}               // ok
-	_ = &T{i: 0, f: 0, s: "", next: {}} // ok
+	_ = &T{i: 0, f: 0, s: "", next: {}} // ERROR "missing type in composite literal"
 	_ = &T{0, 0, "", {}}                // ERROR "missing type in composite literal"
 )

コアとなるコードの解説

src/cmd/gc/typecheck.c の変更

この変更は、Goコンパイラの型チェックロジックの核心部分にあります。typecheckcomplit 関数は、複合リテラル(この場合は構造体リテラル)の各フィールドの値を型チェックする際に呼び出されます。

  • 削除された行: pushtype(r, f->type);
    • pushtype は、Goコンパイラの内部関数で、式の型を特定の型に「プッシュ」または「強制」する役割を持っていました。この文脈では、ネストされた複合リテラル r に対して、それが属するフィールド f の型 f->type を推論し、適用しようとしていました。これにより、next: {i: 1} のような記述でも、next フィールドの型が *T であれば、&T{i: 1} と解釈されることが可能でした。
  • 追加されたコメント: // No pushtype allowed here. Tried and rejected.
    • このコメントは非常に重要です。これは、この型省略のメカニズムがGo言語の設計段階で検討されたものの、最終的に採用されなかったことを明確に示しています。おそらく、前述の曖昧さや可読性の問題が理由で、より明示的な記述を強制する方針が採られたのでしょう。

この変更により、コンパイラはネストされた構造体リテラルの型を自動的に推論しなくなり、プログラマが明示的に型を指定する必要が生じました。

test/complit.go および test/complit1.go の変更

これらのテストファイルの変更は、上記コンパイラ変更の結果として、Go言語の構文がどのように変化したかを示しています。

  • test/complit.go:

    • 変更前は、next: {i: 1, ...} のように、next フィールドがポインタ型 (*T) であるにもかかわらず、ネストされた構造体リテラルの前に &T が省略されていました。これは、型省略が有効であった場合の記述例です。
    • 変更後は、next: &T{i: 1, ...} のように、すべてのネストされた構造体リテラルに対して &T が明示的に追加されています。これは、型省略が削除されたため、ポインタ型のリテラルを生成するには & と型名を明示する必要があることを示しています。
  • test/complit1.go:

    • 変更前は、next: {} のように、型を完全に省略した空の複合リテラルが「OK」とされていました。これは、next フィールドの型が構造体型であればそのゼロ値、ポインタ型であれば nil とは異なるが、その型のゼロ値を持つ構造体へのポインタ、といった解釈が可能であったことを示唆します。しかし、この記述は非常に曖昧です。
    • 変更後は、この記述が「ERROR "missing type in composite literal"」となるように修正されました。これは、{} のような型を完全に省略した記述はもはや許可されず、next: T{}(構造体型の場合)または next: &T{}(ポインタ型の場合)のように、明示的に型を指定する必要があることを強制します。

これらのテストの変更は、Go言語のコンパイラが、構造体リテラルにおける型省略を完全に廃止し、より明示的な記述を要求するようになったことを実証しています。これにより、Goコードの可読性と保守性が向上し、潜在的なバグや誤解釈のリスクが低減されます。

関連リンク

参考にした情報源リンク

  • Go言語のソースコードリポジトリ: https://github.com/golang/go
  • Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/
    • このコミットに関連するGerritチェンジリスト: https://golang.org/cl/5437136 (これは古いGerritのURL形式であり、現在は go-review.googlesource.com/c/go/+/5437136 のような形式にリダイレクトされる可能性があります。)
  • Go言語の設計に関する議論やメーリングリスト(golang-devなど)のアーカイブ。