(2019/02/07; いくつか追記したのでそちらのほうもみてください)
現在の最新版コンパイラ(DMD 2.084.0)だと, このコードはSEGVで落ちてしまう.
auto foo(scope void delegate() @safe dg) @safe { return dg; } auto bar(void delegate() @safe dg) @safe { return foo(() => dg()); } void main() { bar((){})(); }
これは関数 bar fooがクロージャを生成しないためである.
(scope parameter storage classを使わない場合は callq _d_allocmemory
がコード生成される, これはクロージャのためのメモリを確保するために使われる)
(追記: たしかにscope使った場合にclosureを生成しないためなんだけど、実際には開放済のはずのスタック領域に対して不正アクセスしてるのが問題なのであり、しかもbarではなくfooのほうだった…)
0000000000032edc <_D9scopesegv3fooFNfMDFNfZvZQh>: 32edc: 55 push %rbp 32edd: 48 8b ec mov %rsp,%rbp 32ee0: 48 83 ec 10 sub $0x10,%rsp 32ee4: 48 89 7d f0 mov %rdi,-0x10(%rbp) 32ee8: 48 89 75 f8 mov %rsi,-0x8(%rbp) 32eec: 48 8b 55 f8 mov -0x8(%rbp),%rdx 32ef0: 48 8b 45 f0 mov -0x10(%rbp),%rax 32ef4: c9 leaveq 32ef5: c3 retq
正直バグなのかどうか確定的に言えないが, 関数の定義において scope parameter sotrage class は ~ cannot be escaped
といっているのでこれは合法的な振る舞いだと思われる.
(追記: 触っちゃいけないスタック領域さわってるからアウトだな…)
この挙動がバグであるかはおいておいて, scope parameter storage classと似たようなものとして in parameter storage classがある. ただしここで scope のかわりに in を使った場合, クロージャを生成するコードがはかれてプログラムは正常終了する.
https://dlang.org/spec/function.html#param-storage によると in parameter storage class は defined as scope const. However in has not yet been properly implemented so it's current implementation is equivelent to const. ~
とある.
この説明を読むと in は scope const
と定義されているけど, 現状では正しく実装されていなくて const と同じ扱いだよ, なるべく使わないでかわりに scope const
か const
を使ってね ということだそうだ.
というわけだが, いくつか疑問なところがでてくる.
- inの現在の実装は正しくなくてconstと同じ実装だよ, と仕様に書かれている場合に段階的にscope constと同じようにコンパイラ内部の実装を変更するべきだろうか
- constともscope constとも異なる実装になる? stableでおきなかったらいいか
- ユーザは仕様の注釈をみて const と同じ振る舞いを期待してコードを書いているかもしれない, さすがに無視してよさそうだが...
- さらに上の問題のように, in parameter storage classの実装をscope constに近づけたことによっておきる問題が発生した場合, どのように対応すべきだろうか
とりあえず現時点で in paramter storage class は使わないほうがよさそうだ.