Escape Analysis on reddit
Andrei Alexandrescu
SeeWebsiteForEmail at erdani.org
Sat Nov 1 10:12:46 PDT 2008
Michel Fortin wrote:
> On 2008-11-01 12:02:47 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail at erdani.org> said:
>
>> Michel Fortin wrote:
>>> 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;
>>> }
>>
>> "scope" is not needed inside the function (in both cases). The
>> compiler will infer it.
>
> Indeed.
>
>
>>> 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.
>>
>> Aha! Good point. But notice, however, that you are trying to extract
>> more juice from "scope" than there is in it. What scope says in swap
>> is only that swap itself does not escape any of its parameters. It
>> doesn't tell that the parameters have actually the same scope.
>
> But my point was that the "no escape" guarenty doesn't hold if the two
> arguments' scope doesn't match. The parameter may escape via the other
> parameter.
>
>
>>> 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;
>>> }
>>
>> This would require each variable to carry its actual scope either at
>> compilation or at runtime. Either would be very hard to implement if
>> at all. But I'm glad there is a bit of exaggeration on the side of
>> safety :o).
>
> I wouldn't call it exageration. Adding complexity to function signatures
> only to be half-safe isn't very appealing to me.
>
>
>>> 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;
>>> }
>>
>> That's nice, but it would reduce what swap can do. Oftentimes it is
>> not known whether values manipulated by a function are in the same
>> scope or not.
>
> The caller function should know, and from there it could be possible to
> check against the constrain.
>
> About reducing what swap can do, I'd like to see
>
>
>>> 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:
>>
>> I am glad there is awareness of the limitations of scope. There are
>> still things that are outside scope's charter, and the summary is -
>> scope tells something about the function declaring it, not about a
>> relationship between its parameters.
>
> So basically, if you have two scope arguments and you assign some part
> of the first to the other, you risk bypassing the scope-checking system
> despite the appearances to the contrary. I'm not sure it's a good idea
> to have a scope-checking system if you can only trust in half.
I think you're putting forth a compelling argument. Thank you! I will
try to get a hold of Walter today and discuss the matter. Off the top of
my head, there are the following ways to go:
1. Cop-out: do whatever is the syntactic minimum for allowing closure
allocation when requested. Don't check anything, trust the coder.
2. Accept: implement scope with an understanding of its issues and
limitations. Your argument weakens the motivation to pursue this approach.
3. Dig further: look into how scope can be made indeed good. My current
understanding is that a partial ordering of scopes would be interesting
to think about.
On a side note, I'm very glad to see Walter having taken this bull by
the horns. This is entirely his initiative - and if it turns sour, it's
all his fault! :o) - initiative tackling a difficult problem that
revealed an embarrassing corner of the language.
Andrei
More information about the Digitalmars-d
mailing list