Contract checking (Re: enforce()?)

Jay Byrd JayByrd at rebels.com
Fri Sep 10 23:18:23 PDT 2010


On Wed, 30 Jun 2010 20:03:07 +0100, Norbert Nemec wrote:

> On 30/06/10 17:45, Sean Kelly wrote:
>> Norbert Nemec Wrote:
>>
>>> On 28/06/10 12:59, bearophile wrote:
>>>> Norbert Nemec:
>>>>> [...] to place code for input contract checking in the *calling*
>>>>> code. [...] Output contract checks, on the other hand should be
>>>>> compiled inside the returning routine.
>>>>
>>>> Is this a positive thing to do? Can this be done? (D must support
>>>> separate compilation, but in many situations this is not
>>>> done/necessary, so maybe in such situations it can be done). Is
>>>> Eiffel doing it? if it's a good thing and it's doable then what kind
>>>> of changes does it require to the compiler?
>>>
>>> These are good and pragmatic questions that you ask.
>>>
>>> The whole issue only arises when doing separate compilation of a
>>> library and an application. (I use the term "application" for any code
>>> that uses the library.)
>>>
>>> In an idea world (beware, I am switching of "pragmatic thinking mode"
>>> for a moment), I would describe the situation would as follows:
>>>
>>> Either part can be compiled in "debug" mode or in "release" mode.
>>> Debug mode in the library means that you want to debug the library
>>> code itself. Release mode in the library means that you trust the
>>> library code to be correct and switch off all internal checks.
>>
>> I see the choice of "release" for disabling contracts as a huge mistake
>> in nomenclature.  For libraries, I would ship a checked and unchecked
>> build (with -release disabled and enabled), but none with -debug or
>> -unittest set.  Those are for internal testing and the user shouldn't
>> care to turn on debug code in a library simply because he's debugging
>> his own app.
>>
>> The idea of compiling the "in" contract into the application code is an
>> interesting one, but I suspect it could be tricky.  Consider an
>> unchecked build of the library, a checked build of the app, and now
>> taking the address of a library function.  Worse, what if a library
>> routine returns the address of another library routine?  Now the
>> application has a reference to an unchecked version of the function,
>> even if the involved technical hurdles are surmounted (multiple entry
>> points or the like).
> 
> That's indeed an interesting aspect: Design by Contract (DbC) and
> function pointers. I am not sure how these concepts would merge properly
> at all.
> 
> The contracts are part of the interface, so they should in fact be part
> of the function pointer type! Of course this would quickly become
> ridiculous.
> 
> A strongly object oriented language like Eiffel can in principle do
> without function pointers. Instead, one can in most cases use classes
> with virtual functions that offer very similar functionality. A class
> interface comes with all the contracts, so everything is safe and sound.
> 
> I really do not know how to deal with function pointers in the clean DbC
> paradigm. If you assign a function with input contracts to a function
> pointer, whoever uses the pointer does not know about the contracts.
> This however, breaks down the strong DbC concept and turns contracts
> into mere run time checks.
> 
> Does this mean that D should give up the goal of proper DbC? Simply do
> the pragmatic thing and pick the best pieces from DbC without worrying
> about formal completeness? I guess so...

This is all very confused, and is reflected in D implementing contracts 
all wrong. Contracts do not belong to function pointers or any other 
dynamic state -- they apply to the invoker, and thus the static type. 
Isn't that obvious? If I have Foo f = getSomeFoo(); result = f.method
(args), the args must satisfy the contract for Foo.method(), regardless 
of what method() in the object returned by getSomeFoo() is willing to 
accept (it must, of course, not require more than Foo.method() does; TDPL 
at least gets that right). And the guarantees on result must be those 
promised by Foo.method(), not some much weaker promise given by method() 
in its base class (and there is no need to check stronger guarantees made 
by method() the actual derived object, since the caller didn't ask for 
them).

The D implementation works much too hard to get the wrong result, both in 
complexities of the compiler and in executing a bunch of irrelevant code 
that is ignored if it fails in preconditions or succeeds in 
postconditions, and after all that it fails to enforce requirements it 
should and to guarantee promises that it should.

-- JB


More information about the Digitalmars-d mailing list