www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Escaped scoped

reply bearophile <bearophileHUGS lycos.com> writes:
If I compile the following code with DMD with and without the scope annotation
I can see that both versions compile and the version with scope deletes the
object. Is the compiler acting correctly here? I'd like the compiler to refuse
to compile this code when the scope attribute is present (this is a reduced
example from a bug I've just removed from a program of mine):

class Foo {}
class Bar {
    Foo x;
    void spam() {
        scope Foo temp = new Foo();
        this.x = temp;
    }
}
void main() {}

Bye,
bearophile
Jan 04 2010
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 04 Jan 2010 05:52:19 -0500, bearophile <bearophileHUGS lycos.com>  
wrote:

 If I compile the following code with DMD with and without the scope  
 annotation I can see that both versions compile and the version with  
 scope deletes the object. Is the compiler acting correctly here? I'd  
 like the compiler to refuse to compile this code when the scope  
 attribute is present (this is a reduced example from a bug I've just  
 removed from a program of mine):

 class Foo {}
 class Bar {
     Foo x;
     void spam() {
         scope Foo temp = new Foo();
         this.x = temp;
     }
 }
 void main() {}

Scope is not a type constructor, so once the compiler passes the line scope Foo temp = new Foo(); the compiler sees temp as type Foo, not scope Foo. So it is ignorant on the next line to know to stop you from doing something foolish. When using scope, it is on you to ensure that it doesn't escape. I think the compiler cheats a little bit on delegates, but it has severe limitations that I think make using delegates without accidentally allocating closures difficult. -Steve
Jan 04 2010
parent reply Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 On Mon, 04 Jan 2010 05:52:19 -0500, bearophile 
 <bearophileHUGS lycos.com> wrote:
 
 If I compile the following code with DMD with and without the scope 
 annotation I can see that both versions compile and the version with 
 scope deletes the object. Is the compiler acting correctly here? I'd 
 like the compiler to refuse to compile this code when the scope 
 attribute is present (this is a reduced example from a bug I've just 
 removed from a program of mine):

 class Foo {}
 class Bar {
     Foo x;
     void spam() {
         scope Foo temp = new Foo();
         this.x = temp;
     }
 }
 void main() {}

Scope is not a type constructor, so once the compiler passes the line scope Foo temp = new Foo(); the compiler sees temp as type Foo, not scope Foo. So it is ignorant on the next line to know to stop you from doing something foolish.

I don't think that's true, actually. The compiler has a SCOPE storage class internally. Not sure what it's used for, though.
 When using scope, it is on you to ensure that it doesn't escape.

Yes, I suspect it's impossible for the compiler to do perfect escape analysis. But it probably wouldn't be difficult for it to do some.
 I think the compiler cheats a little bit on delegates, but it has severe 
 limitations that I think make using delegates without accidentally 
 allocating closures difficult.

It could probably catch simple cases like this one, though.
Jan 04 2010
parent Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 On Mon, 04 Jan 2010 14:33:24 -0500, Don <nospam nospam.com> wrote:
 
 Steven Schveighoffer wrote:
 On Mon, 04 Jan 2010 05:52:19 -0500, bearophile 
 <bearophileHUGS lycos.com> wrote:

 If I compile the following code with DMD with and without the scope 
 annotation I can see that both versions compile and the version with 
 scope deletes the object. Is the compiler acting correctly here? I'd 
 like the compiler to refuse to compile this code when the scope 
 attribute is present (this is a reduced example from a bug I've just 
 removed from a program of mine):

 class Foo {}
 class Bar {
     Foo x;
     void spam() {
         scope Foo temp = new Foo();
         this.x = temp;
     }
 }
 void main() {}

scope Foo temp = new Foo(); the compiler sees temp as type Foo, not scope Foo. So it is ignorant on the next line to know to stop you from doing something foolish.

I don't think that's true, actually. The compiler has a SCOPE storage class internally. Not sure what it's used for, though.

Not sure of the internals, but from what I understand, a storage class is just a clue on how to store it, it does not affect the type. For instance temp can legally be assigned to a different instance that isn't stack allocated, or another variable that isn't scope can be assigned temp. At that point, any possibility for scope protection is lost. And without some significant analysis, the compiler is lost after the declaration.

Sure. Probably I misunderstood you.
 When using scope, it is on you to ensure that it doesn't escape.

Yes, I suspect it's impossible for the compiler to do perfect escape analysis. But it probably wouldn't be difficult for it to do some.

You need some sort of analysis to prove that the scope variable isn't reassigned to another instance. The reason it works in the way it does for passing non-closure delegates to functions is because the function call and delegate creation are all in one statement, so the compiler has all the info available to make the right decision. Just one extra statement causes a closure allocation: int x = 5; void fn() { x = 6; } auto dg = &fn; // allocates closure foo(dg); // foo accepts a scope delegate. foo(&dg); // no closure allocation.

I presume you mean: foo(&fn). Wow! I didn't realize it was _that_ primitive.
 
 I think the compiler cheats a little bit on delegates, but it has 
 severe limitations that I think make using delegates without 
 accidentally allocating closures difficult.

It could probably catch simple cases like this one, though.

I don't think without some major analysis, or some sort of type marking.

The backend does some quite sophisticated analysis. This kind of escape analysis could probably hook into it, if anyone could be bothered to do it.
 
 -Steve

Jan 04 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 04 Jan 2010 14:33:24 -0500, Don <nospam nospam.com> wrote:

 Steven Schveighoffer wrote:
 On Mon, 04 Jan 2010 05:52:19 -0500, bearophile  
 <bearophileHUGS lycos.com> wrote:

 If I compile the following code with DMD with and without the scope  
 annotation I can see that both versions compile and the version with  
 scope deletes the object. Is the compiler acting correctly here? I'd  
 like the compiler to refuse to compile this code when the scope  
 attribute is present (this is a reduced example from a bug I've just  
 removed from a program of mine):

 class Foo {}
 class Bar {
     Foo x;
     void spam() {
         scope Foo temp = new Foo();
         this.x = temp;
     }
 }
 void main() {}

scope Foo temp = new Foo(); the compiler sees temp as type Foo, not scope Foo. So it is ignorant on the next line to know to stop you from doing something foolish.

I don't think that's true, actually. The compiler has a SCOPE storage class internally. Not sure what it's used for, though.

Not sure of the internals, but from what I understand, a storage class is just a clue on how to store it, it does not affect the type. For instance temp can legally be assigned to a different instance that isn't stack allocated, or another variable that isn't scope can be assigned temp. At that point, any possibility for scope protection is lost. And without some significant analysis, the compiler is lost after the declaration.
 When using scope, it is on you to ensure that it doesn't escape.

Yes, I suspect it's impossible for the compiler to do perfect escape analysis. But it probably wouldn't be difficult for it to do some.

You need some sort of analysis to prove that the scope variable isn't reassigned to another instance. The reason it works in the way it does for passing non-closure delegates to functions is because the function call and delegate creation are all in one statement, so the compiler has all the info available to make the right decision. Just one extra statement causes a closure allocation: int x = 5; void fn() { x = 6; } auto dg = &fn; // allocates closure foo(dg); // foo accepts a scope delegate. foo(&dg); // no closure allocation.
 I think the compiler cheats a little bit on delegates, but it has  
 severe limitations that I think make using delegates without  
 accidentally allocating closures difficult.

It could probably catch simple cases like this one, though.

I don't think without some major analysis, or some sort of type marking. -Steve
Jan 04 2010