Expressions And Operators: Banning Lval As An Expression
Why?
- We'd like to have various rules preventing things we know are unsafe in concurrent blocks. For example, assigning something twice in the middle of two different expressions in a concurrent block.
concurrent { $x1 = await x_async($y = 42); $x2 = await y_async($y = 43); }
- Since we will be allowing
await-as-an-expression
outside of concurrent blocks, we'd like to not have to special case these rules so that they only kick in when adding anawait
to a statement. - There is a general belief that due to the current scoping of variables, complex assignment is very hard to reason about when writing Hack code.
- We considered having more pointed rules that would allow much more complex rules that attempt to enable more possible usages of lval-as-an-expression, but based on initial testing, the level of usage of this feature is already very low, so it doesn't seem useful to support. Having simpler rules is a win in-and-of itself.
Definitions
lval
will be banned in non-final positions. These are positions in which if you forced the lval
to return void
will still be allowed.
lval
positions:
- Assignment:
<lval> <op>= ...
- Inout arg:
f(inout <lval>)
- ++/--:
<lval>++
<lval>--
++<lval>
--<lval>
- foreach:
foreach (... as <lval>) ...
foreach (... as <lval> => <lval>) ...
- list:
list(<lval>, ...)
“final” lval
:
- Positions:
- These are positions which take expressions but do nothing with their result (accept
void
). They promote normal lvals to “final lval”. - ExpressionStatement:
<final_lval>;
- For:
for (<final_lval>, <final_lval>, ...; ...; <final_lval>, <final_lval>, ...) ...
- Using:
using $x = ...;
using ($x = ..., $y = ...) { ... }
await using ($x = await ..., $y = await ...) { ... }
- These are positions which take expressions but do nothing with their result (accept
- Assignment:
- If the assignment is directly in a final lval position
<lval> <op>= ...;
- Inout arg:
- If the call is directly in a final lval position
f(inout <lval>);
Foo::f(inout <lval>);
$bar->f(inout <lval>);
- We also make an exception to allow assigning and inout at the same time.
$x = f(inout <lval>, ...);
- Also we [email protected]'ing the function
$x = @f(inout <lval>, ...);
- & arg:
- Same rules as inout except w/
&
- Same rules as inout except w/
- ++/--:
- Must be directly in a final lval position
<lval>++;
<lval>--;
++<lval>;
--<lval>
;for ($x++; ...; $y++) { ... }
- foreach:
- Always in a final lval position
- list:
- Must be directly in a lval position that is a final lval
list(<lval>, ...) = $x;
foreach (... as list(<lval>, ...)) { ... }