Archive for 2月 17th, 2015
Roslyn コンパイラー仕様
まだ pull-req 通ってないんですが、Roslyn リポジトリ上にこんなものが。
https://github.com/dotnet/roslyn/pull/536
コンパイラー仕様のドキュメント化、始めました。
“コンパイラー仕様”
コンパイラーを作っていると、標準化されている言語仕様では不十分な仕様ってものがどうしても出てきます。C# コンパイラーにもいくつかそういうものがあるんですが、「オープン化したんだからそういう隠れ仕様もドキュメント化しないとダメだよね」という感じで、その手始めに、コンパイラーチームの内部 OneNote で書かれていた仕様を .md 化してリポジトリに追加しようとしているみたい。pull-req が通った暁には、/docs/compilers フォルダー以下にこういう “コンパイラー仕様” が並びます。
具体的にどういうものをドキュメント化しようとしているかというと、
- コマンドライン スイッチとその意味
- 以前のバージョンのコンパイラーからの破壊的変更
- 標準仕様に反するコンパイラーの内部挙動
- 言語仕様に記述されていないコンパイラー機能
- COM 関連や、マイクロソフト特化の動作
- コンパイラーの挙動に影響を与える “well-known” な属性(Obsolete とか Conditional のこと)
- “ruleset” (FxCop 的なものの、どの警告を出してどの警告は抑止するみたいなルール)のファイル構文(syntax)と意味論(semantics)
- C#とVB間の相互運用のための機能
- 名前付きインデクサー(インデックス付きプロパティ)のC#からの利用
- 言語仕様で明確でなく、発散の余地のあるコンパイラー挙動
- 制限次項(例えば識別子長など)
- バージョンごとの変更履歴
など。
そんなのよく気づいたな
今回公開されたコンパイラー仕様のうちの1つ、「Definite Assignment」がネタとして面白かったんで紹介。要するに、「変数には確実に値を与えておけ」仕様に関する話。
C#では、未初期化のローカル変数の参照を認めていません。これ自体は言語仕様にも書かれていることです。問題は、「何をもって未初期化・初期化済みを判定するか」という部分。判定ロジックに関する詳細が言語仕様に足りていないので、「明確でなく、発散の余地のある挙動」になっているみたいです。
例えば、このページで紹介されている例は以下のようなもの。
C#コンパイラーは、到達不能な場所では「未初期化変数を使った」判定をしないみたいです。つまり、 if (false) { ここ } とか、if (true) {} else { ここ } とかでは、別に未初期化変数を使ってもコンパイルエラーにならない。
で、コンパイラー挙動がぶれているのは、さらに、ショートサーキット演算(&& とか || とか、左辺の時点で結果が確定したら右辺を実装しないもの)と組み合わさった場合。false && (ここ) とか true || (ここ) とかは絶対に通らないわけで、前述の if の例に習うなら、別に「未初期化変数を使った」判定をさぼってもいいはず。
ところが…
C# 3.0~5.0 まではエラーになるんですって、これ。C# 2.0の頃はエラーになってなかった。そして、Roslynでもエラーにならなくした。というか、明確なルールを加えた。とのこと。
言われるまで気づかなかった… まあ、こんなコード書かないし、誰も気づかないから言語仕様に漏れるんですが。
なんなんですかね、これ。パターン マッチングとか宣言式とかを実装する過程でこれに類するコードが生成されちゃって困ったとかがあったんですかねぇ。それとも、誰かC# 2.0の頃にこの類のコードを書いてる人がいて、C# 3.0の時に「破壊的変更」を不具合報告入れたとか?