What ever happened to move semantics?
Timon Gehr
timon.gehr at gmx.ch
Thu Feb 29 10:52:12 UTC 2024
On 2/29/24 05:17, Walter Bright wrote:
> On 2/28/2024 1:03 PM, Timon Gehr wrote:
>> No, the idea is that the compiler enforces that it is indeed the last
>> use and produces a compile-time error message if it cannot prove that
>> it is the case.
>
> DFA works with mathematical reliability (absent compiler bugs). The
> optimizer relies heavily on DFA; if DFA was unreliable the whole edifice
> will fall apart. Leave move vs copy to the compiler. The move vs
> copy/destroy choice is an optimization, and should be semantically
> thought of it that way.
>
>> Yes. It still holds that one may want to make sure that a value is
>> really moved at a given point. Sometimes this matters. Anyway, this is
>> by far not the most important point.
>
> If more language features are needed to work around bugs in the DFA,
> you've failed as a language designer/implementer. :-/
> ...
This is not about catching bugs in the DFA. This is about allowing a
reader of the code to not have to execute the DFA in their head in order
to see that something is the last use.
> Last use DFA can be implemented in a mathematically correct manner.
No, it is undecidable. You can only implement a sound approximation.
> The
> downside to DFA is it slows down the compiler, which concerns me in
> adding it to the front end semantics. I'm guessing is that's a reason
> why Rust has a reputation for slow compiles. (C++ doesn't have an excuse!)
> ...
Last-use analysis on a control-flow graph can be implemented in an
efficient manner using strongly connected components. It is even easier
to compute on a structured program. Rust has no goto.
>
>>> A nice feature of this is that the type of a variable can be changed
>>> on redeclaration. Note that Rust allows this.
>>
>> This is a relatively common idiom in languages that support moves. It
>> is annoying if you have to invent a new name for each intermediate
>> result.
>>
>> One use case would be type state:
>>
>> File!(FileState.flushed) file = open("file.txt");
>> File!(FileState.buffered) file = file.write("hello ");
>> File!(FileState.buffered) file = file.writeln("world!");
>> // file.close(); // compile time error
>> File!(FileState.flushed) file = file.flush();
>> file.close(); // moves "file"
>>
>> // file.write("hello"); // compile time error
>>
>> Assume you have some declarations like these and you want to comment
>> out part of the statements. It would now be annoying to have to rename
>> variables.
>>
>> I.e., you want to use the same name for different versions of the same
>> thing, similarly to how you do not have to change the name of a
>> variable when assigning to it.
>
> Interesting that you bring that up. I've been slowly leaning towards the
> "single assignment" style, where a variable is only assigned to once,
> when it is initialized. Sort of a "head const" thing. Some languages
> enforce this (can't remember which ones).
>
> I find it makes code more readable.
> ...
My example in fact follows that style. Each variable is a separate
variable that is initialized once.
> I get the feeling that allowing not only the contents, but the type of
> the variable change after re-assignment makes for less coherent code.
> ...
This is not being proposed. Redeclaration after move is not the same as
reassignment.
> I'm not sure why, but I find Rust code hard to read. Maybe that's part
> of it.
Plenty of unfamiliar things in Rust to throw you off.
> I like the O/B system so much I implemented it in D (@live), not
> only that, but have begun adopting it as my own coding style. And it
> looks sooo much nicer in D syntax!
> ...
@live, while sharing some concepts, is ultimately not even the same kind
of thing.
>
>>> We already disallow shadowing declarations, and that has prevented a
>>> number of bugs at least in my own code (they're very difficult to
>>> spot with a visual check).
>>
>> The reason why shadowing is error prone is that multiple variables
>> with overlapping lifetimes are in scope and the compiler arbitrarily
>> picks one of them. This case is different, as only one variable of the
>> same name exists at any given time. This is not error prone.
>
> I've made that error myself now and then, usually as a result of moving
> code lines about.
> ...
Well, you can always make an error by moving some code lines into a
scope in which they do not fit, but in this case, if you accidentally
redeclare a variable of a name that already exists, you get a
compile-time error.
>> Requiring unique names is more error prone in this case, as you can
>> accidentally copy an older version of a variable.
>
> I can't remember making that error :-/
> ...
Presumably you used in-place updates in cases where it would have been
hard to keep track of different versions.
>
>> Anyway, this is not the most important thing, please do check out the
>> points I initially included in my review. This point is just something
>> I had forgotten to include.
>
> Of course. This was just an easy to respond to issue.
> ...
I do feel like the response was shot from the hip.
> But a caveat. I'm kinda swamped at the moment. I'm working on some cool
> stuff for the upcoming DConf.
Of course! :)
> I also wrote the Move/Copy/Forward DIP
> before I worked on the O/B system for D. The whole Move/Copy/Forward
> needs to be studied in the context of how it fits in with O/B. This is
> going to need some careful study.
Sure. (This was also one of the things I pointed out.)
More information about the Digitalmars-d
mailing list