On Monday, 20 June 2016 at 11:45:28 UTC, Johannes Pfau wrote:
Am Sun, 19 Jun 2016 20:52:52 +0000
schrieb deadalnix <deadal...@gmail.com>:

On Sunday, 19 June 2016 at 11:11:18 UTC, Basile B. wrote:
> On Saturday, 23 April 2016 at 13:37:31 UTC, Andrei > Alexandrescu wrote: >> https://issues.dlang.org/show_bug.cgi?id=15951. I showed a >> few obvious cases, but finding the best code in general is >> tricky. Ideas? -- Andrei
>
> A new "@noinit" attribute could solve this issue and other > cases where the initializer is a handicap:
>
> The runtime would skip the copy of the initializer when
> 1- @noinit is an attribute of an aggregate.
> 2- a ctor that takes at least one parameter is present.
> 3- the default ctor is disabled (only a condition for the
> structs or the new free form unions)
>
> // OK
> @noinit struct Foo
> {
>    uint a;
>    @disable this();
>    this(uint a){}
> }
>
> // not accepted because a ctor with parameters misses
> @noinit struct Foo
> {
>    @disable this();
> }
>
> // Ok but a warning will be emitted...
> @noinit struct Foo
> {
>    uint a = 1; // ...because this value is ignored
>    @disable this();
>    this(uint a){}
> }
>
> // not accepted because there's a default ctor
> @noinit struct Foo
> {
>    this(){}
> }
>
> The rationale is that when there's a constructor that takes > parameters it's really suposed to initialize the aggregate. > At least that would be the contract, the "postulate', put by > the usage of @noinit.

No new attribute please. Just enable the damn thing where there is an argumentless constructor and be done with it.

Can somebody explain how exactly are constructors related to the problem?

The initializer is copied to the chunk that represents the new aggregate instance. The idea here is to explicitly disable this copy to get a faster instantiation, under certain conditions. For example in allocator.make() this would mean "skip the call to emplace() and call directly __ctor() on the new chunk".

If I've got this:
struct Foo
{
    int a = 42;
    int b = void;

    @disable this();
    this(int b)
    {this.b = b;}
}
auto foo = Foo(41);

I'd still expect a to be initialized to 42.

That's exactly why with @noinit you would get a warning

Note that this does _not_ require a initializer symbol or memcpy.

I'be verified again and the initializer is copied. For example with a gap in the static initial values:


struct Foo
{
     int a = 7;
     int gap = void;
     int c = 8;
     @disable this();
     this(int a)
     {this.a = a;}
}
auto fun(){ auto foo = Foo(41); return foo.a;}


I get (-O -release -inline) for fun():

0000000000457D58h  sub rsp, 18h
0000000000457D5Ch mov esi, 004C9390h // typid(Foo).initializer.ptr
0000000000457D61h  lea rdi, qword ptr [rsp+08h]
0000000000457D66h movsq //copy 8, note that the gap is not handled at all
0000000000457D68h  movsb //copy 1
0000000000457D69h  movsb //copy 1
0000000000457D6Ah  movsb //copy 1
0000000000457D6Bh  movsb //copy 1
0000000000457D6Ch  mov eax, 00000029h //inlined __ctor
0000000000457D71h  mov dword ptr [rsp+08h], eax
0000000000457D75h  add rsp, 18h
0000000000457D79h  ret

But that was obvious. How would you expect a = 7 and c = 8 to be generated otherwise ?

with @noinit you would get

sub rsp, 18h
mov eax, 00000029h //inlined __ctor
mov dword ptr [rsp+08h], eax
add rsp, 18h
ret

That's a really simple and pragmatic idea. But I guess that if you manage to get the compiler to generate a smarter initializer copy then the problem is fixed. At least I'll experiment this noinit stuff in my user library.

Reply via email to