Region-Based Memory Management in Cyclone を読んだ その2

September 24, 2017

続き。

ポインタとリージョン

Dangling pointerをコンパイル時に防ぐために、Cycloneではすべてのポインタは1つのリージョンを指すようにできている。ポインタ型にリージョンアノテーションをつけられるようになっていて、int* ρ という型は(サブタイピングを除けば) リージョンρ にあるintを指すポインタの事。

型の一部なので、イメージとしては型パラメータに近く、int* ρ1int* ρ2は違う型。

リージョンが生きていれば、そのリージョンのデータを指すポインタをデリファレンスしても問題ない。逆にいえばリージョンが生きているか(スコープを超えてポインタを使っていないか)をコンパイル時にチェックすることによりDangling pointerを防ぐ。

型パラメータに近いと書いたことからも分かる通り、パラメトリック多相のような、リージョン多相(Region Polymorphism)がある。つまりどのリージョンについても関数を呼び出すことができる。

char? ρH strdup<ρ>(const char?ρ s)

structはポインタを持つことができるが、それらのリージョンが型のパラメータとしても現れる。

sruct Lst<ρ1, ρ2> {
	int* ρ1 hd;
	struct Lst<ρ1, ρ2> *ρ2 tl;
};

リージョンサブタイピング

リージョンにはサブタイプ関係がある。(ちなみにRustもライフタイムにのみサブタイプ関係がある。)

リージョンはスタックに積まれるように作成され、先に作られたリージョンはあとに作られたリージョンより長生きする。

ρ1 {
	ρ2 {
	}
}

たとえばこんな感じでρ1, ρ2が作られたとすると、ρ1はρ2としてふるまえる。つまりρ1がρ2のサブタイプになる。

CycloneではT1T2のサブタイプだとしてもT1* ρT2* ρ として振る舞うことを禁止している。これはリージョンどうこうというよりか参照 + 共変 + ミュータブルの問題。

リージョンアノテーションの記述を減らす

毎回リージョンを記述するのは面倒なので、以下の手法で記述を減らせるようになっている。

  • デフォルトのリージョン
  • リージョン推論

Cycloneでは主に1つ目のデフォルトリージョンの決定ルールを決めることでほとんど明示的に書かなくてもよくなっている。 おそらくRustも同様で、いわゆる「リージョン推論」はないものの、あるルールに従ってライフタイムの記述を省略できる。

Cycloneでの具体的なルールは以下。

  • ローカル変数など、ローカルな宣言に対してはローカル推論を使う
  • 関数の引数にはそれぞれフレッシュなリージョンを割り当てる
  • それ以外の場合はヒープリージョンを割り当てる

(続く)

このエントリーをはてなブックマークに追加