[インデックス 13900] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)における誤ったコンパイルエラー(-u
フラグに関連する)を修正するものです。具体的には、コンパイラが内部的に生成するラッパーコード内でunsafe.Pointer
が使用される際に、不必要にエラーが報告される問題を解決します。
コミット
commit c29f4e00a15d0f710d29ca92ee1001c7cdbd40a1
Author: Russ Cox <rsc@golang.org>
Date: Fri Sep 21 21:12:41 2012 -0400
cmd/gc: fix a spurious -u compile error
Fixes #4082.
R=dsymonds
CC=golang-dev
https://golang.org/cl/6545055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c29f4e00a15d0f710d29ca92ee1001c7cdbd40a1
元コミット内容
Goコンパイラ(cmd/gc
)において、-u
コンパイルエラーが誤って発生する問題を修正します。
これはIssue #4082を修正するものです。
変更の背景
Goコンパイラは、リフレクションやインターフェースメソッドの呼び出しなど、特定の高度な機能を実現するために、実行時に内部的な「ラッパー」関数を生成することがあります。これらのラッパーは、効率的なメモリ操作や型変換のために、Go言語の型安全性を一時的にバイパスするunsafe.Pointer
型を内部的に使用することがあります。
しかし、コンパイラのsafemode
(安全モード)が有効な場合、ユーザーコードにおけるunsafe.Pointer
の使用は通常禁止されており、コンパイルエラーとなります。このコミットが修正しようとしている問題は、コンパイラ自身が生成したラッパーコード内でunsafe.Pointer
を使用しているにもかかわらず、それがユーザーコードによるものと誤認され、不必要な-u
コンパイルエラーが発生していたというものです。この「spurious(偽りの、誤った)」エラーは、コンパイラの内部的な正当な操作が、外部からの不正な操作としてフラグ付けされてしまう状況を示しています。
前提知識の解説
- Goコンパイラ (
cmd/gc
): Go言語の公式コンパイラであり、Goソースコードを機械語に変換する役割を担っています。gc
はGo言語のランタイムと密接に連携し、リフレクションやガベージコレクションなどの機能もサポートします。 unsafe.Pointer
: Go言語のunsafe
パッケージに含まれる特殊な型です。この型は、任意の型のポインタを保持でき、また任意の型のポインタに変換することができます。これにより、Goの厳格な型システムをバイパスして、低レベルのメモリ操作(例: C言語との相互運用、特定のデータ構造の最適化)を可能にします。しかし、その名の通り「unsafe(安全でない)」であり、誤用するとメモリ破壊やプログラムのクラッシュを引き起こす可能性があるため、通常のアプリケーションコードでの使用は推奨されません。- コンパイラの「ラッパー」生成: Go言語では、インターフェースのメソッド呼び出しやリフレクションによるメソッド呼び出しの際に、コンパイラが実行時に実際のメソッドを呼び出すための小さな仲介コード(ラッパーまたはサンク)を生成することがあります。これらのラッパーは、型変換や引数の準備など、実行時のオーバーヘッドを最小限に抑えるために最適化されており、場合によっては
unsafe.Pointer
のような低レベルの機能を利用して効率を高めます。 safemode
: Goコンパイラ内部のフラグまたは状態の一つで、特定の「安全でない」操作(例:unsafe.Pointer
の直接使用)を禁止し、より厳格なコードチェックを強制するモードです。これは、ユーザーが意図せず危険なコードを記述するのを防ぐために存在します。-u
コンパイルエラー: Goコンパイラの特定のバージョンや設定において、unsafe.Pointer
の使用など、安全でないと見なされる操作に対して発生するコンパイルエラーの一種です。このエラーは、通常、ユーザーがunsafe
パッケージを不適切に使用した場合に表示されます。
技術的詳細
このコミットの技術的な解決策は、コンパイラが内部的にラッパーコードを生成している最中であることを示す新しいグローバル変数compiling_wrappers
を導入することです。
-
compiling_wrappers
変数の導入:src/cmd/gc/go.h
にEXTERN int compiling_wrappers;
が追加されました。これは、コンパイラが現在ラッパーコードをコンパイル中であるかどうかを示すフラグとして機能します。EXTERN
キーワードは、この変数が他のファイルで定義され、ここで宣言されていることを示します。 -
ラッパー生成時のフラグ設定:
src/cmd/gc/reflect.c
のmethods
関数内で、genembedtramp
およびgenwrapper
というラッパー生成関数が呼び出される直前にcompiling_wrappers = 1;
が設定されます。これらの関数が処理を終えた後、すぐにcompiling_wrappers = 0;
に戻されます。これにより、コンパイラがラッパーコードを生成している特定の期間だけ、このフラグがtrue
(1)になります。genembedtramp
: 埋め込み型(embedded type)のメソッド呼び出しを最適化するためのトランポリン(trampoline)コードを生成します。ポインタ調整とジャンプ命令だけで済む場合に効率的なコードを生成します。genwrapper
: より一般的なメソッドラッパーを生成します。
-
unsafe.Pointer
チェックの条件変更:src/cmd/gc/typecheck.c
において、unsafe.Pointer
の使用をチェックし、safemode
が有効な場合にエラーを報告する条件が変更されました。 変更前:if(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR)
変更後:
if(safemode && !incannedimport && !importpkg && !compiling_wrappers && t && t->etype == TUNSAFEPTR)
この変更の核心は、
!compiling_wrappers
という条件が追加されたことです。これにより、safemode
が有効であっても、かつincannedimport
やimportpkg
(これらもコンパイラ内部の状態を示すフラグで、特定の内部処理中はunsafe.Pointer
の使用が許可される)がfalse
であっても、コンパイラがラッパーをコンパイルしている最中(compiling_wrappers
がtrue
)であれば、unsafe.Pointer
の使用に関するエラーチェックをスキップするようになります。
この修正により、コンパイラ自身が生成する正当なunsafe.Pointer
の使用が、ユーザーコードの不正な使用と誤認されてエラーになることがなくなりました。
コアとなるコードの変更箇所
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -919,6 +919,7 @@ EXTERN int32 thunk;
EXTERN int funcdepth;
EXTERN int typecheckok;
EXTERN int compiling_runtime;
+EXTERN int compiling_wrappers;
/*
* y.tab.c
src/cmd/gc/reflect.c
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -210,22 +210,26 @@ methods(Type *t)
// but we can generate more efficient code
// using genembedtramp if all that is necessary
// is a pointer adjustment and a JMP.
+\t\t\t\tcompiling_wrappers = 1;
if(isptr[it->etype] && isptr[this->etype]
&& f->embedded && !isifacemethod(f->type))
genembedtramp(it, f, a->isym, 1);
else
genwrapper(it, f, a->isym, 1);
+\t\t\t\tcompiling_wrappers = 0;
}
}
if(!(a->tsym->flags & SymSiggen)) {
a->tsym->flags |= SymSiggen;
if(!eqtype(this, t)) {
+\t\t\t\tcompiling_wrappers = 1;
if(isptr[t->etype] && isptr[this->etype]
&& f->embedded && !isifacemethod(f->type))
genembedtramp(t, f, a->tsym, 0);
else
genwrapper(t, f, a->tsym, 0);
+\t\t\t\tcompiling_wrappers = 0;
}
}
}
src/cmd/gc/typecheck.c
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -1536,9 +1536,7 @@ ret:
}
}
-\t// TODO(rsc): should not need to check importpkg,
-\t// but reflect mentions unsafe.Pointer.
-\tif(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR)
+\tif(safemode && !incannedimport && !importpkg && !compiling_wrappers && t && t->etype == TUNSAFEPTR)
\t\tyyerror("cannot use unsafe.Pointer");
\tevconst(n);
コアとなるコードの解説
-
src/cmd/gc/go.h
:compiling_wrappers
という新しいグローバル変数を宣言しています。この変数は、Goコンパイラが現在、リフレクションやインターフェースメソッド呼び出しのために内部的なラッパーコードを生成している最中であるかどうかを示すフラグとして使用されます。
-
src/cmd/gc/reflect.c
:methods
関数は、型t
のメソッドを処理するGoコンパイラの内部関数です。この関数内で、genembedtramp
(埋め込みメソッドの最適化されたラッパー生成)とgenwrapper
(一般的なメソッドラッパー生成)という2つの重要なラッパー生成ルーチンが呼び出されます。- これらのラッパー生成ルーチンの呼び出しの直前に
compiling_wrappers = 1;
が設定され、呼び出しの直後にcompiling_wrappers = 0;
が設定されています。これにより、コンパイラがラッパーコードを生成しているごく短い期間だけ、compiling_wrappers
フラグがアクティブになります。これは、コンパイラが自身の内部的な目的でunsafe.Pointer
を使用する際に、その期間を正確に識別するためのメカニズムです。
-
src/cmd/gc/typecheck.c
:- このファイルは、Goコンパイラの型チェックフェーズを担当します。特に、
unsafe.Pointer
の使用に関する制約を強制するロジックが含まれています。 - 変更された
if
文は、safemode
が有効な場合にunsafe.Pointer
の使用を禁止する条件を定義しています。 - 元の条件では、
safemode
が有効で、かつincannedimport
(内部的な「缶詰」インポート)やimportpkg
(パッケージインポート)の状況でない場合に、unsafe.Pointer
(TUNSAFEPTR
)が検出されるとエラー(yyerror("cannot use unsafe.Pointer");
)を発生させていました。 - 新しい条件
!compiling_wrappers
が追加されたことで、compiling_wrappers
がtrue
(つまり、コンパイラがラッパーを生成している最中)であれば、このunsafe.Pointer
に関するエラーチェックはスキップされます。これにより、コンパイラが内部的にunsafe.Pointer
を使用することが許可され、ユーザーコードのunsafe.Pointer
使用とは区別されるようになります。
- このファイルは、Goコンパイラの型チェックフェーズを担当します。特に、
この一連の変更により、コンパイラが内部的に生成するコードが、ユーザーが記述したコードと同じunsafe.Pointer
の使用規則に縛られることなく、正しくコンパイルされるようになりました。
関連リンク
- Go Issue #4082: cmd/gc: fix a spurious -u compile error
- Go Code Review: https://golang.org/cl/6545055
- Go
unsafe
パッケージのドキュメント: https://pkg.go.dev/unsafe
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(
src/cmd/gc
ディレクトリ) - Go言語のIssueトラッカー (GitHub)
- Go言語のコードレビューシステム (Gerrit)
unsafe.Pointer
に関する一般的なGo言語の解説記事- Goコンパイラの内部構造に関する技術ブログや論文 (必要に応じてWeb検索で補完)# [インデックス 13900] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)における誤ったコンパイルエラー(-u
フラグに関連する)を修正するものです。具体的には、コンパイラが内部的に生成するラッパーコード内でunsafe.Pointer
が使用される際に、不必要にエラーが報告される問題を解決します。
コミット
commit c29f4e00a15d0f710d29ca92ee1001c7cdbd40a1
Author: Russ Cox <rsc@golang.org>
Date: Fri Sep 21 21:12:41 2012 -0400
cmd/gc: fix a spurious -u compile error
Fixes #4082.
R=dsymonds
CC=golang-dev
https://golang.org/cl/6545055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c29f4e00a15d0f710d29ca92ee1001c7cdbd40a1
元コミット内容
Goコンパイラ(cmd/gc
)において、-u
コンパイルエラーが誤って発生する問題を修正します。
これはIssue #4082を修正するものです。
変更の背景
Goコンパイラは、リフレクションやインターフェースメソッドの呼び出しなど、特定の高度な機能を実現するために、実行時に内部的な「ラッパー」関数を生成することがあります。これらのラッパーは、効率的なメモリ操作や型変換のために、Go言語の型安全性を一時的にバイパスするunsafe.Pointer
型を内部的に使用することがあります。
しかし、コンパイラのsafemode
(安全モード)が有効な場合、ユーザーコードにおけるunsafe.Pointer
の使用は通常禁止されており、コンパイルエラーとなります。このコミットが修正しようとしている問題は、コンパイラ自身が生成したラッパーコード内でunsafe.Pointer
を使用しているにもかかわらず、それがユーザーコードによるものと誤認され、不必要な-u
コンパイルエラーが発生していたというものです。この「spurious(偽りの、誤った)」エラーは、コンパイラの内部的な正当な操作が、外部からの不正な操作としてフラグ付けされてしまう状況を示しています。
前提知識の解説
- Goコンパイラ (
cmd/gc
): Go言語の公式コンパイラであり、Goソースコードを機械語に変換する役割を担っています。gc
はGo言語のランタイムと密接に連携し、リフレクションやガベージコレクションなどの機能もサポートします。 unsafe.Pointer
: Go言語のunsafe
パッケージに含まれる特殊な型です。この型は、任意の型のポインタを保持でき、また任意の型のポインタに変換することができます。これにより、Goの厳格な型システムをバイパスして、低レベルのメモリ操作(例: C言語との相互運用、特定のデータ構造の最適化)を可能にします。しかし、その名の通り「unsafe(安全でない)」であり、誤用するとメモリ破壊やプログラムのクラッシュを引き起こす可能性があるため、通常のアプリケーションコードでの使用は推奨されません。- コンパイラの「ラッパー」生成: Go言語では、インターフェースのメソッド呼び出しやリフレクションによるメソッド呼び出しの際に、コンパイラが実行時に実際のメソッドを呼び出すための小さな仲介コード(ラッパーまたはサンク)を生成することがあります。これらのラッパーは、型変換や引数の準備など、実行時のオーバーヘッドを最小限に抑えるために最適化されており、場合によっては
unsafe.Pointer
のような低レベルの機能を利用して効率を高めます。 safemode
: Goコンパイラ内部のフラグまたは状態の一つで、特定の「安全でない」操作(例:unsafe.Pointer
の直接使用)を禁止し、より厳格なコードチェックを強制するモードです。これは、ユーザーが意図せず危険なコードを記述するのを防ぐために存在します。-u
コンパイルエラー: Goコンパイラの特定のバージョンや設定において、unsafe.Pointer
の使用など、安全でないと見なされる操作に対して発生するコンパイルエラーの一種です。このエラーは、通常、ユーザーがunsafe
パッケージを不適切に使用した場合に表示されます。
技術的詳細
このコミットの技術的な解決策は、コンパイラが内部的にラッパーコードを生成している最中であることを示す新しいグローバル変数compiling_wrappers
を導入することです。
-
compiling_wrappers
変数の導入:src/cmd/gc/go.h
にEXTERN int compiling_wrappers;
が追加されました。これは、コンパイラが現在ラッパーコードをコンパイル中であるかどうかを示すフラグとして機能します。EXTERN
キーワードは、この変数が他のファイルで定義され、ここで宣言されていることを示します。 -
ラッパー生成時のフラグ設定:
src/cmd/gc/reflect.c
のmethods
関数内で、genembedtramp
およびgenwrapper
というラッパー生成関数が呼び出される直前にcompiling_wrappers = 1;
が設定されます。これらの関数が処理を終えた後、すぐにcompiling_wrappers = 0;
に戻されます。これにより、コンパイラがラッパーコードを生成している特定の期間だけ、このフラグがtrue
(1)になります。genembedtramp
: 埋め込み型(embedded type)のメソッド呼び出しを最適化するためのトランポリン(trampoline)コードを生成します。ポインタ調整とジャンプ命令だけで済む場合に効率的なコードを生成します。genwrapper
: より一般的なメソッドラッパーを生成します。
-
unsafe.Pointer
チェックの条件変更:src/cmd/gc/typecheck.c
において、unsafe.Pointer
の使用をチェックし、safemode
が有効な場合にエラーを報告する条件が変更されました。 変更前:if(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR)
変更後:
if(safemode && !incannedimport && !importpkg && !compiling_wrappers && t && t->etype == TUNSAFEPTR)
この変更の核心は、
!compiling_wrappers
という条件が追加されたことです。これにより、safemode
が有効であっても、かつincannedimport
やimportpkg
(これらもコンパイラ内部の状態を示すフラグで、特定の内部処理中はunsafe.Pointer
の使用が許可される)がfalse
であっても、コンパイラがラッパーをコンパイルしている最中(compiling_wrappers
がtrue
)であれば、unsafe.Pointer
の使用に関するエラーチェックをスキップするようになります。
この修正により、コンパイラ自身が生成する正当なunsafe.Pointer
の使用が、ユーザーコードの不正な使用と誤認されてエラーになることがなくなりました。
コアとなるコードの変更箇所
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -919,6 +919,7 @@ EXTERN int32 thunk;
EXTERN int funcdepth;
EXTERN int typecheckok;
EXTERN int compiling_runtime;
+EXTERN int compiling_wrappers;
/*
* y.tab.c
src/cmd/gc/reflect.c
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -210,22 +210,26 @@ methods(Type *t)
// but we can generate more efficient code
// using genembedtramp if all that is necessary
// is a pointer adjustment and a JMP.
+\t\t\t\tcompiling_wrappers = 1;
if(isptr[it->etype] && isptr[this->etype]
&& f->embedded && !isifacemethod(f->type))
genembedtramp(it, f, a->isym, 1);
else
genwrapper(it, f, a->isym, 1);
+\t\t\t\tcompiling_wrappers = 0;
}
}
if(!(a->tsym->flags & SymSiggen)) {
a->tsym->flags |= SymSiggen;
if(!eqtype(this, t)) {
+\t\t\t\tcompiling_wrappers = 1;
if(isptr[t->etype] && isptr[this->etype]
&& f->embedded && !isifacemethod(f->type))
genembedtramp(t, f, a->tsym, 0);
else
genwrapper(t, f, a->tsym, 0);
+\t\t\t\tcompiling_wrappers = 0;
}
}
}
src/cmd/gc/typecheck.c
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -1536,9 +1536,7 @@ ret:
}
}
-\t// TODO(rsc): should not need to check importpkg,
-\t// but reflect mentions unsafe.Pointer.
-\tif(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR)
+\tif(safemode && !incannedimport && !importpkg && !compiling_wrappers && t && t->etype == TUNSAFEPTR)
\t\tyyerror("cannot use unsafe.Pointer");
\tevconst(n);
コアとなるコードの解説
-
src/cmd/gc/go.h
:compiling_wrappers
という新しいグローバル変数を宣言しています。この変数は、Goコンパイラが現在、リフレクションやインターフェースメソッド呼び出しのために内部的なラッパーコードを生成している最中であるかどうかを示すフラグとして使用されます。
-
src/cmd/gc/reflect.c
:methods
関数は、型t
のメソッドを処理するGoコンパイラの内部関数です。この関数内で、genembedtramp
(埋め込みメソッドの最適化されたラッパー生成)とgenwrapper
(一般的なメソッドラッパー生成)という2つの重要なラッパー生成ルーチンが呼び出されます。- これらのラッパー生成ルーチンの呼び出しの直前に
compiling_wrappers = 1;
が設定され、呼び出しの直後にcompiling_wrappers = 0;
が設定されています。これにより、コンパイラがラッパーコードを生成しているごく短い期間だけ、compiling_wrappers
フラグがアクティブになります。これは、コンパイラが自身の内部的な目的でunsafe.Pointer
を使用する際に、その期間を正確に識別するためのメカニズムです。
-
src/cmd/gc/typecheck.c
:- このファイルは、Goコンパイラの型チェックフェーズを担当します。特に、
unsafe.Pointer
の使用に関する制約を強制するロジックが含まれています。 - 変更された
if
文は、safemode
が有効な場合にunsafe.Pointer
の使用を禁止する条件を定義しています。 - 元の条件では、
safemode
が有効で、かつincannedimport
(内部的な「缶詰」インポート)やimportpkg
(パッケージインポート)の状況でない場合に、unsafe.Pointer
(TUNSAFEPTR
)が検出されるとエラー(yyerror("cannot use unsafe.Pointer");
)を発生させていました。 - 新しい条件
!compiling_wrappers
が追加されたことで、compiling_wrappers
がtrue
(つまり、コンパイラがラッパーを生成している最中)であれば、このunsafe.Pointer
に関するエラーチェックはスキップされます。これにより、コンパイラが内部的にunsafe.Pointer
を使用することが許可され、ユーザーコードのunsafe.Pointer
使用とは区別されるようになります。
- このファイルは、Goコンパイラの型チェックフェーズを担当します。特に、
この一連の変更により、コンパイラが内部的に生成するコードが、ユーザーが記述したコードと同じunsafe.Pointer
の使用規則に縛られることなく、正しくコンパイルされるようになりました。
関連リンク
- Go Issue #4082: cmd/gc: fix a spurious -u compile error
- Go Code Review: https://golang.org/cl/6545055
- Go
unsafe
パッケージのドキュメント: https://pkg.go.dev/unsafe
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(
src/cmd/gc
ディレクトリ) - Go言語のIssueトラッカー (GitHub)
- Go言語のコードレビューシステム (Gerrit)
unsafe.Pointer
に関する一般的なGo言語の解説記事- Goコンパイラの内部構造に関する技術ブログや論文 (必要に応じてWeb検索で補完)