[インデックス 10906] ファイルの概要
このコミットは、Go言語のコンパイラ(gc
)において、構造体や配列の等価性比較(==
演算子)のために生成される内部コードが、unsafe.Pointer
型を参照することを許可するように変更を加えるものです。特に、他のパッケージの構造体に含まれるエクスポートされていないフィールドを扱う際に、reflect.Value
のような型が内部的にunsafe.Pointer
を持つ場合に対応するための修正です。コンパイラの「セーフモード」を一時的に無効にすることで、この問題に対処しています。
コミット
commit 82a6a4f39ed3fb78e49122a93c32998a5bcd0624
Author: Russ Cox <rsc@golang.org>
Date: Tue Dec 20 16:25:57 2011 -0500
gc: allow use of unsafe.Pointer in generated code
The functions we generate to implement == on structs
or arrays may need to refer to unsafe.Pointer even in
safe mode, in order to handle unexported fields contained
in other packages' structs.
R=ken2
CC=golang-dev
https://golang.org/cl/5505046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/82a6a4f39ed3fb78e49122a93c32998a5bcd0624
元コミット内容
gc: 生成されたコードでの unsafe.Pointer の使用を許可
構造体や配列の ==
演算子を実装するために生成される関数は、
セーフモードであっても unsafe.Pointer
を参照する必要がある場合があります。
これは、他のパッケージの構造体に含まれるエクスポートされていないフィールドを
扱うためです。
変更の背景
Go言語では、構造体や配列の等価性比較(==
演算子)は、コンパイラによって特別なコードが生成されることで実現されます。この生成されるコードは通常、Goの型安全性の原則に従い、unsafe.Pointer
のような型安全性を損なう可能性のある機能の使用を避けるように設計されています。
しかし、特定のシナリオ、特にreflect
パッケージのようなリフレクション機能を使用する場合に問題が発生しました。reflect.Value
のような型は、その内部にエクスポートされていないフィールドを持つことがあり、これらのフィールドがunsafe.Pointer
型である場合があります。Goの言語仕様では、エクスポートされていないフィールドは、そのフィールドが定義されているパッケージの外からは直接アクセスできません。
コンパイラが構造体や配列の==
演算子を実装するためのコードを生成する際、もし比較対象の構造体がreflect.Value
のような、内部にunsafe.Pointer
を持つエクスポートされていないフィールドを含む場合、生成されるコードはこれらの内部フィールドにアクセスしようとします。このアクセスは、コンパイラが「セーフモード」で動作している場合、unsafe.Pointer
の使用を禁止しているため、コンパイルエラーを引き起こしていました。
このコミットは、このような状況下でもコンパイルが成功するように、コンパイラが内部的にコードを生成する際に一時的にunsafe.Pointer
の使用を許可するメカニズムを導入することで、この問題を解決しています。
前提知識の解説
Go言語の型システムと安全性
Go言語は、メモリ安全性と型安全性を重視した言語です。これは、プログラマが意図しないメモリ領域にアクセスしたり、異なる型のデータを誤って扱ったりすることを防ぐための設計思想です。この原則により、多くの一般的なプログラミングエラー(例: ヌルポインタ参照、バッファオーバーフロー)がコンパイル時または実行時に検出されやすくなります。
unsafe.Pointer
の役割と危険性
unsafe
パッケージは、Goの厳格な型安全性のルールを意図的にバイパスするための機能を提供します。その中でもunsafe.Pointer
は、任意の型のポインタを任意の他の型のポインタに変換できる特殊なポインタ型です。また、uintptr
(整数型)との間で相互変換も可能です。
役割:
- C言語との相互運用性(Cの構造体やメモリレイアウトに直接アクセスする場合)
- 特定のパフォーマンス最適化(メモリレイアウトを直接操作する場合)
- リフレクションやシリアライゼーションライブラリなど、低レベルなメモリ操作が必要な場合
危険性:
- 型安全性の破壊:
unsafe.Pointer
を使用すると、Goの型システムによる保護が失われます。これにより、誤った型へのキャストや無効なメモリ領域へのアクセスが発生し、プログラムのクラッシュや未定義の動作を引き起こす可能性があります。 - 移植性の低下:
unsafe.Pointer
は、Goの内部的なメモリレイアウトや実装の詳細に依存することがあります。これらの詳細はGoのバージョンアップによって変更される可能性があり、unsafe.Pointer
を使用したコードが将来のバージョンで動作しなくなる可能性があります。 - デバッグの困難さ:
unsafe.Pointer
に関連するバグは、通常の型安全なコードのバグよりも特定と修正が困難になる傾向があります。
Goコンパイラ (gc
) の役割
gc
は、Go言語の公式コンパイラです。Goのソースコードを機械語に変換するだけでなく、型チェック、最適化、そして特定の言語機能(例: make
、new
、append
、==
演算子など)の内部的な実装コードの生成も行います。特に、構造体や配列の==
演算子のように、ユーザーが直接実装するのではなく、コンパイラがその型に応じて適切な比較ロジックを生成する場合があります。
構造体や配列の比較 (==
演算子) の内部処理
Go言語では、構造体や配列の==
演算子は、要素ごとに再帰的に比較を行うことで実装されます。これは、コンパイラが各フィールドまたは要素の型を考慮して、適切な比較コードを生成することを意味します。例えば、構造体AがフィールドXとYを持つ場合、A1 == A2
はA1.X == A2.X && A1.Y == A2.Y
のように展開されるイメージです。この生成プロセスは、コンパイラの内部で行われます。
reflect.Value
とその内部構造
reflect
パッケージは、Goプログラムが自身の構造を検査し、実行時にオブジェクトの型を操作できるようにする機能(リフレクション)を提供します。reflect.Value
は、Goの任意の値を抽象的に表現する型です。
reflect.Value
は、その内部に、Goの任意の型の値を指すポインタを保持しています。このポインタは、Goの型安全なポインタではなく、低レベルなメモリ操作を可能にするためにunsafe.Pointer
として実装されている場合があります。特に、reflect.Value
が他のパッケージからエクスポートされていないフィールド(例えば、reflect.flag
やreflect.typ
など)を扱う場合、これらのフィールドは内部的にunsafe.Pointer
を使用していることがあります。
safemode
の概念 (コンパイラ内部のフラグ)
safemode
は、Goコンパイラgc
の内部で使用されるフラグです。このフラグが有効な場合、コンパイラはunsafe
パッケージの使用や、unsafe.Pointer
のような型安全性を損なう可能性のある操作を厳しく制限します。これは、コンパイラが生成するコードの安全性を保証するため、または特定のコンパイルフェーズでunsafe
な操作を禁止するために使用されます。
技術的詳細
このコミットの核心は、Goコンパイラが構造体や配列の等価性比較(geneq
関数)およびハッシュ生成(genhash
関数)のためにコードを生成する際に発生する、unsafe.Pointer
に関するコンパイルエラーの回避です。
-
genhash
とgeneq
関数の役割:genhash
関数は、特定の型(主に構造体や配列)のハッシュ値を計算するためのコードを生成します。これは、マップのキーとして使用される型などで必要になります。geneq
関数は、特定の型(主に構造体や配列)の等価性比較(==
演算子)を行うためのコードを生成します。 これらの関数は、Goのソースコードには直接記述されない、コンパイラが内部的に生成する「合成関数」のようなものです。
-
unsafe.Pointer
を参照する必要性: コミットメッセージにあるように、問題は「他のパッケージの構造体に含まれるエクスポートされていないフィールド」を扱う場合に発生します。具体的には、比較対象の構造体がreflect.Value
のような型を含んでいる場合です。reflect.Value
は、その内部に、Goの任意の値を指すポインタを保持しており、このポインタはunsafe.Pointer
として実装されていることがあります。 コンパイラが==
演算子のコードを生成する際、reflect.Value
の内部構造(特にエクスポートされていないフィールド)にアクセスする必要が生じることがあります。しかし、これらの内部フィールドがunsafe.Pointer
型である場合、コンパイラが「セーフモード」で動作していると、unsafe.Pointer
の使用が禁止されているため、コンパイルエラーが発生していました。 -
safemode
を一時的に無効にするメカニズム: このコミットの解決策は、genhash
およびgeneq
関数が内部コードを生成し、コンパイルする間だけ、コンパイラのsafemode
フラグを一時的に無効にすることです。old_safemode = safemode;
:現在のsafemode
の状態をold_safemode
変数に保存します。safemode = 0;
:safemode
を無効にします(0
は無効を意味します)。これにより、unsafe.Pointer
の使用に関するコンパイラのチェックが一時的に緩和されます。funccompile(fn, 0);
:生成された関数fn
をコンパイルします。この間、safemode
は無効になっているため、unsafe.Pointer
への参照が許可されます。safemode = old_safemode;
:関数fn
のコンパイルが完了した後、safemode
を元の状態に戻します。これにより、コンパイラの他の部分や、ユーザーが記述したコードのコンパイル時には、再びsafemode
が有効になり、型安全性が保証されます。
-
安全性を損なわずに問題を解決: このアプローチは、
unsafe.Pointer
の使用を必要最小限の範囲(コンパイラが内部的にコードを生成する特定のフェーズのみ)に限定することで、Goの全体的な型安全性を損なうことなく問題を解決しています。ユーザーが記述するGoコードは引き続きsafemode
の制約を受け、unsafe.Pointer
を不適切に使用することはできません。この変更は、コンパイラの内部的な挙動を調整するものであり、Go言語のユーザーがunsafe.Pointer
をより自由に使えるようになるわけではありません。
コアとなるコードの変更箇所
変更は src/cmd/gc/subr.c
ファイルに集中しています。
genhash
関数と geneq
関数の両方に同様の変更が加えられています。
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2487,6 +2487,7 @@ genhash(Sym *sym, Type *t)
Node *n, *fn, *np, *nh, *ni, *call, *nx, *na, *tfn;
Node *hashel;
Type *first, *t1;
+ int old_safemode; // 追加された変数
int64 size;
if(debug['r'])
@@ -2616,7 +2617,16 @@ genhash(Sym *sym, Type *t)
typecheck(&fn, Etop);
typechecklist(fn->nbody, Etop);
curfn = nil;
+\n
+\t// Disable safemode while compiling this code: the code we\n
+\t// generate internally can refer to unsafe.Pointer.\n
+\t// In this case it can happen if we need to generate an ==\n
+\t// for a struct containing a reflect.Value, which itself has\n
+\t// an unexported field of type unsafe.Pointer.\n
+\told_safemode = safemode; // safemodeの現在の状態を保存
+\tsafemode = 0; // safemodeを無効化
funccompile(fn, 0);
+\tsafemode = old_safemode; // safemodeを元の状態に戻す
}
// Return node for
@@ -2694,6 +2704,7 @@ geneq(Sym *sym, Type *t)
{
Node *n, *fn, *np, *neq, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange;
Type *t1, *first;
+ int old_safemode; // 追加された変数
int64 size;
if(debug['r'])
@@ -2814,7 +2825,16 @@ geneq(Sym *sym, Type *t)\n \ttypecheck(&fn, Etop);\n \ttypechecklist(fn->nbody, Etop);\n \tcurfn = nil;\n+\t\n+\t// Disable safemode while compiling this code: the code we\n+\t// generate internally can refer to unsafe.Pointer.\n+\t// In this case it can happen if we need to generate an ==\n+\t// for a struct containing a reflect.Value, which itself has\n+\t// an unexported field of type unsafe.Pointer.\n+\told_safemode = safemode; // safemodeの現在の状態を保存
+\tsafemode = 0; // safemodeを無効化
\tfunccompile(fn, 0);\n+\tsafemode = old_safemode; // safemodeを元の状態に戻す
}\n \n static Type*\n```
## コアとなるコードの解説
追加されたコードは、`genhash`関数と`geneq`関数の両方で、生成されたコードをコンパイルする`funccompile`呼び出しの前後に配置されています。
1. **`int old_safemode;`**:
`safemode`の現在の値を一時的に保存するための整数型変数`old_safemode`が宣言されています。
2. **`old_safemode = safemode;`**:
`funccompile`を呼び出す直前に、グローバル変数`safemode`の現在の値が`old_safemode`に代入されます。これにより、`safemode`が元々どのような状態であったか(有効か無効か)が記憶されます。
3. **`safemode = 0;`**:
`safemode`が`0`に設定されます。これは、コンパイラの内部的な「セーフモード」を一時的に無効にすることを意味します。この状態では、`unsafe.Pointer`の使用に関する厳格なチェックが緩和され、コンパイラが内部的に生成するコードが`unsafe.Pointer`を参照できるようになります。
4. **`funccompile(fn, 0);`**:
この行で、`genhash`または`geneq`によって生成された関数`fn`が実際にコンパイルされます。`safemode`が無効になっているため、このコンパイルプロセス中に`unsafe.Pointer`への参照が含まれていてもエラーになりません。
5. **`safemode = old_safemode;`**:
`funccompile`によるコンパイルが完了した後、`safemode`は`old_safemode`に保存されていた元の値に戻されます。これにより、この特定のコード生成フェーズが終了すると、コンパイラは再び通常の`safemode`の制約下で動作し、Go言語全体の型安全性が維持されます。
この一連の操作により、コンパイラは`reflect.Value`のような特殊な型が内部的に`unsafe.Pointer`を使用している場合でも、構造体や配列の等価性比較やハッシュ生成のためのコードを正しく生成できるようになります。
## 関連リンク
* Go CL 5505046: [https://golang.org/cl/5505046](https://golang.org/cl/5505046)
## 参考にした情報源リンク
* Go言語の`unsafe`パッケージに関するドキュメントや解説記事
* Go言語の`reflect`パッケージに関するドキュメントや解説記事
* Goコンパイラ(`gc`)の内部構造に関する一般的な情報
* Web検索: "golang reflect.Value unexported fields unsafe.Pointer"```markdown
# [インデックス 10906] ファイルの概要
このコミットは、Go言語のコンパイラ(`gc`)において、構造体や配列の等価性比較(`==`演算子)のために生成される内部コードが、`unsafe.Pointer`型を参照することを許可するように変更を加えるものです。特に、他のパッケージの構造体に含まれるエクスポートされていないフィールドを扱う際に、`reflect.Value`のような型が内部的に`unsafe.Pointer`を持つ場合に対応するための修正です。コンパイラの「セーフモード」を一時的に無効にすることで、この問題に対処しています。
## コミット
commit 82a6a4f39ed3fb78e49122a93c32998a5bcd0624 Author: Russ Cox rsc@golang.org Date: Tue Dec 20 16:25:57 2011 -0500
gc: allow use of unsafe.Pointer in generated code
The functions we generate to implement == on structs
or arrays may need to refer to unsafe.Pointer even in
safe mode, in order to handle unexported fields contained
in other packages' structs.
R=ken2
CC=golang-dev
https://golang.org/cl/5505046
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/82a6a4f39ed3fb78e49122a93c32998a5bcd0624](https://github.com/golang/go/commit/82a6a4f39ed3fb78e49122a93c32998a5bcd0624)
## 元コミット内容
gc: 生成されたコードでの unsafe.Pointer の使用を許可
構造体や配列の `==` 演算子を実装するために生成される関数は、
セーフモードであっても `unsafe.Pointer` を参照する必要がある場合があります。
これは、他のパッケージの構造体に含まれるエクスポートされていないフィールドを
扱うためです。
## 変更の背景
Go言語では、構造体や配列の等価性比較(`==`演算子)は、コンパイラによって特別なコードが生成されることで実現されます。この生成されるコードは通常、Goの型安全性の原則に従い、`unsafe.Pointer`のような型安全性を損なう可能性のある機能の使用を避けるように設計されています。
しかし、特定のシナリオ、特に`reflect`パッケージのようなリフレクション機能を使用する場合に問題が発生しました。`reflect.Value`のような型は、その内部にエクスポートされていないフィールドを持つことがあり、これらのフィールドが`unsafe.Pointer`型である場合があります。Goの言語仕様では、エクスポートされていないフィールドは、そのフィールドが定義されているパッケージの外からは直接アクセスできません。
コンパイラが構造体や配列の`==`演算子を実装するためのコードを生成する際、もし比較対象の構造体が`reflect.Value`のような、内部に`unsafe.Pointer`を持つエクスポートされていないフィールドを含む場合、生成されるコードはこれらの内部フィールドにアクセスしようとします。このアクセスは、コンパイラが「セーフモード」で動作している場合、`unsafe.Pointer`の使用を禁止しているため、コンパイルエラーを引き起こしていました。
このコミットは、このような状況下でもコンパイルが成功するように、コンパイラが内部的にコードを生成する際に一時的に`unsafe.Pointer`の使用を許可するメカニズムを導入することで、この問題を解決しています。
## 前提知識の解説
### Go言語の型システムと安全性
Go言語は、メモリ安全性と型安全性を重視した言語です。これは、プログラマが意図しないメモリ領域にアクセスしたり、異なる型のデータを誤って扱ったりすることを防ぐための設計思想です。この原則により、多くの一般的なプログラミングエラー(例: ヌルポインタ参照、バッファオーバーフロー)がコンパイル時または実行時に検出されやすくなります。
### `unsafe.Pointer` の役割と危険性
`unsafe`パッケージは、Goの厳格な型安全性のルールを意図的にバイパスするための機能を提供します。その中でも`unsafe.Pointer`は、任意の型のポインタを任意の他の型のポインタに変換できる特殊なポインタ型です。また、`uintptr`(整数型)との間で相互変換も可能です。
**役割:**
* C言語との相互運用性(Cの構造体やメモリレイアウトに直接アクセスする場合)
* 特定のパフォーマンス最適化(メモリレイアウトを直接操作する場合)
* リフレクションやシリアライゼーションライブラリなど、低レベルなメモリ操作が必要な場合
**危険性:**
* **型安全性の破壊:** `unsafe.Pointer`を使用すると、Goの型システムによる保護が失われます。これにより、誤った型へのキャストや無効なメモリ領域へのアクセスが発生し、プログラムのクラッシュや未定義の動作を引き起こす可能性があります。
* **移植性の低下:** `unsafe.Pointer`は、Goの内部的なメモリレイアウトや実装の詳細に依存することがあります。これらの詳細はGoのバージョンアップによって変更される可能性があり、`unsafe.Pointer`を使用したコードが将来のバージョンで動作しなくなる可能性があります。
* **デバッグの困難さ:** `unsafe.Pointer`に関連するバグは、通常の型安全なコードのバグよりも特定と修正が困難になる傾向があります。
### Goコンパイラ (`gc`) の役割
`gc`は、Go言語の公式コンパイラです。Goのソースコードを機械語に変換するだけでなく、型チェック、最適化、そして特定の言語機能(例: `make`、`new`、`append`、`==`演算子など)の内部的な実装コードの生成も行います。特に、構造体や配列の`==`演算子のように、ユーザーが直接実装するのではなく、コンパイラがその型に応じて適切な比較ロジックを生成する場合があります。
### 構造体や配列の比較 (`==` 演算子) の内部処理
Go言語では、構造体や配列の`==`演算子は、要素ごとに再帰的に比較を行うことで実装されます。これは、コンパイラが各フィールドまたは要素の型を考慮して、適切な比較コードを生成することを意味します。例えば、構造体AがフィールドXとYを持つ場合、`A1 == A2`は`A1.X == A2.X && A1.Y == A2.Y`のように展開されるイメージです。この生成プロセスは、コンパイラの内部で行われます。
### `reflect.Value` とその内部構造
`reflect`パッケージは、Goプログラムが自身の構造を検査し、実行時にオブジェクトの型を操作できるようにする機能(リフレクション)を提供します。`reflect.Value`は、Goの任意の値を抽象的に表現する型です。
`reflect.Value`は、その内部に、Goの任意の型の値を指すポインタを保持しています。このポインタは、Goの型安全なポインタではなく、低レベルなメモリ操作を可能にするために`unsafe.Pointer`として実装されている場合があります。特に、`reflect.Value`が他のパッケージからエクスポートされていないフィールド(例えば、`reflect.flag`や`reflect.typ`など)を扱う場合、これらのフィールドは内部的に`unsafe.Pointer`を使用していることがあります。
### `safemode` の概念 (コンパイラ内部のフラグ)
`safemode`は、Goコンパイラ`gc`の内部で使用されるフラグです。このフラグが有効な場合、コンパイラは`unsafe`パッケージの使用や、`unsafe.Pointer`のような型安全性を損なう可能性のある操作を厳しく制限します。これは、コンパイラが生成するコードの安全性を保証するため、または特定のコンパイルフェーズで`unsafe`な操作を禁止するために使用されます。
## 技術的詳細
このコミットの核心は、Goコンパイラが構造体や配列の等価性比較(`geneq`関数)およびハッシュ生成(`genhash`関数)のためにコードを生成する際に発生する、`unsafe.Pointer`に関するコンパイルエラーの回避です。
1. **`genhash`と`geneq`関数の役割**:
* `genhash`関数は、特定の型(主に構造体や配列)のハッシュ値を計算するためのコードを生成します。これは、マップのキーとして使用される型などで必要になります。
* `geneq`関数は、特定の型(主に構造体や配列)の等価性比較(`==`演算子)を行うためのコードを生成します。
これらの関数は、Goのソースコードには直接記述されない、コンパイラが内部的に生成する「合成関数」のようなものです。
2. **`unsafe.Pointer`を参照する必要性**:
コミットメッセージにあるように、問題は「他のパッケージの構造体に含まれるエクスポートされていないフィールド」を扱う場合に発生します。具体的には、比較対象の構造体が`reflect.Value`のような型を含んでいる場合です。`reflect.Value`は、その内部に、Goの任意の値を指すポインタを保持しており、このポインタは`unsafe.Pointer`として実装されていることがあります。
コンパイラが`==`演算子のコードを生成する際、`reflect.Value`の内部構造(特にエクスポートされていないフィールド)にアクセスする必要が生じることがあります。しかし、これらの内部フィールドが`unsafe.Pointer`型である場合、コンパイラが「セーフモード」で動作していると、`unsafe.Pointer`の使用が禁止されているため、コンパイルエラーが発生していました。
3. **`safemode`を一時的に無効にするメカニズム**:
このコミットの解決策は、`genhash`および`geneq`関数が内部コードを生成し、コンパイルする間だけ、コンパイラの`safemode`フラグを一時的に無効にすることです。
* `old_safemode = safemode;`:現在の`safemode`の状態を`old_safemode`変数に保存します。
* `safemode = 0;`:`safemode`を無効にします(`0`は無効を意味します)。これにより、`unsafe.Pointer`の使用に関するコンパイラのチェックが一時的に緩和されます。
* `funccompile(fn, 0);`:生成された関数`fn`をコンパイルします。この間、`safemode`は無効になっているため、`unsafe.Pointer`への参照が許可されます。
* `safemode = old_safemode;`:関数`fn`のコンパイルが完了した後、`safemode`を元の状態に戻します。これにより、コンパイラの他の部分や、ユーザーが記述したコードのコンパイル時には、再び`safemode`が有効になり、型安全性が保証されます。
4. **安全性を損なわずに問題を解決**:
このアプローチは、`unsafe.Pointer`の使用を必要最小限の範囲(コンパイラが内部的にコードを生成する特定のフェーズのみ)に限定することで、Goの全体的な型安全性を損なうことなく問題を解決しています。ユーザーが記述するGoコードは引き続き`safemode`の制約を受け、`unsafe.Pointer`を不適切に使用することはできません。この変更は、コンパイラの内部的な挙動を調整するものであり、Go言語のユーザーが`unsafe.Pointer`をより自由に使えるようになるわけではありません。
## コアとなるコードの変更箇所
変更は `src/cmd/gc/subr.c` ファイルに集中しています。
`genhash` 関数と `geneq` 関数の両方に同様の変更が加えられています。
```diff
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2487,6 +2487,7 @@ genhash(Sym *sym, Type *t)
Node *n, *fn, *np, *nh, *ni, *call, *nx, *na, *tfn;
Node *hashel;
Type *first, *t1;
+ int old_safemode; // 追加された変数
int64 size;
if(debug['r'])
@@ -2616,7 +2617,16 @@ genhash(Sym *sym, Type *t)
typecheck(&fn, Etop);
typechecklist(fn->nbody, Etop);
curfn = nil;
+\n
+\t// Disable safemode while compiling this code: the code we\n
+\t// generate internally can refer to unsafe.Pointer.\n
+\t// In this case it can happen if we need to generate an ==\n
+\t// for a struct containing a reflect.Value, which itself has\n
+\t// an unexported field of type unsafe.Pointer.\n
+\told_safemode = safemode; // safemodeの現在の状態を保存
+\tsafemode = 0; // safemodeを無効化
funccompile(fn, 0);
+\tsafemode = old_safemode; // safemodeを元の状態に戻す
}
// Return node for
@@ -2694,6 +2704,7 @@ geneq(Sym *sym, Type *t)
{
Node *n, *fn, *np, *neq, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange;
Type *t1, *first;
+ int old_safemode; // 追加された変数
int64 size;
if(debug['r'])
@@ -2814,7 +2825,16 @@ geneq(Sym *sym, Type *t)\n \ttypecheck(&fn, Etop);\n \ttypechecklist(fn->nbody, Etop);\n \tcurfn = nil;\n+\t\n+\t// Disable safemode while compiling this code: the code we\n+\t// generate internally can refer to unsafe.Pointer.\n+\t// In this case it can happen if we need to generate an ==\n+\t// for a struct containing a reflect.Value, which itself has\n+\t// an unexported field of type unsafe.Pointer.\n+\told_safemode = safemode; // safemodeの現在の状態を保存
+\tsafemode = 0; // safemodeを無効化
\tfunccompile(fn, 0);\n+\tsafemode = old_safemode; // safemodeを元の状態に戻す
}\n \n static Type*\n```
## コアとなるコードの解説
追加されたコードは、`genhash`関数と`geneq`関数の両方で、生成されたコードをコンパイルする`funccompile`呼び出しの前後に配置されています。
1. **`int old_safemode;`**:
`safemode`の現在の値を一時的に保存するための整数型変数`old_safemode`が宣言されています。
2. **`old_safemode = safemode;`**:
`funccompile`を呼び出す直前に、グローバル変数`safemode`の現在の値が`old_safemode`に代入されます。これにより、`safemode`が元々どのような状態であったか(有効か無効か)が記憶されます。
3. **`safemode = 0;`**:
`safemode`が`0`に設定されます。これは、コンパイラの内部的な「セーフモード」を一時的に無効にすることを意味します。この状態では、`unsafe.Pointer`の使用に関する厳格なチェックが緩和され、コンパイラが内部的に生成するコードが`unsafe.Pointer`を参照できるようになります。
4. **`funccompile(fn, 0);`**:
この行で、`genhash`または`geneq`によって生成された関数`fn`が実際にコンパイルされます。`safemode`が無効になっているため、このコンパイルプロセス中に`unsafe.Pointer`への参照が含まれていてもエラーになりません。
5. **`safemode = old_safemode;`**:
`funccompile`によるコンパイルが完了した後、`safemode`は`old_safemode`に保存されていた元の値に戻されます。これにより、この特定のコード生成フェーズが終了すると、コンパイラは再び通常の`safemode`の制約下で動作し、Go言語全体の型安全性が維持されます。
この一連の操作により、コンパイラは`reflect.Value`のような特殊な型が内部的に`unsafe.Pointer`を使用している場合でも、構造体や配列の等価性比較やハッシュ生成のためのコードを正しく生成できるようになります。
## 関連リンク
* Go CL 5505046: [https://golang.org/cl/5505046](https://golang.org/cl/5505046)
## 参考にした情報源リンク
* Go言語の`unsafe`パッケージに関するドキュメントや解説記事
* Go言語の`reflect`パッケージに関するドキュメントや解説記事
* Goコンパイラ(`gc`)の内部構造に関する一般的な情報
* Web検索: "golang reflect.Value unexported fields unsafe.Pointer"