digitalmars.D - Implementing contract inheritance
- Stewart Gordon (88/88) Dec 26 2005 A long-standing unimplemented D feature is the inheritance of in/out
- Walter Bright (8/85) Dec 26 2005 That's a good idea.
- Stewart Gordon (20/30) Dec 27 2005 True. But development builds generally aren't overly size-critical, are...
- Walter Bright (4/18) Dec 27 2005 Some people writing critical apps will want to ship with contracts turne...
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
"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
Walter Bright wrote:"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:dopuuh$v2r$1 digitaldaemon.com...<snip>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.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.
Dec 27 2005
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:dos85a$16iq$1 digitaldaemon.com...Walter Bright wrote:Some people writing critical apps will want to ship with contracts turned on. I know I would."Stewart Gordon" <smjg_1998 yahoo.com> wrote in message news:dopuuh$v2r$1 digitaldaemon.com...<snip>True. But development builds generally aren't overly size-critical, are they?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.
Dec 27 2005