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

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

このコミットは、Go言語のコンパイラ(gc)において、関数(func)とマップ(map)の直接的な比較を除去し、これらの型をnilとの比較のみに制限するという重要な変更を行いました。この変更は2011年11月13日にRuss Coxによって実装され、Go 1.0リリースに向けた言語仕様の確定において重要な意味を持っています。

コミット

  • 作成者: Russ Cox rsc@golang.org
  • 日付: 2011年11月13日 22:58:08 -0500
  • コミットメッセージ: "gc: remove func, map compare"
  • コードレビュー: R=ken, ken; CC=golang-dev

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

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

元コミット内容

このコミットでは以下の主要な変更が行われました:

  1. 関数とマップの比較制限: 関数とマップの直接的な比較(f == gm1 == m2)を禁止し、nilとの比較のみを許可
  2. 型チェック機能の強化: スイッチ文での型チェックを改善し、適切なエラーメッセージを提供
  3. マップキーの制限: 関数とマップをマップのキーとして使用することを禁止
  4. テストコードの更新: 新しい制限に合わせて既存のテストコードを修正

変更されたファイル:

  • src/cmd/gc/align.c: 比較可能な型の定義を更新
  • src/cmd/gc/subr.c: マップキーの検証を追加
  • src/cmd/gc/swt.c: スイッチ文の型チェックを改善
  • src/cmd/gc/typecheck.c: 型チェック機能を強化
  • 複数のテストファイル: 新しい制限に合わせて更新

変更の背景

このコミットは、Go言語の設計哲学である「シンプルさ」と「明確性」を実現するために行われました。主な背景には以下の要因があります:

  1. Go 1.0リリースに向けた言語仕様の確定: 2011年当時、Go言語はまだ実験的な段階にあり、言語仕様を安定化させる必要がありました

  2. 型システムの一貫性: 他の言語(特にC)からの影響を受けていた初期のGo言語仕様を、より一貫性のあるものに改善する必要がありました

  3. パフォーマンスと実装の複雑性: 関数やマップの比較は実装が複雑で、パフォーマンスに影響を与える可能性がありました

  4. 明確なセマンティクス: 関数やマップの比較における意味論的な曖昧さを排除し、プログラマにとって予測可能な動作を保証する必要がありました

前提知識の解説

Go言語における比較演算子

Go言語では、型の比較可能性(comparability)が厳密に定義されています。比較可能な型は以下の通りです:

  • 基本型: bool、数値型(int、float、complex)、string
  • ポインタ型: *T形式の型
  • チャネル型: chan T形式の型
  • インターフェース型: 適切な条件下でのみ比較可能
  • 構造体型: すべてのフィールドが比較可能な場合のみ
  • 配列型: 要素の型が比較可能で、固定長の場合のみ

比較不可能な型

以下の型は比較演算子(==!=)を使用できません:

  • スライス型: []T形式の型
  • マップ型: map[K]V形式の型
  • 関数型: func(...)形式の型
  • 比較不可能なフィールドを含む構造体

nil との比較

比較不可能な型でも、nilとの比較は特別に許可されています:

var f func()
var m map[int]int
var s []int

// これらは有効
if f == nil { /* ... */ }
if m == nil { /* ... */ }
if s == nil { /* ... */ }

// これらは無効(コンパイルエラー)
// if f == f { /* ... */ }
// if m == m { /* ... */ }
// if s == s { /* ... */ }

技術的詳細

コンパイラの実装変更

このコミットでは、Goコンパイラの型チェック機能に以下の変更が加えられました:

  1. 型チェック段階での制限: typecheck.cにおいて、関数とマップの比較を検出し、適切なエラーメッセージを生成

  2. スイッチ文の改善: swt.cにおいて、スイッチ文での型チェックを強化し、nilとの比較のみを許可

  3. マップキーの検証: subr.cにおいて、マップのキーとして使用できない型を検出

エラーメッセージの改善

新しい実装では、以下のような具体的なエラーメッセージが提供されます:

invalid operation: f == g (func can only be compared to nil)
invalid operation: m == n (map can only be compared to nil)
invalid case m in switch (can only compare map x to nil)

最適化への影響

この変更により、以下の最適化が可能になりました:

  1. クロージャの最適化: 環境変数を使用しないクロージャを単一の実装として最適化
  2. メモリ効率の向上: 比較のための追加情報を保持する必要がなくなった
  3. 実行時オーバーヘッドの削減: 複雑な比較処理の実行時コストを削減

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

1. align.c の変更

// 変更前
okforeq[TMAP] = 1;
okforeq[TFUNC] = 1;
okforeq[TARRAY] = 1;

// 変更後
okforeq[TMAP] = 1;    // nil only; refined in typecheck
okforeq[TFUNC] = 1;   // nil only; refined in typecheck
okforeq[TARRAY] = 1;  // nil slice only; refined in typecheck

2. subr.c の変更

// マップキーの検証に関数とマップを追加
switch(key->etype) {
case TARRAY:
case TSTRUCT:
case TMAP:     // 新規追加
case TFUNC:    // 新規追加
    yyerror("invalid map key type %T", key);
    break;
}

3. typecheck.c の変更

// 関数とマップの比較チェックを追加
if(l->type->etype == TMAP && !isnil(l) && !isnil(r)) {
    yyerror("invalid operation: %N (map can only be compared to nil)", n);
    goto error;
}
if(l->type->etype == TFUNC && !isnil(l) && !isnil(r)) {
    yyerror("invalid operation: %N (func can only be compared to nil)", n);
    goto error;
}

コアとなるコードの解説

型チェック機能の強化

最も重要な変更はtypecheck.cにおける型チェック機能の強化です。この変更により、コンパイラは以下の処理を行います:

  1. 比較演算子の検出: ==および!=演算子を検出
  2. オペランドの型チェック: 左右のオペランドの型を確認
  3. nilチェック: 一方または両方のオペランドがnilかどうかを確認
  4. エラー生成: 不正な比較の場合、適切なエラーメッセージを生成

スイッチ文での型チェック

swt.cの変更により、スイッチ文における型チェックが強化されました:

if(t) {
    if(!okforeq[t->etype] || isfixedarray(t))
        yyerror("cannot switch on %lN", n->ntest);
    else if(t->etype == TARRAY)
        nilonly = "slice";
    else if(t->etype == TFUNC)
        nilonly = "func";
    else if(t->etype == TMAP)
        nilonly = "map";
}

この実装により、スイッチ文での不正な比較を検出し、適切なエラーメッセージを提供できるようになりました。

マップキーの制限

subr.cの変更により、マップのキーとして使用できない型(関数とマップ)を検出できるようになりました。これにより、以下のようなコードがコンパイルエラーになります:

// コンパイルエラー
var m1 map[func()]int
var m2 map[map[int]int]string

関連リンク

参考にした情報源リンク

このコミットは、Go言語が現在の洗練された型システムを持つに至った重要なステップの一つです。関数とマップの比較を制限することで、言語の一貫性と明確性が大幅に向上し、現在のGo言語の堅牢な型システムの基盤となっています。