In contract inheritance (Was: Re: [Issue 302] New: in/out contract inheritance yet to be implemented)

Bruno Medeiros brunodomedeiros+spam at com.gmail
Tue Aug 29 13:49:05 PDT 2006


Stewart Gordon wrote:
> Bruno Medeiros wrote:
> <snip>
>> Does this makes sense? If in-contracts should be less restrictive than 
>> their parents (and they should), why allow in-contracts that are not 
>> less restrictive? Isn't this behavior more adequate:
>> «In a derived function, *all* in-contracts must be satisfied.
> 
> Basic OO principle: whatever is doable on an object of a base class
> should be doable on an object of any class derived from it.
> 
>> If the first in-contract fails, then it is a programmer error on 
>> regards to using the function.
>> If the first in-contract passes, but any parent in-contract fails, 
>> then there is an error in the function specification itself, since the 
>> in-contracts are not less restrictive.»
> 

Oops, I erred writing that description above. -_-'  See below.

> Which is the "first" in-contract?
> - the one of the hierarchy level where the function is introduced in the
> first place?
> - the one in the class of which the object reference is declared to be?
> - the one in the actual class of the object?
> 

"first" meant the most-derived, that is, the first in-contract starting 
from the method being called to the base methods along the overriding 
hierarchy(lineage).

> And what does "parent" mean - base or derived?  I would have thought
> base, but you seem to be using it to mean derived.
> 

I meant base, but you are right, the description above is wrong. Among 
other things I inverted the behaviors, so the terms "first" and "parent" 
didn't seem right. Should be:

«In a derived method, all in-contracts are checked. Then:

If all in-contracts fail, then it is an error regarding the usage of 
method (caller error).

If some in-contract fails, but any subsequent parent in-contracts pass, 
then there is an error in the method specification itself, since the 
in-contracts are not less restrictive. [A runtime error is thrown too?]

If none of the above, which is when one or more of the most-derived 
method in-contracts pass, and all (zero or more) subsequent parent 
in-contracts fail, the preconditions are accepted.»

 >> Consider this example:
 >>
 >> -----
 >> class Foo {
 >>   void cfunc(int a)
 >>   in {
 >>     assert(a != 42);
 >>   } body {
 >>     writefln("Foo.cfunc: != 42 ", a);
 >>   }
 >>
 >> }
 >
 > So on any object of class Foo, cfunc can be called with any argument
 > that is not 42.
 >
 >> class FooBar : Foo {
 >>   override void cfunc(int a)
 >>   in {
 >>     assert(a != 666);
 >>   } body {
 >>     writefln("FooBar.cfunc: != 666 ", a);
 >>   }
 >> }
 >> -----
 >>
 >> The in-contract of derived cfunc is not conceptually valid, but DMD
 >> allows it. It would be nice to detect that at compile-time but that is
 >> likely not feasible in the general-case, so perhaps we could opt for
 >> the aforementioned behavior, that would detect this at runtime?
 >
 > But how?  When a function in a derived class is called, should it call
 > all the in-contracts leading down to it, to make sure that all
 > in-contracts from a certain level down to it pass and all in-contracts
 > above it fail?
 >

It should work the like the behavior stated above, which is roughly like 
what you said.

> Perhaps:
> 
> "If the in-contract in the class of the object reference fails, then it 
> is an error from the caller's point of view.  If this in-contract 
> passes, but the in-contract in any derived class fails, then there is an 
> error in the function specification itself, since the in-contract of the 
> derived class imposes new restrictions."
> 

The checks have to be made from the called method upwards along the 
lineage to the parent methods, and not downwards from the called method, 
as that is not correct nor even possible, as you don't know the derived 
classes of any given class.

> However, this leads to code duplication if the derived class wants to 
> leave the base class's in-contract unchanged.  (Which is bound to be the 
> case if the derived class function unconditionally calls the base class 
> function on the same arguments and then does more.)  By the current 
> spec, one can achieve this by simply using
> 
>     in {
>         assert (false);
>     }
> 
> in the derived class.  Which indeed is cryptic, but can you think of a 
> better idea?  Moreover, what if you're deriving from a library class, 
> and the library author improves the function in the base class to work 
> in more cases.  Should you be able to use this improved functionality 
> straight out of the box?
> 

What I was thinking in regards to this, is, if the in-contract of a 
method is not specified, then it is the same as the parent's one, that 
is, it is inherited. "not specified" means not writing the 'in' clause.
And when one wants to specify an empty pre-condition, use "in { }".


-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D



More information about the Digitalmars-d mailing list