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.