Martin Nowak is officially MIA
Meta
jared771 at gmail.com
Mon Jul 15 17:42:35 UTC 2024
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`).
>
> * An `@rvalue ref` parameter only binds rvalues, which are
> caller-side materialized.
> * A `@universal ref` parameter binds both lvalues and rvalues.
> * As before: Plain `ref` only binds lvalues.
>
> For function returns, there will be no `@universal ref`, but
> `@rvalue ref`. This is, however, equivalent to `ref` inside the
> function, meaning it must return an lvalue. The only difference
> between `ref` and `@rvalue ref` returning functions is
> caller-side. The result of a `@rvalue ref` returning function
> is considered an rvalue: A move instead of a copy is issued, it
> cannot have its address taken, etc.
>
> For `extern(C++)` functions, `@rvalue ref T` mangles like C++’s
> `T&&`; `@universal ref` is not allowed for `extern(C++)`
> functions.
>
> ---
>
> **Rationale:** `ref` can be used to make mutations transparent
> to the caller. There, only lvalue arguments make sense. But
> `ref` can also be used to alleviate copies. While there is `in`
> (with `-preview=in`), that also makes the parameter `const`,
> and some types simply don’t work well with `const`. A
> `@universal ref` binds arguments with whatever qualifier
> specified, including none (i.e. mutable).
>
> ```d
> struct BigStruct;
>
> int getN(BigStruct big) => big.n;
> // ❌ copies lvalues
> // ❌ result is not a reference
> ```
>
> ```d
> ref inout(int) getN(ref inout(BigStruct) big) => big.n;
> // ❌ no rvalue arguments allowed
> ```
>
> ```d
> int getN(in BigStruct big) => big.n;
> // ❌ can’t return by ref: big is scope
> // ❌ if it could, result would be const
> ```
>
> ```d
> int getN(const(BigStruct) big) => big.n; // for rvalues
> ref inout(int) getN(ref inout(BigStruct) big) => big.n; // for
> lvalues
> // ❌ can’t simply take address anymore
> // ❌ for rvalues: result is not an lvalue
> ```
>
> ```d
> ref inout(int) getN(@universal ref inout(BigStruct) big) =>
> big.n;
> // ✔️ no copies
> // ✔️ returns by ref
> // ✔️ allows rvalue arguments
> // ✔️ single function, `auto fp = &getN` works
> // ✔️ for rvalue argument: return value has lifetime to the end
> of the statement
> // ✔️ for mutable argument: result is mutable
> ```
>
> As far as conversions are concerned, an `R function(@universal
> ref T)` implicitly converts to `R function(ref T)` and `R
> function(@rvalue ref T)`. The reasoning being that a
> `@universal ref` parameter behaves exactly like a `ref`
> parameter for lvalue arguments, so the conversion merely
> “forgets” that rvalue arguments would be expected and allowed.
> The conversion to `@rvalue ref` similarly forgets that lvalue
> arguments were allowed.
>
> `ref` and `@rvalue ref` returns are conversion-incompatible for
> implicit conversions. As they are not binary-incompatible, an
> explicit cast can be used, which is a `@system` operation.
You mentioned `auto ref` briefly, but did not describe how it
interacts with this feature. I assume it's an lvalue ref by
default, but would it be more useful if it's a universal ref? Or
what if the user wants it to degrade to an rvalue ref instead?
How would this work?
More information about the dip.ideas
mailing list