Skip to content

yield* desugaring doesn't handle errors correctly #52464

@osa1

Description

@osa1

This is covered in test language/async_star/async_star_test. Minimal repro:

import "dart:async";

Stream<int> testtt() async* {
  yield* Stream.error("error");
  yield 1;
}

void main() async {
  await testtt().handleError(print).forEach(print);
}

This should print "error" and then "1", but currently it prints "error" and then crashes with uncaught exception undefined:0: [object WebAssembly.Exception]. https://dart-review.googlesource.com/c/sdk/+/304501 somewhat improves this (it no longer crashes), but it still doesn't print "1".

With the linked CL, desugared testtt looks like this:

Stream<int> testtt() {
  StreamController<Object?> controller = StreamController<Object?>();

  Future<void> body() async {
    bool async_temporary_2;
    Completer<bool> completer = Completer<bool>();
    controller.add(completer);
    try {
      await completer.future;

      // Desugared `yield* ...`
      {
        StreamIterator<int> forIterator =
            StreamIterator<int>(Stream<int>.error("error"));

        bool jumpSentinel = false;

        try {
          for (;;) {
            async_temporary_2 = await forIterator.moveNext();
            if (jumpSentinel = async_temporary_2) {
              int awaitForVar = forIterator.current;
              controller.add(awaitForVar);
              completer = Completer<bool>();
              controller.add(completer);
              await completer.future;
            } else {
              break;
            }
          }
        } finally {
          if (jumpSentinel) {
            await forIterator.cancel();
          }
        }
      }

      // Desugared `yield 1`
      {
          controller.add(1);
          completer = Completer<bool>();
          controller.add(completer);
          await completer.future;
      }
    } catch (_6) {
      controller.addError(_6);
    } finally {
      controller.close();
    }
  }
  bool isFirst = true;
  bool isEven = false;
  controller.add(null);
  return controller.stream.asyncMap<Object?>((Object? value) {
    if (isFirst) {
      body();
      return null;
    }
    if (value is Completer<bool>) value.complete(true);
    return value;
  }).where((Object? value) {
    if (isFirst) {
      isFirst = false;
      return false;
    }
    bool keep = isEven;
    isEven = !isEven;
    return keep;
  }).cast<int>();
}

I think we need a catch somewhere in the desugared await* ... to call controller.addError(...).

Metadata

Metadata

Assignees

Labels

area-dart2wasmIssues for the dart2wasm compiler.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions