[cfe] Allow (inferred) void in for loop variables
Closes https://github.com/dart-lang/sdk/issues/48347
Change-Id: Idc8c4c81dac66574c5d24d41c4a81b66fcf34597
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/232601
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 2e8000f..7c41343 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1111,6 +1111,7 @@
TreeNode parent = variable.parent!;
Expression implicitDowncast = inferrer.ensureAssignable(
variable.type, inferredType, variableGet,
+ isVoidAllowed: true,
fileOffset: parent.fileOffset,
errorTemplate: templateForInLoopElementTypeNotAssignable,
nullabilityErrorTemplate:
diff --git a/pkg/front_end/testcases/general/inferred_void.dart b/pkg/front_end/testcases/general/inferred_void.dart
new file mode 100644
index 0000000..5a39d07
--- /dev/null
+++ b/pkg/front_end/testcases/general/inferred_void.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2022, 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.
+
+void method() {}
+
+void v1 = method();
+var v2 = method();
+List<void> l1 = [method()];
+var l2 = [method()];
+
+test(Iterable<void> iterable, Stream<void> stream) async {
+ void v1 = method();
+ var v2 = method();
+ for (var v3 in iterable) {}
+ for (void v4 in iterable) {}
+ await for (var v5 in stream) {}
+ await for (void v6 in stream) {}
+ List<void> l1 = [method()];
+ var l2 = [method()];
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/inferred_void.dart.textual_outline.expect b/pkg/front_end/testcases/general/inferred_void.dart.textual_outline.expect
new file mode 100644
index 0000000..258ee94
--- /dev/null
+++ b/pkg/front_end/testcases/general/inferred_void.dart.textual_outline.expect
@@ -0,0 +1,7 @@
+void method() {}
+void v1 = method();
+var v2 = method();
+List<void> l1 = [method()];
+var l2 = [method()];
+test(Iterable<void> iterable, Stream<void> stream) async {}
+main() {}
diff --git a/pkg/front_end/testcases/general/inferred_void.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/inferred_void.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..f8f634b
--- /dev/null
+++ b/pkg/front_end/testcases/general/inferred_void.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+List<void> l1 = [method()];
+main() {}
+test(Iterable<void> iterable, Stream<void> stream) async {}
+var l2 = [method()];
+var v2 = method();
+void method() {}
+void v1 = method();
diff --git a/pkg/front_end/testcases/general/inferred_void.dart.weak.expect b/pkg/front_end/testcases/general/inferred_void.dart.weak.expect
new file mode 100644
index 0000000..c8d8b7d
--- /dev/null
+++ b/pkg/front_end/testcases/general/inferred_void.dart.weak.expect
@@ -0,0 +1,25 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+static field void v1 = self::method();
+static field void v2 = self::method();
+static field core::List<void> l1 = <void>[self::method()];
+static field core::List<void> l2 = <void>[self::method()];
+static method method() → void {}
+static method test(core::Iterable<void> iterable, asy::Stream<void> stream) → dynamic async {
+ void v1 = self::method();
+ void v2 = self::method();
+ for (void v3 in iterable) {
+ }
+ for (void v4 in iterable) {
+ }
+ await for (void v5 in stream) {
+ }
+ await for (void v6 in stream) {
+ }
+ core::List<void> l1 = <void>[self::method()];
+ core::List<void> l2 = <void>[self::method()];
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/inferred_void.dart.weak.modular.expect b/pkg/front_end/testcases/general/inferred_void.dart.weak.modular.expect
new file mode 100644
index 0000000..c8d8b7d
--- /dev/null
+++ b/pkg/front_end/testcases/general/inferred_void.dart.weak.modular.expect
@@ -0,0 +1,25 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+static field void v1 = self::method();
+static field void v2 = self::method();
+static field core::List<void> l1 = <void>[self::method()];
+static field core::List<void> l2 = <void>[self::method()];
+static method method() → void {}
+static method test(core::Iterable<void> iterable, asy::Stream<void> stream) → dynamic async {
+ void v1 = self::method();
+ void v2 = self::method();
+ for (void v3 in iterable) {
+ }
+ for (void v4 in iterable) {
+ }
+ await for (void v5 in stream) {
+ }
+ await for (void v6 in stream) {
+ }
+ core::List<void> l1 = <void>[self::method()];
+ core::List<void> l2 = <void>[self::method()];
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/inferred_void.dart.weak.outline.expect b/pkg/front_end/testcases/general/inferred_void.dart.weak.outline.expect
new file mode 100644
index 0000000..c0d467d
--- /dev/null
+++ b/pkg/front_end/testcases/general/inferred_void.dart.weak.outline.expect
@@ -0,0 +1,15 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+static field void v1;
+static field void v2;
+static field core::List<void> l1;
+static field core::List<void> l2;
+static method method() → void
+ ;
+static method test(core::Iterable<void> iterable, asy::Stream<void> stream) → dynamic async
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/general/inferred_void.dart.weak.transformed.expect b/pkg/front_end/testcases/general/inferred_void.dart.weak.transformed.expect
new file mode 100644
index 0000000..6528bab
--- /dev/null
+++ b/pkg/front_end/testcases/general/inferred_void.dart.weak.transformed.expect
@@ -0,0 +1,101 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+import "dart:_internal" as _in;
+
+static field void v1 = self::method();
+static field void v2 = self::method();
+static field core::List<void> l1 = core::_GrowableList::_literal1<void>(self::method());
+static field core::List<void> l2 = core::_GrowableList::_literal1<void>(self::method());
+static method method() → void {}
+static method test(core::Iterable<void> iterable, asy::Stream<void> stream) → dynamic /* originally async */ {
+ final asy::_Future<dynamic> :async_future = new asy::_Future::•<dynamic>();
+ core::bool* :is_sync = false;
+ dynamic :return_value;
+ (dynamic) → dynamic :async_op_then;
+ (core::Object, core::StackTrace) → dynamic :async_op_error;
+ core::int :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ dynamic :saved_try_context_var1;
+ dynamic :exception0;
+ dynamic :stack_trace0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L1:
+ {
+ void v1 = self::method();
+ void v2 = self::method();
+ {
+ core::Iterator<void> :sync-for-iterator = iterable.{core::Iterable::iterator}{core::Iterator<void>};
+ for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
+ void v3 = :sync-for-iterator.{core::Iterator::current}{void};
+ {}
+ }
+ }
+ {
+ core::Iterator<void> :sync-for-iterator = iterable.{core::Iterable::iterator}{core::Iterator<void>};
+ for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
+ void v4 = :sync-for-iterator.{core::Iterator::current}{void};
+ {}
+ }
+ }
+ {
+ asy::Stream<void> :stream = stream;
+ asy::_StreamIterator<void>? :for-iterator = new asy::_StreamIterator::•<void>(:stream);
+ try
+ #L2:
+ while (true) {
+ dynamic #t1 = asy::_asyncStarMoveNextHelper(:stream);
+ [yield] let dynamic #t2 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>}, :async_op_then, :async_op_error, :async_op) in null;
+ if(_in::unsafeCast<core::bool>(:result)) {
+ void v5 = :for-iterator.{asy::_StreamIterator::current}{void};
+ {}
+ }
+ else
+ break #L2;
+ }
+ finally
+ if(!(:for-iterator.{asy::_StreamIterator::_subscription}{asy::StreamSubscription<void>?} == null)) {
+ [yield] let dynamic #t3 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::cancel}(){() → asy::Future<dynamic>}, :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ }
+ }
+ {
+ asy::Stream<void> :stream = stream;
+ asy::_StreamIterator<void>? :for-iterator = new asy::_StreamIterator::•<void>(:stream);
+ try
+ #L3:
+ while (true) {
+ dynamic #t4 = asy::_asyncStarMoveNextHelper(:stream);
+ [yield] let dynamic #t5 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>}, :async_op_then, :async_op_error, :async_op) in null;
+ if(_in::unsafeCast<core::bool>(:result)) {
+ void v6 = :for-iterator.{asy::_StreamIterator::current}{void};
+ {}
+ }
+ else
+ break #L3;
+ }
+ finally
+ if(!(:for-iterator.{asy::_StreamIterator::_subscription}{asy::StreamSubscription<void>?} == null)) {
+ [yield] let dynamic #t6 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::cancel}(){() → asy::Future<dynamic>}, :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ }
+ }
+ core::List<void> l1 = core::_GrowableList::_literal1<void>(self::method());
+ core::List<void> l2 = core::_GrowableList::_literal1<void>(self::method());
+ }
+ asy::_completeWithNoFutureOnAsyncReturn(:async_future, :return_value, :is_sync);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace stack_trace) {
+ asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
+ }
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_op(){() → dynamic};
+ :is_sync = true;
+ return :async_future;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/issue48347.dart b/pkg/front_end/testcases/general/issue48347.dart
new file mode 100644
index 0000000..8b9e1ab
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue48347.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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';
+
+test(StreamController<void> _eventStreamController) async {
+ await for (final _ in _eventStreamController.stream) {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue48347.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue48347.dart.textual_outline.expect
new file mode 100644
index 0000000..0278a37
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue48347.dart.textual_outline.expect
@@ -0,0 +1,4 @@
+import 'dart:async';
+
+test(StreamController<void> _eventStreamController) async {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue48347.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue48347.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..50c55af
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue48347.dart.textual_outline_modelled.expect
@@ -0,0 +1,4 @@
+import 'dart:async';
+
+main() {}
+test(StreamController<void> _eventStreamController) async {}
diff --git a/pkg/front_end/testcases/general/issue48347.dart.weak.expect b/pkg/front_end/testcases/general/issue48347.dart.weak.expect
new file mode 100644
index 0000000..555e190
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue48347.dart.weak.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:async" as asy;
+
+import "dart:async";
+
+static method test(asy::StreamController<void> _eventStreamController) → dynamic async {
+ await for (final void _ in _eventStreamController.{asy::StreamController::stream}{asy::Stream<void>}) {
+ }
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/issue48347.dart.weak.modular.expect b/pkg/front_end/testcases/general/issue48347.dart.weak.modular.expect
new file mode 100644
index 0000000..555e190
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue48347.dart.weak.modular.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:async" as asy;
+
+import "dart:async";
+
+static method test(asy::StreamController<void> _eventStreamController) → dynamic async {
+ await for (final void _ in _eventStreamController.{asy::StreamController::stream}{asy::Stream<void>}) {
+ }
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/issue48347.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue48347.dart.weak.outline.expect
new file mode 100644
index 0000000..3f670be
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue48347.dart.weak.outline.expect
@@ -0,0 +1,10 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:async" as asy;
+
+import "dart:async";
+
+static method test(asy::StreamController<void> _eventStreamController) → dynamic async
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/general/issue48347.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue48347.dart.weak.transformed.expect
new file mode 100644
index 0000000..b10bc16
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue48347.dart.weak.transformed.expect
@@ -0,0 +1,59 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:async" as asy;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+import "dart:async";
+
+static method test(asy::StreamController<void> _eventStreamController) → dynamic /* originally async */ {
+ final asy::_Future<dynamic> :async_future = new asy::_Future::•<dynamic>();
+ core::bool* :is_sync = false;
+ dynamic :return_value;
+ (dynamic) → dynamic :async_op_then;
+ (core::Object, core::StackTrace) → dynamic :async_op_error;
+ core::int :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ dynamic :saved_try_context_var1;
+ dynamic :exception0;
+ dynamic :stack_trace0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L1:
+ {
+ {
+ asy::Stream<void> :stream = _eventStreamController.{asy::StreamController::stream}{asy::Stream<void>};
+ asy::_StreamIterator<void>? :for-iterator = new asy::_StreamIterator::•<void>(:stream);
+ try
+ #L2:
+ while (true) {
+ dynamic #t1 = asy::_asyncStarMoveNextHelper(:stream);
+ [yield] let dynamic #t2 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>}, :async_op_then, :async_op_error, :async_op) in null;
+ if(_in::unsafeCast<core::bool>(:result)) {
+ final void _ = :for-iterator.{asy::_StreamIterator::current}{void};
+ {}
+ }
+ else
+ break #L2;
+ }
+ finally
+ if(!(:for-iterator.{asy::_StreamIterator::_subscription}{asy::StreamSubscription<void>?} == null)) {
+ [yield] let dynamic #t3 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::cancel}(){() → asy::Future<dynamic>}, :async_op_then, :async_op_error, :async_op) in null;
+ :result;
+ }
+ }
+ }
+ asy::_completeWithNoFutureOnAsyncReturn(:async_future, :return_value, :is_sync);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace stack_trace) {
+ asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
+ }
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_op(){() → dynamic};
+ :is_sync = true;
+ return :async_future;
+}
+static method main() → dynamic {}