www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Implementing contract inheritance

reply Stewart Gordon <smjg_1998 yahoo.com> writes:
A long-standing unimplemented D feature is the inheritance of in/out 
contracts as described here

http://www.digitalmars.com/d/dbc.html

I hereby propose a strategy for finally implementing this corner of the 
D spec.  It's basically a combination of things I've briefly mentioned 
before, put all in one place and expanded upon to form a set of steps 
that will lead to the feature being properly implemented.


1. Fix the memory leak problem with AssertError, since the 
implementation logic I am proposing relies on catching AssertErrors. 
Just add this destructor to the AssertError class:

     ~this() {
         std.c.stdlib.free(msg.ptr);
     }

2. Fix the compiler to allow a method with no body (be it abstract,
interface or part of a closed-source library) to have in/out contracts.

3. Separate the contracts from the function body at compile time.  When
this is done, this code

     int qwert(int yuiop)
     in {
         // in code
     }
     out (result) {
         // out code
     }
     body {
         // body code
     }

     // imagine we're within a function here
     qwert(42);

would compile to the equivalent of this:

     int qwert(int yuiop) {
         int result = qwert_BODY(yuiop);
         qwert_OUT(yuiop, result);
         return result;
     }

     void qwert_IN(int yuiop) {
         // in code
     }

     int qwert_BODY(int yuiop) {
         // body code
     }

     void qwert_OUT(int yuiop, int result) {
         // out code
     }

     qwert_IN(42);
     qwert(42);

The advantage of having the in contract enforced on the caller side is 
that once a library has been tested, it can be compiled in release mode, 
and subsequently applications that use the library will still have this 
check that they are using it correctly.  A separate benefit from 
implementing contract inheritance, but perhaps worth doing while we're 
at it.

4. Clarify the documentation a little.  At the moment it says:

"If a function in a derived class overrides a function in its super 
class, then only one of the in contracts of the base functions must be 
satisified Overriding functions then becomes a process of loosening the 
in contracts."

Firstly to correct a few typos:

"If a function in a derived class overrides a function in its super 
class, then only one of the in contracts of the function and its base 
functions must be satisified.  Overriding functions then becomes a 
process of loosening the in contracts."

Secondly, add a sentence or two to make the following issue clearer: 
What if the overridden function doesn't specify an in contract?  Does it 
inherit the base's one unchanged, or is it equivalent to an empty in 
contract?

Moreover, if the base class function has no in contract, should it be 
illegal to specify one in the override?  Or will it just be ignored?

5. Do the inheritance implementation itself.  Using the above example to 
illustrate, if qwert is overridden, the qwert_IN and qwert_OUT functions 
in the derived class will become

     override void qwert_IN(int yuiop) {
         try {
             // in code
         } catch (AssertError e) {
             super.qwert_IN(yuiop);
         }
     }

     override void qwert_OUT(int yuiop, int result) {
         // out code
         super.qwert_OUT(yuiop, result);
     }

Of course, if the override's in contract is empty, then the code in 
qwert_IN would be optimised away altogether.

Stewart.

-- 
My e-mail is valid but not my primary mailbox.  Please keep replies on
on the 'group where everyone may benefit.
Dec 26 2005
parent reply "Walter Bright" <newshound digitalmars.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message 
news:dopuuh$v2r$1 digitaldaemon.com...
 1. Fix the memory leak problem with AssertError, since the implementation 
 logic I am proposing relies on catching AssertErrors. Just add this 
 destructor to the AssertError class:

     ~this() {
         std.c.stdlib.free(msg.ptr);
     }

That's a good idea.
 2. Fix the compiler to allow a method with no body (be it abstract,
 interface or part of a closed-source library) to have in/out contracts.

That's a good idea too.
 3. Separate the contracts from the function body at compile time.  When
 this is done, this code

     int qwert(int yuiop)
     in {
         // in code
     }
     out (result) {
         // out code
     }
     body {
         // body code
     }

     // imagine we're within a function here
     qwert(42);

 would compile to the equivalent of this:

     int qwert(int yuiop) {
         int result = qwert_BODY(yuiop);
         qwert_OUT(yuiop, result);
         return result;
     }

     void qwert_IN(int yuiop) {
         // in code
     }

     int qwert_BODY(int yuiop) {
         // body code
     }

     void qwert_OUT(int yuiop, int result) {
         // out code
     }

     qwert_IN(42);
     qwert(42);

 The advantage of having the in contract enforced on the caller side is 
 that once a library has been tested, it can be compiled in release mode, 
 and subsequently applications that use the library will still have this 
 check that they are using it correctly.  A separate benefit from 
 implementing contract inheritance, but perhaps worth doing while we're at 
 it.

Enforcing it on the caller side leads to code bloat.
 4. Clarify the documentation a little.  At the moment it says:

 "If a function in a derived class overrides a function in its super class, 
 then only one of the in contracts of the base functions must be satisified 
 Overriding functions then becomes a process of loosening the in 
 contracts."

 Firstly to correct a few typos:

 "If a function in a derived class overrides a function in its super class, 
 then only one of the in contracts of the function and its base functions 
 must be satisified.  Overriding functions then becomes a process of 
 loosening the in contracts."

Good catch.
 Secondly, add a sentence or two to make the following issue clearer: What 
 if the overridden function doesn't specify an in contract?  Does it 
 inherit the base's one unchanged, or is it equivalent to an empty in 
 contract?

It's equivalent to an empty contract, i.e. anything is allowed.
 Moreover, if the base class function has no in contract, should it be 
 illegal to specify one in the override?  Or will it just be ignored?

It'll wind up being a no-op.
 5. Do the inheritance implementation itself.  Using the above example to 
 illustrate, if qwert is overridden, the qwert_IN and qwert_OUT functions 
 in the derived class will become

     override void qwert_IN(int yuiop) {
         try {
             // in code
         } catch (AssertError e) {
             super.qwert_IN(yuiop);
         }
     }

     override void qwert_OUT(int yuiop, int result) {
         // out code
         super.qwert_OUT(yuiop, result);
     }

 Of course, if the override's in contract is empty, then the code in 
 qwert_IN would be optimised away altogether.

Dec 26 2005
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Walter Bright wrote:

 "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message 
 news:dopuuh$v2r$1 digitaldaemon.com...

 The advantage of having the in contract enforced on the caller side 
 is that once a library has been tested, it can be compiled in 
 release mode, and subsequently applications that use the library 
 will still have this check that they are using it correctly.  A 
 separate benefit from implementing contract inheritance, but 
 perhaps worth doing while we're at it.

Enforcing it on the caller side leads to code bloat.

True. But development builds generally aren't overly size-critical, are they? And how does it compare to the code bloat that can be created by inlining functions? A further possibility is to have one more function int qwert_WITH_IN(int yuiop) { qwert_IN(yuiop); return qwert(yuiop); } And then when compiling in non-release mode, calls to qwert would become calls to qwert_WITH_IN. Of course, the _IN and _WITH_IN functions ought to be left out of a library that's compiled in release mode. Maybe some algorithm similar to whatever is used to instantiate templates could be used to generate _IN and _WITH_IN functions on demand. Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on on the 'group where everyone may benefit.
Dec 27 2005
parent "Walter Bright" <newshound digitalmars.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message 
news:dos85a$16iq$1 digitaldaemon.com...
 Walter Bright wrote:

 "Stewart Gordon" <smjg_1998 yahoo.com> wrote in message 
 news:dopuuh$v2r$1 digitaldaemon.com...

 The advantage of having the in contract enforced on the caller side is 
 that once a library has been tested, it can be compiled in release mode, 
 and subsequently applications that use the library will still have this 
 check that they are using it correctly.  A separate benefit from 
 implementing contract inheritance, but perhaps worth doing while we're 
 at it.

Enforcing it on the caller side leads to code bloat.

True. But development builds generally aren't overly size-critical, are they?

Some people writing critical apps will want to ship with contracts turned on. I know I would.
Dec 27 2005