move icon indicating copy to clipboard operation
move copied to clipboard

[move-lang] Make control expressions terms. Fix their associativity.

Open tnowacki opened this issue 2 years ago • 2 comments

  • 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, and abort) 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)
  • 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 as if (cond) s1 else (s2.f)

Test Plan

  • many new tests

tnowacki avatar May 13 '22 23:05 tnowacki

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 :)

tnowacki avatar May 13 '22 23:05 tnowacki

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 avatar May 13 '22 23:05 tnowacki

@tnowacki I think we can merge this now

oxade avatar Aug 13 '22 21:08 oxade

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?

vgao1996 avatar Aug 14 '22 18:08 vgao1996

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

tnowacki avatar Aug 15 '22 17:08 tnowacki