Fix a bug in the async transformation of let expressions

The transformation worked as if there was never an await "to the
right" of the body of a let expression (i.e., an expression evaluated
after the let expression's body but before the value of the let
expression's body is used).  This is obviously not right.

Fixes https://github.com/dart-lang/sdk/issues/33206

Change-Id: Idc175dc8c65f3d520de8b65f2285164d361ff38e
Reviewed-on: https://dart-review.googlesource.com/56492
Commit-Queue: Kevin Millikin <kmillikin@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/pkg/front_end/testcases/bug33206.dart b/pkg/front_end/testcases/bug33206.dart
new file mode 100644
index 0000000..79892c5
--- /dev/null
+++ b/pkg/front_end/testcases/bug33206.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+class X {
+  final x;
+  final y;
+
+  X(this.x, this.y);
+
+  toString() => "X($x, $y)";
+}
+
+class Y {
+  f(_) {}
+}
+
+Future<List<Object>> f1() async {
+  return [1];
+}
+
+List<Object> f2() => [2];
+
+Future<Object> f3() async {
+  return 3;
+}
+
+Future<X> foo() async {
+  return X(Y()..f(await f1())..f(f2()), await f3());
+}
+
+Future<void> main() async {
+  print(await foo());
+}
diff --git a/pkg/front_end/testcases/bug33206.dart.direct.expect b/pkg/front_end/testcases/bug33206.dart.direct.expect
new file mode 100644
index 0000000..45898a6
--- /dev/null
+++ b/pkg/front_end/testcases/bug33206.dart.direct.expect
@@ -0,0 +1,34 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+class X extends core::Object {
+  final field dynamic x;
+  final field dynamic y;
+  constructor •(dynamic x, dynamic y) → void
+    : self::X::x = x, self::X::y = y, super core::Object::•()
+    ;
+  method toString() → dynamic
+    return "X(${this.{self::X::x}}, ${this.{self::X::y}})";
+}
+class Y extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  method f(dynamic _) → dynamic {}
+}
+static method f1() → asy::Future<core::List<core::Object>> async {
+  return <dynamic>[1];
+}
+static method f2() → core::List<core::Object>
+  return <dynamic>[2];
+static method f3() → asy::Future<core::Object> async {
+  return 3;
+}
+static method foo() → asy::Future<self::X> async {
+  return new self::X::•(let final dynamic #t1 = new self::Y::•() in let final dynamic #t2 = #t1.f(await self::f1()) in let final dynamic #t3 = #t1.f(self::f2()) in #t1, await self::f3());
+}
+static method main() → asy::Future<void> async {
+  core::print(await self::foo());
+}
diff --git a/pkg/front_end/testcases/bug33206.dart.direct.transformed.expect b/pkg/front_end/testcases/bug33206.dart.direct.transformed.expect
new file mode 100644
index 0000000..9d44bff
--- /dev/null
+++ b/pkg/front_end/testcases/bug33206.dart.direct.transformed.expect
@@ -0,0 +1,137 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+class X extends core::Object {
+  final field dynamic x;
+  final field dynamic y;
+  constructor •(dynamic x, dynamic y) → void
+    : self::X::x = x, self::X::y = y, super core::Object::•()
+    ;
+  method toString() → dynamic
+    return "X(${this.{self::X::x}}, ${this.{self::X::y}})";
+}
+class Y extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  method f(dynamic _) → dynamic {}
+}
+static method f1() → asy::Future<core::List<core::Object>> /* originally async */ {
+  final asy::Completer<core::List<core::Object>> :async_completer = asy::Completer::sync<core::List<core::Object>>();
+  asy::FutureOr<core::List<core::Object>> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L1:
+      {
+        :return_value = <dynamic>[1];
+        break #L1;
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
+static method f2() → core::List<core::Object>
+  return <dynamic>[2];
+static method f3() → asy::Future<core::Object> /* originally async */ {
+  final asy::Completer<core::Object> :async_completer = asy::Completer::sync<core::Object>();
+  asy::FutureOr<core::Object> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L2:
+      {
+        :return_value = 3;
+        break #L2;
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
+static method foo() → asy::Future<self::X> /* originally async */ {
+  final asy::Completer<self::X> :async_completer = asy::Completer::sync<self::X>();
+  asy::FutureOr<self::X> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  dynamic :saved_try_context_var0;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L3:
+      {
+        final dynamic #t1 = new self::Y::•();
+        [yield] let dynamic #t2 = asy::_awaitHelper(self::f1(), :async_op_then, :async_op_error, :async_op) in null;
+        final dynamic #t3 = #t1.f(:result);
+        final dynamic #t4 = #t1.f(self::f2());
+        [yield] let dynamic #t5 = asy::_awaitHelper(self::f3(), :async_op_then, :async_op_error, :async_op) in null;
+        :return_value = new self::X::•(#t1, :result);
+        break #L3;
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
+static method main() → asy::Future<void> /* originally async */ {
+  final asy::Completer<void> :async_completer = asy::Completer::sync<void>();
+  asy::FutureOr<void> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  dynamic :saved_try_context_var0;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L4:
+      {
+        [yield] let dynamic #t6 = asy::_awaitHelper(self::foo(), :async_op_then, :async_op_error, :async_op) in null;
+        core::print(:result);
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
diff --git a/pkg/front_end/testcases/bug33206.dart.outline.expect b/pkg/front_end/testcases/bug33206.dart.outline.expect
new file mode 100644
index 0000000..bc0040e
--- /dev/null
+++ b/pkg/front_end/testcases/bug33206.dart.outline.expect
@@ -0,0 +1,29 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+class X extends core::Object {
+  final field dynamic x;
+  final field dynamic y;
+  constructor •(dynamic x, dynamic y) → void
+    ;
+  method toString() → dynamic
+    ;
+}
+class Y extends core::Object {
+  synthetic constructor •() → void
+    ;
+  method f(dynamic _) → dynamic
+    ;
+}
+static method f1() → asy::Future<core::List<core::Object>>
+  ;
+static method f2() → core::List<core::Object>
+  ;
+static method f3() → asy::Future<core::Object>
+  ;
+static method foo() → asy::Future<self::X>
+  ;
+static method main() → asy::Future<void>
+  ;
diff --git a/pkg/front_end/testcases/bug33206.dart.strong.expect b/pkg/front_end/testcases/bug33206.dart.strong.expect
new file mode 100644
index 0000000..027b1f8
--- /dev/null
+++ b/pkg/front_end/testcases/bug33206.dart.strong.expect
@@ -0,0 +1,34 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+class X extends core::Object {
+  final field dynamic x;
+  final field dynamic y;
+  constructor •(dynamic x, dynamic y) → void
+    : self::X::x = x, self::X::y = y, super core::Object::•()
+    ;
+  method toString() → core::String
+    return "X(${this.{self::X::x}}, ${this.{self::X::y}})";
+}
+class Y extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  method f(dynamic _) → dynamic {}
+}
+static method f1() → asy::Future<core::List<core::Object>> async {
+  return <core::Object>[1];
+}
+static method f2() → core::List<core::Object>
+  return <core::Object>[2];
+static method f3() → asy::Future<core::Object> async {
+  return 3;
+}
+static method foo() → asy::Future<self::X> async {
+  return new self::X::•(let final self::Y #t1 = new self::Y::•() in let final dynamic #t2 = #t1.{self::Y::f}(await self::f1()) in let final dynamic #t3 = #t1.{self::Y::f}(self::f2()) in #t1, await self::f3());
+}
+static method main() → asy::Future<void> async {
+  core::print(await self::foo());
+}
diff --git a/pkg/front_end/testcases/bug33206.dart.strong.transformed.expect b/pkg/front_end/testcases/bug33206.dart.strong.transformed.expect
new file mode 100644
index 0000000..cf29898
--- /dev/null
+++ b/pkg/front_end/testcases/bug33206.dart.strong.transformed.expect
@@ -0,0 +1,137 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+class X extends core::Object {
+  final field dynamic x;
+  final field dynamic y;
+  constructor •(dynamic x, dynamic y) → void
+    : self::X::x = x, self::X::y = y, super core::Object::•()
+    ;
+  method toString() → core::String
+    return "X(${this.{self::X::x}}, ${this.{self::X::y}})";
+}
+class Y extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  method f(dynamic _) → dynamic {}
+}
+static method f1() → asy::Future<core::List<core::Object>> /* originally async */ {
+  final asy::Completer<core::List<core::Object>> :async_completer = asy::Completer::sync<core::List<core::Object>>();
+  asy::FutureOr<core::List<core::Object>> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L1:
+      {
+        :return_value = <core::Object>[1];
+        break #L1;
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
+static method f2() → core::List<core::Object>
+  return <core::Object>[2];
+static method f3() → asy::Future<core::Object> /* originally async */ {
+  final asy::Completer<core::Object> :async_completer = asy::Completer::sync<core::Object>();
+  asy::FutureOr<core::Object> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L2:
+      {
+        :return_value = 3;
+        break #L2;
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
+static method foo() → asy::Future<self::X> /* originally async */ {
+  final asy::Completer<self::X> :async_completer = asy::Completer::sync<self::X>();
+  asy::FutureOr<self::X> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  dynamic :saved_try_context_var0;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L3:
+      {
+        final self::Y #t1 = new self::Y::•();
+        [yield] let dynamic #t2 = asy::_awaitHelper(self::f1(), :async_op_then, :async_op_error, :async_op) in null;
+        final dynamic #t3 = #t1.{self::Y::f}(:result);
+        final dynamic #t4 = #t1.{self::Y::f}(self::f2());
+        [yield] let dynamic #t5 = asy::_awaitHelper(self::f3(), :async_op_then, :async_op_error, :async_op) in null;
+        :return_value = new self::X::•(#t1, :result);
+        break #L3;
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
+static method main() → asy::Future<void> /* originally async */ {
+  final asy::Completer<void> :async_completer = asy::Completer::sync<void>();
+  asy::FutureOr<void> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  dynamic :saved_try_context_var0;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L4:
+      {
+        [yield] let dynamic #t6 = asy::_awaitHelper(self::foo(), :async_op_then, :async_op_error, :async_op) in null;
+        core::print(:result);
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
diff --git a/pkg/kernel/lib/transformations/async.dart b/pkg/kernel/lib/transformations/async.dart
index 72a0d20..21da8d9 100644
--- a/pkg/kernel/lib/transformations/async.dart
+++ b/pkg/kernel/lib/transformations/async.dart
@@ -454,15 +454,12 @@
   }
 
   TreeNode visitLet(Let expr) {
-    var shouldName = seenAwait;
-
-    seenAwait = false;
     var body = expr.body.accept(this);
 
     VariableDeclaration variable = expr.variable;
     if (seenAwait) {
-      // The body in `let var x = initializer in body` contained an await.  We
-      // will produce the sequence of statements:
+      // There is an await in the body of `let var x = initializer in body` or
+      // to its right.  We will produce the sequence of statements:
       //
       // <initializer's statements>
       // var x = <initializer's value>
@@ -488,7 +485,6 @@
     } else {
       // The body in `let x = initializer in body` did not contain an await.  We
       // can leave a let expression.
-      seenAwait = shouldName;
       return transform(expr, () {
         // The body has already been translated.
         expr.body = body..parent = expr;