March 19, 2020
On Thursday, 19 March 2020 at 13:34:11 UTC, kinke wrote:
>
> Another simple workaround:
>
> import core.lifetime : move;
>
> ...
> case Good.Baguette:
>     Foo f = { type_: type, f1: typeof(Foo.f1)("Hello World") };
>     return move(f);
> ...

From my tests, it seems to do NRVO on LDC, so I suppose you recognize it ?
On DMD, it just does a blit, which can still be expensive and breaks interior pointers.

Another major advantage, which I also realized recently, is that having NRVO all the way means that the stack usage is bounded. Beside the obvious embedded code, it matters a lot when using Fibers (we have a Vibe.d style handling of connection, with 1 connection == 1 fiber).

I also found out that one of the earlier case I mentioned (https://gist.github.com/Geod24/61ef0d8c57c3916cd3dd7611eac8234e#file-nrvo_struct_ctor-d) only does NRVO on LDC. DMD (and GDC 9.3.0) just silently move it.
March 19, 2020
On Thursday, 19 March 2020 at 17:38:42 UTC, Mathias Lang wrote:
> From my tests, it seems to do NRVO on LDC, so I suppose you recognize it ?
> On DMD, it just does a blit, which can still be expensive and breaks interior pointers.

Yeah, I'd suggest not to look at how this is currently implemented (Walter doesn't seem to see the advantage of a move compiler intrinsic). With a proper optimizer, you shouldn't have to care (see LDC result).
Wrt. interior pointers, that's in the spec and could bite you in different places where moving is implicit.

> I also found out that one of the earlier case I mentioned (https://gist.github.com/Geod24/61ef0d8c57c3916cd3dd7611eac8234e#file-nrvo_struct_ctor-d) only does NRVO on LDC. DMD (and GDC 9.3.0) just silently move it.

I've been working on improving things in this regard, especially with v1.19.
March 19, 2020
On Thursday, 19 March 2020 at 13:34:11 UTC, kinke wrote:
> Another simple workaround:
>
> import core.lifetime : move;
>
> ...
> case Good.Baguette:
>     Foo f = { type_: type, f1: typeof(Foo.f1)("Hello World") };
>     return move(f);
> ...


case Good.Baguette:
  return (){
    Foo f = { type_: type, f1: typeof(Foo.f1)("Hello World") };
    return f;
  }();
March 19, 2020
As I understand, the problem is that switch statement was taken from C where it's a goto with a bunch of labels, so the compiler can't easily reason about variable lifetime or something like that.
March 19, 2020
On 2020-03-19 11:17, Mathias Lang wrote:

> but as often, the solution is to turn a runtime parameter into a compile time one and to add another level of indirection.

If you don't use static initialization and instead assign the fields one by one it should work without the indirection.

-- 
/Jacob Carlborg
March 19, 2020
On Thu, Mar 19, 2020 at 06:28:17PM +0000, Kagamin via Digitalmars-d wrote:
> As I understand, the problem is that switch statement was taken from C where it's a goto with a bunch of labels, so the compiler can't easily reason about variable lifetime or something like that.

In D, this code (Duff's device) compiles:

	int i = 0, j = 0;
	switch (i % 4) {
		while (i < 10) {
			case 0: j++; goto case 1;
			case 1: j++; goto case 2;
			case 2: j++; goto case 3;
			case 3: default: j++;
		}
	}

Lifetime analysis is either going to break down or become seriously convoluted, if you try to apply it here!


T

-- 
Don't throw out the baby with the bathwater. Use your hands...
March 19, 2020
On Thursday, 19 March 2020 at 18:46:13 UTC, H. S. Teoh wrote:
> Lifetime analysis is either going to break down or become seriously convoluted, if you try to apply it here!

case Good.Baguette:
  if(true){
    Foo f = { type_: type, f1: typeof(Foo.f1)("Hello World") };
    return f;
  }

Pretty sure if statement must be a self-contained scope, but even it doesn't reset scope here.
March 19, 2020
On Thu, Mar 19, 2020 at 08:09:44PM +0000, Kagamin via Digitalmars-d wrote:
> On Thursday, 19 March 2020 at 18:46:13 UTC, H. S. Teoh wrote:
> > Lifetime analysis is either going to break down or become seriously convoluted, if you try to apply it here!
> 
> case Good.Baguette:
>   if(true){
>     Foo f = { type_: type, f1: typeof(Foo.f1)("Hello World") };
>     return f;
>   }
> 
> Pretty sure if statement must be a self-contained scope, but even it doesn't reset scope here.

That sounds like a bug, similar to the bug with identically-named local identifiers in disjoint subscopes in a function body.


T

-- 
"How are you doing?" "Doing what?"
March 20, 2020
On 3/19/20 11:59 PM, H. S. Teoh wrote:
> On Thu, Mar 19, 2020 at 08:09:44PM +0000, Kagamin via Digitalmars-d wrote:
>> On Thursday, 19 March 2020 at 18:46:13 UTC, H. S. Teoh wrote:
>>> Lifetime analysis is either going to break down or become seriously
>>> convoluted, if you try to apply it here!
>>
>> case Good.Baguette:
>>    if(true){
>>      Foo f = { type_: type, f1: typeof(Foo.f1)("Hello World") };
>>      return f;
>>    }
>>
>> Pretty sure if statement must be a self-contained scope, but even it
>> doesn't reset scope here.
> 
> That sounds like a bug, similar to the bug with identically-named
> local identifiers in disjoint subscopes in a function body.
> 

Today I can't understand compiler output and then remember this post. That bug makes using subscopes useless.

March 20, 2020
On Thursday, 19 March 2020 at 18:17:45 UTC, Kagamin wrote:
>
> case Good.Baguette:
>   return (){
>     Foo f = { type_: type, f1: typeof(Foo.f1)("Hello World") };
>     return f;
>   }();

This satisfies the compiler (the frontend doesn't complain), but it doesn't do NRVO, it's a move, even on LDC. Still good to know.