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.