auto ref escaping local variable

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Tue Jan 24 02:03:45 PST 2017


On Tuesday, January 24, 2017 00:47:31 Ali Çehreli via Digitalmars-d wrote:
> On 01/24/2017 12:12 AM, Patrick Schluter wrote:
>  > On Tuesday, 24 January 2017 at 06:51:40 UTC, Stefan Koch wrote:
>  >> On Tuesday, 24 January 2017 at 00:52:34 UTC, Robert burner Schadek
>
> wrote:
>  >>> I have this program that used to compile with 72 but with 73 dmd is
>  >>> complaining that
>  >>> "Error: escaping reference to local variable t"
>  >>>
>  >>> auto ref f2(T)(auto ref T t, auto ref T s) {
>  >>>
>  >>>     return t;
>  >>>
>  >>> }
>  >>>
>  >>> auto ref f1(T)(auto ref T t, auto ref T s) {
>  >>>
>  >>>     return f2(t, s);
>  >>>
>  >>> }
>  >>>
>  >>> unittest {
>  >>>
>  >>>     int a = f1(1,2);
>  >>>
>  >>> }
>  >>>
>  >>> I'm not sure why, or how to fix that.
>  >>>
>  >>> https://issues.dlang.org/show_bug.cgi?id=17117
>  >>
>  >> Seems to work as expected.
>  >> The literals 1,2 cannot be ref.
>  >> Therefore it's a normal parameter.
>  >> A function parameter is the same as a local
>  >> hence retuning a ref to it will cause this to happen.
>  >
>  > Depends on what auto ref is supposed to do, I suppose. What is the
>  > heuristic used to determine if it should handle a parameter as a
>  > reference or as a copy?
>
> Lvalues are passed by reference and rvalues are copied.
>
>  > In the case of f1(1,2) it's obvious, as it is impossible to have a
>  > reference on the literals, but for f(t, s); both are possible. In the
>  > case of int and small parameter types sizes a copy would be faster.
>  > So the question is, does auto ref use a reference as soon as it is
>  > possible to have a ref, or should it do so only when it "knows" that it
>  > is better for performance? I imagine, it's actually the first option.
>  > (just thinking loud).
>
> No, it has nothing to do with performance.
>
> The problem with auto ref is that in the case of rvalues, what you have
> is a local variable, which makes it almost given that when it's 'auto
> ref', it better be 'auto ref const' because you don't want to mutate
> anyway because your mutations would be lost in the case of rvalues.

If you're looking to mutate the argument that's passed in, then it needs to
be ref. If you're looking to have a copy to mutate, then it should not be
ref. If you don't intend to mutate it, then auto ref works just fine. It
just doesn't prevent you from mutating anything. And if you're using auto
ref and it's expected that the argument will be mutated, then it needs to
make sense for the result to be thrown away as would occur with an rvalue
argument.

> I'm under the impression (and started to write a blog post about) that
> the following two overloads is better than 'auto ref' (const or not).
> Given S is a struct,
>
>    void foo(const(S));        // Takes rvalue
>    void foo(ref const(S));    // Takes lvalue
>
> Those two work with all kinds of S arguments: lvalue, rvalue, imuttable,
> different kinds of member indirections, etc.

I don't see why that would be better than auto ref. It's doing exactly the
same thing except that it's const. You're just manually doing what auto ref
does. And given how restrictive const is, I would be very slow to mark much
of anything with it unless the types are known. And if you want const, then
just use const auto ref. The only advantage of

    void foo(const(S));        // Takes rvalue
    void foo(ref const(S));    // Takes lvalue

over const auto ref is that it works with virtual functions. Otherwise,
you're just manually doing what const auto ref does - which scales horribly
as you add more parameters.

Personally, most of the time, I simply don't worry about the performance of
copying. I just pass everything by value and don't use ref unless it's
expected that the argument's value will be used and that it will be given a
new value as part of the call (in which case, there is no non-ref overload).
If profiling indicates that there's too much of a performance hit from
copying structs around, then I'll look at using ref or auto ref for
performance, but it's not something that I do if I don't need to. It just
makes the code messier and more verbose - especially if you use ref instead
of auto ref.

I also tend to not bother with const at this point. Too little works with it
for it to be worth it most of the time - especially since ranges don't work
with it. Built-in types and simple structs can work with it just fine, but
much beyond that becomes a bundle of pain really fast, and even simple
structs fall flat on their face once you need a postblit constructor. So, I
would _not_ be in a hurry to suggest to anyone that they start using const
ref or const auto ref anywhere.

- Jonathan M Davis




More information about the Digitalmars-d mailing list