enforce()?

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sun Jun 27 16:26:39 PDT 2010


On 06/21/2010 01:14 PM, Sean Kelly wrote:
> Andrei Alexandrescu Wrote:
>
>> On 06/20/2010 06:18 PM, Vladimir Panteleev wrote:
>>> On Mon, 21 Jun 2010 00:17:28 +0300, Walter Bright
>>> <newshound2 at digitalmars.com>  wrote:
>>>
>>>> An input to a dll is user input, and should be validated (for
>>>> the sake of security, and other reasons). Validating it is not
>>>> debugging.
>>>
>>> I don't understand why you're saying this. Security checks in
>>> DLL functions are pointless, for the reasons I already outlined:
>> [snip]
>>
>> I think the matter is simpler than that. Essentially DbC is
>> modular integrity checking. If Phobos enforce()s parameters in
>> calls, then it considers its own integrity a different matter than
>> the integrity of the application it's used with.
>>
>> If Phobos used contracts to validate parameters, it would directly
>> share responsibility for the integrity of the entire application.
>> That way, users will not be sure whether the failure is a bug in
>> Phobos or one in their own code.
>
> If a unrecoverable failure occurs within the process, does it matter
> where the error originated?

It does matter for the postmortem. Black box ftw.

> I've been thinking about this a bit and am starting to wonder about
> the benefit of distinguishing API boundary integrity checking vs.
> internal integrity checking.  First, what if a library eats its own
> dogfood?  If my library provides a public method to spawn threads and
> the library itself uses threads internally then I have two different
> methods of checking the integrity of my own library, each possibly
> throwing different exceptions (enforce throws Exception while assert
> throws AssertError).  At the very least, this seems like it could
> cause maintenance issues because a logic change deep within the
> library may require additional exception handling to deal with what
> are intended to be user-facing errors.

Any complex API will face at some point some tension between reusing and 
duplicating code. Per the classic joke:

"A mathematician was in a habit of making a cup of tea when working late 
at night. His normal method was to get the teapot from the cupboard, 
take the teapot to the sink, add water, heat to boiling, then make the 
cup of tea. Unfortunately, one night when he went to make tea, the 
teapot was already full of water and sitting on the stove. He thought 
about this for several minutes, then emptied the teapot and put it back 
in the cupboard, thereby reducing this to a previously solved problem."

It's often the case that an API throws some water for the sake of 
reusing itself.

> In a similar vein, if contracts are used within an API but a
> different mode of checking is used at the API boundary, when
> contracts are enabled in that API the user is faced with the bizarre
> issue of receiving AssertErrors from internal API logic errors but
> only Exceptions from his own logic errors for API boundary calls.
> When you say that DbC is modular I'd presume that means it's
> encapsulated within each distinct subsystem, but it seems like this
> isn't true at all.  The alternative is to turn DbC off in the library
> and either live with undefined behavior or a hard crash if there's a
> bug in the library, a circumstance which is again forced upon the
> user.

When I say DbC is modular I have in mind the following: "assert() inside 
a well-defined library entity (e.g. Phobos) is supposed to check the 
integrity of that entity, not the integrity of the entity using it."

I think that's reasonable. Integrity is a cross-cutting concern in 
memory-unsafe programs, but not in memory-safe programs. (Whoa, that's 
interesting.)

> Regarding DbC, I can't say that I've ever worked on a system where
> lives hung in the balance (an admittedly extreme example of where DbC
> is useful), but if I were sufficiently concerned about process
> integrity that I had contracts enabled then I don't think I would
> trust that a third-party library was bug-free and therefore didn't
> need its own contract checking in place.  Once I've accepted the cost
> of integrity checking I want it everywhere, not just in my own code.
> It makes for consistent error checking behavior (I'd assume there is
> a system in place to trap DbC errors specifically) and provides
> full-process integrity checking.

There's a large spectrum between "people will die" etc. and "I don't 
give a flying frak". I think an application under construction may well 
choose to use contracts for itself but not for the well-tested (ahem) 
Phobos.

> I think the only boundary that really matters is the process
> boundary.  Any error within a process has the same effect regardless
> of whether it's in user code or library code--corrupted memory,
> segfaults, etc--so why make a distinction between code I wrote and
> code someone else wrote?

For post-mortem and for assigning blame appropriately. If Phobos used 
DbC on user-passed inputs it would essentially share blame for the 
application integrity with all applications.

> By the same token, if the user chooses to
> disable contracts then why force them upon him for some errors but
> not others?  The user is making the explicit choice to run his
> process through a meat-grinder if something unexpected happens, he's
> effectively asserting that his code is perfect, so why tell him that
> "no, it's actually not."

That's a good point, but imho not enough to challenge the status quo. It 
might make sense to have a "unsafe" build for Phobos that assumes 
absolutely all arguments are correct. I wonder how much of an 
improvement it would bring.

> For me, the more difficult issue is how much integrity checking
> should be done.  For example, I created an AVL tree a while back that
> verified that the tree was still properly balanced after every API
> call.  This was great from a code verification standpoint, but the
> check completely violated the complexity guarantees and ultimately
> checked something that could have been proven to a reasonable degree
> of confidence through code reviews and unit testing.  Should checks
> like this be enabled automatically when DbC is turned on?  Should
> there be different levels of DbC?  In some respects I feel like
> there's a difference between in/out contracts and invariants, but
> even that doesn't seem completely right.  Thoughts?

I take no prisoners there: integrity checks MUST NOT affect complexity. 
Complexity is part of the spec, so such checks would automatically 
violate the spec.

At some point in its history, the binary search functions in 
std.algorithm had a enforce(isSorted) check. Should be somewhere buried 
in the svn history. It was my fault, and I didn't realize it until after 
I waited for one day next to a script to complete.


Andrei


More information about the Digitalmars-d mailing list