Thread overview
Implementing contract inheritance
Dec 26, 2005
Stewart Gordon
Dec 27, 2005
Walter Bright
Dec 27, 2005
Stewart Gordon
Dec 28, 2005
Walter Bright
December 26, 2005
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.
December 27, 2005
"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.


December 27, 2005
Walter Bright wrote:

> "Stewart Gordon" <smjg_1998@yahoo.com> wrote in message news:dopuuh$v2r$1@digitaldaemon.com...
<snip>
>> 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.
December 28, 2005
"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...
> <snip>
>>> 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.