Thread overview
can't initialize .outer in inner class
May 06, 2015
Timon Gehr
May 06, 2015
Kenji Hara
May 06, 2015
Consider:

void fun()
{
    int x;
    class C
    {
        ...
    }
}

Objects of type C have access to x because they have an .outer pseudo-member.

Problem is, emplace() and any other in-situ initialization techniques fail (e.g. emplace() will fail with inner classes).

This seems to be a compiler issue - there's no way to initialize outer without calling new. What would be a proper fix to this?


Thanks,

Andrei
May 06, 2015
On 05/06/2015 10:01 AM, Andrei Alexandrescu wrote:
> Consider:
>
> void fun()
> {
>      int x;
>      class C
>      {
>          ...
>      }
> }
>
> Objects of type C have access to x because they have an .outer
> pseudo-member.
>
> Problem is, emplace() and any other in-situ initialization techniques
> fail (e.g. emplace() will fail with inner classes).
>
> This seems to be a compiler issue - there's no way to initialize outer
> without calling new.


The following workaround seems to do it (I didn't test it thoroughly though, in particular, I didn't test whether escape analysis always works correctly for this implementation):

T nestedEmplace(T,alias x,S...)(void[] mem,S args){
    auto res=cast(T)mem.ptr;
    enum siz=__traits(classInstanceSize, T);
    (cast(byte[])mem)[0..siz]=typeid(T).init[];
    auto dg=(){ return x; };
    res.outer=dg.ptr;
    static if(is(typeof(res.__ctor(args)))) res.__ctor(args);
    else assert(!is(typeof(&res.__ctor))&&args.length==0);
    return res;
}

void main(){
    int x=12345;
    class C{
        this(){}
        int foo(){
            return x;
        }
    }
    void[__traits(classInstanceSize,C)] mem=void;
    auto c=nestedEmplace!(C,x)(mem);
    assert(c.foo()==12345);
    x=3;
    assert(c.foo()==3);
}


> What would be a proper fix to this?
>

Probably nested types should invoke nested template instantiation, as the context pointer is runtime data. Then add a way to access the context pointer associated with some symbol (e.g. __traits).

>
> Thanks,
>
> Andrei

May 06, 2015
On 5/6/15 3:31 AM, Timon Gehr wrote:
> On 05/06/2015 10:01 AM, Andrei Alexandrescu wrote:
>> Consider:
>>
>> void fun()
>> {
>>      int x;
>>      class C
>>      {
>>          ...
>>      }
>> }
>>
>> Objects of type C have access to x because they have an .outer
>> pseudo-member.
>>
>> Problem is, emplace() and any other in-situ initialization techniques
>> fail (e.g. emplace() will fail with inner classes).
>>
>> This seems to be a compiler issue - there's no way to initialize outer
>> without calling new.
>
>
> The following workaround seems to do it (I didn't test it thoroughly
> though, in particular, I didn't test whether escape analysis always
> works correctly for this implementation):
>
> T nestedEmplace(T,alias x,S...)(void[] mem,S args){
>      auto res=cast(T)mem.ptr;
>      enum siz=__traits(classInstanceSize, T);
>      (cast(byte[])mem)[0..siz]=typeid(T).init[];
>      auto dg=(){ return x; };
>      res.outer=dg.ptr;
>      static if(is(typeof(res.__ctor(args)))) res.__ctor(args);
>      else assert(!is(typeof(&res.__ctor))&&args.length==0);
>      return res;
> }
>
> void main(){
>      int x=12345;
>      class C{
>          this(){}
>          int foo(){
>              return x;
>          }
>      }
>      void[__traits(classInstanceSize,C)] mem=void;
>      auto c=nestedEmplace!(C,x)(mem);
>      assert(c.foo()==12345);
>      x=3;
>      assert(c.foo()==3);
> }

Thanks, that's quite the tour de force. I tried to massage things in various places to avoid changing emplace()'s signature, no avail. I think we have a problem here. -- Andrei

May 06, 2015
2015/05/06 19:35 "Timon Gehr via Digitalmars-d" <digitalmars-d@puremagic.com
>:
>
> Probably nested types should invoke nested template instantiation, as the
context pointer is runtime data. Then add a way to access the context pointer associated with some symbol (e.g. __traits).

Related: https://issues.dlang.org/show_bug.cgi?id=8863

Kenji Hara