7

Eric Lippertがこのビデオの約13分前に転送すると、C#コンパイラに加えられた、次のコードを無効にする変更について説明します(.NET 2より前で、このコードはコンパイルされていたはずです)。

int y;
int x = 10;
if (x * 0 == 0)
    y = 123;

Console.Write(y);

今、私は明らかに上記のコードの実行が実際に評価されることを理解しています

int y;
int x = 10;
y = 123;
Console.Write(y);

しかし、私が理解していないのは、次のコードをコンパイル不能にすることが「望ましい」と見なされる理由です。IE:そのような推論がコースを実行できるようにすることのリスクは何ですか?

4

2 に答える 2

8

この質問はまだ少しわかりにくいと思いますが、質問を答えられる形式に言い換えることができるかどうか見てみましょう. まず、質問の背景をもう一度述べさせてください。

C# 2.0 では、このコード:

int x = 123;
int y;
if (x * 0 == 0) 
    y = 345;
Console.WriteLine(y);

あなたが書いたかのように扱われた

int x = 123;
int y;
if (true) 
    y = 345;
Console.WriteLine(y);

これは次のように扱われます。

int x = 123;
int y;
y = 345;
Console.WriteLine(y);

これは合法的なプログラムです。

しかし、C# 3.0 では、これを防ぐために重大な変更を行いました。あなたも私も条件が常に真であることを知っているにもかかわらず、コンパイラーはもはや条件を「常に真」であるとは見なしません。コンパイラは、「if」の本体が常に実行されることを認識していないため、ローカル変数 y が使用される前に常に割り当てられていることを認識していないため、これを不正なプログラムにします。

C# 3.0 の動作が正しいのはなぜですか?

仕様に次のように記載されているため、正しいです。

  • 定数式には定数のみを含める必要があります。x * 0 == 0非定数項 が含まれているため、 は定数式ではありませんx

  • の結果はif、条件が に等しい定数式である場合にのみ、常に到達可能であることが知られていますtrue

したがって、与えられたコードは、条件ステートメントの結果を常に到達可能であると分類すべきではなく、したがって、ローカルyを確実に割り当てられるものとして分類すべきではありません。

定数式に定数のみを含めることが望ましいのはなぜですか?

私たちは、C# 言語をユーザーが明確に理解し、コンパイラの作成者が正しく実装できるようにしたいと考えています。コンパイラが式の値について考えられるすべての論理的推論を行うことを要求することは、それらの目標に反します。与えられた式が定数であるかどうか、定数である場合はその値を簡単に判断できる必要があります。簡単に言えば、定数評価コードは算術の実行方法を知っている必要がありますが、算術操作に関する事実を知っている必要はありません。定数評価器は 2 * 1 の乗算方法を知っていますが、「1 は整数の乗法恒等式である」という事実を知る必要はありません。

現在、コンパイラの作成者は、賢くできる領域があると判断し、それによってより最適なコードを生成する可能性があります。コンパイラの作成者はこれを行うことが許可されていますが、コードが合法か違法かを変更する方法では許可されていません正当なコードが与えられた場合に、コンパイラの出力を改善する最適化のみを行うことが許可されています。

このバグは C# 2.0 でどのように発生したのですか?

何が起こったのかというと、コンパイラが算術オプティマイザを実行するのが早すぎるように書かれているためです。オプティマイザは、賢いはずのビットであり、プログラムが正当であると判断された後に実行されるべきでした。プログラムが合法であると判断されるに実行されたため、結果に影響を与えていました。

これは互換性を破る可能性のある変更でした。これにより、コンパイラが仕様に準拠するようになりましたが、作業コードがエラー コードに変わる可能性もありました。変更の動機は何ですか?

LINQ 機能、特に式ツリー。次のようなことを言った場合:

(int x)=>x * 0 == 0

それを式ツリーに変換すると、次の式ツリーが生成されると思いますか?

(int x)=>true

? おそらくそうではありません!おそらく、「x をゼロで乗算し、結果をゼロと比較する」ための式ツリーが生成されることを期待していました。 式ツリーは、本文の式の論理構造を保持する必要があります。

式ツリーのコードを書いたとき、設計委員会が決定するかどうかはまだ明確ではありませんでした。

()=>2 + 3

「add two to three」の式ツリーまたは「five」の式ツリーを生成しようとしていました。私たちは後者を選択しました。定数式ツリーが生成される前に折り畳まれますが、式ツリーが生成される前に演算がオプティマイザーを介して実行されるべきではありません。

それでは、今述べた依存関係を考えてみましょう。

  • codegen の前に算術最適化を行う必要があります。
  • 式ツリーの書き換えは、算術最適化の前に行う必要があります
  • 式ツリーの書き換え前に一定の折り畳みが発生する必要がある
  • フロー解析の前に一定の折り畳みが発生する必要があります
  • 式ツリーの書き換え前にフロー分析を行う必要があります (式ツリーが初期化されていないローカルを使用しているかどうかを知る必要があるため)

これらすべての依存関係を考慮して、このすべての作業を実行する順序を見つける必要があります。C# 2.0 のコンパイラは、次の順序でそれらを実行しました。

  • 定数畳み込みと算術最適化を同時に
  • 流動解析
  • コード生成

式ツリーの書き換えはどこに入ることができますか? どこにも!フロー分析は現在、算術オプティマイザーによって推定された事実を考慮しているため、これは明らかにバグです。次の順序で処理を行うように、コンパイラを作り直すことにしました。

  • 一定の折り畳み
  • 流動解析
  • 式ツリーの書き換え
  • 算術最適化
  • コード生成

これは明らかに重大な変更を必要とします。

さて、これを行うことで、既存の壊れた動作を維持することを検討しました。

  • 一定の折り畳み
  • 算術最適化
  • 流動解析
  • 算術最適化解除
  • 式ツリーの書き換え
  • 再び算術最適化
  • コード生成

最適化された算術式には、最適化されていない形式へのポインターが含まれます。これはバグを残すには複雑すぎると判断しました。代わりに、バグを修正し、重大な変更を行い、コンパイラ アーキテクチャをより簡単に理解できるようにする方がよいと判断しました。

于 2012-01-24T16:50:32.570 に答える
3

仕様では、ブロック内でのみ割り当てられるものの明確な割り当てifは未定であると述べています。if仕様は、不要なブロックを削除するコンパイラの魔法について何も述べていません。特に、条件を変更すると、非常に紛らわしいエラー メッセージが表示されif、突然、y割り当てられていないというエラーが表示されます。

コンパイラは、明らかなコードの削除を自由に実行できますが、最初にルールの仕様に従う必要があります。

具体的には、セクション 5.3.3.5 (MS 4.0 仕様):

5.3.3.5 If 文 if 文 stmt の形式は次のとおりです。

if ( expr ) then-stmt else else-stmt

  • v は、expr の先頭で、stmt の先頭と同じ明確な代入状態を持ちます。
  • v が expr の末尾に確実に割り当てられている場合、制御フロー転送で then-stmt に確実に割り当てられ、else-stmt または else 節がない場合は stmt のエンドポイントに割り当てられます。
  • v が expr の最後に「真の式の後に確実に代入された」状態を持っている場合、それは制御フロー転送で then-stmt に確実に割り当てられ、制御フロー転送で else-stmt またはelse 句がない場合は stmt の終点。
  • v が expr の最後に「false 式の後に確実に割り当てられた」状態を持っている場合、else-stmt への制御フロー転送では確実に割り当てられ、then-stmt への制御フロー転送では確実に割り当てられません。then-stmt のエンドポイントで確実に割り当てられている場合にのみ、stmt のエンドポイントで確実に割り当てられます。
  • それ以外の場合、v は制御フローで then-stmt または else-stmt のいずれかに確実に割り当てられていないと見なされます。else がない場合は stmt のエンドポイントに転送されます。

最初に割り当てられていない変数が特定の場所で確実に割り当てられたと見なされるためには、変数への割り当てが、その場所につながるすべての可能な実行パスで発生する必要があります。

技術的ifには、条件が falseの場合に実行パスが存在します。ifyも に割り当てられていれば問題ありませんが、仕様では、条件が常に trueelseであることを明示的に特定する必要はありません。if

于 2012-01-24T06:03:47.457 に答える