Issue 12247 - in contract in interfaces is not checked
Summary: in contract in interfaces is not checked
Status: RESOLVED DUPLICATE of issue 6856
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: x86_64 All
: P3 normal
Assignee: No Owner
URL:
Keywords: contracts
Depends on:
Blocks:
 
Reported: 2014-02-25 02:13 UTC by Ruslan Mullakhmetov
Modified: 2023-05-18 13:30 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Ruslan Mullakhmetov 2014-02-25 02:13:48 UTC
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 Simon Naarmann 2019-03-12 23:11:46 UTC
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 Dennis 2023-02-26 19:48:59 UTC
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 Simon Naarmann 2023-02-26 20:43:14 UTC
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 FeepingCreature 2023-05-18 13:22:36 UTC
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 FeepingCreature 2023-05-18 13:30:57 UTC
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.