Martin Nowak is officially MIA
Meta
jared771 at gmail.com
Tue Jul 9 00:27:11 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 briefly mentioned `auto ref`, but didn't say how it interacts
with this new feature. I assume it defaults to an lvalue ref, but
what if the programmer wants it to default to an rvalue ref, or
universal? It seems they don't work well together.
More information about the dip.ideas
mailing list