Escape Analysis on reddit

Michel Fortin michel.fortin at michelf.com
Sat Nov 1 03:58:21 PDT 2008


On 2008-10-31 23:51:45 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail at erdani.org> said:

>> The problem is, unless you allow fully specifying dependencies, there 
>> is going to be ways people call functions that you didn't think of, 
>> which are legitimate designs, and just can't be done with your new 
>> regime.  The ultimate result is either people cast around the system, 
>> don't use the system, or end up allocating unecessarily on the heap to 
>> work around it.
> 
> It all depends on how often those cases are encountered.

Tell me how often you encounter a function that swap two variables then.

Basically, it's implemented like this:

	void swap(scope ref int a, scope ref int b)
	{
		int tmp = a;
		a = b; b = tmp;
	}

All fine, no reference is escaping. Now change the parameters for 
pointers or object references. You could naively implement it like this 
for pointers:

	void swap(scope ref int* a, scope ref int* b)
	{
		scope int* tmp = a;
		a = b; b = tmp;
	}

But if swap compiles like this, it's dangerous because the scope for *a 
may be different from the scope for *b and you may leak a pointer to a 
narrower scope in a broader one. For instance:

	// broader scope (global)
	int a = 1;
	int* pa = &a;

	void test()
	{
		// narrower scope (local)
		int b = 2;
		int* pb = &b;
		swap(pa, pb); // should be an error
	}

Here, swap(pa, pb), if allowed, would attempt to put a pointer to the 
local variable b into the global variable pa. After the function 
returns, pointer pa becomes invalid. To avoid this, you need the 
signature for swap to tell you a more complext scope constrain. Using 
Robert Jacques's suggested notation, I think it should be like:

	void swap(scope ref int* a, scope ref int* b)
		if ((*a).scope <= b.scope && (*b).scope <= a.scope)
	{
		scope int* tmp = a;
		a = b; b = tmp;
	}

Basically, if you're giving b a pointer to the value pointed by a, the 
value pointed by a needs to be alive as long or longer than b if you 
want to avoid having b point to invalid memory. Same for a and the 
value pointed by b.

Robert's notation is good for expressing that in the function 
signature, but I don't like it because it places constrains on the 
signature, not on the type, making constrains propagation a lot more 
complicated.

So I've been thinking of this notation:

	void swap(scopeof(b*) ref int* a, scopeof(*a) ref int* b)
	{
		scopeof(b) int* tmp = a;
		a = b; b = tmp;
	}

Note that tmp here needs to be scopeof(a): this guarenties that 
whatever you put in tmp, you can copy in b later (since scopeof(a) == 
scopeof(*b)).

Oh, and if you don't about the type of tmp, including the scoping 
constrains, you should be allowed to substitute it all with "auto":

	void swap(scopeof(b*) ref int* a, scopeof(*a) ref int* b)
	{
		auto tmp = a;
		a = b; b = tmp;
	}

 - - -

That's enough for the swap function. Now lets take a look at a plain 
setter function, which, I'm pretty sure, is a common pattern.

Let's try this first:

	struct S
	{
		int *pi;
	}

	void setPI(scope S s, scope int* pi)
	{
		s.pi = pi;
	}

Simple enough you think? But we have the same problem: the scope of the 
value pointed by pi may be narrower than the one s is in:

	S s1; // global scope
	int i1;
	
	void test()
	{
		int i2;
		S s2;
		setPI(s1, &i1); // ok, i1 same scope as s1.pi.
		setPI(s2, &i1); // ok, i1 is of a broader scope than s2.pi.
		setPI(s1, &i2); // should be an error, i2 scope narrower than s1.pi.
		setPI(s2, &i2); // ok, i2 is same scope as s2.pi.
	}

To enforce correctly the constrains while still allowing the usage of 
s2 above, where the second argument given to the setter may be a local 
variable, here's what we need:

	struct S
	{
		scope int *pi;
	}

	void setPI(scope S s, scopeof(s.pi) int* pi)
	{
		s.pi = pi;
	}

"scope int *pi" in the struct means you can't take the value pointed to 
by pi and keep it beyond S's scope. "scopeof(s.pi) int* pi" in the 
function signature means that the scope of the value pointed to by the 
second argument is at least as broad as the given scope for s.pi.

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




More information about the Digitalmars-d mailing list