Thread overview
Finding out ref-ness of the return of an auto ref function
Jun 12, 2020
Arafel
Jun 12, 2020
Paul Backus
Jun 12, 2020
Arafel
Jun 12, 2020
Stanislav Blinov
Jun 13, 2020
Arafel
Jun 13, 2020
Stanislav Blinov
June 12, 2020
Hi all,

I'm hitting a problem that it's making crazy... is there any way to find out if the return of an `auto ref` function is actually ref or not?

So, according to the documentation [1] it depends on the return expressions... however in my case I'm implementing `opDispatch` in a wrapper type (and trying to carry over the ref-ness), so I don't know how can I check it.

Now, the whole point of this wrapper is to act differently based on whether the return is a reference or not (it already checks for `hasIndirections`, which btw doesn't help here either).

I've tried to use `__traits(isRef, ??? ) but I haven't been able to find out how to use it, it seems to be meant for parameters. Perhaps it would make sense to have something like `traits(isRef, return)`?

Also I have tried making two different overloads, with and without ref, but it didn't work either...

A.


[1]: https://dlang.org/spec/function.html#auto-ref-functions
June 12, 2020
On Friday, 12 June 2020 at 14:56:41 UTC, Arafel wrote:
> Hi all,
>
> I'm hitting a problem that it's making crazy... is there any way to find out if the return of an `auto ref` function is actually ref or not?
>
> So, according to the documentation [1] it depends on the return expressions... however in my case I'm implementing `opDispatch` in a wrapper type (and trying to carry over the ref-ness), so I don't know how can I check it.
>
> Now, the whole point of this wrapper is to act differently based on whether the return is a reference or not (it already checks for `hasIndirections`, which btw doesn't help here either).
>
> I've tried to use `__traits(isRef, ??? ) but I haven't been able to find out how to use it, it seems to be meant for parameters. Perhaps it would make sense to have something like `traits(isRef, return)`?
>
> Also I have tried making two different overloads, with and without ref, but it didn't work either...
>
> A.
>
>
> [1]: https://dlang.org/spec/function.html#auto-ref-functions

I think I have something that works:

ref int foo();
int bar();

enum isLvalue(string expr) = q{
    __traits(compiles, (auto ref x) {
        static assert(__traits(isRef, x));
    }(} ~ expr ~ q{))
};

pragma(msg, mixin(isLvalue!"foo()")); // true
pragma(msg, mixin(isLvalue!"bar()")); // false

Basically, you can pass the result of the function call to a function with an `auto ref` parameter and check whether that parameter is inferred as ref or not.
June 12, 2020
On 12/6/20 18:15, Paul Backus wrote:
> 
> I think I have something that works:
> 
> ref int foo();
> int bar();
> 
> enum isLvalue(string expr) = q{
>      __traits(compiles, (auto ref x) {
>          static assert(__traits(isRef, x));
>      }(} ~ expr ~ q{))
> };
> 
> pragma(msg, mixin(isLvalue!"foo()")); // true
> pragma(msg, mixin(isLvalue!"bar()")); // false
> 
> Basically, you can pass the result of the function call to a function with an `auto ref` parameter and check whether that parameter is inferred as ref or not.

Thanks a lot!

I have to say, it works, it's really, really clever... but it's also ugly as hell, and feels like a kludge. I had already tried something similar with an identity function, but I couldn't make it compile-time... I was missing the "compiles"+"static assert" trick.

Also, it can become quite hard to check from within the function itself... in my case it's more or less doable because it's basically a forwarding, so I'm essentially doing a simple mixin, but in a more complex case (with perhaps even static ifs and whatever) I can see it becoming essentially unmanageable.

All in all, I still think something like `__traits(isRef,return)` would still be worth adding! After all the compiler already has all the information, so it's just about exposing it. I'm trying to think of a library solution, but I find it very hard to express "the hypothetical result of calling the current function with the current parameters in the current context".

A.
June 12, 2020
On Friday, 12 June 2020 at 17:50:43 UTC, Arafel wrote:

> All in all, I still think something like `__traits(isRef,return)` would still be worth adding! After all the compiler already has all the information, so it's just about exposing it. I'm trying to think of a library solution, but I find it very hard to express "the hypothetical result of calling the current function with the current parameters in the current context".
>
> A.

If you're wrapping a function you can use a 'getFunctionAttributes' trait [1], which would contain a "ref" if that function returns ref.

If, however, you're wrapping a function template, however, you won't know until you actually instantiate it, which is basically going back to Paul Backus' solution. So the compiler doesn't always have all the information :)

[1] https://dlang.org/spec/traits.html#getFunctionAttributes
June 13, 2020
On 12/6/20 20:34, Stanislav Blinov wrote:
> On Friday, 12 June 2020 at 17:50:43 UTC, Arafel wrote:
> 
>> All in all, I still think something like `__traits(isRef,return)` would still be worth adding! After all the compiler already has all the information, so it's just about exposing it. I'm trying to think of a library solution, but I find it very hard to express "the hypothetical result of calling the current function with the current parameters in the current context".
>>
>> A.
> 
> If you're wrapping a function you can use a 'getFunctionAttributes' trait [1], which would contain a "ref" if that function returns ref.
> 
> If, however, you're wrapping a function template, however, you won't know until you actually instantiate it, which is basically going back to Paul Backus' solution. So the compiler doesn't always have all the information :)
> 
> [1] https://dlang.org/spec/traits.html#getFunctionAttributes

Well, the compiler can know `typeof (return)`, so at that point and under the same circumstances it has know (and thus it could expose) the ref-ness.

Also there could be a better and more straightforward way of checking if an expression would be an l-value... it seems it's not the first time it has appeared:

https://issues.dlang.org/show_bug.cgi?id=15634

The forum thread linked in the bug report is also quite interesting.
June 13, 2020
On Saturday, 13 June 2020 at 09:13:36 UTC, Arafel wrote:

>> If, however, you're wrapping a function template, however, you won't know until you actually instantiate it, which is basically going back to Paul Backus' solution. So the compiler doesn't always have all the information :)
>
> Well, the compiler can know `typeof (return)`, so at that point and under the same circumstances it has know (and thus it could expose) the ref-ness.

And it does, for functions, via the aforementioned trait. It can't for templates until those templates are instantiated, which you can only do correctly by actually forwarding arguments to it (because a function template may have auto ref parameters).

Steven's issue report doesn't account for templates, nor overloading, and correctly testing the latter would pretty much require you to match argument types and ref-ness by hand anyway (yuck).