www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Feature request - isolated "in" contracts

reply Arcane Jill <Arcane_member pathlink.com> writes:
I would very much like this to compile:

#    double squareRoot(double d)
#    in
#    {
#        assert(d >= 0);
#    };
#    // function body supplied in linked library

Until yesterday, this compiled, but incorrectly. Walter has now fixed this bug,
but he's fixed it in such a way that it now won't compile at all.

The issue is this. A function's "in" contract is a precondition with which
/callers/ are expected to comply. If they do not, the bug is in the callers'
code, not in the function body. Now, if the function is in a commercial library,
the creators of that library may wish to keep their source code to themselves -
but they'd still want to expose the precondition.

Worse, the callers' code could still be at the debug stage, while the library
code may be a release build - in which case, not only will the application
developer not be made aware of the precondition, but it won't even be executed!

The resolution is that preconditions must always be executed by the caller, not
the callee. That way, caller bugs caused by violating the contract will be
found, as they should. And these means that it must be possible to expose an
"in" contract without exposing the implementation.

Does that sound reasonable?

Arcane Jill
Aug 26 2004
next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Arcane Jill wrote:
 I would very much like this to compile:
 
 #    double squareRoot(double d)
 #    in
 #    {
 #        assert(d >= 0);
 #    };
 #    // function body supplied in linked library
 
 Until yesterday, this compiled, but incorrectly. Walter has now fixed this bug,
 but he's fixed it in such a way that it now won't compile at all.
 
 The issue is this. A function's "in" contract is a precondition with which
 /callers/ are expected to comply. If they do not, the bug is in the callers'
 code, not in the function body. Now, if the function is in a commercial
library,
 the creators of that library may wish to keep their source code to themselves -
 but they'd still want to expose the precondition.
 
 Worse, the callers' code could still be at the debug stage, while the library
 code may be a release build - in which case, not only will the application
 developer not be made aware of the precondition, but it won't even be executed!
 
 The resolution is that preconditions must always be executed by the caller, not
 the callee. That way, caller bugs caused by violating the contract will be
 found, as they should. And these means that it must be possible to expose an
 "in" contract without exposing the implementation.

I agree, but I would further argue that isolated "out" contracts should be allowed as well: * Explicitly documents what callers can expect from a library * Ensures that if the library changes (i.e. a future version changes its contracts), those who call it will report errors (when the previous contract is violated) * In the future, optimizing compilers will probably use "out" contracts to optimize code that runs after the function call.
Aug 26 2004
prev sibling next sibling parent reply Charlie <Charlie_member pathlink.com> writes:
Couldn't you just comment out the in statement ?  Or will that break the
contracts ?

Worse, the callers' code could still be at the debug stage, while the library
code may be a release build - in which case, not only will the application
developer not be made aware of the precondition, but it won't even be executed

I'm not sure what can be done about this, other than slap the developer.
The resolution is that preconditions must always be executed by the caller, not
the callee. That way, caller bugs caused by violating the contract will be
found, as they should.

Ive lost you here, in what way is 'in' not behaving as you would like, or when does 'in' not catch the conditions ( besides in a release build ? ). And how can you control who 'executes' the conditions ? Charlie In article <cgl5ni$hfb$1 digitaldaemon.com>, Arcane Jill says...
I would very much like this to compile:

#    double squareRoot(double d)
#    in
#    {
#        assert(d >= 0);
#    };
#    // function body supplied in linked library

Until yesterday, this compiled, but incorrectly. Walter has now fixed this bug,
but he's fixed it in such a way that it now won't compile at all.

The issue is this. A function's "in" contract is a precondition with which
/callers/ are expected to comply. If they do not, the bug is in the callers'
code, not in the function body. Now, if the function is in a commercial library,
the creators of that library may wish to keep their source code to themselves -
but they'd still want to expose the precondition.

Worse, the callers' code could still be at the debug stage, while the library
code may be a release build - in which case, not only will the application
developer not be made aware of the precondition, but it won't even be executed!

The resolution is that preconditions must always be executed by the caller, not
the callee. That way, caller bugs caused by violating the contract will be
found, as they should. And these means that it must be possible to expose an
"in" contract without exposing the implementation.

Does that sound reasonable?

Arcane Jill

Aug 26 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cgl94t$j0i$1 digitaldaemon.com>, Charlie says...

The resolution is that preconditions must always be executed by the caller, not
the callee. That way, caller bugs caused by violating the contract will be
found, as they should.

Ive lost you here, in what way is 'in' not behaving as you would like, or when does 'in' not catch the conditions ( besides in a release build ? ). And how can you control who 'executes' the conditions ?

It would have to be done by the compiler. Currently, given the code: # // Caller # f(x); # # // Callee # int f(uint n) # in { /*precondition*/ } # out(r) { /*postcondition*/ } # body { /*body*/ } the compiler invisibly transforms it to: # // Caller # f(x); # # // Callee # int f(uint n) # { # /*precondition*/ # /*body*/ # /*postcondition*/ # } I would prefer that it instead transform it to: # // Caller # /*precondition*/ # f(x); # # // Callee # int f(uint n) # { # /*body*/ # /*postcondition*/ # } See what I mean? Arcane Jill
Aug 26 2004
parent Charlie <Charlie_member pathlink.com> writes:
See what I mean?

Ahh yes, thanks for clarifying. Yes that looks like a good call, and like sean said could be extended for out contracts too. Charlie In article <cglaf6$joc$1 digitaldaemon.com>, Arcane Jill says...
In article <cgl94t$j0i$1 digitaldaemon.com>, Charlie says...

The resolution is that preconditions must always be executed by the caller, not
the callee. That way, caller bugs caused by violating the contract will be
found, as they should.

Ive lost you here, in what way is 'in' not behaving as you would like, or when does 'in' not catch the conditions ( besides in a release build ? ). And how can you control who 'executes' the conditions ?

It would have to be done by the compiler. Currently, given the code: # // Caller # f(x); # # // Callee # int f(uint n) # in { /*precondition*/ } # out(r) { /*postcondition*/ } # body { /*body*/ } the compiler invisibly transforms it to: # // Caller # f(x); # # // Callee # int f(uint n) # { # /*precondition*/ # /*body*/ # /*postcondition*/ # } I would prefer that it instead transform it to: # // Caller # /*precondition*/ # f(x); # # // Callee # int f(uint n) # { # /*body*/ # /*postcondition*/ # } See what I mean? Arcane Jill

Aug 26 2004
prev sibling next sibling parent reply Andy Friesen <andy ikagames.com> writes:
Arcane Jill wrote:
 I would very much like this to compile:
 
 #    double squareRoot(double d)
 #    in
 #    {
 #        assert(d >= 0);
 #    };
 #    // function body supplied in linked library
 
 Until yesterday, this compiled, but incorrectly. Walter has now fixed this bug,
 but he's fixed it in such a way that it now won't compile at all.
 
 The issue is this. A function's "in" contract is a precondition with which
 /callers/ are expected to comply. If they do not, the bug is in the callers'
 code, not in the function body. Now, if the function is in a commercial
library,
 the creators of that library may wish to keep their source code to themselves -
 but they'd still want to expose the precondition.
 
 Worse, the callers' code could still be at the debug stage, while the library
 code may be a release build - in which case, not only will the application
 developer not be made aware of the precondition, but it won't even be executed!
 
 The resolution is that preconditions must always be executed by the caller, not
 the callee. That way, caller bugs caused by violating the contract will be
 found, as they should. And these means that it must be possible to expose an
 "in" contract without exposing the implementation.
 
 Does that sound reasonable?

Very! This also opens the doorway to finally implementing contract inheritance without having to store things in the vtbl. The first thing that came to mind is that contracts on classes accessed through an interface still need to test those contracts, even though which exact contracts are to be tested isn't even known at compile-time. If the caller was responsible for testing contracts, this would be impossible. However, this is completely counter to the spirit of both DbC and interfaces. The contract is part of the interface, not the implementation. Logically, then, interfaces need to be able to specify contracts, and methods implementing an interface must be /forbidden/ from specifying contracts of their own. (ditto for abstract classes/methods) This could potentially cause some minor problems with mix-in styled interface implementation, though: interface IFace { // contracts allowed, but no body void hello() in { ... } out { ... }; void goodbye() in { ... }; } class PartialImpl { void hello() in { ... } out { ... } body { ... } } class FullImpl : PartialImpl, IFace { void goodbye() { // no contracts allowed here ... } /* * bam. PartialImpl.hello has its own contracts, and therefore * does not satisfy IFace (unless the two happened to be * identical, but let's not go there) * * Because of this, an alias won't do. */ void hello() { super.hello(); } } In this manner, all contracts are known at compile time, can therefore be checked by the caller and not the callee, contracts and interfaces conform more closely to their intended purposes, and we even keep link compatibility between debug and release builds. -- Captain Sidetrack
Aug 26 2004
next sibling parent Andy Friesen <andy ikagames.com> writes:
Andy Friesen wrote:
 Does that sound reasonable?

Very! [stuff]

... doh. This is all hogwash because testing against instance data is kind of handy. Polymorphic contracts aren't optional. Walter, how did you intend to implement contract inheritance? If they're just pasted into a method, then they can't be inherited. Implementing them as full-blown methods in their own right means three vtbl entries per method which have to stick around even in release mode lest debug/release builds fail to link. -- andy
Aug 26 2004
prev sibling parent Sean Kelly <sean f4.ca> writes:
In article <cglbl7$kdq$1 digitaldaemon.com>, Andy Friesen says...
This also opens the doorway to finally implementing contract inheritance 
without having to store things in the vtbl.

The first thing that came to mind is that contracts on classes accessed 
through an interface still need to test those contracts, even though 
which exact contracts are to be tested isn't even known at compile-time. 
  If the caller was responsible for testing contracts, this would be 
impossible.

However, this is completely counter to the spirit of both DbC and 
interfaces.  The contract is part of the interface, not the 
implementation.  Logically, then, interfaces need to be able to specify 
contracts, and methods implementing an interface must be /forbidden/ 
from specifying contracts of their own. (ditto for abstract classes/methods)

But aren't classes also interfaces? And what about free functions (though I grant that inheritance issues are not a problem here)? Or were you just saying that if classes use DbC then those clauses need to be in library headers and not obscured in object code? Sean
Aug 26 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cgl5ni$hfb$1 digitaldaemon.com...
 I would very much like this to compile:

 #    double squareRoot(double d)
 #    in
 #    {
 #        assert(d >= 0);
 #    };
 #    // function body supplied in linked library

 Until yesterday, this compiled, but incorrectly. Walter has now fixed this

 but he's fixed it in such a way that it now won't compile at all.

 The issue is this. A function's "in" contract is a precondition with which
 /callers/ are expected to comply. If they do not, the bug is in the

 code, not in the function body. Now, if the function is in a commercial

 the creators of that library may wish to keep their source code to

 but they'd still want to expose the precondition.

 Worse, the callers' code could still be at the debug stage, while the

 code may be a release build - in which case, not only will the application
 developer not be made aware of the precondition, but it won't even be

 The resolution is that preconditions must always be executed by the

 the callee. That way, caller bugs caused by violating the contract will be
 found, as they should. And these means that it must be possible to expose

 "in" contract without exposing the implementation.

 Does that sound reasonable?

I think there's a small misunderstanding when it comes to in contracts. Parameter validation for, let's say, operating system API functions, is a requirement for it to be it release builds. That is akin to user input validation, and is not what DbC is for. DbC is for programs that, once debugged, can have the DbC removed. Anything that exposes an interface to arbitrary other code needs to keep its input validation turned on - this would include operating system APIs, DLLs, shared libraries, and COM objects. If a vendor is shipping libraries meant to be statically linked in, it would be perfectly sensible for them to produce two builds - a 'release' build with DbC turned off, and a 'debug' build with DbC on which the user uses to debug his interface to that library. This does not require that the vendor ship the source or expose the implementation. (Furthermore, there's a problem with having the caller execute the preconditions - the essential polymorphic aspect of them would be lost.) If you wish to *add* preconditions to an existing library function, and for good reason do not wish to modify the library source, the right approach is to put a wrapper around it with the DbC in the wrapper.
Aug 26 2004
next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cglsov$tbd$1 digitaldaemon.com>, Walter says...

I think there's a small misunderstanding when it comes to in contracts.
Parameter validation for, let's say, operating system API functions, is a
requirement for it to be it release builds. That is akin to user input
validation, and is not what DbC is for. DbC is for programs that, once
debugged, can have the DbC removed.

I know. I didn't misunderstand.
Anything that exposes an interface to
arbitrary other code needs to keep its input validation turned on - this
would include operating system APIs, DLLs, shared libraries, and COM
objects.

That is the situation /now/, yes. However, this was a feature request. If preconditions were instead to be done by the caller, that situation would change, and vendors would be able to release only one version of their library.
If a vendor is shipping libraries meant to be statically linked in, it would
be perfectly sensible for them to produce two builds - a 'release' build
with DbC turned off, and a 'debug' build with DbC on which the user uses to
debug his interface to that library.

Again, this is the situation /now/. I was suggesting that it be changed.
This does not require that the vendor
ship the source or expose the implementation.

? How can you expose the in contract without the implentation? It won't compile! If you mean that vendors may be able to expose: # int f( uint n ) in { /*precondition*/ }; then is exactly what I'm asking for. If they can't, how can they possibly expose the precondition without exposing the implementation? Oh right! You mean they have to cheat. I get it: # int f( uint n ) in { /*precondition*/ } body { real_f(n); } Hmmm. Maybe you should document that, if it's how it's supposed to be done.
(Furthermore, there's a problem with having the caller execute the
preconditions - the essential polymorphic aspect of them would be lost.)

Is it essential? I've been thinking about this (though perhaps now as much as you). Okay, let's say we have a class Stream with precondition that the stream must not have been closed. Now let's say we have a class AlignedStream, deriving from Stream, with additional precondition that the input must always be in multiples of four bytes. You're saying that: # Stream s = new AlignedStream(); # s.write("x"); should check the AlignedStream precondition, not the Stream precondition? I'm not so sure. Shouldn't a derived class always weaken, not strengthen, the precondition? That is, in the above example, AlignedStream shouldn't be deriving from Stream - or should make a runtime check or something. Can this kind of polymorphic aspect work like this? I may have misunderstood this part.
If you wish to *add* preconditions to an existing library function,

I don't. Arcane Jill
Aug 27 2004
parent "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cgmni7$1a9p$1 digitaldaemon.com...
This does not require that the vendor
ship the source or expose the implementation.

? How can you expose the in contract without the implentation?

You don't need to expose the contract - the contract just has to be executed. It can be executed in compiled form by the library, so the library (compiled with DbC on) doesn't need to ship the source.
(Furthermore, there's a problem with having the caller execute the
preconditions - the essential polymorphic aspect of them would be lost.)

Is it essential?

Yes. Bertrand Meyer goes through it all in his book on the topic "Object-Oriented Software Construction". He shows how it is essential for polymorphic functions to have polymorphic pre/post conditions.
 I've been thinking about this (though perhaps now as much as
 you). Okay, let's say we have a class Stream with precondition that the

 must not have been closed. Now let's say we have a class AlignedStream,

 from Stream, with additional precondition that the input must always be in
 multiples of four bytes. You're saying that:

 #    Stream s = new AlignedStream();
 #    s.write("x");

 should check the AlignedStream precondition, not the Stream precondition?

 not so sure. Shouldn't a derived class always weaken, not strengthen, the
 precondition?

A derived class weakens the precondition in that either the base precondition must be true OR the derived precondition. But when you have a reference to a base class, you do not know if is in reality a derived class, so you do not know which preconditions to check.
Aug 27 2004
prev sibling next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cglsov$tbd$1 digitaldaemon.com>, Walter says...

If a vendor is shipping libraries meant to be statically linked in, it would
be perfectly sensible for them to produce two builds - a 'release' build
with DbC turned off, and a 'debug' build with DbC on which the user uses to
debug his interface to that library.

I see only one version of phobos.lib in my download. Does it have DbC enabled? Jill
Aug 27 2004
parent "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cgmrdh$1bal$1 digitaldaemon.com...
 In article <cglsov$tbd$1 digitaldaemon.com>, Walter says...

If a vendor is shipping libraries meant to be statically linked in, it


be perfectly sensible for them to produce two builds - a 'release' build
with DbC turned off, and a 'debug' build with DbC on which the user uses


debug his interface to that library.

I see only one version of phobos.lib in my download. Does it have DbC enabled?

No. But it does come with source, and you can adjust the makefile to compile it with DbC on.
Aug 27 2004
prev sibling next sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Walter wrote:

<snip>
 I think there's a small misunderstanding when it comes to in 
 contracts.  Parameter validation for, let's say, operating system API 
 functions, is a requirement for it to be it release builds.  That is 
 akin to user input validation, and is not what DbC is for.

I disagree. The end user isn't going to be calling OS API functions directly, any more than functions in the library being talked about or functions in the app itself.
 DbC is for programs that, once debugged, can have the DbC removed.  
 Anything that exposes an interface to arbitrary other code needs to 
 keep its input validation turned on - this would include operating 
 system APIs, DLLs, shared libraries, and COM objects.

Indeed. Obviously an app wouldn't pass potentially bad user input to an API if the only check is DbC. It would do user input validation first. AIUI, the OP's request is for the DbC validation to work in those situations when it should, and when it is correct to use DbC - for checking that the application (not the user) is passing valid arguments to lib functions. This is part of the Phobos philosophy, isn't it? Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Aug 27 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Stewart Gordon" <smjg_1998 yahoo.com> wrote in message
news:cgn0jc$1deu$1 digitaldaemon.com...
 Walter wrote:

 <snip>
 I think there's a small misunderstanding when it comes to in
 contracts.  Parameter validation for, let's say, operating system API
 functions, is a requirement for it to be it release builds.  That is
 akin to user input validation, and is not what DbC is for.

I disagree. The end user isn't going to be calling OS API functions directly, any more than functions in the library being talked about or functions in the app itself.

Yes, they do. I call OS API functions all the time in my code.
 DbC is for programs that, once debugged, can have the DbC removed.
 Anything that exposes an interface to arbitrary other code needs to
 keep its input validation turned on - this would include operating
 system APIs, DLLs, shared libraries, and COM objects.

Indeed. Obviously an app wouldn't pass potentially bad user input to an API if the only check is DbC. It would do user input validation first.

Any OS API or DLL interface must consider all programs trying to connect to its functions as rogue, and must do parameter validation on them.
Aug 27 2004
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Walter wrote:
<snip>
I disagree.  The end user isn't going to be calling OS API functions
directly, any more than functions in the library being talked about or
functions in the app itself.

Yes, they do. I call OS API functions all the time in my code.

You mean when you're doing word processing, graphic design, browsing the web, playing games or whatever, you type in random API calls to be executed? <snip>
 Any OS API or DLL interface must consider all programs trying to connect to
 its functions as rogue, and must do parameter validation on them.

Yes. But a lib might also do its own checks as an added safety net. These might be done by either DbC or by checks in the release code. E.g. to make the lib functions fail-fast, or to translate vague zero return values into context-sensitive exceptions. Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Sep 01 2004
prev sibling next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 I think there's a small misunderstanding when it comes to in contracts.
 Parameter validation for, let's say, operating system API functions, is a
 requirement for it to be it release builds. That is akin to user input
 validation, and is not what DbC is for. DbC is for programs that, once
 debugged, can have the DbC removed. Anything that exposes an interface to
 arbitrary other code needs to keep its input validation turned on - this
 would include operating system APIs, DLLs, shared libraries, and COM
 objects.

What about the reasons I posted? Contracts are useful for optimization, and for making sure that later versions of a library haven't chosen to abandon some contracts that they used to obey.
 (Furthermore, there's a problem with having the caller execute the
 preconditions - the essential polymorphic aspect of them would be lost.)

Not necessarily. As I undertsand it, we currently have contracts that work like this: void CalledFunction(<whatever>) { /* in contracts */ /* body */ /* out contracts */ } Which is good, and is polymorphic. Don't change that. What I think is that when you have a function without a body, you should *add* these tests to the caller: /* in contracts */ CalledFunction(<whatever>); /* out contracts */ This retains polymorphism but also allows us to assert things about libraries for which we don't have the source. Yeah, it means that the assert()s are run twice, but, as you note, contracts are designed for debug builds, not release builds. Plus, it lets us run contracts even if the vendor only gave us a release build. Of course, this leads us quite quickly back to the question of contracts on interfaces and on delegates. The callee could add the contracts to his own code in those cases, as well...
 If you wish to *add* preconditions to an existing library function, and for
 good reason do not wish to modify the library source, the right approach is
 to put a wrapper around it with the DbC in the wrapper.

Yeah, but that's ugly...
Aug 27 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:cgnpqm$1rrr$1 digitaldaemon.com...
 What about the reasons I posted?  Contracts are useful for optimization,
 and for making sure that later versions of a library haven't chosen to
 abandon some contracts that they used to obey.

That's true, contracts are possibly valuable hints for optimization.
 (Furthermore, there's a problem with having the caller execute the
 preconditions - the essential polymorphic aspect of them would be lost.)

Not necessarily. As I undertsand it, we currently have contracts that work like this: void CalledFunction(<whatever>) { /* in contracts */ /* body */ /* out contracts */ } Which is good, and is polymorphic. Don't change that. What I think is that when you have a function without a body, you should *add* these tests to the caller: /* in contracts */ CalledFunction(<whatever>); /* out contracts */ This retains polymorphism but also allows us to assert things about libraries for which we don't have the source. Yeah, it means that the assert()s are run twice, but, as you note, contracts are designed for debug builds, not release builds. Plus, it lets us run contracts even if the vendor only gave us a release build.

It still breaks polymorphism. If you have: class A { void foo() in { ... } body { ... } } class B:A { void foo() in { ... } body { ... } } ... A a; ... a.foo(); you'll be calling B.foo(), not A.foo(), and A.foo()'s preconditions do not apply.
Aug 27 2004
parent reply Andy Friesen <andy ikagames.com> writes:
Walter wrote:
 It still breaks polymorphism. If you have:
     class A { void foo() in { ... } body { ... } }
     class B:A { void foo() in { ... } body { ... } }
     ...
     A a;
     ...
     a.foo();
 
 you'll be calling B.foo(), not A.foo(), and A.foo()'s preconditions do not
 apply.

Should they? It /is/ an A, after all. Shouldn't it be expected to behave like one? Polymorphic preconditions could (hypothetically) produce subtle bugs in code that satisfies the conditions set forth by an ancestor of A, but not A itself. (I haven't read any of Bertrand Meyer's writings, though, so I shall assume that I am wrong regardless) Also, how is DMD going to cope with inheriting a class whose source is not available? The contracts can't loosen/tighten properly if you don't know what they are. -- andy
Aug 27 2004
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Andy Friesen wrote:
 Walter wrote:
 
 It still breaks polymorphism. If you have:
     class A { void foo() in { ... } body { ... } }
     class B:A { void foo() in { ... } body { ... } }
     ...
     A a;
     ...
     a.foo();

 you'll be calling B.foo(), not A.foo(), and A.foo()'s preconditions do 
 not
 apply.

Should they? It /is/ an A, after all. Shouldn't it be expected to behave like one?

Right! It should be valid for the caller to assert A's preconditions, because the caller doesn't know about the fact that B loosened them. Likewise, the caller can assert A's postconditions, even though B may have tightened them. Frankly, I'm starting to think that this *should* be done. I'm thinking that the following code should be an error: class A { void foo(int i) in { assert(i> 0); } body {} } class B:A { void foo(int i) in { assert(i>=0); } body {} } A a = new B; a.foo(0); // assert should fail here So code, in order to work would have to do this: B b = cast(B)a; b.foo(0); // ok, uses B's preconditions
Aug 28 2004
prev sibling parent Norbert Nemec <Norbert Nemec-online.de> writes:
Walter wrote:
 I think there's a small misunderstanding when it comes to in contracts.
 Parameter validation for, let's say, operating system API functions, is a
 requirement for it to be it release builds. That is akin to user input
 validation, and is not what DbC is for. DbC is for programs that, once
 debugged, can have the DbC removed. Anything that exposes an interface to
 arbitrary other code needs to keep its input validation turned on - this
 would include operating system APIs, DLLs, shared libraries, and COM
 objects.

I strongly disagree. The basic question here is: who is to be trusted? Should a library trust the application to do the calls correctly? This is basically a design decision: for distributed systems, like the COM objects you are talking about, I agree that every object should thoroughly check any input it gets. The individual objects are rather weakly linked with each other, so each one should take care of it's own integrity. For DLLs, though, the matter is different: The code of the DLL runs under the same priviledges as the code of the application. In principle, the application has all the power to fiddle with the data of the DLL or walk around the interface in any way it likes. It is the responsibility of the application programmer not to abuse this power. It is not up to the library to put up safeguards. Whether or not the in-contracts of a library need to be checked depends on the stability of the application. There is no difference whether it is a static or dynamic library. True, a DLL can be linked in a different version, but whatever version you use, it has to follow the correct interface. Contracts are part of the interface. If you change the interface, be prepared to recompile the application. Therefore: in-contracts should be checked by the caller, and it depends on the stability of the caller whether this check has to be performed. Any check of the user-input or other untrusted data has to be performed before the check of the in-contract.
 If a vendor is shipping libraries meant to be statically linked in, it
 would be perfectly sensible for them to produce two builds - a 'release'
 build with DbC turned off, and a 'debug' build with DbC on which the user
 uses to debug his interface to that library. This does not require that
 the vendor ship the source or expose the implementation.

There is no point at all in shipping "debug"-compiles of a library. If the library code is correct, out-contracts and invariants need not be checked. On the other hand - no matter how correct the library is: it is up to the application developer to decide whether in-contracts need to be checked.
 (Furthermore, there's a problem with having the caller execute the
 preconditions - the essential polymorphic aspect of them would be lost.)

The worry about breaking polymorphism is totally unfounded: Already, there is the rule, that in-contracts are "or"ed together in inheriting classes. If you call a method on a class reference, you can always check the in-contracts of the reference class, even if the class of the object class is a subclass of that. If the check passes, this will be fine in any case (because of the "or" rule) Furthermore: the in-contracts may only depend on public information about a class. There is no point of talking about a "contract" if one of the parties cannot check whether it is kept.
Aug 30 2004