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

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

このコミットは、Go言語の初期開発段階において、gcコンパイラ(当時のGoコンパイラ)が構造体からインターフェースへの変換を一時的に拒否するように変更したものです。これは、特定の型(特に単純な型やポインタ型ではない構造体)がインターフェースとして使用される際の未実装の挙動に対する暫定的な対応として導入されました。

コミット

commit 73653841af0ec9da0037e412d07ecff24d014494
Author: Russ Cox <rsc@golang.org>
Date:   Thu Dec 11 15:56:13 2008 -0800

    reject struct to interface conversion for now

    R=ken
    OCL=21007
    CL=21007

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

https://github.com/golang/go/commit/73653841af0ec9da0037e412d07ecff24d014494

元コミット内容

reject struct to interface conversion for now

R=ken
OCL=21007
CL=21007

変更の背景

Go言語は、その設計思想として「インターフェースの暗黙的な実装」を採用しています。これは、型がインターフェースで宣言されたすべてのメソッドを実装していれば、明示的な宣言なしにそのインターフェースを満たすと見なされるというものです。しかし、Go言語の初期段階では、コンパイラ(特にgcコンパイラ)がすべてのケースにおいて構造体からインターフェースへの変換を完全にサポートしていませんでした。

このコミットは、特に単純な型(issimple)やポインタ型(isptr)ではない構造体がインターフェースに変換される際に、コンパイラがまだその変換ロジックを完全に実装していないため、一時的にエラーを発生させてコンパイルを拒否するように変更されました。これは、未定義の動作やクラッシュを防ぐための安全策であり、将来的な完全な実装に向けた一時的な措置と考えられます。

前提知識の解説

Go言語のインターフェース

Go言語のインターフェースは、メソッドのシグネチャの集合を定義する型です。Goのインターフェースの最大の特徴は、型がインターフェースを「暗黙的に」実装するという点です。つまり、ある型がインターフェースで定義されたすべてのメソッドを持っている場合、その型はそのインターフェースを満たしていると見なされます。JavaやC#のような言語に見られるimplementsキーワードはGoには存在しません。

gcコンパイラ

gcは、Go言語の公式コンパイラツールチェーンの一部であり、Goプログラムをコンパイルするために使用されます。Go言語の初期から存在し、6g(amd64向け)、8g(x86向け)、5g(ARM向け)といったアーキテクチャ固有のコンパイラを指すこともありました。このコミットが作成された2008年当時、Go言語はまだ開発の初期段階にあり、コンパイラも継続的に機能追加や改善が行われていました。

型変換と型アサーション

Go言語では、異なる型間で値を変換する際に型変換(Type Conversion)や型アサーション(Type Assertion)が用いられます。インターフェースへの変換は、特に具体的な型がインターフェース型として扱われる際に発生します。

src/cmd/gc/walk.c

このファイルは、Goコンパイラのgcにおける「ウォーカー(walker)」または「ツリーウォーカー(tree walker)」の一部です。コンパイラのフロントエンドが生成した抽象構文木(AST)を走査し、型チェック、最適化、コード生成の前段階の変換など、様々な処理を行います。isandss関数は、型間の互換性や変換の可能性をチェックする役割を担っていると推測されます。

技術的詳細

このコミットは、src/cmd/gc/walk.cファイル内のisandss関数に修正を加えています。この関数は、左辺の型(lt)と右辺のノード(r)の間の関係を評価し、型変換の種類(例: I2I - インターフェースからインターフェース、T2I - 具体的な型からインターフェース)を返します。

変更前のコードでは、isnilinter(lt)(左辺がnilインターフェースであるか)またはismethod(rt) != T(右辺がメソッドを持たない型であるか)の場合にT2I(具体的な型からインターフェースへの変換)を返していました。

変更後のコードでは、isnilinter(lt)が真の場合に、さらに詳細なチェックが追加されています。 具体的には、!issimple[rt->etype] && !isptr[rt->etype]という条件が追加されました。

  • rt->etype: 右辺のノードの基本型(element type)。
  • issimple[rt->etype]: rt->etypeが単純な型(例: 整数、浮動小数点数、ブール値など)であるかを示すフラグ。
  • isptr[rt->etype]: rt->etypeがポインタ型であるかを示すフラグ。

この条件が真、つまり右辺の型が「単純な型でもなく、ポインタ型でもない」場合に、yyerror("using %T as interface is unimplemented", rt);というエラーメッセージを出力し、コンパイルを中断します。これは、構造体のような複合型がインターフェースに変換されるケースで、まだコンパイラがその変換ロジックを完全に実装していないことを示唆しています。

この変更は、Go言語のインターフェース値の内部表現と密接に関連しています。Goのインターフェース値は通常、2つのワードで表現されます。1つは基となる具体的なデータへのポインタ、もう1つは「itable」(インターフェーステーブル)または型記述構造体へのポインタです。itableは、具体的な型のメソッドをインターフェースのメソッドにマッピングするためにランタイムで計算されます。初期のコンパイラでは、すべての種類の具体的な型(特に複雑な構造体)からインターフェースへの変換におけるitableの生成やメモリ管理がまだ完全に整備されていなかった可能性があります。

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

src/cmd/gc/walk.cファイルのisandss関数内の以下の部分が変更されました。

--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -2733,7 +2733,12 @@ isandss(Type *lt, Node *r)\n  				return I2I;\n  			return Inone;\n  		}\n-	if(isnilinter(lt) || ismethod(rt) != T)\n+	if(isnilinter(lt)) {\n+	if(!issimple[rt->etype] && !isptr[rt->etype])\n+	yyerror("using %T as interface is unimplemented", rt);\n+	return T2I;\n+	}\n+	if(ismethod(rt) != T)\n  		return T2I;\n  	return Inone;\n  }\n```

## コアとなるコードの解説

変更の核心は、`isnilinter(lt)`(左辺がnilインターフェースである)という条件分岐の中に、新たなチェックが追加された点です。

**変更前:**
```c
if(isnilinter(lt) || ismethod(rt) != T)
    return T2I;

この行は、左辺がnilインターフェースであるか、または右辺がメソッドを持たない型である場合に、具体的な型からインターフェースへの変換(T2I)を許可していました。

変更後:

if(isnilinter(lt)) {
    if(!issimple[rt->etype] && !isptr[rt->etype])
        yyerror("using %T as interface is unimplemented", rt);
    return T2I;
}
if(ismethod(rt) != T)
    return T2I;

この変更により、左辺がnilインターフェースである場合(if(isnilinter(lt))ブロック内)に、以下の追加チェックが行われるようになりました。

  1. !issimple[rt->etype]: 右辺の型が単純な型ではない。
  2. !isptr[rt->etype]: 右辺の型がポインタ型ではない。

この両方の条件が真である場合、つまり「単純な型でもポインタ型でもない型(例: 構造体)をインターフェースとして使用しようとしている」場合に、yyerror関数が呼び出され、「%Tをインターフェースとして使用することは未実装です」というエラーメッセージが表示されます。これにより、コンパイラは未サポートの型変換を検出し、コンパイル時にエラーを発生させることで、ランタイムでの予期せぬ挙動を防ぎます。

この修正は、Go言語のインターフェースが持つ「暗黙的な実装」という強力な機能の裏側で、コンパイラがどのように型変換を処理し、その実装が段階的に進められていたかを示す良い例です。初期段階では、すべての型がインターフェースに変換できるわけではなく、特に複雑な型についてはコンパイラの実装が追いついていなかったため、このような一時的な制限が設けられました。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語の初期開発段階において、gcコンパイラ(当時のGoコンパイラ)が構造体からインターフェースへの変換を一時的に拒否するように変更したものです。これは、特定の型(特に単純な型やポインタ型ではない構造体)がインターフェースとして使用される際の未実装の挙動に対する暫定的な対応として導入されました。

コミット

commit 73653841af0ec9da0037e412d07ecff24d014494
Author: Russ Cox <rsc@golang.org>
Date:   Thu Dec 11 15:56:13 2008 -0800

    reject struct to interface conversion for now

    R=ken
    OCL=21007
    CL=21007

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

https://github.com/golang/go/commit/73653841af0ec9da0037e412d07ecff24d014494

元コミット内容

reject struct to interface conversion for now

R=ken
OCL=21007
CL=21007

変更の背景

Go言語は、その設計思想として「インターフェースの暗黙的な実装」を採用しています。これは、型がインターフェースで宣言されたすべてのメソッドを実装していれば、明示的な宣言なしにそのインターフェースを満たすと見なされるというものです。しかし、Go言語の初期段階では、コンパイラ(特にgcコンパイラ)がすべてのケースにおいて構造体からインターフェースへの変換を完全にサポートしていませんでした。

このコミットは、特に単純な型(issimple)やポインタ型(isptr)ではない構造体がインターフェースに変換される際に、コンパイラがまだその変換ロジックを完全に実装していないため、一時的にエラーを発生させてコンパイルを拒否するように変更されました。これは、未定義の動作やクラッシュを防ぐための安全策であり、将来的な完全な実装に向けた一時的な措置と考えられます。

前提知識の解説

Go言語のインターフェース

Go言語のインターフェースは、メソッドのシグネチャの集合を定義する型です。Goのインターフェースの最大の特徴は、型がインターフェースを「暗黙的に」実装するという点です。つまり、ある型がインターフェースで定義されたすべてのメソッドを持っている場合、その型はそのインターフェースを満たしていると見なされます。JavaやC#のような言語に見られるimplementsキーワードはGoには存在しません。

gcコンパイラ

gcは、Go言語の公式コンパイラツールチェーンの一部であり、Goプログラムをコンパイルするために使用されます。Go言語の初期から存在し、6g(amd64向け)、8g(x86向け)、5g(ARM向け)といったアーキテクチャ固有のコンパイラを指すこともありました。このコミットが作成された2008年当時、Go言語はまだ開発の初期段階にあり、コンパイラも継続的に機能追加や改善が行われていました。

型変換と型アサーション

Go言語では、異なる型間で値を変換する際に型変換(Type Conversion)や型アサーション(Type Assertion)が用いられます。インターフェースへの変換は、特に具体的な型がインターフェース型として扱われる際に発生します。

src/cmd/gc/walk.c

このファイルは、Goコンパイラのgcにおける「ウォーカー(walker)」または「ツリーウォーカー(tree walker)」の一部です。コンパイラのフロントエンドが生成した抽象構文木(AST)を走査し、型チェック、最適化、コード生成の前段階の変換など、様々な処理を行います。isandss関数は、型間の互換性や変換の可能性をチェックする役割を担っていると推測されます。

技術的詳細

このコミットは、src/cmd/gc/walk.cファイル内のisandss関数に修正を加えています。この関数は、左辺の型(lt)と右辺のノード(r)の間の関係を評価し、型変換の種類(例: I2I - インターフェースからインターフェース、T2I - 具体的な型からインターフェース)を返します。

変更前のコードでは、isnilinter(lt)(左辺がnilインターフェースであるか)またはismethod(rt) != T(右辺がメソッドを持たない型であるか)の場合にT2I(具体的な型からインターフェースへの変換)を返していました。

変更後のコードでは、isnilinter(lt)が真の場合に、さらに詳細なチェックが追加されています。 具体的には、!issimple[rt->etype] && !isptr[rt->etype]という条件が追加されました。

  • rt->etype: 右辺のノードの基本型(element type)。
  • issimple[rt->etype]: rt->etypeが単純な型(例: 整数、浮動小数点数、ブール値など)であるかを示すフラグ。
  • isptr[rt->etype]: rt->etypeがポインタ型であるかを示すフラグ。

この条件が真、つまり右辺の型が「単純な型でもなく、ポインタ型でもない」場合に、yyerror("using %T as interface is unimplemented", rt);というエラーメッセージを出力し、コンパイルを中断します。これは、構造体のような複合型がインターフェースに変換されるケースで、まだコンパイラがその変換ロジックを完全に実装していないことを示唆しています。

この変更は、Go言語のインターフェース値の内部表現と密接に関連しています。Goのインターフェース値は通常、2つのワードで表現されます。1つは基となる具体的なデータへのポインタ、もう1つは「itable」(インターフェーステーブル)または型記述構造体へのポインタです。itableは、具体的な型のメソッドをインターフェースのメソッドにマッピングするためにランタイムで計算されます。初期のコンパイラでは、すべての種類の具体的な型(特に複雑な構造体)からインターフェースへの変換におけるitableの生成やメモリ管理がまだ完全に整備されていなかった可能性があります。

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

src/cmd/gc/walk.cファイルのisandss関数内の以下の部分が変更されました。

--- a/src/cmd/gc/walk.c
+++ b/cmd/gc/walk.c
@@ -2733,7 +2733,12 @@ isandss(Type *lt, Node *r)\n  				return I2I;\n  			return Inone;\n  		}\n-	if(isnilinter(lt) || ismethod(rt) != T)\n+	if(isnilinter(lt)) {\n+	if(!issimple[rt->etype] && !isptr[rt->etype])\n+	yyerror("using %T as interface is unimplemented", rt);\n+	return T2I;\n+	}\n+	if(ismethod(rt) != T)\n  		return T2I;\n  	return Inone;\n  }\n```

## コアとなるコードの解説

変更の核心は、`isnilinter(lt)`(左辺がnilインターフェースである)という条件分岐の中に、新たなチェックが追加された点です。

**変更前:**
```c
if(isnilinter(lt) || ismethod(rt) != T)
    return T2I;

この行は、左辺がnilインターフェースであるか、または右辺がメソッドを持たない型である場合に、具体的な型からインターフェースへの変換(T2I)を許可していました。

変更後:

if(isnilinter(lt)) {
    if(!issimple[rt->etype] && !isptr[rt->etype])
        yyerror("using %T as interface is unimplemented", rt);
    return T2I;
}
if(ismethod(rt) != T)
    return T2I;

この変更により、左辺がnilインターフェースである場合(if(isnilinter(lt))ブロック内)に、以下の追加チェックが行われるようになりました。

  1. !issimple[rt->etype]: 右辺の型が単純な型ではない。
  2. !isptr[rt->etype]: 右辺の型がポインタ型ではない。

この両方の条件が真である場合、つまり「単純な型でもポインタ型でもない型(例: 構造体)をインターフェースとして使用しようとしている」場合に、yyerror関数が呼び出され、「%Tをインターフェースとして使用することは未実装です」というエラーメッセージが表示されます。これにより、コンパイラは未サポートの型変換を検出し、コンパイル時にエラーを発生させることで、ランタイムでの予期せぬ挙動を防ぎます。

この修正は、Go言語のインターフェースが持つ「暗黙的な実装」という強力な機能の裏側で、コンパイラがどのように型変換を処理し、その実装が段階的に進められていたかを示す良い例です。初期段階では、すべての型がインターフェースに変換できるわけではなく、特に複雑な型についてはコンパイラの実装が追いついていなかったため、このような一時的な制限が設けられました。

関連リンク

参考にした情報源リンク