kubo39's blog

ただの雑記です。

scope parameter storage class と in parameter storage class

(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 constconst を使ってね ということだそうだ.

というわけだが, いくつか疑問なところがでてくる.

  • inの現在の実装は正しくなくてconstと同じ実装だよ, と仕様に書かれている場合に段階的にscope constと同じようにコンパイラ内部の実装を変更するべきだろうか
    • constともscope constとも異なる実装になる? stableでおきなかったらいいか
    • ユーザは仕様の注釈をみて const と同じ振る舞いを期待してコードを書いているかもしれない, さすがに無視してよさそうだが...
  • さらに上の問題のように, in parameter storage classの実装をscope constに近づけたことによっておきる問題が発生した場合, どのように対応すべきだろうか
    • 仮に今回のscope parameter storage classの挙動(SEGV)が正しい場合があるとして, ユーザがコードを変更してないのにコンパイラのバージョンをあげたらSEGVる, とか.
    • そもそもコンパイラのBug Fixなので正しいし, ユーザが in parameter storage class の使い方を間違えているケースと考えてることはできる.
    • とはいえわりと使われていそうなので, かなり影響がでそうだし慎重にやるべきだろうな.

とりあえず現時点で in paramter storage class は使わないほうがよさそうだ.