May 05, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #50 from deadalnix <deadalnix@gmail.com> 2012-05-05 05:04:07 PDT ---
(In reply to comment #33)
> (In reply to comment #32)
> > (In reply to comment #30)
> > > fizzbuzz() clearly has a bug. It will fail if given an A which isn't actually a
> > > B.
> > 
> > Exactly.  fizzbuzz is calling a method of A, not a method of B.  As such, as I've already said, it must conform to A's API, but it is failing to do so.
> 
> First off, if fizzbuzz() is passed an A, then it will (correctly) fail. Where's
> the bug in the contract design? Secondly, the -1 may not be a literal, it may
> be a value computed from B, as in:
> 
> 
>  class A {
>      void foo(int x) in { assert(x > 0); } body {}
>      int bar() { return 1; }
>  }
> 
>  class B : A {
>      void foo(int x) in { assert(x > -2); } body {}
>      int bar() { return -1; }
>  }
> 
>  void fizzbuzz(A a) {
>      a.foo(bar());
>  }
> 
> So, clearly is NOT required that a.foo(arg) pass A.foo's contract.
> 
> The design of OOP and contracts is sound. Correct programs will pass, and incorrect programs will fail. I don't see any "corner cases" otherwise.

This piece of code is good for an horror museum.

If bar is a valid argument for foo, then A should have the following definition :

class A {
    void foo(int x) in { assert(x > 0); } body {}
    int bar() out(result) { assert( result > 0); } body { return 1; }
}

And, with such a definition, out contract would prevent B.bar to return -1 .

BTW, I found this PDF on Meyer's website : se.ethz.ch/~meyer/publications/computer/contract.pdf . It seems that he decided to publish the part of the book that is relevant to us online. I'm reading it right now.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 05, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #51 from deadalnix <deadalnix@gmail.com> 2012-05-05 06:22:21 PDT ---
(In reply to comment #49)
> Runtime polymorphism is about overriding behaviour, not overriding legality.
> 

This is, indeed, the whole essence of the problem.

(In reply to comment #48)
> And lastly, your request is quite different from Example #1, which is asserting
> that the contract for A.foo() must always pass, even if it's calling B.foo().
> 

I'm afraid this explains why we are not getting anywhere in this discussion. The whole proposal is, and have always been that one. Read again example code I've posted and which check is performed in which case.

I have read Mayer's document about contract and OOP, and I want to comment here the explanations given by Meyer himself. Quoted passage are from the book. Mayer have a very similar example to the one I gave above. You have classes A and B inheriting from A. A define a method r with an in contract, that B override. A variable u of type A is used in method X.

You can map as this : r <=> foo, u <=> a and X <=> fizzbuzzA

« To ascertain the properties of the call u.r, the author of X can only look at the contract for r in A. Yet, because of dynamic binding, A may subcontract the execution of r to B, and it is B’s contract that will be applied. »

Here, Meyer is stating HOW thing work before it explain WHY. Hence, the whole stuff is to show how this specific implementation satisfy certain properties. Let see what are these properties and what is the situation with the new proposed behavior.

« How do you avoid “fooling” X in the process? There are two ways B could violate its prime contractor’s promises:

 - B could make the precondition stronger, raising the risk that some calls
that are correct from x’s viewpoint (they satisfy the original client
obligations) will not be handled properly. »

I omitted the second one as it is about out contract so off topic here. the constraint expressed here is respected by both current solution, and proposed solution.

« None of this, then, is permitted. But the reverse changes are of course legitimate. A redeclaration may weaken the original’s precondition or it may strengthen the postcondition. »

Again, both proposals allow this.

« Redeclaration. for all the power it brings to software development. is not a way to turn a routine into something completely different. The new version must remain compatible with the original specification. although it may improve on it. The noted rules express this precisely. »

Meyer is right. His rules express this. But proposed rules express this too.

He then conclude « In this way. the new precondition is guaranteed to be weaker than or equal to the originals, and the new postcondition is guaranteed to be stronger than or equal to the originals. »

Again according to Meyer's argumentation, both behavior are corrects.

It appears (and I have read the chapter several times to make sure I'm not missing something) that Meyer's doesn't provide any argument about our special case here.

I'm sorry, but this reading can't close the discussion.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 05, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #52 from Andrei Alexandrescu <andrei@metalanguage.com> 2012-05-05 08:54:17 PDT ---
(In reply to comment #51)
> I'm sorry, but this reading can't close the discussion.

I think it does. The proposed behavior does not allow this:

"None of this, then, is permitted. But the reverse changes are of course legitimate. A redeclaration may weaken the original’s precondition or it may strengthen the postcondition. Changes of either kind mean that the subcon- tractor does a better job than the original contractor-which there is no reason to prohibit."

Doing a better job is succeeding where the parent method would have failed its precondition. It all boils down to the fact that it's natural to have methods that can't work in the parent but do work in the child.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 05, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #53 from deadalnix <deadalnix@gmail.com> 2012-05-05 08:58:21 PDT ---
(In reply to comment #52)
> (In reply to comment #51)
> > I'm sorry, but this reading can't close the discussion.
> 
> I think it does. The proposed behavior does not allow this:
> 
> "None of this, then, is permitted. But the reverse changes are of course legitimate. A redeclaration may weaken the original’s precondition or it may strengthen the postcondition. Changes of either kind mean that the subcon- tractor does a better job than the original contractor-which there is no reason to prohibit."
> 

And indeed, it is not prohibited.

> Doing a better job is succeeding where the parent method would have failed its precondition. It all boils down to the fact that it's natural to have methods that can't work in the parent but do work in the child.

It is stated (quoting myself) that :
« fizzbuzzB(B b) {
    b.foo(); // (A.foo OR B.foo)'s in contract is valid
} »

Which exactly the behavior you talk about.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 05, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #54 from Stewart Gordon <smjg@iname.com> 2012-05-05 09:21:02 PDT ---
(In reply to comment #52)
> Doing a better job is succeeding where the parent method would have failed its precondition. It all boils down to the fact that it's natural to have methods that can't work in the parent but do work in the child.

And to take advantage of this, one would deal with the child directly.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 06, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857


Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |
           Severity|enhancement                 |normal


--- Comment #55 from Walter Bright <bugzilla@digitalmars.com> 2012-05-05 17:27:24 PDT ---
Mea culpa.

I read Meyer's book again. Chapter 16.1 "Cutting out the middleman" pg. 575 says:

"A client of MATRIX must satisfy the original (stronger) precondition, and may only expect the original (weaker) postcondition; even if its request gets served dynamically by NEW_MATRIX it has no way of benefiting from the broader tolerance of inputs and tighter precision of results. To get this improved specification it must declare the matric to be of type NEW_MATRIX, thereby losing access to other implementations represented by descendants of MATRIX that are not also descendants of NEW_MATRIX."

(MATRIX is the base class, NEW_MATRIX is the derived class.)

So I'm reopening it as a normal bug.

Unfortunately, I do not currently see a reasonable way of implementing this. Fortunately, as is it does not inhibit correct programs, it only accepts some invalid ones.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 06, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #56 from Don <clugdbug@yahoo.com.au> 2012-05-06 00:36:04 PDT ---
(In reply to comment #55)
> Mea culpa.
> 
> I read Meyer's book again. Chapter 16.1 "Cutting out the middleman" pg. 575 says:
> 
> "A client of MATRIX must satisfy the original (stronger) precondition, and may only expect the original (weaker) postcondition; even if its request gets served dynamically by NEW_MATRIX it has no way of benefiting from the broader tolerance of inputs and tighter precision of results. To get this improved specification it must declare the matric to be of type NEW_MATRIX, thereby losing access to other implementations represented by descendants of MATRIX that are not also descendants of NEW_MATRIX."
> 
> (MATRIX is the base class, NEW_MATRIX is the derived class.)
> 
> So I'm reopening it as a normal bug.
> 
> Unfortunately, I do not currently see a reasonable way of implementing this. Fortunately, as is it does not inhibit correct programs, it only accepts some invalid ones.

I think implementation is simpler than what happens at present. You need a
static function thunk for each virtual function with a precondition.
For example:

class A {
  int foo(int n) in { assert(n >0); } body { return n; }
}
class B {
  int foo(int n) { return n+1; } // no change to precondition
}
class C {
  int foo(int n) in { assert(n > -10 && n<-6); } body { return n+2; }

becomes (I've put 'virtual' in to make things clearer):

class A {
 static void foo_in(A x) {  assert(n>0); }
 virtual int foo(int n) { foo_in(this, n);  return foo_body(n); }
 virtual int foo_body(int n) { return n; }
}

class B {
 virtual int foo_body(int n) { return n+1; }
}

class C {
  static void foo_in(C x, int n) { assert(n > -10 && n<-6); }
  virtual int foo(int n ) {
    if (C.foo_in(this, n)) return foo_body(n); // passed C's precondition
   return A.foo(n); // failed, try A's precondition
 }
  virtual int foo_body(int n) { return n+2; }
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 06, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #57 from deadalnix <deadalnix@gmail.com> 2012-05-06 02:20:47 PDT ---
(In reply to comment #55)
> So I'm reopening it as a normal bug.
> 

Thank you !

> Unfortunately, I do not currently see a reasonable way of implementing this. Fortunately, as is it does not inhibit correct programs, it only accepts some invalid ones.

That is not this complicated. you need a final function that check the in contract, then call another virtual function that actually execute the function body and the out contract.

With such a configuration, the in contract is checked and only then the virtual dispatch is done.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 06, 2012
On 5/6/2012 2:19 AM, d-bugmail@puremagic.com wrote:
> That is not this complicated. you need a final function that check the in
> contract, then call another virtual function that actually execute the function
> body and the out contract.
>
> With such a configuration, the in contract is checked and only then the virtual
> dispatch is done.


It's not that simple. Several considerations have to be met:

1. Because of struct construction/destruction, you really only want to construct the parameter list *once*, but you're calling two functions with the same parameter list.

2. Variadic functions mean that one function cannot forward to another one using standard functions. (Perhaps a dirty magic thunk can work.)

3. The presence or absence of contracts must not change the ABI of the function.

4. The virtual table must be unchanged.

5. It's not so practical to jump into the middle of another function - things just aren't designed that way.

6. The caller now has to be aware of contracts in the called function, this was never necessary before.
May 06, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #59 from deadalnix <deadalnix@gmail.com> 2012-05-06 03:27:45 PDT ---
(In reply to comment #58)
> It's not that simple. Several considerations have to be met:
> 
> 1. Because of struct construction/destruction, you really only want to construct the parameter list *once*, but you're calling two functions with the same parameter list.
> 

Arguments should really be const in contracts. And if they are not modified (and considering that argument are the same for in contract function and real function) it is probably doable to jump right into the implementation function just after the prolog.

> 2. Variadic functions mean that one function cannot forward to another one using standard functions. (Perhaps a dirty magic thunk can work.)
> 

Again, I think jumping right into the function can do the trick.

> 3. The presence or absence of contracts must not change the ABI of the function.
> 

That one seem really difficult. An alternative would be to do 2 function calls, one for in contract and one for the function body. Another option is to consider contract as being part of the declaration and so cannot be opaque.

> 4. The virtual table must be unchanged.
> 

With proposed solution it will, even if a direct call throw the virtual table would skip the in contract.

> 5. It's not so practical to jump into the middle of another function - things just aren't designed that way.
> 

Already did that. This isn't something you want to do every day, but it doable.

> 6. The caller now has to be aware of contracts in the called function, this was never necessary before.

Indeed, the caller now has to be aware of the existence of in contracts. This is required if you want to check contract according to caller's type and not real type.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------