March 07, 2009
Sean Kelly Wrote:

> Sean Kelly wrote:
> > John Simon wrote:
> >> I'd like to propose a new use for the 'scope' keyword within an aggregate body.
> >>
> >> Members of class type declared with the scope keyword are allocated not as references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.
> > 
> > A while back, Walter said that he planned to do exactly this.  I'm not sure what the timetable is though, or if plans have changed.
> 
> Oh, I should mention that I'm not sure how the compiler would handle this scenario:
> 
> class A { byte[16]; }
> class B { byte[32]; }
> class C {
>      this( bool b ) {
>          if( b ) o = new A;
>          else    o = new B;
>      }
>      scope Object o;
> }
> 
> If I had to guess I'd say that the compiler would either reserve the max size necessary to store both A or B, or that in non-trivial cases it just wouldn't bother with reserving space for o at all.

Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.

In C++ it's equivalent to:

class Object{void *classInfo; ...};
class A : Object {};

void normalDClass() {
    Object *o;
    o = new A(); // OK!
    o = new B(); // OK!
}

void scopeDClass() {
    Object o; // initialized when entering function

    o = A();
    // usually calls operator=(A &), i.e. opAssign(A &).
    // This is forbidden in D since A& is implicitly converible to Object.
    // Therefore a compile time error happens.

    // also, o is destructed here
}

The existing rules would work very well. The only thing you need to be careful of, is not to pass the object to someone who will keep the reference around.
March 07, 2009
Christopher Wright Wrote:

> John Simon wrote:
> > You can always upcast and downcast safely, since it isn't actually a 'value' type, only the address is passed around. The type of the class remains intact.
> 
> Right, but a scope variable has its type decided at compile time, always. In that way, it's not polymorphic.

True. But as long as you can still pass its reference around and have it behave correctly, I don't see how that's an issue.
March 08, 2009
John Simon wrote:
> Sean Kelly Wrote:
>>
>> Oh, I should mention that I'm not sure how the compiler would handle this scenario:
>>
>> class A { byte[16]; }
>> class B { byte[32]; }
>> class C {
>>      this( bool b ) {
>>          if( b ) o = new A;
>>          else    o = new B;
>>      }
>>      scope Object o;
>> }
>>
>> If I had to guess I'd say that the compiler would either reserve the max size necessary to store both A or B, or that in non-trivial cases it just wouldn't bother with reserving space for o at all.
> 
> Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.

I'm talking about a proposed new feature, not an existing one.  Please take this example in context.
March 08, 2009
Christopher Wright wrote:
> Sean Kelly wrote:
>> Andrei Alexandrescu wrote:
>>> Sean Kelly wrote:
>>>> John Simon wrote:
>>>>> I'd like to propose a new use for the 'scope' keyword within an aggregate body.
>>>>>
>>>>> Members of class type declared with the scope keyword are allocated not as references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.
>>>>
>>>> A while back, Walter said that he planned to do exactly this.  I'm not sure what the timetable is though, or if plans have changed.
>>>
>>> I'd be happier if we investigated scope in classes as an ownership mechanism. In-situ storage is nice, but ownership management is more important.
>>
>> Yeah, in-situ storage would just be a QOI feature like it is for scope variables at function level.  I agree that the logical effect of scope at class level is more important.
> 
> You're talking about something like this?
> class A
> {
>     scope Object o;
>     this ()
>     {
>         o = new Object;
>     }
> }
> 
> converts to:
> class A
> {
>     Object o;
>     this ()
>     {
>         void* ptr = gc.allocAndIgnore(Object.classinfo.size);
>         o = new(ptr) Object;
>     }
>     ~this ()
>     {
>         delete o;
>     }
> }

For the logical aspect of scope variables, I was thinking more about possibly restricting whether they are allowed to escape from the scope in which they are declared, etc.  The QOI feature would be to make the related allocations occur within the enclosing scope.  So, for example:

class C { byte[16] x; }

Object fn()
{
    scope C var = new C; // allocated on the stack
    return var; // possibly flagged as an error by the compiler
}

class D
{
    scope C var;
    this()
    {
        var = new C; // in-place constructed within D
    }

    C get()
    {
        return var; // possibly flagged as an error
    }
}
March 08, 2009
Sean Kelly Wrote:

> John Simon wrote:
> > Sean Kelly Wrote:
> >>
> >> Oh, I should mention that I'm not sure how the compiler would handle this scenario:
> >>
> >> class A { byte[16]; }
> >> class B { byte[32]; }
> >> class C {
> >>      this( bool b ) {
> >>          if( b ) o = new A;
> >>          else    o = new B;
> >>      }
> >>      scope Object o;
> >> }
> >>
> >> If I had to guess I'd say that the compiler would either reserve the max size necessary to store both A or B, or that in non-trivial cases it just wouldn't bother with reserving space for o at all.
> > 
> > Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
> 
> I'm talking about a proposed new feature, not an existing one.  Please take this example in context.

Sorry man, I thought you were disputing with me. My apologies. Let me rephrase.

I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it.

So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.
March 08, 2009
John Simon wrote:
> Sean Kelly Wrote:
> 
>> John Simon wrote:
>>> Sean Kelly Wrote:
>>>> Oh, I should mention that I'm not sure how the compiler would handle this scenario:
>>>>
>>>> class A { byte[16]; }
>>>> class B { byte[32]; }
>>>> class C {
>>>>      this( bool b ) {
>>>>          if( b ) o = new A;
>>>>          else    o = new B;
>>>>      }
>>>>      scope Object o;
>>>> }
>>>>
>>>> If I had to guess I'd say that the compiler would either reserve the max size necessary to store both A or B, or that in non-trivial cases it just wouldn't bother with reserving space for o at all.
>>> Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
>> I'm talking about a proposed new feature, not an existing one.  Please take this example in context.
> 
> Sorry man, I thought you were disputing with me. My apologies. Let me rephrase.
> 
> I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it.
> 
> So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.

Ah, that makes sense.  And it's an easy rule to follow.
March 08, 2009
On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01@gmail.com> wrote:
> Sean Kelly Wrote:
>
>> John Simon wrote:
>> > Sean Kelly Wrote:
>> >>
>> >> Oh, I should mention that I'm not sure how the compiler would handle this scenario:
>> >>
>> >> class A { byte[16]; }
>> >> class B { byte[32]; }
>> >> class C {
>> >>      this( bool b ) {
>> >>          if( b ) o = new A;
>> >>          else    o = new B;
>> >>      }
>> >>      scope Object o;
>> >> }
>> >>
>> >> If I had to guess I'd say that the compiler would either reserve the max size necessary to store both A or B, or that in non-trivial cases it just wouldn't bother with reserving space for o at all.
>> >
>> > Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
>>
>> I'm talking about a proposed new feature, not an existing one.  Please take this example in context.
>
> Sorry man, I thought you were disputing with me. My apologies. Let me rephrase.
>
> I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it.
>
> So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.

I'm pretty sure that assigning to a scope class on the stack is not done by copying the memory.  I'm pretty sure what happens is basically something like follows.   This scope statement:

    scope foo = new TheClass;

Basically becomes this:

    static ubyte[sizeof_TheClass] _mem;
    auto foo = new(_mem.ptr)  TheClass;

Where new(addr) indicates a "placement new".  So the memory for the initial instance is allocated on the stack, yes, but other than that foo act just like any other class pointer.  In particular if you say foo=otherfoo;  you're just reassigning the pointer, not actually doing anything with the object allocated on the stack.  It just becomes unreferenced junk.

And before Walter says it -- I know, I should use obj2asm and look at the assembly to see for sure.

--bb
March 08, 2009
Bill Baxter wrote:
> On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01@gmail.com> wrote:
>> Sean Kelly Wrote:
>>
>>> John Simon wrote:
>>>> Sean Kelly Wrote:
>>>>> Oh, I should mention that I'm not sure how the compiler would handle
>>>>> this scenario:
>>>>>
>>>>> class A { byte[16]; }
>>>>> class B { byte[32]; }
>>>>> class C {
>>>>>      this( bool b ) {
>>>>>          if( b ) o = new A;
>>>>>          else    o = new B;
>>>>>      }
>>>>>      scope Object o;
>>>>> }
>>>>>
>>>>> If I had to guess I'd say that the compiler would either reserve the max
>>>>> size necessary to store both A or B, or that in non-trivial cases it
>>>>> just wouldn't bother with reserving space for o at all.
>>>> Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
>>> I'm talking about a proposed new feature, not an existing one.  Please
>>> take this example in context.
>> Sorry man, I thought you were disputing with me. My apologies. Let me rephrase.
>>
>> I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it.


>>
>> So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.
> 
> I'm pretty sure that assigning to a scope class on the stack is not
> done by copying the memory.  I'm pretty sure what happens is basically
> something like follows.   This scope statement:
> 
>     scope foo = new TheClass;
> 
> Basically becomes this:
> 
>     static ubyte[sizeof_TheClass] _mem;
>     auto foo = new(_mem.ptr)  TheClass;

To that add:

scope(exit) foo.~this();

I don't think there's a syntax for destructor invocation. Never liked scope that much. Extremely dangerous for such a cute syntax.


Andrei
March 09, 2009
Sun, 08 Mar 2009 16:06:56 -0700, Andrei Alexandrescu wrote:

> Bill Baxter wrote:
>> On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01@gmail.com> wrote:
>>> Sean Kelly Wrote:
>>>
>>>> John Simon wrote:
>>>>> Sean Kelly Wrote:
>>>>>> Oh, I should mention that I'm not sure how the compiler would handle this scenario:
>>>>>>
>>>>>> class A { byte[16]; }
>>>>>> class B { byte[32]; }
>>>>>> class C {
>>>>>>      this( bool b ) {
>>>>>>          if( b ) o = new A;
>>>>>>          else    o = new B;
>>>>>>      }
>>>>>>      scope Object o;
>>>>>> }
>>>>>>
>>>>>> If I had to guess I'd say that the compiler would either reserve the max size necessary to store both A or B, or that in non-trivial cases it just wouldn't bother with reserving space for o at all.
>>>>> Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
>>>> I'm talking about a proposed new feature, not an existing one.  Please take this example in context.
>>> Sorry man, I thought you were disputing with me. My apologies. Let me rephrase.
>>>
>>> I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it.
> 
>>>
>>> So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.
>> 
>> I'm pretty sure that assigning to a scope class on the stack is not done by copying the memory.  I'm pretty sure what happens is basically something like follows.   This scope statement:
>> 
>>     scope foo = new TheClass;
>> 
>> Basically becomes this:
>> 
>>     static ubyte[sizeof_TheClass] _mem;
>>     auto foo = new(_mem.ptr)  TheClass;
> 
> To that add:
> 
> scope(exit) foo.~this();
> 
> I don't think there's a syntax for destructor invocation. Never liked scope that much. Extremely dangerous for such a cute syntax.

Shouldn't it be sort of

scope(exit) (cast(TheClass)_mem.ptr).~this();

since foo may be re-assigned to something absolutely different?
March 09, 2009
On 09/03/2009 00:12, John Simon wrote:
> Sean Kelly Wrote:
>
>> John Simon wrote:
>>> Sean Kelly Wrote:
>>>> Oh, I should mention that I'm not sure how the compiler would
>>>> handle this scenario:
>>>>
>>>> class A { byte[16]; } class B { byte[32]; } class C { this(
>>>> bool b ) { if( b ) o = new A; else    o = new B; } scope Object
>>>> o; }
>>>>
>>>> If I had to guess I'd say that the compiler would either
>>>> reserve the max size necessary to store both A or B, or that in
>>>> non-trivial cases it just wouldn't bother with reserving space
>>>> for o at all.
>>> Wrong. The Object is constructed when it comes into scope, and
>>> destructed when it leaves scope. Classes can't have an
>>> 'opAssign', instead the reference is reassigned. Since the
>>> reference is invariant/immutable here, this throws a compile time
>>> error.
>> I'm talking about a proposed new feature, not an existing one.
>> Please take this example in context.
>
> Sorry man, I thought you were disputing with me. My apologies. Let me
> rephrase.
>
> I believe that 'scope' declared objects shouldn't allow an assignment
> of a derived type. Reasoning being that there wouldn't be enough
> stack space allocated for it.
>
> So in your example above, Object could only recieve a 'new Object',
> and nothing further down in the hierarchy.

how can the compiler enforce such a rule?

class A { byte[16]; }
class B : A { byte[32]; }

class C {
   this(A a) { obj = a; }
   scope A obj;
}