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
Comment #1 by timon.gehr — 2013-12-29T16:40:53Z
*** This issue has been marked as a duplicate of issue 6856 ***
Comment #2 by smjg — 2013-12-30T15:03:58Z
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."
(In reply to comment #0)
> 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.
Comment #3 by timon.gehr — 2013-12-30T15:16:28Z
(In reply to comment #2)
> This isn't a duplicate of issue 6856. ..
D'oh. You are right. This is the second time this happens. :o)
Comment #4 by chalucha — 2013-12-30T17:14:17Z
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.
Comment #5 by timon.gehr — 2013-12-30T17:20:10Z
(In reply to comment #4)
> ...
>
> 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.
Comment #6 by smjg — 2013-12-30T18:01:40Z
(In reply to comment #4)
> 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.