| |
| Posted by Timon Gehr in reply to Walter Bright | PermalinkReply |
|
Timon Gehr
Posted in reply to Walter Bright
| 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.)
|