digitalmars.D - Shortcut evaluation for hierarchy of in contracts
- Jens Mueller (40/40) Jun 30 2011 Hi,
- bearophile (4/24) Jun 30 2011 I think it's a DMD bug, fit for Bugzilla if not already present.
- Jens Mueller (4/28) Jun 30 2011 The shortcut evaluation is specified in TDPL. That's why I assume the
- Robert Jacques (6/37) Jun 30 2011 A subclass must be able to handle all the inputs the base class accepts,...
- Jens Mueller (3/44) Jun 30 2011 So you agree that the current behavior is error-prone?
- Robert Jacques (6/49) Jul 01 2011 No. I think the current behavior is correct. In fact, if anything, D
- simendsjo (3/53) Jul 01 2011 Have to agree. By tightening the contracts in subclasses, you'll break
- Andrei Alexandrescu (3/7) Jul 02 2011 That is a sensible enhancement.
- Timon Gehr (5/12) Jul 02 2011 How so? The child class should still be able to take more general inputs...
- David Nadlinger (21/35) Jul 02 2011 In any case, I consider the current implementation buggy, as having no
- Daniel Murphy (3/23) Jul 02 2011 There's a patch for this!
- Walter Bright (2/8) Jul 02 2011 This would throw out the whole idea of 'loosening'.
- Andrei Alexandrescu (4/13) Jul 02 2011 I mean disallow an override to add an "in" contract where the overridden...
- Walter Bright (2/4) Jul 02 2011 Ok, that makes sense.
- Andrei Alexandrescu (3/8) Jul 02 2011 I hereby am making a request for a pull request.
- Daniel Murphy (4/6) Jul 02 2011 I'll do it if you make a bug report for it.
- Andrei Alexandrescu (4/13) Jul 02 2011 No. They should be the workaround.
- Daniel Murphy (3/4) Jul 02 2011 What should the error message be?
- Walter Bright (7/9) Jul 01 2011 The behavior is both intended and crucial to the notion of contract inhe...
- Jens Mueller (11/20) Jul 01 2011 Yes. But the problem with passing only one of the in contracts is that
- Walter Bright (3/12) Jul 01 2011 The loosening rule is that one of the in contracts must pass. That's exa...
- Daniel Murphy (9/12) Jul 01 2011 This might be the recommended way to do it, but this is not enforced by ...
- Timon Gehr (69/81) Jul 01 2011 Actually that is not true. One passing contract is enough. The current (...
- Daniel Murphy (8/21) Jul 01 2011 I don't disagree that tightening contracts for derived functions is a ba...
- Timon Gehr (14/39) Jul 01 2011 How would you catch it? I am sure it cannot be caught trivially:
- David Nadlinger (6/15) Jul 01 2011 In my opinion, one *is* able to declare the child contract invalid in
- Walter Bright (5/9) Jul 01 2011 The rule for in contracts is:
- Daniel Murphy (7/13) Jul 01 2011 In case 2, the contract is invalid.
- Timon Gehr (22/37) Jul 01 2011 My first post on the subject features an example where case 2 can occur ...
- Timon Gehr (2/2) Jul 01 2011 Disregard "That is case 3. And it is perfectly valid."
- Timon Gehr (4/4) Jul 01 2011 Disregard
- Daniel Murphy (35/76) Jul 01 2011 Sorry for the mixin of terms, I meant parent in the tree, which is the
- Jesse Phillips (2/11) Jul 01 2011 No, in this case the child has allowed more values of 'a' then the paren...
- Timon Gehr (29/76) Jul 01 2011 It is equivalent to:
- Daniel Murphy (5/16) Jul 01 2011 And it finally clicks! Thanks for taking the time to explain it to me.
- Jens Mueller (12/31) Jul 05 2011 Just found the time to go over this thread.
- Simen Kjaeraas (7/23) Jul 01 2011 for a deeper hierarchy, one would simply have a bool flag - 'has a contr...
- Timon Gehr (20/47) Jul 01 2011 All arguments given for a runtime check of this kind are the result of a
- Simen Kjaeraas (5/22) Jul 01 2011 Absolutely. I was in the process of answering your other post where you
- Daniel Murphy (10/16) Jul 01 2011 If I understand this correctly, you think the following code should be
- Walter Bright (7/16) Jul 01 2011 It isn't. An overriding function *must* accept all input that the overri...
- Walter Bright (2/18) Jul 01 2011 Right.
- Walter Bright (4/9) Jul 01 2011 Yes, it is.
- Daniel Murphy (3/14) Jul 01 2011 Yeah, I did get this eventually.
Hi, a student of our seminar observed a questionable behavior when checking in contracts/pre conditions in a polymorphic hierarchy. When checking in contracts for polymorphic classes they are verified starting at the base class but with shortcuts. I.e. the first in contract that works causes to skip executing of following contracts. This is perfectly valid because in contracts are only allowed to require less. But what if the programmer fails to follow this rule? Why not checking all in contracts. Since contracts are compiled out in release mode I argue it's worth the extra cycles to detect some inconsistency in the contracts. Polymorphic hierarchies can be deep. Consider the following example check_require_less.d: unittest { class Base { void foo(uint i) in { assert(i <= 10); } body { } } class Sub : Base { override void foo(uint i) in { assert(i <= 5); } // fails to require less but I won't know body { assert(i <= 5); // fails here because in contract wasn't checked } } auto s = new Sub; //s.foo(10); // fails as expected s.foo(7); // due to shortcut evaluation of in contracts this call passes all contracts } $ rdmd --main -unittest check_require_less.d In the case above s.foo(7) will fail but this is only due to the fact that I recheck the contract. If I wouldn't s.foo() may be executed and the programmer assumes that i <= 5 holds. Thus, s.foo() gets executed based on wrong assumptions. Hence, s.foo() may not work properly. A fact that is hidden from the programmer. Jens
Jun 30 2011
Jens Mueller:unittest { class Base { void foo(uint i) in { assert(i <= 10); } body { } } class Sub : Base { override void foo(uint i) in { assert(i <= 5); } // fails to require less but I won't know body { assert(i <= 5); // fails here because in contract wasn't checked } } auto s = new Sub; //s.foo(10); // fails as expected s.foo(7); // due to shortcut evaluation of in contracts this call passes all contracts }I think it's a DMD bug, fit for Bugzilla if not already present. Bye, bearophile
Jun 30 2011
bearophile wrote:Jens Mueller:The shortcut evaluation is specified in TDPL. That's why I assume the behavior is intended. Jensunittest { class Base { void foo(uint i) in { assert(i <= 10); } body { } } class Sub : Base { override void foo(uint i) in { assert(i <= 5); } // fails to require less but I won't know body { assert(i <= 5); // fails here because in contract wasn't checked } } auto s = new Sub; //s.foo(10); // fails as expected s.foo(7); // due to shortcut evaluation of in contracts this call passes all contracts }I think it's a DMD bug, fit for Bugzilla if not already present.
Jun 30 2011
On Thu, 30 Jun 2011 06:42:57 -0400, Jens Mueller <jens.k.mueller gmx.de> wrote:bearophile wrote:A subclass must be able to handle all the inputs the base class accepts, otherwise it isn't true polymorphism anymore. Not being able to use Sub where Base is expected, and maybe only Base was tested, can lead to major bugs.Jens Mueller:The shortcut evaluation is specified in TDPL. That's why I assume the behavior is intended. Jensunittest { class Base { void foo(uint i) in { assert(i <= 10); } body { } } class Sub : Base { override void foo(uint i) in { assert(i <= 5); } // fails to require less but Iwon't knowbody { assert(i <= 5); // fails here because in contract wasn'tchecked} } auto s = new Sub; //s.foo(10); // fails as expected s.foo(7); // due to shortcut evaluation of in contracts this callpasses all contracts}I think it's a DMD bug, fit for Bugzilla if not already present.
Jun 30 2011
Robert Jacques wrote:On Thu, 30 Jun 2011 06:42:57 -0400, Jens Mueller <jens.k.mueller gmx.de> wrote:So you agree that the current behavior is error-prone? Jensbearophile wrote:A subclass must be able to handle all the inputs the base class accepts, otherwise it isn't true polymorphism anymore. Not being able to use Sub where Base is expected, and maybe only Base was tested, can lead to major bugs.Jens Mueller:The shortcut evaluation is specified in TDPL. That's why I assume the behavior is intended. Jensunittest { class Base { void foo(uint i) in { assert(i <= 10); } body { } } class Sub : Base { override void foo(uint i) in { assert(i <= 5); } // fails to require lessbut I won't knowbody { assert(i <= 5); // fails here because in contractwasn't checked} } auto s = new Sub; //s.foo(10); // fails as expected s.foo(7); // due to shortcut evaluation of in contractsthis call passes all contracts}I think it's a DMD bug, fit for Bugzilla if not already present.
Jun 30 2011
On Fri, 01 Jul 2011 02:39:29 -0400, Jens Mueller <jens.k.mueller gmx.de> wrote:Robert Jacques wrote:No. I think the current behavior is correct. In fact, if anything, D shouldn't allow you to define an in contract on any override method. A Sub is a Base and therefore must be able to handle all inputs that are valid for a Base.On Thu, 30 Jun 2011 06:42:57 -0400, Jens Mueller <jens.k.mueller gmx.de> wrote:So you agree that the current behavior is error-prone?bearophile wrote:A subclass must be able to handle all the inputs the base class accepts, otherwise it isn't true polymorphism anymore. Not being able to use Sub where Base is expected, and maybe only Base was tested, can lead to major bugs.Jens Mueller:The shortcut evaluation is specified in TDPL. That's why I assume the behavior is intended. Jensunittest { class Base { void foo(uint i) in { assert(i <= 10); } body { } } class Sub : Base { override void foo(uint i) in { assert(i <= 5); } // fails to require lessbut I won't knowbody { assert(i <= 5); // fails here because in contractwasn't checked} } auto s = new Sub; //s.foo(10); // fails as expected s.foo(7); // due to shortcut evaluation of in contractsthis call passes all contracts}I think it's a DMD bug, fit for Bugzilla if not already present.
Jul 01 2011
On 02.07.2011 01:32, Robert Jacques wrote:On Fri, 01 Jul 2011 02:39:29 -0400, Jens Mueller <jens.k.mueller gmx.de> wrote:Have to agree. By tightening the contracts in subclasses, you'll break Liskov substitution principle.Robert Jacques wrote:No. I think the current behavior is correct. In fact, if anything, D shouldn't allow you to define an in contract on any override method. A Sub is a Base and therefore must be able to handle all inputs that are valid for a Base.On Thu, 30 Jun 2011 06:42:57 -0400, Jens Mueller <jens.k.mueller gmx.de> wrote:So you agree that the current behavior is error-prone?bearophile wrote:A subclass must be able to handle all the inputs the base class accepts, otherwise it isn't true polymorphism anymore. Not being able to use Sub where Base is expected, and maybe only Base was tested, can lead to major bugs.Jens Mueller:The shortcut evaluation is specified in TDPL. That's why I assume the behavior is intended. Jensunittest { class Base { void foo(uint i) in { assert(i <= 10); } body { } } class Sub : Base { override void foo(uint i) in { assert(i <= 5); } // fails to require lessbut I won't knowbody { assert(i <= 5); // fails here because in contractwasn't checked} } auto s = new Sub; //s.foo(10); // fails as expected s.foo(7); // due to shortcut evaluation of in contractsthis call passes all contracts}I think it's a DMD bug, fit for Bugzilla if not already present.
Jul 01 2011
On 7/1/11 6:32 PM, Robert Jacques wrote:No. I think the current behavior is correct. In fact, if anything, D shouldn't allow you to define an in contract on any override method. A Sub is a Base and therefore must be able to handle all inputs that are valid for a Base.That is a sensible enhancement. Andrei
Jul 02 2011
Andrei Alexandrescu wrote:On 7/1/11 6:32 PM, Robert Jacques wrote:How so? The child class should still be able to take more general inputs than the parent: by specifying an in contract on an override method. How would you reach that goal otherwise? TimonNo. I think the current behavior is correct. In fact, if anything, D shouldn't allow you to define an in contract on any override method. A Sub is a Base and therefore must be able to handle all inputs that are valid for a Base.That is a sensible enhancement. Andrei
Jul 02 2011
On 7/3/11 1:16 AM, Timon Gehr wrote:Andrei Alexandrescu wrote:In any case, I consider the current implementation buggy, as having no in contract in the superclass is different from having an empty in contract there: --- class Foo { void baz(int i) {} } class Bar : Foo { override void baz(int i) in { assert(i < 5, "I am triggered, but should not be."); } body { assert(i < 5, "I should be triggered instead of the contract."); } } void main() { auto bar = new Bar; bar.baz(6); } --- DavidOn 7/1/11 6:32 PM, Robert Jacques wrote:How so? The child class should still be able to take more general inputs than the parent: by specifying an in contract on an override method. How would you reach that goal otherwise? TimonNo. I think the current behavior is correct. In fact, if anything, D shouldn't allow you to define an in contract on any override method. A Sub is a Base and therefore must be able to handle all inputs that are valid for a Base.That is a sensible enhancement. Andrei
Jul 02 2011
"David Nadlinger" <see klickverbot.at> wrote in message news:iuodav$2n7s$1 digitalmars.com...In any case, I consider the current implementation buggy, as having no in contract in the superclass is different from having an empty in contract there: --- class Foo { void baz(int i) {} } class Bar : Foo { override void baz(int i) in { assert(i < 5, "I am triggered, but should not be."); } body { assert(i < 5, "I should be triggered instead of the contract."); } } void main() { auto bar = new Bar; bar.baz(6); } --- DavidThere's a patch for this!
Jul 02 2011
On 7/2/2011 4:04 PM, Andrei Alexandrescu wrote:On 7/1/11 6:32 PM, Robert Jacques wrote:This would throw out the whole idea of 'loosening'.No. I think the current behavior is correct. In fact, if anything, D shouldn't allow you to define an in contract on any override method. A Sub is a Base and therefore must be able to handle all inputs that are valid for a Base.That is a sensible enhancement.
Jul 02 2011
On 7/2/11 8:02 PM, Walter Bright wrote:On 7/2/2011 4:04 PM, Andrei Alexandrescu wrote:I mean disallow an override to add an "in" contract where the overridden method had a body but no "in" contract. AndreiOn 7/1/11 6:32 PM, Robert Jacques wrote:This would throw out the whole idea of 'loosening'.No. I think the current behavior is correct. In fact, if anything, D shouldn't allow you to define an in contract on any override method. A Sub is a Base and therefore must be able to handle all inputs that are valid for a Base.That is a sensible enhancement.
Jul 02 2011
On 7/2/2011 6:56 PM, Andrei Alexandrescu wrote:I mean disallow an override to add an "in" contract where the overridden method had a body but no "in" contract.Ok, that makes sense.
Jul 02 2011
On 7/2/11 9:41 PM, Walter Bright wrote:On 7/2/2011 6:56 PM, Andrei Alexandrescu wrote:I hereby am making a request for a pull request. AndreiI mean disallow an override to add an "in" contract where the overridden method had a body but no "in" contract.Ok, that makes sense.
Jul 02 2011
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:iuonck$586$1 digitalmars.com...I hereby am making a request for a pull request. AndreiI'll do it if you make a bug report for it. Should empty in contracts be treated the same?
Jul 02 2011
On 7/2/11 10:35 PM, Daniel Murphy wrote:"Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org> wrote in message news:iuonck$586$1 digitalmars.com...http://d.puremagic.com/issues/show_bug.cgi?id=6242I hereby am making a request for a pull request. AndreiI'll do it if you make a bug report for it.Should empty in contracts be treated the same?No. They should be the workaround. Andrei
Jul 02 2011
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:iuorep$bn2$1 digitalmars.com...http://d.puremagic.com/issues/show_bug.cgi?id=6242What should the error message be?
Jul 02 2011
On 6/30/2011 3:42 AM, Jens Mueller wrote:The shortcut evaluation is specified in TDPL. That's why I assume the behavior is intended.The behavior is both intended and crucial to the notion of contract inheritance. The 'in' contract must pass at least one of the 'in' contracts of its inheritance hierarchy. Derived functions "loosen" the requirements for input. The 'out' contract must pass all of the 'out' contracts. Derived functions "tighten" the requirements for output.
Jul 01 2011
Walter Bright wrote:On 6/30/2011 3:42 AM, Jens Mueller wrote:Yes. But the problem with passing only one of the in contracts is that it is error-prone because it *assumes* that for in contracts the requirements are widened. But what if the programmer fails at loosening a derived in contract, i.e. he restricted it. In the current setting the programmer won't know that he did an error. And with deep inheritance hierarchies it gets more complicated to have all the contracts of super classes in mind. Why not check all in contracts to make sure that they follow the loosening rule? Why not enforce the loosing rule? JensThe shortcut evaluation is specified in TDPL. That's why I assume the behavior is intended.The behavior is both intended and crucial to the notion of contract inheritance. The 'in' contract must pass at least one of the 'in' contracts of its inheritance hierarchy. Derived functions "loosen" the requirements for input.
Jul 01 2011
On 7/1/2011 1:10 AM, Jens Mueller wrote:Yes. But the problem with passing only one of the in contracts is that it is error-prone because it *assumes* that for in contracts the requirements are widened. But what if the programmer fails at loosening a derived in contract, i.e. he restricted it. In the current setting the programmer won't know that he did an error. And with deep inheritance hierarchies it gets more complicated to have all the contracts of super classes in mind. Why not check all in contracts to make sure that they follow the loosening rule? Why not enforce the loosing rule?The loosening rule is that one of the in contracts must pass. That's exactly what it does. As soon as one passes, there is no need to check the rest.
Jul 01 2011
"Walter Bright" <newshound2 digitalmars.com> wrote in message news:iuju03$27ip$1 digitalmars.com...The 'in' contract must pass at least one of the 'in' contracts of its inheritance hierarchy. Derived functions "loosen" the requirements for input.This might be the recommended way to do it, but this is not enforced by the compiler, just assumed. To enforce this, the compiler should give an error if a base function's precondition passes but a derived precondition does not. The requirement is not really that any 'in' contract passing is good enough, it's just assumed that all preconditions on inherited functions will pass if the base contract does.
Jul 01 2011
Daniel Murphy wrote:"Walter Bright" <newshound2 digitalmars.com> wrote in message news:iuju03$27ip$1 digitalmars.com...Actually that is not true. One passing contract is enough. The current (and correct) behavior enforces that when the parents contract passes, the child's contract passes too. If both had to pass, a child class could actually make contracts more restrictive. It is this what we want to avoid, right? The mistake is to assume, that the in contract of the child classes method consists of what is written there only. But arguably that is a problem with the language. Compare CP in D to Bertrand Meyer's DBC in Eiffel: //D class Foo{ void bar(int a, int) in{assert(a>0);} // require that a>0 out(r){assert r>0;} body{ return a; } } class Qux : Foo{ override void bar(int a,int b)loosen in{assert(b>0);} //'if parents contract fails, I can still produce sensible behavior if b>0 holds' body{ return a>0?a:b; } } --Eiffel class Foo feature bar(a,b:INTEGER) require a_positive: a>0 do Result:=a ensure result_positive: Result>0 end end class Qux inherit Foo redefine bar end feature bar(a,b:INTEGER) require else -- important! b_positive: b>0 do if a>0 then Result:=a else Result:=b end end As you can see, in Eiffel, contract extension has a different syntax than contract definition. ('require else' vs. require) In D the syntax is identical, even though the two things done are quite different. What effectively is done depends on whether or not the override keyword is present. I assume it is this that has led to the misunderstanding. Now, sure, if the parents contract is in{assert(a<=10);} and the child's contract is in{assert(a<=5);} then that is almost certainly an error because the child's contract fails to loosen any restrictions. But to catch this in the general case, the compiler would have to incorporate a theorem prover. (And validity of D code would start to depend on the quality of the theorem prover of the respective D compiler ;)) Cheers, -TimonThe 'in' contract must pass at least one of the 'in' contracts of its inheritance hierarchy. Derived functions "loosen" the requirements for input.This might be the recommended way to do it, but this is not enforced by the compiler, just assumed. To enforce this, the compiler should give an error if a base function's precondition passes but a derived precondition does not. The requirement is not really that any 'in' contract passing is good enough, it's just assumed that all preconditions on inherited functions will pass if the base contract does.
Jul 01 2011
I don't disagree that tightening contracts for derived functions is a bad idea. I didn't mean the contract should fail, I meant that the program should fail with an error that the contract is invalid. "Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iuklvm$pks$1 digitalmars.com...Now, sure, if the parents contract is in{assert(a<=10);} and the child's contract is in{assert(a<=5);} then that is almost certainly an error because the child's contract fails to loosen any restrictions. But to catch this in the general case, the compiler would have to incorporate a theorem prover. (And validity of D code would start to depend on the quality of the theorem prover of the respective D compiler ;))This can be caught at runtime without a theorem prover. (And I think it should be)
Jul 01 2011
Daniel Murphy wrote:I don't disagree that tightening contracts for derived functions is a bad idea. I didn't mean the contract should fail, I meant that the program should fail with an error that the contract is invalid. "Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iuklvm$pks$1 digitalmars.com...How would you catch it? I am sure it cannot be caught trivially: There are 4 possibilities: 1. Both parent and child contract would pass. 2. Parent passes, child would fail. 3. Parent fails, child passes. 4. Parent fails, child fails. The only case where any statement can be made is case 3: "Contracts are certainly well-formed". You cannot deduce the well- or ill-formedness of the contracts from any of the other outcomes. In fact, you have to prove that case 3 is unfulfillable to catch ill-formed contracts. Cheers, -TimonNow, sure, if the parents contract is in{assert(a<=10);} and the child's contract is in{assert(a<=5);} then that is almost certainly an error because the child's contract fails to loosen any restrictions. But to catch this in the general case, the compiler would have to incorporate a theorem prover. (And validity of D code would start to depend on the quality of the theorem prover of the respective D compiler ;))This can be caught at runtime without a theorem prover. (And I think it should be)
Jul 01 2011
On 7/1/11 5:18 PM, Timon Gehr wrote:There are 4 possibilities: 1. Both parent and child contract would pass. 2. Parent passes, child would fail. 3. Parent fails, child passes. 4. Parent fails, child fails. The only case where any statement can be made is case 3: "Contracts are certainly well-formed". You cannot deduce the well- or ill-formedness of the contracts from any of the other outcomes.In my opinion, one *is* able to declare the child contract invalid in case 2 – if the parent passes but the child fails, it certainly violates the »loosening« property of in contract inheritance. If you don't think so, could you please explain your doubts in more detail? David
Jul 01 2011
On 7/1/2011 8:16 AM, David Nadlinger wrote:In my opinion, one *is* able to declare the child contract invalid in case 2 – if the parent passes but the child fails, it certainly violates the »loosening« property of in contract inheritance. If you don't think so, could you please explain your doubts in more detail?The rule for in contracts is: pass = P || C; The rule for out contracts is: pass = P && C;
Jul 01 2011
"Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iukoge$u82$1 digitalmars.com...How would you catch it? I am sure it cannot be caught trivially: There are 4 possibilities: 1. Both parent and child contract would pass. 2. Parent passes, child would fail. 3. Parent fails, child passes. 4. Parent fails, child fails.In case 2, the contract is invalid. In reality, the overriding graph forms a tree, and the contract is invalid if any precondition passes if its parent's precondition fails. (the root being the most derived function) It is definately possible to implement runtime checking to enforce this.
Jul 01 2011
Daniel Murphy wrote:"Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iukoge$u82$1 digitalmars.com...That is case 3. And it is perfectly valid.How would you catch it? I am sure it cannot be caught trivially: There are 4 possibilities: 1. Both parent and child contract would pass. 2. Parent passes, child would fail. 3. Parent fails, child passes. 4. Parent fails, child fails.In case 2, the contract is invalid. In reality, the overriding graph forms a tree, and the contract is invalid if any precondition passes if its parent's precondition fails. (the root being the most derived function)It is definately possible to implement runtime checking to enforce this.My first post on the subject features an example where case 2 can occur and be valid. Have a look at it please. In general: The precondition of the parent is: pass(in_parent) And the precondition of the child is: pass(in_parent) || pass(in_child) Trivially, the precondition of the child is always fulfilled when the precondition of the parent is fulfilled. What you'd want to catch is iE: pass(in_parent) = a<=10 pass(in_child) = a<=5 Giving: precondition of parent: a<=10 precondition of child: a<=10 || a<=5 The proposed runtime check is ineffective here, as well as completely redundant in the general case. Again: in contract of child != precondition of child Cheers, -Timon
Jul 01 2011
Disregard "That is case 3. And it is perfectly valid." I had in contract and precondition mixed up there myself. sry.
Jul 01 2011
Disregard `Disregard "That is case 3. And it is perfectly valid." I had in contract and precondition mixed up there myself. sry.` It was actually a valid remark: It is valid for a child to loosen the precondition.
Jul 01 2011
"Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iukptu$10nq$1 digitalmars.com...Daniel Murphy wrote:Sorry for the mixin of terms, I meant parent in the tree, which is the opposite direction to parent in inheritance."Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iukoge$u82$1 digitalmars.com...That is case 3. And it is perfectly valid.How would you catch it? I am sure it cannot be caught trivially: There are 4 possibilities: 1. Both parent and child contract would pass. 2. Parent passes, child would fail. 3. Parent fails, child passes. 4. Parent fails, child fails.In case 2, the contract is invalid. In reality, the overriding graph forms a tree, and the contract is invalid if any precondition passes if its parent's precondition fails. (the root being the most derived function)I disagree. If the child's in condition requires something that the parent's didn't, you've just tightened the precondition. If I understand correctly, it's equivilent to: parent: require a is greater than 0 child: require b is greater than 0 In this case, the parent allows more values of b than the child does, so the precondition has been tightened.It is definately possible to implement runtime checking to enforce this.My first post on the subject features an example where case 2 can occur and be valid. Have a look at it please.In general: The precondition of the parent is: pass(in_parent) And the precondition of the child is: pass(in_parent) || pass(in_child) Trivially, the precondition of the child is always fulfilled when the precondition of the parent is fulfilled. What you'd want to catch is iE: pass(in_parent) = a<=10 pass(in_child) = a<=5 Giving: precondition of parent: a<=10 precondition of child: a<=10 || a<=5 The proposed runtime check is ineffective here, as well as completely redundant in the general case. Again: in contract of child != precondition of childOk, I doubt I used the right terms everywhere. Reading your other post, I'm starting to think we just have different ideas of how contract inheritance should work. I _think_ mine is in line with how it is supposed to work in D. If you could post some D examples that would help, doing this all in my head is starting to hurt. I'm fairly confident a runtime test can catch the following case: class A { void func(int a) in { assert(a < 10); } body {} } class B : A { void func(int a) in { assert(a < 5); } body {} } void main() { A x = new B(); x.func(7); }
Jul 01 2011
Daniel Murphy Wrote:I disagree. If the child's in condition requires something that the parent's didn't, you've just tightened the precondition. If I understand correctly, it's equivilent to: parent: require a is greater than 0 child: require b is greater than 0 In this case, the parent allows more values of b than the child does, so the precondition has been tightened.No, in this case the child has allowed more values of 'a' then the parent. If the parent fails the child will check and say things are ok because 'b' is greater than 0.
Jul 01 2011
Daniel Murphy wrote:"Timon Gehr" <timon.gehr gmx.ch> wrote in messagenews:iukptu$10nq$1 digitalmars.com...It is equivalent to: parent: require a is greater than 0 child: ... else require b is greater than 0Daniel Murphy wrote:Sorry for the mixin of terms, I meant parent in the tree, which is the opposite direction to parent in inheritance."Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iukoge$u82$1 digitalmars.com...That is case 3. And it is perfectly valid.How would you catch it? I am sure it cannot be caught trivially: There are 4 possibilities: 1. Both parent and child contract would pass. 2. Parent passes, child would fail. 3. Parent fails, child passes. 4. Parent fails, child fails.In case 2, the contract is invalid. In reality, the overriding graph forms a tree, and the contract is invalid if any precondition passes if its parent's precondition fails. (the root being the most derived function)I disagree. If the child's in condition requires something that the parent's didn't, you've just tightened the precondition. If I understand correctly, it's equivilent to: parent: require a is greater than 0 child: require b is greater than 0It is definately possible to implement runtime checking to enforce this.My first post on the subject features an example where case 2 can occur and be valid. Have a look at it please.In this case, the parent allows more values of b than the child does, so the precondition has been tightened.No, the child allows arbitrary values of b if a>0, due to the short-circuiting behavior.[snip.]I didn't either :).Again: in contract of child != precondition of childOk, I doubt I used the right terms everywhere.Reading your other post, I'm starting to think we just have different ideas of how contract inheritance should work. I _think_ mine is in line with how it is supposed to work in D.I think mine is in line with how it is supposed to work, based on the following facts: 1. It is how it currently is implemented. 2. It is how it works in Eiffel. The site on Contract Programming has a reference to DBC in Eiffel. 3. Yours requires that all child classes duplicate the precondition of their parents in their in-contracts. (which will then be ensured by a runtime check.).If you could post some D examples that would help, doing this all in my head is starting to hurt.Sure, if you remember the Foo/Qux example (Foo is parent, Qux is child, method bar(a,b), parent requires a>0 child requires else b>0) void main(){ auto a=new Foo, b=new Qux; a.bar(1,-1);//ok b.bar(1,-1);//ok, child works everywhere parent works //a.bar(-1,1);// not ok //b.bar(-1,-1)// not ok b.bar(-1,1);//ok, child works too if b>0 }I'm fairly confident a runtime test can catch the following case:Yes but that is not correct in general (unless the behavior is changed to reflect your ideas). The two contracts could be uncorrelated. [snip] Cheers, -Timon
Jul 01 2011
"Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iukvrl$1c1c$1 digitalmars.com...Sure, if you remember the Foo/Qux example (Foo is parent, Qux is child, method bar(a,b), parent requires a>0 child requires else b>0) void main(){ auto a=new Foo, b=new Qux; a.bar(1,-1);//ok b.bar(1,-1);//ok, child works everywhere parent works //a.bar(-1,1);// not ok //b.bar(-1,-1)// not ok b.bar(-1,1);//ok, child works too if b>0 }And it finally clicks! Thanks for taking the time to explain it to me. (If only I'd got it sooner, I wouldn't have spent the last two hours implementing my behaviour in the compiler)
Jul 01 2011
Daniel Murphy wrote:"Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iukvrl$1c1c$1 digitalmars.com...Just found the time to go over this thread. Thanks Daniel for asking until it was obvious for you. I now understand it as well and I hope my initial post wasn't the cause for your confusion. Thanks Timon for patiently answering and pushing us to correct understanding. The crucial thing to understand is that the in contract or precondition is the disjunction of the super's in contract/precondition and the statements written in "in { ... }" (note this definition is recursive). This was pointed out several times. But I never understood why. Timon's example makes it clear. JensSure, if you remember the Foo/Qux example (Foo is parent, Qux is child, method bar(a,b), parent requires a>0 child requires else b>0) void main(){ auto a=new Foo, b=new Qux; a.bar(1,-1);//ok b.bar(1,-1);//ok, child works everywhere parent works //a.bar(-1,1);// not ok //b.bar(-1,-1)// not ok b.bar(-1,1);//ok, child works too if b>0 }And it finally clicks! Thanks for taking the time to explain it to me. (If only I'd got it sooner, I wouldn't have spent the last two hours implementing my behaviour in the compiler)
Jul 05 2011
On Fri, 01 Jul 2011 17:18:38 +0200, Timon Gehr <timon.gehr gmx.ch> wrote:for a deeper hierarchy, one would simply have a bool flag - 'has a contract passed?'. If a subsequent contract fails, the contract is in error, not the input to the function. Capiche? -- SimenThis can be caught at runtime without a theorem prover. (And I think it should be)How would you catch it? I am sure it cannot be caught trivially: There are 4 possibilities: 1. Both parent and child contract would pass. 2. Parent passes, child would fail. 3. Parent fails, child passes. 4. Parent fails, child fails. The only case where any statement can be made is case 3: "Contracts are certainly well-formed". You cannot deduce the well- or ill-formedness of the contracts from any of the other outcomes. In fact, you have to prove that case 3 is unfulfillable to catch ill-formed contracts.
Jul 01 2011
Simen Kjaeraas wrote:On Fri, 01 Jul 2011 17:18:38 +0200, Timon Gehr <timon.gehr gmx.ch> wrote:All arguments given for a runtime check of this kind are the result of a misunderstanding of contract inheritance. The 'in'-contract of an overridden method merely extends the in-contract of the parent's method, and does not override it. In that way, the compiler statically ensures that the precondition of a child cannot possibly fail if the parent's passed. Thats the sole reason for the short-circuiting behavior. The child's contract says: If my parent's contract failed, I can still satisfy the postcondition, if this _alternative_ precondition holds. But it does not necessarily have to pass on all input the parent passes on, because it does not even get checked if the parent's precondition holds. If you add such a check, the child's in-contract will have to carefully duplicate the parent's precondition in order not to provoke a nonsensical runtime-error. Adding such a check would make D's contracts unusable in anything but the most trivial cases. (Analogously, the child's 'out'-contract does not have to re-check the parent's postcondition.) 'Capiche?' ;) -- Timonfor a deeper hierarchy, one would simply have a bool flag - 'has a contract passed?'. If a subsequent contract fails, the contract is in error, not the input to the function. Capiche? -- SimenThis can be caught at runtime without a theorem prover. (And I think it should be)How would you catch it? I am sure it cannot be caught trivially: There are 4 possibilities: 1. Both parent and child contract would pass. 2. Parent passes, child would fail. 3. Parent fails, child passes. 4. Parent fails, child fails. The only case where any statement can be made is case 3: "Contracts are certainly well-formed". You cannot deduce the well- or ill-formedness of the contracts from any of the other outcomes. In fact, you have to prove that case 3 is unfulfillable to catch ill-formed contracts.
Jul 01 2011
On Fri, 01 Jul 2011 18:24:08 +0200, Timon Gehr <timon.gehr gmx.ch> wrote:The child's contract says: If my parent's contract failed, I can still satisfy the postcondition, if this _alternative_ precondition holds. But it does not necessarily have to pass on all input the parent passes on, because it does not even get checked if the parent's precondition holds. If you add such a check, the child's in-contract will have to carefully duplicate the parent's precondition in order not to provoke a nonsensical runtime-error. Adding such a check would make D's contracts unusable in anything but the most trivial cases. (Analogously, the child's 'out'-contract does not have to re-check the parent's postcondition.) 'Capiche?' ;)Absolutely. I was in the process of answering your other post where you outlined this. Believe me, I'm on your side now. :p -- Simen
Jul 01 2011
"Timon Gehr" <timon.gehr gmx.ch> wrote in message news:iuksb8$15l0$1 digitalmars.com...The child's contract says: If my parent's contract failed, I can still satisfy the postcondition, if this _alternative_ precondition holds. But it does not necessarily have to pass on all input the parent passes on, because it does not even get checked if the parent's precondition holds.If I understand this correctly, you think the following code should be perfectly valid: class A { void func(uint x) in { assert(x < 10); } body {} } class B : A { void func(uint x) in { assert(x == 50); } body {} } If A.func can be called with any value 0..10, why is it legal to override it with a function that can't accept these values? Can you give an example where accepting input that is not a superset of the overriden function's possible input is valid?
Jul 01 2011
On 7/1/2011 9:56 AM, Daniel Murphy wrote:If I understand this correctly, you think the following code should be perfectly valid: class A { void func(uint x) in { assert(x< 10); } body {} } class B : A { void func(uint x) in { assert(x == 50); } body {} }Yes.If A.func can be called with any value 0..10, why is it legal to override it with a function that can't accept these values?It isn't. An overriding function *must* accept all input that the overridden function does. In other words, overriding functions accept a superset of the input, and deliver a subset of the output.Can you give an example where accepting input that is not a superset of the overriden function's possible input is valid?No.
Jul 01 2011
On 7/1/2011 9:24 AM, Timon Gehr wrote:All arguments given for a runtime check of this kind are the result of a misunderstanding of contract inheritance. The 'in'-contract of an overridden method merely extends the in-contract of the parent's method, and does not override it. In that way, the compiler statically ensures that the precondition of a child cannot possibly fail if the parent's passed. Thats the sole reason for the short-circuiting behavior. The child's contract says: If my parent's contract failed, I can still satisfy the postcondition, if this _alternative_ precondition holds. But it does not necessarily have to pass on all input the parent passes on, because it does not even get checked if the parent's precondition holds. If you add such a check, the child's in-contract will have to carefully duplicate the parent's precondition in order not to provoke a nonsensical runtime-error. Adding such a check would make D's contracts unusable in anything but the most trivial cases. (Analogously, the child's 'out'-contract does not have to re-check the parent's postcondition.)Right.
Jul 01 2011
On 7/1/2011 6:53 AM, Daniel Murphy wrote:To enforce this, the compiler should give an error if a base function's precondition passes but a derived precondition does not.No, it should not. Only one of the contracts needs to pass.The requirement is not really that any 'in' contract passing is good enough,Yes, it is.it's just assumed that all preconditions on inherited functions will pass if the base contract does.Only one contract needs to pass.
Jul 01 2011
"Walter Bright" <newshound2 digitalmars.com> wrote in message news:iul14g$1dfs$2 digitalmars.com...On 7/1/2011 6:53 AM, Daniel Murphy wrote:Yeah, I did get this eventually.To enforce this, the compiler should give an error if a base function's precondition passes but a derived precondition does not.No, it should not. Only one of the contracts needs to pass.The requirement is not really that any 'in' contract passing is good enough,Yes, it is.it's just assumed that all preconditions on inherited functions will pass if the base contract does.Only one contract needs to pass.
Jul 01 2011