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.