www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 11835] New: Assert not thrown from contract

reply d-bugmail puremagic.com writes:
https://d.puremagic.com/issues/show_bug.cgi?id=11835

           Summary: Assert not thrown from contract
           Product: D
           Version: D2
          Platform: x86_64
        OS/Version: Linux
            Status: NEW
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: nobody puremagic.com
        ReportedBy: chalucha gmail.com



PST ---
With code like this:

import std.stdio;
import core.exception;
import std.exception;

interface IFoo
{
    void test()
    in
    {
        writeln("IFoo.test");
        assert(false);
    }
}

class Foo : IFoo
{
    abstract void test()
    in
    {
        writeln("Foo.test");
    }
    body{}
}

class Bar : Foo
{
    override void test()
    {
        writeln("Bar.test");
    }
}

unittest
{
    auto bar = new Bar();
    assertThrown!AssertError(bar.test());
}

I expected, that AssertError should be thrown from interface contract, but only
Bar.test is called.

With modification:
class Bar : Foo
{
    override void test()
    in
    {
        writeln("Bar.test");
    }
    body{}
}

Foo.test is called this time (Bar.test is not), but AssertError is still not
thrown so assertThrown fails.

With further modification:
class Foo : IFoo
{
    abstract void test()
    in
    {
        writeln("Foo.test");
        assert(false);
    }
    body{}
}

Output is:
Foo.test
IFoo.test
Bar.test
core.exception.AssertError source/app.d(45): assertThrown failed: No
AssertError was thrown.

So this time all contracts are called but still no AssertError thrown.

With this modification:
class Bar : Foo
{
    override void test()
    in
    {
        writeln("Bar.test");
        assert(false);
    }
    body{}
}

All contracts are called and AssertError is thrown from Bar.test().
I think that this is a bug related to 6856 but as it seems that even
AssertErrors are somehow "eaten" somewhere, I filled this bug so it can be
checked too.

Tested with DMD 2.064.2 on Gentoo linux

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Dec 27 2013
next sibling parent d-bugmail puremagic.com writes:
https://d.puremagic.com/issues/show_bug.cgi?id=11835


timon.gehr gmx.ch changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |timon.gehr gmx.ch
         Resolution|                            |DUPLICATE



*** This issue has been marked as a duplicate of issue 6856 ***

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Dec 29 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
https://d.puremagic.com/issues/show_bug.cgi?id=11835


Stewart Gordon <smjg iname.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |smjg iname.com
         Resolution|DUPLICATE                   |INVALID



This isn't a duplicate of issue 6856.  That issue is about the handling of the
situation where an override has no in { ... } block.  While the original code
sample here is just this, this issue is about variations of this code in which
the override _does_ have an in { ... } block.  It is a complete
misunderstanding of how in contracts work.

http://dlang.org/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."


 With modification:
 class Bar : Foo
 {
     override void test()
     in
     {
         writeln("Bar.test");
     }
     body{}
 }
 
 Foo.test is called this time (Bar.test is not), but AssertError is still not
 thrown so assertThrown fails.
Foo.test has passed, and so there is no need to call Bar.test.
 With further modification:
 class Foo : IFoo
 {
     abstract void test()
     in
     {
         writeln("Foo.test");
         assert(false);
     }
     body{}
 }
 
 Output is:
 Foo.test
 IFoo.test
 Bar.test
 core.exception.AssertError source/app.d(45): assertThrown failed: No
 AssertError was thrown.
 
 So this time all contracts are called but still no AssertError thrown.
The AssertError _has_ been thrown - if it hadn't, then Bar.test's in block wouldn't have been called. What the compiler does is to generate code equivalent to: try { try { // Foo.test.in writeln("Foo.test"); assert(false); } catch (AssertError e) { // IFoo.test.in writeln("IFoo.test"); assert(false); } } catch (AssertError e) { // Bar.test.in writeln("Bar.test"); assert(false); } // Bar.test.body - empty
 With this modification:
 class Bar : Foo
 {
     override void test()
     in
     {
         writeln("Bar.test");
         assert(false);
     }
     body{}
 }
 
 All contracts are called and AssertError is thrown from Bar.test().
This is correct. All in contracts have been checked, and they have all failed, therefore an AssertError is thrown back to the original caller. -- Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 30 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
https://d.puremagic.com/issues/show_bug.cgi?id=11835





 This isn't a duplicate of issue 6856. ..
D'oh. You are right. This is the second time this happens. :o) -- Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 30 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
https://d.puremagic.com/issues/show_bug.cgi?id=11835




PST ---
Ok, now I see why it is working like that, but consider this sample:

interface IFoo
{
        void test()
        in
        {
                writeln("IFoo.test");
                assert(false);
        }
}

class Foo : IFoo
{
        void test()
        in
        {
                writeln("Foo.test");
        }
        body{}
}

class Bar : IFoo
{
        void test()
        in
        {
                writeln("Bar.test");
        }
        body{}
}

as it works now it means, that interface contract will never throw an
AssertError, because Foo and Bar tests passes.

What is then the point to define contract in the interface if I have to set the
same in class definitions of that interface?

In upper sample it can be understandable as new "in" contracts are defined, but
if I use just this?:

interface IFoo
{
        void test()
        in
        {
                writeln("IFoo.test");
                assert(false);
        }
}

class Foo : IFoo
{
        void test() { writeln("Foo"); }
}

class Bar : IFoo
{
        void test() { writeln("Bar"); }
}

here I will definitelly expect that contract defined in interface will be used
to check input of actual methods.

It just seems weird to me. For example if I write a library with the public
interface or abstract class and want to check input params of it so when other
project uses it it will check if it is used properly. But as it works now, this
is not possible.

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Dec 30 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
https://d.puremagic.com/issues/show_bug.cgi?id=11835





 ...
 
 In upper sample it can be understandable as new "in" contracts are defined, but
 if I use just this?:
 ...
 
 here I will definitelly expect that contract defined in interface will be used
 to check input of actual methods.
 ...
Me too. This is what issue 6856 covers. -- Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 30 2013
prev sibling parent d-bugmail puremagic.com writes:
https://d.puremagic.com/issues/show_bug.cgi?id=11835





 Ok, now I see why it is working like that, but consider this sample:
 
 interface IFoo
 {
         void test()
         in
         {
                 writeln("IFoo.test");
                 assert(false);
         }
 }
 
 class Foo : IFoo
 {
         void test()
         in
         {
                 writeln("Foo.test");
         }
         body{}
 }
 
 class Bar : IFoo
 {
         void test()
         in
         {
                 writeln("Bar.test");
         }
         body{}
 }
 
 as it works now it means, that interface contract will never throw 
 an AssertError, because Foo and Bar tests passes.
 
 What is then the point to define contract in the interface if I 
 have to set the same in class definitions of that interface?
To define the legal inputs to IFoo.test, and hence the minimum that implementations of the method must support.
 here I will definitelly expect that contract defined in interface 
 will be used to check input of actual methods.
Foo.test or Bar.test could be called on an object reference of type Foo or Bar. Then it is allowed to use the wider range of inputs that the class-specific version supports. At the moment, the way to just inherit the contract from a superclass or interface is to use in { assert (false); }
 It just seems weird to me. For example if I write a library with 
 the public interface or abstract class and want to check input 
 params of it so when other project uses it it will check if it is 
 used properly. But as it works now, this is not possible.
See issue 6857. -- Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 30 2013