Bug 12247 – in contract in interfaces is not checked
Status
RESOLVED
Resolution
DUPLICATE
Severity
normal
Priority
P3
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
All
Creation time
2014-02-25T02:13:48Z
Last change time
2023-05-18T13:30:57Z
Keywords
contracts
Assigned to
No Owner
Creator
Ruslan Mullakhmetov
Comments
Comment #0 by public — 2014-02-25T02:13:48Z
dmd 2.064.2
----test.d ---
import std.stdio: writefln;
interface I
{
int foo(int i)
in { assert(i > 7); }
out (result) { assert(result & 1); }
void bar();
}
class Impl : I
{
int foo(int i)
{
writefln("%s: %s", __FUNCTION__, i);
return i;
}
void bar()
{
writefln("%s", __FUNCTION__);
}
}
void main()
{
I i = new Impl;
i.foo(7); // in contract do not satisfied, though executed.
i.bar();
i.foo(8); // throws
}
---- output ---
$ dmd test.d && ./test
test.Impl.foo: 7
test.Impl.bar
test.Impl.foo: 8
core.exception.AssertError@test(8): Assertion failure
----------------
5 test 0x0000000106a454ea _d_assertm + 38
6 test 0x0000000106a3901f void test.__assert(int) + 23
7 test 0x0000000106a390da int test.I.foo(int).void __ensure(ref const(int)) + 26
8 test 0x0000000106a3916b int test.Impl.foo(int) + 139
9 test 0x0000000106a39000 _Dmain + 80
10 test 0x0000000106a51805 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll().void __lambda1() + 33
11 test 0x0000000106a51751 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) + 45
12 test 0x0000000106a517b1 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll() + 45
13 test 0x0000000106a51751 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) + 45
14 test 0x0000000106a516cd _d_run_main + 449
15 test 0x0000000106a39034 main + 20
16 libdyld.dylib 0x00007fff8f6dd5fd start + 1
17 ??? 0x0000000000000001 0x0 + 1
Comment #1 by eiderdaus — 2019-03-12T23:11:46Z
This issue still exists in DMD 2.084.1, tested on Linux 64-bit.
In the example code, the interface's in contract should throw, but does not. Only the out contract throws as desired.
Comment #2 by dkorpel — 2023-02-26T19:48:59Z
This is working as designed, though it is somewhat controversial.
> The fundamental nature of 'in' contracts is that they are "loosened"
> on derivation. If an instance of B is passed to parameter A, then
> if either the contract for A or the contract for B passes, then
> it passes. It is NOT necessary for the A contract to pass.
See issue 6856
*** This issue has been marked as a duplicate of issue 6856 ***
Comment #3 by eiderdaus — 2023-02-26T20:43:14Z
Ah, I wouldn't have guessed that absence of contract means an always-passing contract. Thanks!
I agree to close it as a duplicate of 6856.
Comment #4 by default_357-line — 2023-05-18T13:22:36Z
Note that in theory there's `-preview=inclusiveincontracts`, which changes the way inconditions work to require (at runtime) that child in-contracts are a subset of parent in-contracts. Though I haven't worked on it for a while now, because we've generally reduced the amount of objects being passed to functions (or non-autogenerated methods), so it hasn't come up in a while.
Comment #5 by default_357-line — 2023-05-18T13:30:57Z
Hm, apologies, nevermind - that seems unrelated.
Right now, "no in-contract" is equivalent to "in (true)".
`I::foo(int i) in (i > 7)` creates an in-contract of "at least i > 7 must be permitted for foo".
`Impl::foo(int i)` has no additional in-contract, so it allows in all values, causing the behavior in this bug report.
preview=inclusiveincontracts does not affect this. The only thing it would affect is if you wrote `Impl::foo(int i) in (false)`, which today would succeed if called with, say, `8`, because 8 would pass the interface in-contract, and it only has to pass the in-contract of any of the override parents (because they are implicitly inclusive). But `in (true)`, which writing no in-contract implicitly is, works with either behavior.
Sorry for the spam, feel free to delete both comments.