Skip to content

Unsound promotion for async functions and assignment outside a closure #61698

@lrhn

Description

@lrhn

The code:

FutureOr<void> foo(void Function(void Function(Object?)) callback) {
  Completer<void>? completer;
  var wasSync = false;
  callback((value) {
    if (completer != null) {
      completer.complete(value);
    } else {
      wasSync = true;
  });
  if (wasSync) return;   
  completer = Completer();
  return completer.future;
}

analyses and compiles with no errors. The completer != null check inside the closure promotes completer even though it's assigned outside of the closure.

I think that's actually sound, because the assignments outside of the closure cannot occur while the closure is running, not unless they're inside another closure, and that would disable promotion.

However, this promotion is also applied to asynchronous functions, across asynchronous gaps, and that is not sound.

Example:

import "dart:async";

void main() {
  void Function(Object) f = (_) {};

  foo((void Function(Object) c) => f = c, () => f(42));
}

void foo(
  void Function(void Function(Object o)) callback,
  void trigger(),
) {
  Completer<Object>? completer;
  print("unset");
  callback((Object value) async {
    if (completer != null) {
      print("checked");
      await 0;
      print("uses");
      completer.complete(value);
      print("used");
    }
  });
  completer = Completer();
  print("set");
  completer.future.then(print); // Sanity check.
  trigger();
  completer = null;
  print("unset");
}

This code runs and prints:

unset
set
checked
unset
uses

and then fails when it tries to call complete on null.

Assuming the synchronous promotion is deliberate, and we believe it's sound, then that promotion needs to be disabled if there is any asynchronous gap between the check and the use.

The only thing that makes the promotion sound is that the code in the surrounding function cannot run while the inner function does, and that's only true if the inner function is synchronous from check to use.

Metadata

Metadata

Assignees

Labels

area-dart-modelFor issues related to conformance to the language spec in the parser, compilers or the CLI analyzer.model-flowImplementation of flow analysis in analyzer/cfemodel-inferenceImplementation of type inferencesoundness

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions