View mode: basic / threaded / horizontal-split · Log in · Help
February 29, 2012
Thoughts about in contract inheritance
There's been a lot of debate about the inheritance of in contracts, in particular what's 
supposed to happen if a method in a base class has an in contract and its override doesn't.
http://d.puremagic.com/issues/show_bug.cgi?id=6856

The spec isn't explicit on whether the overridden method retains the base's in contract 
unchanged or loses its in contract altogether.

Some claim that the absence of an in contract is just syntactic sugar for an empty in 
contract, in which case the override loses the in contract (can be called with any 
arguments of the correct types).  This is the view taken by DMD.

But others claim that you shouldn't have to specify an in contract in order to inherit the 
one you already have, and that if you want to remove it altogether then you should have to 
do it explicitly.

What's more, there's a hole in the current equivalence between no in contract and an empty 
in contract: in only the former case does the compiler reject an in contract on an 
override further down the hierarchy.  This will want to be fixed if an explicit empty in 
contract becomes the only way to lift all argument restrictions when overriding a method.


Moreover, there are hijacking vulnerabilities.

http://dlang.org/hijack.html
addresses the danger that a derived class may define a method, and then a later version of 
the base class coincidentally defines a method with the same name but different semantics. 
 Further problems in this area would ensue if the default behaviour were to pass through 
the base class's in contract unchanged.  Though doing and dusting
http://d.puremagic.com/issues/show_bug.cgi?id=3836
would alleviate this.

Another scenario I've thought of is:
- library class defines a method with an in contract
- application class overrides this method, and has the same argument restrictions as the 
library class
- a later version of the library class widens the range of acceptable inputs
- however, the app's override is not prepared to deal with inputs that are newly allowed 
by the API

I don't know if there's a way to deal with this short of doing away with in contract 
inheritance altogether.  Of course, the app programmer could protect against this by 
duplicating the contract checking in the body of the method, and doing so would even show 
whether the input to the function was actually outside the range allowed by the API or 
merely outside the range that the app class's version of the method can deal with.  But 
it's better not to have to duplicate code like this.

But if we avoided this by getting rid of in contract inheritance, it might just lead more 
programmers to break the inheritance model by forbidding operations on objects of the 
derived class that are allowed on objects of the base class.  I suppose this is a variant of
http://en.wikipedia.org/wiki/Circle-ellipse_problem
It would also get in the way of another proposal I'm inclined to agree with, that anything 
that calls a method on an object reference of the base class type should be required to 
adhere to the base class's API, of which the in contract is a part.
http://d.puremagic.com/issues/show_bug.cgi?id=6857
(see also the thread "define in contract according to the caller, not the callee." on this 
'group)

Still, is there a good way of dealing with the scenario I've brought up here?


Another thing that's needed for a robust DbC system in D:
http://d.puremagic.com/issues/show_bug.cgi?id=6549

Stewart.
February 29, 2012
Re: Thoughts about in contract inheritance
On 02/29/2012 03:06 PM, Stewart Gordon wrote:
> There's been a lot of debate about the inheritance of in contracts, in
> particular what's supposed to happen if a method in a base class has an
> in contract and its override doesn't.
> http://d.puremagic.com/issues/show_bug.cgi?id=6856
>
> The spec isn't explicit on whether the overridden method retains the
> base's in contract unchanged or loses its in contract altogether.
>

The front page of the web site is quite explicit about this:

// Interfaces and classes
interface Printable {
    void print(uint level)
    in { assert(level > 0); } // contract is part of the interface
}

// Interface implementation
class Widget : Printable {
    void print(uint level) { ... } // <-- assumed to inherit contract
}

// Single inheritance of state
class ExtendedWidget : Widget {
    override void print(uint level)
    in { /* weakening precondition is okay */ } body { // <-- see here!
         ... level may be 0 here ...
    }
}

Anyway, probably it is not stated explicitly in the relevant parts of 
the spec because it is assumed that the reader is familiar with similar 
features in other languages.

> Some claim that the absence of an in contract is just syntactic sugar
> for an empty in contract, in which case the override loses the in
> contract (can be called with any arguments of the correct types). This
> is the view taken by DMD.
>

This is the bug in DMD.

> But others claim that you shouldn't have to specify an in contract in
> order to inherit the one you already have, and that if you want to
> remove it altogether then you should have to do it explicitly.
>
> What's more, there's a hole in the current equivalence between no in
> contract and an empty in contract: in only the former case does the
> compiler reject an in contract on an override further down the
> hierarchy.
> This will want to be fixed if an explicit empty in contract
> becomes the only way to lift all argument restrictions when overriding a
> method.
>

Probably.

>
> Moreover, there are hijacking vulnerabilities.
>
> http://dlang.org/hijack.html
> addresses the danger that a derived class may define a method, and then
> a later version of the base class coincidentally defines a method with
> the same name but different semantics. Further problems in this area
> would ensue if the default behaviour were to pass through the base
> class's in contract unchanged.


Not at all. If anything, it would alleviate the issue. Likely, assertion 
failures would be introduced that show that there is a consistency 
problem in the application.

> Though doing and dusting
> http://d.puremagic.com/issues/show_bug.cgi?id=3836
> would alleviate this.
>

Indeed.

> Another scenario I've thought of is:
> - library class defines a method with an in contract
> - application class overrides this method, and has the same argument
> restrictions as the library class
> - a later version of the library class widens the range of acceptable
> inputs
> - however, the app's override is not prepared to deal with inputs that
> are newly allowed by the API
>
> ...

This is not a contract-related problem. It is a breaking API change, 
whether or not the library class defines language level contracts.

>
> But if we avoided this by getting rid of in contract inheritance,

If we want to avoid this we'd have to get rid of inheritance altogether, 
not just contract inheritance. A contract always has a corresponding 
implementation.

> it might just lead more programmers to break the inheritance model by
> forbidding operations on objects of the derived class that are allowed
> on objects of the base class. I suppose this is a variant of
> http://en.wikipedia.org/wiki/Circle-ellipse_problem
> It would also get in the way of another proposal I'm inclined to agree
> with, that anything that calls a method on an object reference of the
> base class type should be required to adhere to the base class's API, of
> which the in contract is a part.
> http://d.puremagic.com/issues/show_bug.cgi?id=6857
> (see also the thread "define in contract according to the caller, not
> the callee." on this 'group)
>
> Still, is there a good way of dealing with the scenario I've brought up
> here?
>

Library writers shouldn't silently change functionality and/or redefine 
interfaces. The new stuff should be introduced under a different name 
and the old name should be deprecated. (As an inferior alternative, the 
library user could just read the change log and notice that there is now 
a problem.)

Anyway, this second scenario has a similar severity under the current 
contract inheritance behavior in DMD 2.058 and the right inheritance 
behavior in a future version of DMD.


>
> Another thing that's needed for a robust DbC system in D:
> http://d.puremagic.com/issues/show_bug.cgi?id=6549
>
> Stewart.
February 29, 2012
Re: Thoughts about in contract inheritance
On 29/02/2012 14:44, Timon Gehr wrote:
<snip>
>> The spec isn't explicit on whether the overridden method retains the
>> base's in contract unchanged or loses its in contract altogether.
>
> The front page of the web site is quite explicit about this:

What web site?  Certainly not www.digitalmars.com or d-programming-language.org or 
dlang.org as I look at the moment.

<snip>
> Anyway, probably it is not stated explicitly in the relevant parts of the spec because it
> is assumed that the reader is familiar with similar features in other languages.

Then it's a hole in the spec.  If it's only meant to state how D differs from some other 
language, it would have to state what language that is.

<snip>
>> Another scenario I've thought of is:
>> - library class defines a method with an in contract
>> - application class overrides this method, and has the same argument
>> restrictions as the library class
>> - a later version of the library class widens the range of acceptable
>> inputs
>> - however, the app's override is not prepared to deal with inputs that
>> are newly allowed by the API
>>
>> ...
>
> This is not a contract-related problem. It is a breaking API change, whether or not the
> library class defines language level contracts.

How do you mean?

<snip>
> Library writers shouldn't silently change functionality and/or redefine interfaces.
<snip>

So you think that, once a library is written and released, no new functionality should 
ever be added?

Stewart.
February 29, 2012
Re: Thoughts about in contract inheritance
On 02/29/2012 05:26 PM, Stewart Gordon wrote:
> On 29/02/2012 14:44, Timon Gehr wrote:
> <snip>
>>> The spec isn't explicit on whether the overridden method retains the
>>> base's in contract unchanged or loses its in contract altogether.
>>
>> The front page of the web site is quite explicit about this:
>
> What web site? Certainly not www.digitalmars.com or
> d-programming-language.org or dlang.org as I look at the moment.
>

The official website, d-programming-language.org or dlang.org. You have 
to click on "See example". It is at the first bulb of "Power".

> <snip>
>> Anyway, probably it is not stated explicitly in the relevant parts of
>> the spec because it
>> is assumed that the reader is familiar with similar features in other
>> languages.
>
> Then it's a hole in the spec. If it's only meant to state how D differs
> from some other language, it would have to state what language that is.
>

It surely is a hole in the spec.


> <snip>
>>> Another scenario I've thought of is:
>>> - library class defines a method with an in contract
>>> - application class overrides this method, and has the same argument
>>> restrictions as the library class
>>> - a later version of the library class widens the range of acceptable
>>> inputs
>>> - however, the app's override is not prepared to deal with inputs that
>>> are newly allowed by the API
>>>
>>> ...
>>
>> This is not a contract-related problem. It is a breaking API change,
>> whether or not the
>> library class defines language level contracts.
>
> How do you mean?
>

Language level contracts as in D are (basically) a way to introduce 
runtime checks into interfaces. If the interface is redefined then that 
potentially breaks all code that implements the interface, even if this 
is not explicitly stated in form of contracts.

> <snip>
>> Library writers shouldn't silently change functionality and/or
>> redefine interfaces.
> <snip>
>
> So you think that, once a library is written and released, no new
> functionality should ever be added?
>

Obviously there can be additional changes without redefining what an 
existing part of the API does.

Timon Gehr wrote:
> The new stuff should be introduced under a different name and the old name should be deprecated.
February 29, 2012
Re: Thoughts about in contract inheritance
Le 29/02/2012 17:26, Stewart Gordon a écrit :
> So you think that, once a library is written and released, no new
> functionality should ever be added?

We are reaching the discussion of evolving API.

I do think that if change such a thing, you should rename your function 
to express its new functionality. And mark the previous function as 
deprecated, with forwarding to the deprecated function, when 
appropriate, for a transitional period, so code using the old API have 
some time to adapt before it is removed completely.

Additionally, this transition process require to be able to provide a 
default implementation for interface's function. Java did face the same 
problem (actually it is worse because of bad choice in generic 
implementation) and it will be implemented in java 8 or 9 IIRC.

I think this problem is way more broad than a contract issue. Both seems 
orthogonal to me.

Any change in the base class can mess up its subclasses, the change 
being in contract or somewhere else isn't that important. It is an API 
evolution strategy problem.
February 29, 2012
Re: Thoughts about in contract inheritance
On 29/02/2012 17:01, Timon Gehr wrote:
<snip>
> The official website, d-programming-language.org or dlang.org. You have to click on "See
> example". It is at the first bulb of "Power".

But the comment "// <-- assumed to inherit contract" isn't actually there, so what's 
"quite explicit" about it?  Besides, the interface declaration there isn't allowed by the 
grammar.

<snip>

> Language level contracts as in D are (basically) a way to introduce runtime checks into
> interfaces. If the interface is redefined then that potentially breaks all code that
> implements the interface, even if this is not explicitly stated in form of contracts.

You mean the fault lies on the part of the library creator for widening the in contract of 
a non-final method?

<snip>
> Obviously there can be additional changes without redefining what an existing part of the
> API does.
<snip>

So you consider illegal inputs to a function to be part of the API?

Stewart.
February 29, 2012
Re: Thoughts about in contract inheritance
On 02/29/2012 07:06 PM, Stewart Gordon wrote:
> On 29/02/2012 17:01, Timon Gehr wrote:
> <snip>
>> The official website, d-programming-language.org or dlang.org. You
>> have to click on "See
>> example". It is at the first bulb of "Power".
>
> But the comment "// <-- assumed to inherit contract" isn't actually
> there, so what's "quite explicit" about it?

That was just for documentation... The part that is explicit is:

// Single inheritance of state
class ExtendedWidget : Widget {
    override void print(uint level)
    in { /* weakening precondition is okay */ } body {
        ... level may be 0 here ...
    }
}

The fact that this weakens the precondition tells us that it was not 
weakened before.

> Besides, the interface
> declaration there isn't allowed by the grammar.
>

The compiler implements it.

> <snip>
>
>> Language level contracts as in D are (basically) a way to introduce
>> runtime checks into
>> interfaces. If the interface is redefined then that potentially breaks
>> all code that
>> implements the interface, even if this is not explicitly stated in
>> form of contracts.
>
> You mean the fault lies on the part of the library creator for widening
> the in contract of a non-final method?
>

Sure. This adds additional requirements that any deriving class needs to 
fulfill. It is not a backwards-compatible change.

> <snip>
>> Obviously there can be additional changes without redefining what an
>> existing part of the
>> API does.
> <snip>
>
> So you consider illegal inputs to a function to be part of the API?
>

Yes. Put differently, I consider legal inputs to a overridable virtual 
function to be part of the API.
February 29, 2012
Re: Thoughts about in contract inheritance
On 29/02/2012 19:24, Timon Gehr wrote:
<snip>
> That was just for documentation... The part that is explicit is:
>
> // Single inheritance of state
> class ExtendedWidget : Widget {
> override void print(uint level)
> in { /* weakening precondition is okay */ } body {
> ... level may be 0 here ...
> }
> }
>
> The fact that this weakens the precondition tells us that it was not weakened before.

Of course.  I see now.

>> Besides, the interface declaration there isn't allowed by the grammar.
>
> The compiler implements it.

So what?  It's a bug that compiler behaviour doesn't match the documentation.  You seem to 
be agreed that this is the case with what happens to the contract where the override has 
no InStatement at all.

<snip>
>> So you consider illegal inputs to a function to be part of the API?
>
> Yes. Put differently, I consider legal inputs to a overridable virtual function to be part
> of the API.

That legal inputs are part of the API is something we're agreed on.  It's illegal inputs 
we were debating.

But I can see what you really mean: the spec of what inputs to an overridable function are 
legal and what inputs are illegal is part of the API.

Stewart.
February 29, 2012
Re: Thoughts about in contract inheritance
On 02/29/2012 09:30 PM, Stewart Gordon wrote:
> On 29/02/2012 19:24, Timon Gehr wrote:
> <snip>
>> That was just for documentation... The part that is explicit is:
>>
>> // Single inheritance of state
>> class ExtendedWidget : Widget {
>> override void print(uint level)
>> in { /* weakening precondition is okay */ } body {
>> ... level may be 0 here ...
>> }
>> }
>>
>> The fact that this weakens the precondition tells us that it was not
>> weakened before.
>
> Of course. I see now.
>

OK.

>>> Besides, the interface declaration there isn't allowed by the grammar.
>>
>> The compiler implements it.
>
> So what? It's a bug that compiler behaviour doesn't match the
> documentation. You seem to be agreed that this is the case with what
> happens to the contract where the override has no InStatement at all.
>

I think in this case the documentation has not been updated yet because 
the feature is still experimental. Anyway, even with the documentation, 
many essential parts of the language are documented only on this 
newsgroup or through the compiler implementation, unspecified completely 
or only partly specified. I don't think it is currently possible to 
become completely proficient in D without reading this newsgroup.


> <snip>
>>> So you consider illegal inputs to a function to be part of the API?
>>
>> Yes. Put differently, I consider legal inputs to a overridable virtual
>> function to be part
>> of the API.
>
> That legal inputs are part of the API is something we're agreed on. It's
> illegal inputs we were debating.
>
> But I can see what you really mean: the spec of what inputs to an
> overridable function are legal and what inputs are illegal is part of
> the API.
>
> Stewart.

An input that is not legal is illegal and vice-versa.
February 29, 2012
Re: Thoughts about in contract inheritance
On Wednesday, February 29, 2012 21:45:19 Timon Gehr wrote:
> On 02/29/2012 09:30 PM, Stewart Gordon wrote:
> > So what? It's a bug that compiler behaviour doesn't match the
> > documentation. You seem to be agreed that this is the case with what
> > happens to the contract where the override has no InStatement at all.
> 
> I think in this case the documentation has not been updated yet because
> the feature is still experimental. Anyway, even with the documentation,
> many essential parts of the language are documented only on this
> newsgroup or through the compiler implementation, unspecified completely
> or only partly specified. I don't think it is currently possible to
> become completely proficient in D without reading this newsgroup.

It's certainly the case that when the spec does not match the compiler, you 
_cannot_ assume that it's the spec that's correct. We have the spec, the 
compiler, _and_ TDPL to worry about. If they don't agree, then TDPL is most 
likely to be right out of the 3, but there's no guarantee. And definitely 
between the spec and the compiler, you can't trust that the spec is more 
correct than the compiler. It depends entirely on what the mismatch is. There 
have been some recent fixes to the spec, so it's not as bad as it used to be, 
but you can't really assume that what the spec is correct when it and the 
compiler disagree.

- Jonathan M Davis
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home