-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support "&&=" and "||=" operators #26996
Comments
In "An expression like |
Oops, fixed! |
do we want a DDC bug too? if so it's here: dart-archive/dev_compiler#617 |
The desugar of // An expression like `e1?.name &&= e3` desugars to:
(() {
var x = e1;
if (x == null) return null;
var y = x.name;
if (!y) return y;
return x.name = e3;
}())
// An expression like `e1?.name ||= e3` desugars to:
(() {
var x = e1;
if (x == null) return null;
var y = x.name;
if (y) return y;
return x.name = e3;
}()) |
I also notice that if |
Yes, added it to the list, thanks!
Oof, no. Thought it through better and updated the above. This will teach me to put out a proposal before I write tests. :) |
I would also expect the rules for // An expression like `v &&= e3` desugars to:
(() {
var x = v;
if (x) return true;
return v = boolean_conversion(e3);
})()
// An expression like `e1.name &&= e3` where `e1` is not a type name, desugars to:
(() {
var x = e1;
var y = x.name;
if (y) return true;
return x.name = boolean_conversion(e3);
}())
// An expression like `e1?.name &&= e3` desugars to:
(() {
var x = e1;
if (x == null) return null;
var y = x.name;
if (y) return true;
return x.name = boolean_conversion(e3);
}())
// An expression like `e1[e2] &&= e3` desugars to:
(() {
var x = e1;
var y = e2;
var z = x[y];
if (z) return true;
return x[y] = boolean_conversion(e3);
}()) and similar for We also need a specail case for (As usual, the rewrite using an applied function expression isn't sound (#25858), but it's consistent with the other unsound rewrites in the spec). |
you could write |
Ah, right. I didn't realize
Done. |
We do have a bunch of ways to say this, I think we're just converging on the level of obscurity of presentation that we are ready to accept. See this gist for some variants of the semantic specification. A tricky point is, for instance, how many times can we call the relevant getter and setter? If we wish to fix that to "call the getter exactly once" and "call the setter at most once, and do not call it in the case that corresponds to shortcut evaluation of the underlying operator |
PS: |
(haha, oops. In my head I've already moved on to strong mode/dart2 where a "bool" is actually true, false, or null, so I was just trying to get the cast+assert-non-null ... yeah, in Dart 1 you'd need |
One shorthand that could work is, say for (v.name && (v.name = boolean_convert(e))) You still need the temporary variables when the LHS isn't simple, like (x = e1)[y = e2] && x[y] = boolean_convert(e3) (where |
Why not just have the following rule:
I think this is true for all existing compound assignment operators. |
That's more or less what the proposal specifies. It's just that saying " |
You will never get That latter is idiomatic in JavaScript, but Dart has rarely needed it because most programs are checked-mode correct, so non-boolean values almost never end up in a boolean situation anyway. This specification has to account for it since it is defining a new boolean context, but everybody else should generally just not worry. |
The reason for It gets slightly more complicated because unchecked mode can allow a non-bool left operand to evaluate to |
Executing the setter in the case where shortcutting is supposed to occur could have arbitrary side-effects, so it does matter. |
Correct @eernstg . The question is if the assignment is executed or not. I think it is important that the programmer, who refactors code from E1 = E1 op E2 into E1 op= E2 does not get caught by surprise, because this refactoring is a breaking change for the short-circuit operators? @lrhn If the assignment boils down to assigning back the value itself (i.e. the setter is a simple assignment), the compiler would figure this out and not perform the store. - If it does not do this right now, it would be easy to make sure it does. I would rather change ??= to be consistent with the rule I proposed than adding these new operators with surprising semantics. |
It's potentially a breaking change but is rarely one in practice.
That's hard to do in the general case since setters can have arbitrary side effects.
That would be a breaking change to the language. I don't personally have a strong opinion here (in fact, I don't personally care about this feature at all), I just tried to come up with semantics that are consistent with The other logical operators seem to have a rule of being "maximally lazy"—they only evaluate a subexpression if needed, so I did the same thing here. |
Most of the "Tracking issues" have been closed as "Will implement in CFE". I don't know if there is an issue for CFE because GitHub search does not like |
it looks like it is at least partially implemented, but disabled in CFE (LAZY_ASSIGNMENT_ENABLED). I wasn't able to find any open CFE bugs tracking this. I don't know whether it is implemented beyond parsing. Analyzer has some support as well, and is also disabled (scanLazyAssignmentOperators). |
Closing this in favor of dart-lang/language#23 |
Dart defines the following "regular" binary operators
*
,/
,%
,~/
,+
,-
,<<
,>>
,>=
,>
,<=
,<
,==
,&
,ˆ
,|
. By "regular", I mean that they do no short-circuiting and are just syntactic sugar for a regular method call. These can all be implemented by user-defined classes.It also defines three short-circuiting binary operators
||
,&&
, and??
. These cannot be overridden and do not desugar to method calls.The regular operators also have self-assignment forms (except for the comparison operators),
+=
,-=
, etc. It used to be the case that none of the short-circuiting operators did. However, when??
was added,??=
was too. Given that, there is no good reason to omit||=
and&&=
. They are consistent with??=
and—more importantly!—are sometimes useful.So, to harmonize this, we intend to add
||=
and&&=
to Dart.Syntax
We add
&&=
and||=
to compoundAssignmentOperator in the grammar.Semantics
The semantics are pretty straightforward. The general vibe is that evaluation is "maximally lazy". A subexpression is evaluated at most once, and only if its result is needed.
To describe the desugaring, we need something akin to a "template language". It's basically Dart syntax, with a couple of extra bits:
C
is any identifier that resolves to a type name.e
is a placeholder for an arbitrary expression (except for a typename). It gets inserted as-is in the result.name
can be any identifier and is substituted as-is.bool_convert()
invokes the "boolean conversion" procedure in the language spec.&&=
||=
This desugaring has one wrinkle. It doesn't handle cases where the surrounding code is async or a generator since the function literals don't correctly propagate the "async-ness" or "generator-ness". Instead of actual functions, imagine them to be "block expressions" that let you define variables scoped to the expression instead of actually declaring a function, and you'll have the right idea.
Flag
While this is being implemented, to ensure we don't expose users to inconsistency in our tools, it should be put behind a flag named
logical-self-assign
.Tracking issues
The text was updated successfully, but these errors were encountered: