move
move copied to clipboard
[move-lang] Make control expressions terms. Fix their associativity.
- Made control expressions terms to fix cases where they did not behave like other expressions
- Allowed blocks to switch their associativity
- Before this PR, control expressions (
if
,else
,loop
,while
,return
, andabort
) were always right associative. - After this PR, control expressions only consume the next block if there is one. Otherwise, they continue their right associative behavior.
- It is a bit funky to describe, but I think the examples make sense
- and it mostly matches the behavior with if/else and begin/end in OCaml (and similar constructs in other ML languages)
- Before this PR, control expressions (
- Fixed some issues with return's value not being parsed correctly
Motivation
- Fixing an issue @awelc found.
if (cond) { s1 } else { s2 }.f
looks like it should be parsed as(if (cond) s1 else s2).f
but it was actually being parsed asif (cond) s1 else (s2.f)
Test Plan
- many new tests
For what its worth, I'm not 100% sure why it wasn't like this before. A lot of the current syntactic restrictions are relics from the use of the crate lalrpop
. So sometimes things didn't work how I intended.
Things should now be working as originally designed/intended :)
language/move-compiler/transactional-tests/tests/parser/control_exp_associativity.move
Is probably the best example for anyone confused
EDIT, copying over the test for quick access
// 1 + (if (false) 0 else (10 + 10))
let x = 1 + if (false) 0 else 10 + 10;
assert!(x == 21, 0);
// true && (if (false) false else (10 == 10))
let x = true && if (false) false else 10 == 10;
assert!(x, 0);
// (if (false) 0 else 10 ) == 10
let x = if (false) 0 else { 10 } == 10;
assert!(x, 0);
// (if (true) 0 else 10) + 1
let x = if (true) 0 else { 10 } + 1;
assert!(x == 1, 0);
// if (true) 0 else (10 + 1)
let x = if (true) 0 else ({ 10 }) + 1;
assert!(x == 0, 0);
@tnowacki I think we can merge this now
Yeah looks pretty reasonable to me.
if (cond) { s1 } else { s2 }.f looks like it should be parsed as (if (cond) s1 else s2).f but it was actually being parsed as if (cond) s1 else (s2.f)
This definitely feels like a bug to me and thank you for getting it fixed! One followup question though: do you think we should mandate parentheses for more clarity in this case?
This definitely feels like a bug to me and thank you for getting it fixed! One followup question though: do you think we should mandate parentheses for more clarity in this case?
It is a very good question. My long term vision is roughly that we should open up the grammar as much as possible, and have the formatter slap in parens when confusing.
I imagine this could be a bit of a brittle fix, and might not work super well all the time (probably will need lots of settings here) But I think it could be interesting for the formatter to change x * y - z / a
to (x * y) - (z / a)
. And we could do something similar for some of these weird cases