`@rvalue ref` and `@universal ref`

Quirin Schroll qs.il.paperinik at gmail.com
Wed Aug 7 10:45:55 UTC 2024


On Wednesday, 7 August 2024 at 03:47:24 UTC, IchorDev wrote:
> On Tuesday, 6 August 2024 at 15:26:49 UTC, Quirin Schroll wrote:
>> On Monday, 29 July 2024 at 05:52:40 UTC, IchorDev wrote:
>>> 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`
>>
>> The issue is, it can’t be done anymore. For all the time it 
>> was there, `in` meant `const`.
>
> And it still does. Just this week I’ve had to dissuade two 
> people from using `in` instead of `const`.

The issue is, if we make `in` mean something that doesn’t include 
`const`, it’ll be confusing for the simple reason of what it 
meant since forever and what it’s supposed to be.

>> 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
>
> This meaning is nebulous at best. What would we actually make 
> `in` *do* to have it make parameters inputs? `-preview=in` 
> making `in` into `scope ref const` that binds to rvalues is 
> just as related to a parameter being an input as `in` binding 
> to rvalues in general is.

I can only say that I disagree. An input parameter is for the 
function to only take information from to base decisions on. 
Everything it is follows logically from that:
- Can’t mutate it.
- Can’t escape it.
- Can bind it in any way it likes (by reference or by value).

If `in` binds rvalues so that they’re being moved from, it’s 
basically the opposite of an input parameter.

Of course, there could be a different meaning of “input” in the 
sense that it goes in and never gets out, but that was never 
intended.

>> and this is what informed the `-preview=in` semantics.
>
> And time has proven that `-preview=in` is:
> 1. never going to become the default; and
> 2. is too limiting to even be very useful.
>
> Your DIP would make `-preview=in` an obsolete dinosaur, so just 
> replace it.

Preview-`in` gives you an optimization based on the size of the 
type. `@universal ref` wouldn’t do that.

The reason it’s behind a preview switch is the same reason as 
`fieldwise`, `fixAliasThis`, `nosharedaccess`, 
`inclusiveincontracts`, and `fixImmutableConv` are: They are 
breaking changes and some silently change behavior.

If you have a `@system` function that uses an `in` slice 
parameter, with `-preview=in`, that parameter becomes `scope`, 
but unchecked. With `DIP1000`, a caller that passes a literal can 
allocate the literal on the stack and that introduces a bug.

My bet is that all but `rvaluerefparam` are going to be the 
default in the next Edition.

>> 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.
>
> Is that a huge problem? You could always try suggest a better 
> syntax for that case though, I was just invoking a recognisable 
> idiom.
>
>> 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.
>
> I think you should try rephrasing this because it doesn’t make 
> sense.

“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. Binding `arg` to an `@rvalue ref` would be 
exactly like passing passing `arg.materializeR.value` for binding 
to to a `ref` parameter.”

>>> This way it’s […] shorter to write
>>
>> That I consider the wordiness a win[sic]. Not only is it 
>> clearer […]
>
> It’s harder to read more text, particularly when it overflows 
> the available horizontal space. It’s not ‘clearer‘ at all.
>
>>> […] [A]nd we can **finally** make `in` do something useful by 
>>> default.
>>
>> But `-preview=in` already does that.
>
> `-preview=in` is not the default. Saying that ‘as long as you 
> change the defaults, the defaults are good’ is misleading. 
> `-preview=in` is not the default.

I don’t understand what you’re meaning here.

> No matter how we move forwards with `in`, something is going to 
> break;

Not with Editions. The whole idea of Editions is that an Edition 
is a set of Previews that are enabled together, and that modules 
opt into an Edition. Because every Edition starts with zero code 
written for it, it can’t break code.

> but I think making `in` a useful feature instead of 
> dead-on-arrival would be a welcome change rather than a 
> disappointing one.

The reason `in` isn’t useful as-is is because it’s just `const` 
and `const` isn’t that much longer than `in` while both being 
clear and not having a preview that is likely to change its 
semantics.

The reason `in` isn’t useful in Preview is because almost no-one 
uses this preview switch. It’s kind of niche, and most code works 
great without it. The difference between `scope const` 
pass-by-value and `in` (under preview) is an optimization in 
almost all cases (aliasing is the exception), and if a type is 
small, it’s not even that. Also, preview-`in` is actively 
dangerous with DIP1000 unless `@safe` isn’t the default.


More information about the dip.ideas mailing list