ジェネリックレコード(クラス)と無名メソッドの組み合わせでバグ。
まずは再現コード。
// 以下Project1.dpr program Project1; {$APPTYPE CONSOLE} uses SysUtils, Unit1 in 'Unit1.pas'; var foo: TFoo<TObject>; proc: TProc; begin proc := foo.Bar(); // *1 proc; // *2 end. // 以下Unit1.pas unit Unit1; interface uses SysUtils; type TFoo<T> = class public function Bar(AValue: T): TProc; overload; // *3 function Bar: TProc; overload; // *4 end; implementation function TFoo<T>.Bar(AValue: T): TProc; begin Result := procedure begin Writeln('foobar?'); // *5 end; end; function TFoo<T>.Bar: TProc; begin Result := procedure begin Writeln('foobar'); // *6 end; end; end.
これを解説すると、*1で引数無しで呼び出すため、オーバーロードされたBarメソッドのうち、*4の方が実行される。そのため*1で返ってくる無名メソッドは*6のはず。ところが*2が実行されると、画面に表示されるのは「foobar」ではなく、何故か*5が実行されて「foobar?」と表示される。つまりオーバーロードされたメソッドでそれぞれ別の無名メソッドを返しても、実行されるのは一方になってしまう、というバグ。
適当に追加テストしたところ、このバグはUnit1に書かれたものをすべてProject1に移すと発生しなくなる。また、実行される無名メソッドは宣言部で一番最初に宣言されているもの(この場合*3)になる。なので*3と*4の位置を入れ替えると*6が実行されるようになるので問題ないように見えるが、今度は*3を実行すると*5ではなく*6が実行されてしまうのでダメ、と。
これD2009からあるのかなぁ。とりあえずオーバーロードじゃなくてそれぞれ別の名前にすれば回避できるけど(できなかった、追記参照)、それはそれで何か負けた気分になる。かといってQC*1に登録する気もあんまりない。べ、別に英語ができないから登録もできないってわけじゃないんだからねっ!これを見た時に「バグ報告してるのに『英語翻訳して本社に報告しなおせ』とかお前ら仮にも日本法人名乗ってんだから自分で報告しろよバーカたらい回しとか客に手間かけさせんじゃねうんこ!」と思っただけなんだからねっ!勘違いしないでよね!!
追記:オーバーロードではなく別の名前にすると内部エラーで死にました。もうやだこのDelphi。せっかく買ったけどそろそろ本気でC#への移行を検討すべきかもしんない。