The interface's 'in' contract passes if it makes a virtual function call

Steven Schveighoffer via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Nov 4 12:41:39 PST 2014


On 11/4/14 3:26 PM, Steven Schveighoffer wrote:
> On 11/4/14 3:01 PM, Ali Çehreli wrote:
>> Perhaps I am expecting too much from the current 'in' contract design
>> and implementation. ;)
>>
>> Still, the virtual function call in the following interface's 'in'
>> contract should be dispatched to the implementaion in the derived class,
>> right?
>>
>> It seems like mere presence of that virtual function call causes the
>> 'in' contract of the interface succeed and the derived's 'in' contract
>> never gets called.
>>
>> import std.stdio;
>>
>> void main()
>> {
>>      /* EXPECTATION: The following call should execute both the
>>       * base's and the derived's in contracts 50% of the time
>>       * because the base's contract fails randomly. */
>>      (new C()).foo();
>> }
>>
>> interface I
>> {
>>      void foo()
>>      in {
>>          writeln("I.foo.in");
>>
>>          /* This check succeeds without calling virtualCheck! */
>>          assert(virtualCheck());
>>      }
>>
>>      bool virtualCheck();
>> }
>>
>> class C : I
>> {
>>      void foo()
>>      in {
>>          writeln("C.foo.in");
>>      }
>>      body
>>      {}
>>
>>      bool virtualCheck()
>>      {
>>          writeln("C.virtualCheck");
>>
>>          /* Fail randomly 50% of the time */
>>          import std.random;
>>          import std.conv;
>>          return uniform(0, 2).to!bool;
>>      }
>> }
>>
>> The output has no mention of C.virtualCheck nor C.foo.in:
>>
>> I.foo.in
>>            <-- Where is C.virtualCheck?
>>            <-- Where is C.foo.in?
>>
>> Ali
>
> This looks like a dmd bug. My theory is that the call to virtualCheck is
> going to the WRONG vtbl address. I have seen stuff like this before. It
> likely is calling something like toString. You would have to debug to
> figure it out.
>
> So what I think happens is it calls the wrong virtual function, which
> returns non-zero always, and obviously doesn't print anything, and then
> continues on. I added a writeln("after virtual check") to the in
> contract of I.foo, and it writes that too.

Yep. I debugged it. It's calling toHash instead.

Proof (the weird casting thing is because I wanted to call writeln from 
toHash, but toHash is nothrow and writeln is not) :

import std.stdio;

void main()
{
     /* EXPECTATION: The following call should execute both the
      *      * base's and the derived's in contracts 50% of the time
      *           * because the base's contract fails randomly. */
     (new C()).foo();
}

interface I
{
     void foo()
         in {
             writeln("I.foo.in");

             /* This check succeeds without calling virtualCheck! */
             assert(this.virtualCheck());
             writeln("after virtual check");
         }

     bool virtualCheck();
}

void printToHash() { writeln("in toHash");}

class C : I
{
     void foo()
         in {
             writeln("C.foo.in");
         }
     body
     {}

     bool virtualCheck()
     {
         writeln("C.virtualCheck");

         /* Fail randomly 50% of the time */
         import std.random;
         import std.conv;
         return uniform(0, 2).to!bool;
     }
     override size_t toHash() @trusted
     {
         auto f = cast(void function() nothrow)&printToHash;
         f();
         return 1;
     }
}


output:

I.foo.in
in toHash
after virtual check

Please report to bugzilla.

-Steve




More information about the Digitalmars-d-learn mailing list