February 11, 2013
On Monday, 11 February 2013 at 06:52:33 UTC, deadalnix wrote:
> I'm thinking about it for a while now and I'm now convinced that we should allow the compiler to do that job for us. Let me explain.

struct Foo { int x, y; }

class Bar
{
    Foo[] foos = [Foo(1, 2)];

    void swap(const(Foo) f)
    {
        foos[0].x = f.y;
        foos[0].y = f.x;
    }
}

void quux(Bar bar, const(Foo) foo)
{
    bar.swap(foo);
}

Are the foo parameters allowed to be passed by ref in both these functions? I see no reason why not from your criteria, but doing so changes the program.
February 11, 2013
How about this approach:
You almost need functions, which takes structs as an rvalue, for things like:
----
struct A { }
void foo(A a) { }

foo(A());
----
So what's about something like this (Yes I mark it with '&'):
----
struct A { }
void foo(A& a) { }
----
The compiler decides at compile time whether A should be taken by ref or by value.
So for example, if A.sizeof > 16, it changes to:
----
void foo(ref A a) {
----
otherwise:
----
void foo(A a) {
----
In the latter case nothing happens for calls like: foo(A());
But in the former case the call:
foo(A());
is changed to:
auto __temp = A(); foo(__temp);
February 12, 2013
> To be honest, up until recently, I mistakenly thought the compiler DID
> do this. (Yea, my mistake.)
>
> Is it possible I had confused structs with static arrays? Do static
> arrays do that?

Maybe you were thinking about named return value optimization (which applies to return values, not parameters)?
February 12, 2013
On 02/11/2013 04:31 PM, jerro wrote:
>> To be honest, up until recently, I mistakenly thought the compiler DID
>> do this. (Yea, my mistake.)
>>
>> Is it possible I had confused structs with static arrays? Do static
>> arrays do that?
>
> Maybe you were thinking about named return value optimization (which
> applies to return values, not parameters)?

There is a corresponding optimization for parameters, which can be applied only in some cases.

The related guideline in C++ is this: If you are going to make a copy of the parameter inside the function anyway, take the parameter by-value. Here is a related article:

  http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

Especially this part: "Also, although the compiler is normally required to make a copy when a function parameter is passed by value (so modifications to the parameter inside the function can’t affect the caller), it is allowed to elide the copy, and simply use the source object itself, when the source is an rvalue."

Ali

February 12, 2013
On Monday, 11 February 2013 at 14:54:48 UTC, kinke wrote:
> I'd propose a small change so that suited structs are passed transparently byref only if the parameter is not mutable, e.g., for a function foo(const BigStruct s). The compiler would therefore not need to analyze the code flow in the callee to determine if the parameter is modified and hence a copy is needed.
>
> The compiler would nevertheless need to be quite smart though:
>
> ---
> struct MyBigStruct { double a, b, c; }
> double foo(const MyBigStruct s, double* x)
> {
>     *x = s.a;
>     return s.b;
> }
> // naive optimization by byref passing
> double foo_ref(const ref MyBigStruct s, double* x)
> {
>     *x = s.a;
>     return s.b;
> }
>
> MyBigStruct s = { 1, 2, 3 };
> double* x = &s.b;
> auto bla = foo(s, x); // returns 2; now s.b = 1
> s.b = 2;              // reset s.b to 2
> bla = foo_ref(s, x);  // returns 1! (the new s.b = 1)
> ---
>
> So the compiler would need to prove there is no way the argument can be modified and handle tricky aliasing issues accordingly.

Thinking about it some more, I'm fairly convinced the proposal in this thread is just too dangerous. Proving the argument passed transparently byref cannot be modified is just too complex imo; e.g., it could be modified by the callee via aliasing issues shown in the example above, or it could be modified by another thread while the callee is running, hence modifying the callee's parameter at the same time!

I think the intended move semantics (byval passing for small structs w/o postblit constructor/destructor, otherwise byref) are only really safe if the argument is an rvalue - the rvalue is guaranteed not to be used after the call, so potential modifications are not visible for the caller, and the rvalue is guaranteed not to be used simultaneously in another thread. Certain lvalue cases could be optimized as well, e.g., if the lvalue is a private variable of the caller (local variable or parameter) AND is not used after the call.

---
struct MyBigStruct { double a, b, c; }

double bar(MyBigStruct s) { return s.a; }

double foo(MyBigStruct s) // s is an lvalue (parameter)
{
  return bar(s); // s NOT used afterwards => byref passing
}

void main()
{
  MyBigStruct s; // s is an lvalue (local variable)
  invoke foo(s) in another thread; // s used afterwards => byval
  if (...)
  {
    foo(s);      // s used afterwards (next line) => byval
    s.a += 1;
  }
  else
    foo(s);      // s NOT used afterwards => byref
}
---

So imo parameters need to be denoted by something special like 'auto ref' if a copy is to be elided for performance reasons - not in its current form though (only for templates and leading to code-bloating); instead, rvalues should simply be transformed to lvalues before the call and then passed byref just like ordinary lvalues. And as I've already pointed out a few times in recent discussions, I'm not a fan of 'const auto ref' for not-mutable parameters, so I'd very much like to see 'const ref' for these (allowing rvalues too, just like C++).
The function signature therefore clearly indicates that these params are references to the caller's arguments, with all potentially dangerous implications.
February 12, 2013
Read my approach. I suggest something like A& a. It's short and known from C++.
Only in my approach, I suggest a link between the proposal of deadalnix (compiler optimizations) and generally rvalue references.
'const ref' will never work the way as we know it from C++. Walter and Andrei and many others are totally against it.
However, 'auto ref' is also not a real solution, because 'auto ref' generates 2^(n - 1) permuationen of the same function - code bloat.
'auto ref' accept of course lvalues and rvalues but you gain no performance.
February 12, 2013
On Tuesday, 12 February 2013 at 13:59:45 UTC, Namespace wrote:
> Read my approach. I suggest something like A& a. It's short and known from C++.

... where A& stands for a reference to an A instance. But in your approach it denotes either a plain A instance OR a reference to it, so I don't like your suggestion at all, I'm afraid. Additionally, afaik D/DMD (?) already implements move semantics for rvalues, so 1) the goal of your approach is already implemented and 2) the existing optimization doesn't require any special parameter denotation (plain A is fine).

> Only in my approach, I suggest a link between the proposal of deadalnix (compiler optimizations) and generally rvalue references.

Yes, and I'd extend it for the illustrated lvalue cases as well.

> 'const ref' will never work the way as we know it from C++. Walter and Andrei and many others are totally against it.

And there are many others totally in favour of it. ;) I'm still waiting for a plausible argument as to why the callee needs to know whether a passed const reference actually references an lvalue or an rvalue. I'm tired of this const ref discussion, and probably many others are too.

> However, 'auto ref' is also not a real solution, because 'auto ref' generates 2^(n - 1) permuationen of the same function - code bloat.

That's what I said basically regarding the current 'auto ref' implementation.

> 'auto ref' accept of course lvalues and rvalues but you gain no performance.

Of course you'd gain performance because lvalues would not be copied (just like 'ref' only). But in contrast to 'ref', you'd also be able to pass rvalues directly (byref), without having to overload the function or turn the rvalue manually to an lvalue right before the call.

The thing is that with 'auto ref', you have to determine if the struct is suited for copy-elision and then decorate the params manually with 'auto ref'/'const auto ref'/'const ref' ;). deadalnix listed some good reasons why the compiler should do that for us. But I think I also mentioned good reasons as to why the compiler most likely can't due to the huge complexity involved to make sure code doesn't break. Although it is unlikely to break in most cases, an optimization feature such as this needs to work for ALL cases.

This is why I suggested applying deadalnix' intended implicit move semantics only for rvalues and the mentioned, safe lvalue cases. Afaik D already implements move semantics for rvalues, but I guess it doesn't optimize the lvalue cases. For all other cases, I think we sadly need to resort to a revisited 'auto ref' approach.
February 12, 2013
Don't get me wrong, I also hope that this unfortunate and lengthy discussion ends. And I also like the idea that const ref works as in C++.
But I'm sure you can convince neither Walter nor Andrei still the core developer team.

And I don't quite understand what speaks against my suggestion.
If the compiler decides that by value is a better solution, then structs are received by value, otherwise by ref. But the user don't need to worry about it, because, whatever the compiler may decide to do, he adapt calls to these functions automatically. It is in principle nothing more than what you want.
February 12, 2013
> For all other cases, I think we sadly need to resort to
> a revisited 'auto ref' approach.

I.e., something like this:
https://github.com/D-Programming-Language/dmd/pull/1019
February 12, 2013
On Tuesday, 12 February 2013 at 15:57:42 UTC, kinke wrote:
>> For all other cases, I think we sadly need to resort to
>> a revisited 'auto ref' approach.
>
> I.e., something like this:
> https://github.com/D-Programming-Language/dmd/pull/1019

http://forum.dlang.org/thread/nirfuenixutsbgyrcsla@forum.dlang.org
;)