When D feels unfinished: union initialization and NRVO
Mathias Lang
pro.mathias.lang at gmail.com
Thu Mar 19 10:17:20 UTC 2020
On Wednesday, 18 March 2020 at 19:09:12 UTC, Jacob Carlborg wrote:
> On 2020-03-18 07:55, Mathias Lang wrote:
>
>> This set of requirement led me to a few simple observations:
>> - I cannot use a temporary and `cast`. Aside from the fact
>> that most casts are an admission that the type system is
>> insufficient, it would force me to pass the type by `ref` when
>> composing, which would expose the `cast` to user code, hence
>> not `@safe`;
>
> How would the `cast` be exposed to the user code?
Since there is a way to hook into the deserialization, if I
create a temporary variable which contains an elaborate type
(e.g. which defines `opAssign` / postblit, etc..), it would get
called at least once, while users usually expect construction /
deserialization to be "in one go".
In order to avoid it being called, I did explore making
`deserialize` a method of the aggregate / take a `ref` to the
place it should write into, but then we run into other problems.
If it's a method contracts / invariants are called, and if it
takes a `ref`, you don't know what the hook will do with the
already-deserialized data that you just aliased to mutable.
>> But is it really a problem ? Can't we just do the following:
>> ```
>> QT ret = { type: type, _confirm_t: deserialize!_confirm_t(dg,
>> opts) };
>> return ret;
>> ```
>> Well no, because then, NRVO is not performed anymore.
>
> I haven't looked at the generated code, but this compiles at
> least:
>
> struct QT
> {
> int type;
> int _confirm_t;
>
> @disable this(this);
> @disable ref QT opAssign () (auto ref QT other);
> }
>
> QT foo()
> {
> QT ret = { type: 1, _confirm_t: 2 };
> ret.type = 4;
> return ret;
> }
>
> void main()
> {
> auto qt = foo();
> }
>
> As long as you return the same variable in all branches it
> compiles at least. If you start to return a literal in one
> branch and a variable in a different branch it will fail to
> compile.
Ah, thanks! So this is consistent with what C++ does as well. DMD
is just being a bit conservative here, but as often, the solution
is to turn a runtime parameter into a compile time one and to add
another level of indirection.
This is how I solved the problem:
https://gist.github.com/Geod24/61ef0d8c57c3916cd3dd7611eac8234e#file-nrvo_switch-d
If you turn the `version(none)` into `version(all)` you'll see:
```
nrvo.d(54): Error: struct nrvo.Foo is not copyable because it is
annotated with @disable
nrvo.d(57): Error: struct nrvo.Foo is not copyable because it is
annotated with @disable
nrvo.d(60): Error: struct nrvo.Foo is not copyable because it is
annotated with @disable
nrvo.d(63): Error: struct nrvo.Foo is not copyable because it is
annotated with @disable
```
I guess I could raise an issue for this (it's a frontend issue so
LDC and GDC also suffer from it).
More information about the Digitalmars-d
mailing list