Escape analysis

Steven Schveighoffer schveiguy at yahoo.com
Tue Oct 28 19:27:24 PDT 2008


"Walter Bright" wrote
> Steven Schveighoffer wrote:
>> "Walter Bright" wrote
>>> First off, the mangled names will be different, so it won't link until 
>>> you recompile. This is critical because the caller's code depends on the 
>>> scope/noscope characteristic.
>>
>> This 'feature' is basically useless ;)  D has no shared libraries, so I 
>> don't think anyone generally keeps their stale object files around and 
>> tries to link with them instead of trying to recompile the sources.  You 
>> are asking for trouble otherwise.
>
> I disagree. The whole idea behind separate compilation and using makefiles 
> is to recompile only what is necessary. Encoding the function 
> specification into its identifier is a tried and true way of detecting 
> mistakes in that.

Any decent build tool (including make, assuming dependencies are created)
will rebuild the source when it sees the dependency changed.  In this case,
if the new signature can be used, it will recompile silently.  That was my
point.

However, I'm no longer sure what you are planning, because you have
sufficiently confused me ;)

So if the recompile causes a compile failure, then it would fail.  But that
is unrelated to the requirement that you have to recompile to get it to
link.  Even if the function signatures are the same, the build tool is going
to recompile the file instead of linking the stale object.

>
>
>>> Secondly, passing a scoped reference to a noscope parameter should be a 
>>> compile time error.
>>
>> OK, so when does a closure happen?  I thought the point of this was to 
>> specify when a closure was necessary...
>>
>> compiler sees foo(noscope int *x)
>>
>> I try to pass in an address to a local variable.  Compiler says, hm... I 
>> need a closure to convert my scope variable into a noscope.
>
> Either the compiler issues an error, or it allocates the scoped variable 
> on the heap. I prefer the former behavior.

Huh?  So no automatic closures?  If the compiler can't prove that a closure
is or is not necessary, does code now just fail to compile?

>
>
>> But the compiler's lack of knowledge/proof about the escape intricacies 
>> of a function will cause either a) unnecessary closure allocation, or b) 
>> impossible specifications.  i.e. I want to specify that either a scope or 
>> noscope variable can be passed in, and the variable might escape 
>> depending on what you pass in for other arguments, how do I do that?
>
> You make it noscope. Remember that scope is an optimization.
>
>>>> The most common case I think which will cause unnecessary allocations, 
>>>> is a very common case.  A class setter:
>>>>
>>>> class X
>>>> {
>>>>    private int *v_;
>>>>    int *v(int *newV) {return v_ = newV;}
>>>>    int *v() { return v_;}
>>>> }
>>>>
>>>> Clearly, newV escapes into the class instance,
>>> Then it's noscope.
>>
>> So then to call X.v, the function must allocate a closure?  How does this 
>> improve the current situation where closures are allocated by default?
>
> If it's escaping, you MUST allocate it in a way that doesn't disappear 
> when the escape happens.

The problem is, what if I know it's escaping in some cases, but not in
others, but the compiler can't tell either way?  (see example below)

>
>
>>> We take the conservative approach, and regard "might escape" and "don't 
>>> know if it escapes" as "treat as if it does escape".
>>
>> Also untenable.  We have the same situation today.  You will have 
>> achieved nothing with this syntax except making people write scope or 
>> noscope everywhere to satisfy incomplete compiler rules.
>
> The improvement with the 'scope' keyword is it allows the compiler to 
> assume that the reference does not escape.

And is that property enforced while compiling the function, or does the
compiler assume the author knows best?

Like I said, I'm sufficiently confused...

How do I markup class X so that at least foo and foo2 compile without
issues?

class X
{
   int *p;
   this(int *p_) {p = p_;}
}

// I expect this to compile and work.
void foo()
{
   int i;
   auto x = new X(&i);
}

// I expect this to compile and work.
X foo2()
{
   int[] arr = new int[1];
   return new X(&arr[0]);
}

// What happens here, a closure or a failure?
X foo3()
{
  int i;
  auto x = new X(&i);
  return x;
}

If you have some syntax such that all 3 compile (i.e. foo3 creates a
closure), then how does the compiler know foo3 is ok?

-Steve






More information about the Digitalmars-d mailing list