`@rvalue ref` and `@universal ref`
Quirin Schroll
qs.il.paperinik at gmail.com
Tue Aug 6 15:26:49 UTC 2024
On Monday, 29 July 2024 at 05:52:40 UTC, IchorDev wrote:
> On Wednesday, 3 July 2024 at 11:50:19 UTC, Quirin Schroll wrote:
>> Add two function parameter attributes `@rvalue` and
>> `@universal` which must be used together with `ref` (similar
>> as `auto` is only valid as `auto ref`).
>
> This would be really useful for sure, as long as escaping
> rvalue references isn’t possible, so I guess it’d have to work
> like scope?
An `@rvalue ref` parameter definitely could be `return`. Like
every `ref`, you can’t actually escape it as it might local, in
fact, even an rvalue:
```d
// Use -dip1000
struct MaterializeL(T) { T* _value; @property ref T value() =>
*_value; }
struct MaterializeR(T) { T value; }
MaterializeL!T materializeU(T)(return ref T value)
{
return MaterializeL!T(&value);
}
MaterializeR!T materializeU(T)(T value)
{
import core.lifetime : move;
return MaterializeR!T(move(value));
}
@disable MaterializeL!T materializeR(T)(ref T value) @nogc
nothrow pure @safe
{
return MaterializeL!T(&value);
}
MaterializeR!T materializeR(T)(T value)
{
import core.lifetime : move;
return MaterializeR!T(move(value));
}
ref int f(return ref int x) @safe { x = 10; return x; }
ref int g(return ref int x) @safe => x += 1;
void main() @safe
{
int x = (0).materializeU.value.f.g;
assert(x == 11);
// int* p = &(0).materializeU.value.f.g; // Error, escapes
reference to temporary
// On lvalues, materializeU.value is a no-op:
int* p = &x.materializeU.value.f.g;
int y = (0).materializeR.value.f.g;
// int* q = &y.materializeR.value.f.g; // Error: materializeR
disabled for lvalues
}
```
> Also I’m not sure about the at-attribute-based syntax. It makes
> the feature feel like an afterthought, even though this
> functionality is very useful. Perhaps a better solution
> syntax-wise could be to use `in` as the ‘rvalue’ attribute, and
> `auto in` could be the ‘universal’ attribute. So then:
> - `@rvalue` —> `in`
> - `@rvalue ref` —> `in ref`
> - `@universal ref` —> `auto in ref`
> - `@universal auto ref` —> `auto in auto ref`
I have a DIP draft on my old computer which proposed something
very similar to that: `in` and `ref` both mean *bind by
reference*, in particular, `in` would have meant *allow rvalues*
and `ref` *allow lvalues* (so both means allow both, none means
bind-by-value). The idea of making `in` mean rvalues and `ref`
mean lvalues isn’t new. The issue is, it can’t be done anymore.
For all the time it was there, `in` meant `const`. Also, and this
is the key killer: Conceptually, `in` means input parameter, i.e.
something that supplies your function with data to make decisions
on, call it configuration if you like, and this is what informed
the `-preview=in` semantics.
Another aspect I don’t like stylistically, is `auto in`. As a
parameter storage class, `auto` means infer and requires
templates, plus one could introspect what the inference
determined. Although it doesn’t work like that, `auto ref` for
variables could be used in non-template contexts, where `ref` is
inferred from the initializer.
Contrary to that, `@universal` requires materializing rvalues.
Essentially, binding `arg` to a `@universal ref` parameter would
be exactly like passing `arg.materializeU.value` to a `ref`
parameter, and likewise passing `arg.materializeR.value` for
binding to an `@rvalue ref` parameter.
When writing DIPs, I take the future state of the language into
account, that includes the new `in` semantics. It makes no sense
proposing something that would be incompatible with something
that’s going to be in the language; e.g. in my Primary Type
Syntax DIP, I took care it won’t conflict (conceptually) with
`ref` variables, and lo and behold, `ref` variables are here to
stay. `in` is going to mean `scope const @universal ref` with the
caveat that for small trivially copyable types, ignore the
`@universal ref`. Maybe that could also go into the DIP:
`@optimized (@rvalue/@universal) ref` which is defined to be a
copy for small trivially copyable types. That would render `in`
an abbreviation for `scope const @optimized @universal ref`. It’s
been the only thing I disliked about the new `in` semantics. It
can do things in a bundle that aren’t available as single items.
Same with non-static struct member functions binding the instance
by reference, no matter if it’s called on an lvalue or rvalue. It
makes little sense that this exclusive to the implicit `this`
parameter.
> This way it’s both shorter to write[.]
That I consider the wordiness a win. Not only is it clearer and a
pure addition, those parameter attributes are an advanced tool.
> […] [A]nd we can **finally** make `in` do something useful by
> default.
But `-preview=in` already does that.
More information about the dip.ideas
mailing list