www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - segfault in invariant { assert(super); }

reply SimonN <eiderdaus gmail.com> writes:
Hi,

the following code compiles fine, then segfaults upon running.

     class Base {
         this(int) { }
     }

     class Derived : Base {
         this(int a) { super(a); }
         invariant() { assert (super); }
     }

     void main()
     {
         new Derived(5);
     }

Tested both with dmd 2.069.2 on Linux 64-bit, and on dpaste's dmd 
2.069.1:

     http://dpaste.dzfl.pl/4b9475c668f1

Backtrace on my home machine:

     Program received signal SIGSEGV, Segmentation fault.
     0x00000000004246a5 in _D9invariant12_d_invariantFC6ObjectZv ()
     (gdb) bt
     #0  0x00000000004246a5 in 
_D9invariant12_d_invariantFC6ObjectZv ()
     #1  0x0000000000647bf0 in _D3app7Derived6__initZ ()
     #2  0x00007fffff7ff030 in ?? ()
     #3  0x000000000042301f in _D3app7Derived12__invariant1MxFZv 
(this=0x0)
         at source/app.d:7
     Backtrace stopped: previous frame inner to this frame 
(corrupt stack?)

So, looks like endless recursion inside the invairant.

Questions:

1) Is this recursion expected?

2) The example is a dustmite'd version of this: I have a public 
final method Base.f(), and the compiler won't let me call f() in 
Derived's invariant. This is understandable, because f() is also 
a public method of Derived. However, I can call super.f() 
explicitly in Derived's invariant, with no compiler error. Is 
that expected to work, or should it lead to a similar segfault? 
(I get the segfault.)

-- Simon
Dec 19 2015
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/19/15 11:01 PM, SimonN wrote:
 Hi,

 the following code compiles fine, then segfaults upon running.

      class Base {
          this(int) { }
      }

      class Derived : Base {
          this(int a) { super(a); }
          invariant() { assert (super); }
      }

      void main()
      {
          new Derived(5);
      }

 Tested both with dmd 2.069.2 on Linux 64-bit, and on dpaste's dmd 2.069.1:

      http://dpaste.dzfl.pl/4b9475c668f1

 Backtrace on my home machine:

      Program received signal SIGSEGV, Segmentation fault.
      0x00000000004246a5 in _D9invariant12_d_invariantFC6ObjectZv ()
      (gdb) bt
      #0  0x00000000004246a5 in _D9invariant12_d_invariantFC6ObjectZv ()
      #1  0x0000000000647bf0 in _D3app7Derived6__initZ ()
      #2  0x00007fffff7ff030 in ?? ()
      #3  0x000000000042301f in _D3app7Derived12__invariant1MxFZv (this=0x0)
          at source/app.d:7
      Backtrace stopped: previous frame inner to this frame (corrupt stack?)

 So, looks like endless recursion inside the invairant.

 Questions:

 1) Is this recursion expected?
Yes. assert calls the virtual invariant function, which in the case of super is equivalent to this. So you are essentially calling assert(this).
 2) The example is a dustmite'd version of this: I have a public final
 method Base.f(), and the compiler won't let me call f() in Derived's
 invariant. This is understandable, because f() is also a public method
 of Derived. However, I can call super.f() explicitly in Derived's
 invariant, with no compiler error. Is that expected to work, or should
 it lead to a similar segfault? (I get the segfault.)
It seems like something you shouldn't do. AFAIK, invariant should not call any public functions on your existing class. It would make sense that super.f() should be disallowed if f() is disallowed (good idea to file an enhancement report). But the compiler can't prevent all issues here. All you need to do is obscure that the object you are asserting is 'this', and you can achieve the behavior. The runtime could potentially use a gate to prevent recursive calls, though I think the compiler would have to do this. -Steve
Dec 21 2015
parent SimonN <eiderdaus gmail.com> writes:
On Monday, 21 December 2015 at 20:29:14 UTC, Steven Schveighoffer 
wrote:
 1) Is this recursion expected?
Yes. assert calls the virtual invariant function, which in the case of super is equivalent to this. So you are essentially calling assert(this).
 2) The example is a dustmite'd version of this:
It seems like something you shouldn't do. AFAIK, invariant should not call any public functions on your existing class.
Thanks for the reply! Yeah, this helps for a clearer picture of what's happening. In particular, even though all invariants of a class hierarchy are tested instead of only the most-subclassed-one, triggering the invariant check remains virtual. I didn't know that. Even if Base.f() is const, it's not allowed inside Derived.invariant(). This is again understandable: By OOP principles, Derived shouldn't impose further restrictions on Base than what Base imposes on itself already.
 (good idea to file an enhancement report).
For that, I was trying earlier today to find the exact instances of when there is a warning, and when there is not. I didn't get warnings to come up consistently. (Even the case I described in question 2 doens't always give a warning.) I'd have to take a look at this some time again. -- Simon
Dec 22 2015