Shiro(2011/06/08 18:44:01 PDT): Gauche:Bugsより:
teppey(2011/05/29 17:56:03 PDT): メタクラスがどういうものかよく分かっていないのですが、以下のスクリプトがsegmentation faultになりました。
$ uname -srm Linux 2.6.32-5-amd64 x86_64 $ ./gosh -V Gauche scheme shell, version 0.9.1 [utf-8,pthreads], x86_64-unknown-linux-gnu $ cat /tmp/x.scm (define-class <c-meta> (<class>) ()) (define-class <c> () () :metaclass <c-meta>) (define c (make <c>)) (change-class <c> <c-meta>) (write c) $ ./gosh -ftest /tmp/x.scm zsh: segmentation fault ./gosh -ftest /tmp/x.scm
SEGVの直接の原因は、デフォルトのchange-classメソッド (実質はchange-object-class 手続き) が、builtinスロット(Schemeレベルで定義されたスロットではなく、 Cの構造体メンバがSchemeスロットとして見えているもの) をコピーしない、 というものだった。<class>オブジェクトの重要なスロットであるcplとか accessorsとかはbuiltinスロットなんで、これらがコピーされないと change-classした後の<class>オブジェクトは不完全で、 それを不完全なまま使おうとするとSEGVる。
で、そこを直して、クラスオブジェクトのchange-classについて若干 カスタマイズしてやれば上のコードは動くことは動くんだけど、 どう動くのが正しいか、ということについて考え出すとはまった。
インスタンスのchange-classは、
というものだ。インスタンスの振る舞いや構造を決めるのはクラスなので、 クラスが変わればそれらが変わる、というのは意味的に納得できる。
で、構造を変えるためには、インスタンスの「変える前のクラス」と「変える先のクラス」 を比べて、例えば共通するスロットの値は持ち越すとか、 新しく追加されたスロットは初期化するとか、そういう操作が必要になる。
クラスオブジェクトはメタクラスのインスタンスなので、 その点ではインスタンスのchange-classと変わることはない。 つまり、クラスのidentityは変わらずに、その振る舞いや構造が 新たなメタクラスで指定されるものに変わる。
ここまでは何も問題がない。
ただ、クラスオブジェクトの場合、そのクラスから作られたインスタンス が既にあるかもしれない。そして、クラスオブジェクトが変化するということは、 既存のインスタンスの振る舞いや構造も変わる可能性がある。
だから、概念的には、クラスオブジェクトがchange-classされたら、 それはクラス再定義と同じようなことになると考えられる。 クラス再定義が起きると、既存のインスタンスはlazyに「前のクラス」から 「新たに定義されたクラス」へとchange-classされる。
ここが問題。クラス再定義の場合、「前のクラス」と「新たに定義されたクラス」は オブジェクトとしては別のものだ。だからchange-classが使える。 ところが、クラスオブジェクト自身がchange-classされた場合、 クラスオブジェクトのidentityはそのままで、情報が上書きされる。 「前のクラス」と「新たに定義されたクラス」の両方を 見比べることができない。
インスタンスがchange-classしようにも、そのインスタンスの現在のクラスと、 変えるべき先のクラスは、全く同一なのだ。
策はいくつか考えられる。それぞれについて帰結を考え中。