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