Skip to content
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

Flow analysis feature: Assignment Promotion #1844

Open
stereotype441 opened this issue Sep 7, 2021 · 7 comments
Open

Flow analysis feature: Assignment Promotion #1844

stereotype441 opened this issue Sep 7, 2021 · 7 comments
Labels
feature Proposed language feature that solves one or more problems flow-analysis Discussions about possible future improvements to flow analysis

Comments

@stereotype441
Copy link
Member

(Note: this feature request was raised in #1420 under the heading "Feature: Assignment Promotion", but since it's a flow analysis request, and that issue is tagged "field-promotion", I'm splitting the feature request out to its own separate issue).

It would be nice to allow a local variable assignment of the form id assignmentOp expression (potentially parenthesized) to act like the variable itself if used in a test. We currently allow if (x != null) ... to promote x. This change would also allow if ((x = something) != null) ... to promote x.

It only affects the left-most variable of an assignment, so if ((x = y = something) != null) ... will only promote x, not both x and y (although that's also an option if we really want it - treat both x and the assigned expression as being tested)

If you do if ((x += 1) != null) ... that still works, but there aren't that many operators which return a nullable result, so the usefulness is limited.

This is a very small feature, but it allows you to do promotion by:

int? c;
if ((c = this.capacity) != null) { ... use(c)... }
@stereotype441 stereotype441 added feature Proposed language feature that solves one or more problems flow-analysis Discussions about possible future improvements to flow analysis labels Sep 7, 2021
@mateusfccp
Copy link
Contributor

Seems a lot like #1201.

@lrhn
Copy link
Member

lrhn commented Sep 7, 2021

If-variables (#1201) introduces a new variable. This limited feature would not introduce any new syntax, it would just allow promoting in more cases than we currently do.

(Promoting where we didn't before is a potentially breaking change, so we'd still need to introduce it in a new language version and with proper warning about things that might break.)

@mateusfccp
Copy link
Contributor

I didn't notice that this syntax is already valid today... That does make sense, as it's less work.

@eernstg
Copy link
Member

eernstg commented Sep 7, 2021

We should keep in mind that the following rewrite is available today:

void main() {
  {
    // With the feature we can do this:
    int? c;
    if ((c = this.capacity) != null) { ... use(c)... }
  }

  {
    // But we can do this already today:
    int? c = this.capacity;
    if (c != null) { ... use(c)... }
  }
}

So how much of an improvement is the new feature? I guess it could be an improvement with a long if-else chain:

void main() {
  int? c;
  if ((c = exp1) != null) { ... use(c) ... }
  else if ((c = exp2) != null) { ... use-c-in-some-other-way ... }
  ...
  else { c = some-non-null-expression; }
  // At this point we know that `c` is non-null.
}

This if-else chain isn't as easy to rewrite into something which is similarly concise without the feature, because we would have to put an assignment before each if. We'd probably use the following, but that is a bit more error prone and inconvenient:

void main() {
  int? c;
  L: {
    c = exp1;
    if (c != null) { ... use(c) ...; break L; }
    c = exp2;
    if (c != null) { ... use-c-in-some-other-way ...; break L; }
    ...
    c = some-non-null-expression;
  }
  // At this point we know that `c` is non-null.
}

Does someone have an example where this kind of promotion really shines?

@lrhn
Copy link
Member

lrhn commented Sep 7, 2021

I'd go with loops.

Node lastInChain(Node node) {
  Node? next;
  while ((next = node.next) != null) {
    node = next;
  }
  return node;
}

Or nested expressions.

Node? next;
if (node != null && (next = node.next) != null) {
   actBetween(node, next);
}

@eernstg
Copy link
Member

eernstg commented Sep 7, 2021

But also (I know, dart format sez no ;-):

Node lastInChain(Node node) {
  while (true) {
    var next = node.next;
    if (next != null) node = next; else break;
  }
  return node;  
}

@lrhn
Copy link
Member

lrhn commented Sep 8, 2021

Dart format always win in the end. That, and reviewers not being used to labeled breaks.

(That's why I say:

do {
  var next = node.next;
} while (next != null) {
  node = next;
}

in #171 - a combined do-and-while loop with a shared scope).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems flow-analysis Discussions about possible future improvements to flow analysis
Projects
None yet
Development

No branches or pull requests

4 participants