Jump to page: 1 24  
Page
Thread overview
[Issue 6856] New: Preconditions are not inherited
Oct 26, 2011
timon.gehr@gmx.ch
Oct 30, 2011
Stewart Gordon
Nov 01, 2011
Leandro Lucarella
Nov 01, 2011
Leandro Lucarella
Nov 01, 2011
Leandro Lucarella
Nov 01, 2011
timon.gehr@gmx.ch
Nov 01, 2011
timon.gehr@gmx.ch
Nov 01, 2011
Leandro Lucarella
Nov 01, 2011
Stewart Gordon
Nov 02, 2011
Don
Nov 02, 2011
Leandro Lucarella
Nov 02, 2011
Leandro Lucarella
Nov 03, 2011
Don
Nov 03, 2011
Leandro Lucarella
Feb 26, 2012
deadalnix
Feb 26, 2012
timon.gehr@gmx.ch
Feb 26, 2012
deadalnix
Feb 26, 2012
timon.gehr@gmx.ch
Feb 26, 2012
deadalnix
Feb 26, 2012
Stewart Gordon
Feb 26, 2012
timon.gehr@gmx.ch
Feb 27, 2012
Jesse Phillips
Feb 27, 2012
Don
Feb 27, 2012
timon.gehr@gmx.ch
Feb 28, 2012
Don
Feb 28, 2012
timon.gehr@gmx.ch
May 03, 2012
timon.gehr@gmx.ch
[Issue 6856] Absence of in() contract (precondition) should mean "use default precondition" instead of "ignore inherited in() contracts"
Jan 22, 2013
Stewart Gordon
Jan 22, 2013
timon.gehr@gmx.ch
Jan 22, 2013
Leandro Lucarella
[Issue 6856] Absence of in contract (precondition) on override of method that has one is badly designed
Jan 22, 2013
Stewart Gordon
October 26, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856

           Summary: Preconditions are not inherited
           Product: D
           Version: D2
          Platform: Other
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: nobody@puremagic.com
        ReportedBy: timon.gehr@gmx.ch


--- Comment #0 from timon.gehr@gmx.ch 2011-10-26 13:58:18 PDT ---
Precondition inheritance does not work in a satisfying way:

import std.stdio;

class A{
    void foo()in{writeln("in!");}out{writeln("out!");}body{}
}
class B:A{
    override void foo(){} // add in{assert(false);}body to get it working
}

void main(){
    A x = new A;
    x.foo(); // in! \ out!
    B y = new B;
    y.foo(); // out!
}

If the child class does not specify an in contract on an overriding method, the in contract of the parent should be inherited, without adding a contract that always passes. The current behavior makes it too easy to inadvertently widen the interface and have undetected bugs. Chapter 10.9 in TDPL does not explicitly mention the fact that it is supposed to work that way, but it seems to assume it.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
October 30, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856


Stewart Gordon <smjg@iname.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |smjg@iname.com


--- Comment #1 from Stewart Gordon <smjg@iname.com> 2011-10-29 18:20:20 PDT ---
http://www.digitalmars.com/d/1.0/dbc.html
"If a function in a derived class overrides a function in its super class, then
only one of the in contracts of the function and its base functions must be
satisfied. Overriding functions then becomes a process of loosening the in
contracts.

A function without an in contract means that any values of the function parameters are allowed."

On an override, the semantics of an in contract are to _add_ to what is a legal call of the method.  And the absence of an in contract in a function definition is really syntactic sugar for an empty in contract.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 01, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856


Leandro Lucarella <llucax@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |llucax@gmail.com
            Version|D2                          |D1 & D2


--- Comment #2 from Leandro Lucarella <llucax@gmail.com> 2011-11-01 05:25:05 PDT ---
What about this:

extern (C) int printf(char* s, ...);

class X
{
    void f()
    in { printf("\tX.f() in\n"); }
    body {}
}

class Y : X
{
    override void f()
    in { printf("\tY.f() in\n"); }
    body {}
}

class Z : Y
{
    override void f()
    body {}
}

void main()
{
    printf("X x\n");
    X xx = new X;
    xx.f();
    printf("\t---\n");

    printf("X y\n");
    X xy = new Y;
    xy.f();
    printf("\t---\n");

    printf("X z\n");
    X xz = new Z;
    xz.f();
    printf("\t---\n");

    printf("--------------------\n");
    printf("Y y\n");
    Y yy = new Y;
    yy.f();
    printf("\t---\n");

    printf("Y z\n");
    Y yz = new Z;
    yz.f();
    printf("\t---\n");

    printf("--------------------\n");
    printf("Z z\n");
    Z z = new Z;
    z.f();
    printf("\t---\n");
}


It prints:

X x
    X.f() in
    ---
X y
    X.f() in
    ---
X z
    ---
--------------------
Y y
    X.f() in
    ---
Y z
    ---
--------------------
Z z
    ---


Shouldn't "Y y" print "Y.f()" if Y can loose the in contract?
Shouldn't "X z" and "Y z" print *something* (probably "X.f()" and "Y.f()"
respectively)?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 01, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856



--- Comment #3 from Leandro Lucarella <llucax@gmail.com> 2011-11-01 05:27:32 PDT ---
BTW, that was DMD 1.071.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 01, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856



--- Comment #4 from Leandro Lucarella <llucax@gmail.com> 2011-11-01 05:46:43 PDT ---
BTW, "in" contracts seems to be very ill defined, because overriding a method with an "in" contract without specifying an "in" contract should inherit the contract from the base class/interface, not remove the contract completely. You have to repeat the contract from the base class manually, that sucks. There should be some syntax to remove the contract instead, maybe something like:

void f()
in delete
body
{
  // ...
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 01, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856



--- Comment #5 from timon.gehr@gmx.ch 2011-11-01 05:57:01 PDT ---
@Leandro: Your first example is okay. The precondition test shortcuts. It is
more or less equivalent to passes(X_foo_in) || passes(Y_foo_in). If
passes(X_foo_in), then the second part does not have to be evaluated, ergo it
never prints "Y.f() in".

The fact that in order to just inherit an in contract it is necessary to add an in{assert(0);} contract is hopefully just an oversight and not by design.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 01, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856



--- Comment #6 from timon.gehr@gmx.ch 2011-11-01 05:59:14 PDT ---
(In reply to comment #4)
> BTW, "in" contracts seems to be very ill defined, because overriding a method with an "in" contract without specifying an "in" contract should inherit the contract from the base class/interface, not remove the contract completely. You have to repeat the contract from the base class manually, that sucks. There should be some syntax to remove the contract instead, maybe something like:
> 
> void f()
> in delete
> body
> {
>   // ...
> }

I think it should just look like this:

override void f()
in{}body{
  // ...
}

If no explicit contract is added, it should be inherited.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 01, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856



--- Comment #7 from Leandro Lucarella <llucax@gmail.com> 2011-11-01 09:50:56 PDT ---
OK, then the docs should be more clear I think, is really hard to infer this behavior from the docs (unless someone explains it better :).

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 01, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856



--- Comment #8 from Stewart Gordon <smjg@iname.com> 2011-11-01 10:06:36 PDT ---
(In reply to comment #2)
> Shouldn't "Y y" print "Y.f()" if Y can loose the in contract?
> Shouldn't "X z" and "Y z" print *something* (probably "X.f()" and "Y.f()"
> respectively)?

Only one of the contracts needs to pass for the overall contract to pass.  So once it's tried one and found that it's passed, it doesn't need to try the others.

When you have a Y, it first tries X's in contract.  This passes, so it doesn't need to check Y's as well.  Of course, an implementation could just as well check Y's contract first and then fall back to X's if that fails.

But because Z's in contract is empty, the compiler just optimises away the whole contract checking.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 02, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=6856


Don <clugdbug@yahoo.com.au> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |clugdbug@yahoo.com.au


--- Comment #9 from Don <clugdbug@yahoo.com.au> 2011-11-02 01:30:27 PDT ---
(In reply to comment #8)
> (In reply to comment #2)
> > Shouldn't "Y y" print "Y.f()" if Y can loose the in contract?
> > Shouldn't "X z" and "Y z" print *something* (probably "X.f()" and "Y.f()"
> > respectively)?
> 
> Only one of the contracts needs to pass for the overall contract to pass.  So once it's tried one and found that it's passed, it doesn't need to try the others.
> 
> When you have a Y, it first tries X's in contract.  This passes, so it doesn't need to check Y's as well.  Of course, an implementation could just as well check Y's contract first and then fall back to X's if that fails.
> 
> But because Z's in contract is empty, the compiler just optimises away the whole contract checking.

I'm a little confused by the relationship between this bug and bug 6857.
If you accept 6857, then if you call a function f() from a base class B, only
the precondition of the base class should matter. Although one derived class
C.f() may accept a weaker precondition, the caller doesn't actually know it had
a C, so it's making an unwarranted assumption.

So, if you accept that, then contracts in derived classes don't matter unless they are called directly.

That's really odd, because you have a single function which has two different
semantic guarantees depending on who is calling it.
Following this through, I don't see the need for explicit widening of
preconditions at all. If we didn't have the feature, and you needed it (which I
believe happens very rarely), you could just create a separate function g() for
the direct calls, and give _it_ the weaker contract. The derived function f()
can just call g(). If you need to rely on the weaker contract, call g() instead
of f(). Easy to implement, easy to understand.

This explicit widening of preconditions of virtual functions seems to be a really niche feature.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
« First   ‹ Prev
1 2 3 4