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

[インデックス 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を導入することです。

  1. compiling_wrappers変数の導入: src/cmd/gc/go.hEXTERN int compiling_wrappers;が追加されました。これは、コンパイラが現在ラッパーコードをコンパイル中であるかどうかを示すフラグとして機能します。EXTERNキーワードは、この変数が他のファイルで定義され、ここで宣言されていることを示します。

  2. ラッパー生成時のフラグ設定: src/cmd/gc/reflect.cmethods関数内で、genembedtrampおよびgenwrapperというラッパー生成関数が呼び出される直前にcompiling_wrappers = 1;が設定されます。これらの関数が処理を終えた後、すぐにcompiling_wrappers = 0;に戻されます。これにより、コンパイラがラッパーコードを生成している特定の期間だけ、このフラグがtrue(1)になります。

    • genembedtramp: 埋め込み型(embedded type)のメソッド呼び出しを最適化するためのトランポリン(trampoline)コードを生成します。ポインタ調整とジャンプ命令だけで済む場合に効率的なコードを生成します。
    • genwrapper: より一般的なメソッドラッパーを生成します。
  3. 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が有効であっても、かつincannedimportimportpkg(これらもコンパイラ内部の状態を示すフラグで、特定の内部処理中はunsafe.Pointerの使用が許可される)がfalseであっても、コンパイラがラッパーをコンパイルしている最中(compiling_wrapperstrue)であれば、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.PointerTUNSAFEPTR)が検出されるとエラー(yyerror("cannot use unsafe.Pointer");)を発生させていました。
    • 新しい条件!compiling_wrappersが追加されたことで、compiling_wrapperstrue(つまり、コンパイラがラッパーを生成している最中)であれば、このunsafe.Pointerに関するエラーチェックはスキップされます。これにより、コンパイラが内部的にunsafe.Pointerを使用することが許可され、ユーザーコードのunsafe.Pointer使用とは区別されるようになります。

この一連の変更により、コンパイラが内部的に生成するコードが、ユーザーが記述したコードと同じunsafe.Pointerの使用規則に縛られることなく、正しくコンパイルされるようになりました。

関連リンク

参考にした情報源リンク

  • 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を導入することです。

  1. compiling_wrappers変数の導入: src/cmd/gc/go.hEXTERN int compiling_wrappers;が追加されました。これは、コンパイラが現在ラッパーコードをコンパイル中であるかどうかを示すフラグとして機能します。EXTERNキーワードは、この変数が他のファイルで定義され、ここで宣言されていることを示します。

  2. ラッパー生成時のフラグ設定: src/cmd/gc/reflect.cmethods関数内で、genembedtrampおよびgenwrapperというラッパー生成関数が呼び出される直前にcompiling_wrappers = 1;が設定されます。これらの関数が処理を終えた後、すぐにcompiling_wrappers = 0;に戻されます。これにより、コンパイラがラッパーコードを生成している特定の期間だけ、このフラグがtrue(1)になります。

    • genembedtramp: 埋め込み型(embedded type)のメソッド呼び出しを最適化するためのトランポリン(trampoline)コードを生成します。ポインタ調整とジャンプ命令だけで済む場合に効率的なコードを生成します。
    • genwrapper: より一般的なメソッドラッパーを生成します。
  3. 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が有効であっても、かつincannedimportimportpkg(これらもコンパイラ内部の状態を示すフラグで、特定の内部処理中はunsafe.Pointerの使用が許可される)がfalseであっても、コンパイラがラッパーをコンパイルしている最中(compiling_wrapperstrue)であれば、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.PointerTUNSAFEPTR)が検出されるとエラー(yyerror("cannot use unsafe.Pointer");)を発生させていました。
    • 新しい条件!compiling_wrappersが追加されたことで、compiling_wrapperstrue(つまり、コンパイラがラッパーを生成している最中)であれば、このunsafe.Pointerに関するエラーチェックはスキップされます。これにより、コンパイラが内部的にunsafe.Pointerを使用することが許可され、ユーザーコードのunsafe.Pointer使用とは区別されるようになります。

この一連の変更により、コンパイラが内部的に生成するコードが、ユーザーが記述したコードと同じunsafe.Pointerの使用規則に縛られることなく、正しくコンパイルされるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード(src/cmd/gcディレクトリ)
  • Go言語のIssueトラッカー (GitHub)
  • Go言語のコードレビューシステム (Gerrit)
  • unsafe.Pointerに関する一般的なGo言語の解説記事
  • Goコンパイラの内部構造に関する技術ブログや論文 (必要に応じてWeb検索で補完)