alwaysAssert() [was: Against enforce()]

Michel Fortin michel.fortin at michelf.com
Thu Mar 17 18:27:38 PDT 2011


On 2011-03-17 20:31:43 -0400, Walter Bright <newshound2 at digitalmars.com> said:

> On 3/17/2011 5:02 PM, Andrei Alexandrescu wrote:
>  > [...]
> 
> 
> I don't disagree with anything you wrote. But I am suggesting that one 
> liners should have a high utility to be justifiably included in Phobos.
> 
> ---------------------------------
> 
> You mentioned wondering where we should draw the line in using asserts 
> to check function inputs as opposed to using enforce. I suggest that 
> line should be when a shared library/dll boundary is crossed. 
> Statically linked libs should use assert.
> 
> The reason is straightforward - a shared library/dll cannot know in 
> advance what will be connected to it, so it should treat data coming in 
> from an external source as untrusted input. A statically linked 
> library, on the other hand, is inextricably bound to a specific caller 
> and is debugged/tested as a whole.
> 
> This raises the spectre about what to do with Phobos if Phobos is built 
> as a dll.

This would be much easier to work with if the decision about checking 
"in" contracts was taken at the call site. If user code is compiled 
with contracts, any user code calling Phobos would check the contracts 
too, dynamic library or not.

One way to make this work is by making the compiler take the contract 
as a separate function to call. For instance, this function:

	int test(int i)
	in {
		assert(i >= 0);
	}
	body {
		return 100 / i;
	}

would be split in two:

	int testContract(int i)
	{
		assert(i >= 0);
		return test(i); // could hard-code tail call optimization here
	}
	int test(int i) {
		return 100 / i;
	}

If the function that calls this test function is compiled with 
contracts turned on, it substitutes the call to test() for a call to 
testContract(), and the contracts gets checked. The testContract() 
function does not necessarily need to be part of the library, it can be 
instantiated as needed at the call point.

It could even work with virtual functions: if test() is virtual, 
testContract() would remain non-virtual and a call to the virtual 
function test() would be replaced with a call to the non-virtual 
function testContract() (which would be charged to call test).

There's two concerns however:

1. the contract would be checked against the static type instead of the 
dynamic type as it is right now. Checking against the dynamic type 
would require a new slot on the vtable -- or a separate vtable, perhaps 
inside of the ClassInfo -- which would require contracts for public and 
protected functions to be part of the library at all times.

2. taking the address of a function (or a delegate) could give you the 
one that includes the contract if contracts are turned on, but that 
would mean that code compiled with contracts and code compiled without 
them would get different addresses for the same function inside of the 
same executable, which could break some expectations.


-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/



More information about the Digitalmars-d mailing list