[ddc] Move FutureOr normalization to its own visitor
Will make it easier to reuse the normalization when compiling
with the new runtime type representation.
Change-Id: Ie767a2b676950205b0b50eadac305c29914433f2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/247420
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 04deb4c..b249140 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -32,6 +32,7 @@
import '../js_ast/source_map_printer.dart'
show NodeEnd, NodeSpan, HoverComment, continueSourceMap;
import 'constants.dart';
+import 'future_or_normalizer.dart';
import 'js_interop.dart';
import 'js_typerep.dart';
import 'kernel_helpers.dart';
@@ -939,7 +940,7 @@
}
return _emitInterfaceType(t, emitNullability: emitNullability);
} else if (t is FutureOrType) {
- var normalizedType = _normalizeFutureOr(t);
+ var normalizedType = normalizeFutureOr(t, _coreTypes);
if (normalizedType is FutureOrType) {
_declareBeforeUse(_coreTypes.deprecatedFutureOrClass);
var typeRep = _emitFutureOrTypeWithArgument(
@@ -2911,64 +2912,6 @@
? visitNullType(const NullType())
: _emitNullabilityWrapper(runtimeCall('Never'), type.nullability);
- /// Normalizes `FutureOr` types.
- ///
- /// Any changes to the normalization logic here should be mirrored in the
- /// classes.dart runtime library method named `normalizeFutureOr`.
- DartType _normalizeFutureOr(FutureOrType futureOr) {
- var typeArgument = futureOr.typeArgument;
- if (typeArgument is DynamicType) {
- // FutureOr<dynamic> --> dynamic
- return typeArgument;
- }
- if (typeArgument is VoidType) {
- // FutureOr<void> --> void
- return typeArgument;
- }
-
- if (typeArgument is InterfaceType &&
- typeArgument.classNode == _coreTypes.objectClass) {
- // Normalize FutureOr of Object, Object?, Object*.
- var nullable = futureOr.nullability == Nullability.nullable ||
- typeArgument.nullability == Nullability.nullable;
- var legacy = futureOr.nullability == Nullability.legacy ||
- typeArgument.nullability == Nullability.legacy;
- var nullability = nullable
- ? Nullability.nullable
- : legacy
- ? Nullability.legacy
- : Nullability.nonNullable;
- return typeArgument.withDeclaredNullability(nullability);
- } else if (typeArgument is NeverType) {
- // FutureOr<Never> --> Future<Never>
- return InterfaceType(
- _coreTypes.futureClass, futureOr.nullability, [typeArgument]);
- } else if (typeArgument is NullType) {
- // FutureOr<Null> --> Future<Null>?
- return InterfaceType(
- _coreTypes.futureClass, Nullability.nullable, [typeArgument]);
- } else if (futureOr.declaredNullability == Nullability.nullable &&
- typeArgument.nullability == Nullability.nullable) {
- // FutureOr<T?>? --> FutureOr<T?>
- return futureOr.withDeclaredNullability(Nullability.nonNullable);
- }
- // The following is not part of the normalization spec but this is a
- // convenient place to perform this change of nullability consistently. This
- // only applies at compile-time and is not needed in the runtime version of
- // the FutureOr normalization.
- // FutureOr<T%>% --> FutureOr<T%>
- //
- // If the type argument has undetermined nullability the CFE propagates
- // it to the FutureOr type as well. In this case we can represent the
- // FutureOr type without any nullability wrappers and rely on the runtime to
- // handle the nullability of the instantiated type appropriately.
- if (futureOr.nullability == Nullability.undetermined &&
- typeArgument.nullability == Nullability.undetermined) {
- return futureOr.withDeclaredNullability(Nullability.nonNullable);
- }
- return futureOr;
- }
-
@override
js_ast.Expression visitInterfaceType(InterfaceType type) =>
_emitInterfaceType(type);
@@ -2979,7 +2922,7 @@
@override
js_ast.Expression visitFutureOrType(FutureOrType type) {
- var normalizedType = _normalizeFutureOr(type);
+ var normalizedType = normalizeFutureOr(type, _coreTypes);
return normalizedType is FutureOrType
? _emitFutureOrType(normalizedType)
: normalizedType.accept(this);
diff --git a/pkg/dev_compiler/lib/src/kernel/future_or_normalizer.dart b/pkg/dev_compiler/lib/src/kernel/future_or_normalizer.dart
new file mode 100644
index 0000000..492167b
--- /dev/null
+++ b/pkg/dev_compiler/lib/src/kernel/future_or_normalizer.dart
@@ -0,0 +1,91 @@
+// 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 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart';
+import 'package:kernel/kernel.dart';
+import 'package:kernel/src/replacement_visitor.dart';
+
+/// Normalizes all `FutureOr` types found in [type].
+DartType normalizeFutureOr(DartType type, CoreTypes coreTypes) =>
+ type.accept1(_FutureOrNormalizer.instance(coreTypes), Variance.unrelated) ??
+ type;
+
+/// Visit methods returns a normalized version of `FutureOr` types or `null` if
+/// no normalization was applied.
+///
+/// The `variance` parameters in the visit methods are unused in this type
+/// replacement.
+///
+/// `FutureOr` types are normalized per the spec:
+/// https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md
+///
+/// Any changes to the normalization logic here should be mirrored in the
+/// classes.dart runtime library method named `normalizeFutureOr`.
+class _FutureOrNormalizer extends ReplacementVisitor {
+ final CoreTypes _coreTypes;
+
+ static _FutureOrNormalizer? _instance;
+
+ _FutureOrNormalizer._(this._coreTypes);
+
+ factory _FutureOrNormalizer.instance(CoreTypes coreTypes) =>
+ _instance ?? (_instance = _FutureOrNormalizer._(coreTypes));
+
+ @override
+ DartType? visitFutureOrType(FutureOrType futureOr, int variance) {
+ var normalizedTypeArg = futureOr.typeArgument.accept1(this, variance);
+ var typeArgument = normalizedTypeArg ?? futureOr.typeArgument;
+ if (typeArgument is DynamicType) {
+ // FutureOr<dynamic> --> dynamic
+ return typeArgument;
+ }
+ if (typeArgument is VoidType) {
+ // FutureOr<void> --> void
+ return typeArgument;
+ }
+
+ if (typeArgument is InterfaceType &&
+ typeArgument.classNode == _coreTypes.objectClass) {
+ // Normalize FutureOr of Object, Object?, Object*.
+ var nullable = futureOr.nullability == Nullability.nullable ||
+ typeArgument.nullability == Nullability.nullable;
+ var legacy = futureOr.nullability == Nullability.legacy ||
+ typeArgument.nullability == Nullability.legacy;
+ var nullability = nullable
+ ? Nullability.nullable
+ : legacy
+ ? Nullability.legacy
+ : Nullability.nonNullable;
+ return typeArgument.withDeclaredNullability(nullability);
+ } else if (typeArgument is NeverType) {
+ // FutureOr<Never> --> Future<Never>
+ return InterfaceType(
+ _coreTypes.futureClass, futureOr.nullability, [typeArgument]);
+ } else if (typeArgument is NullType) {
+ // FutureOr<Null> --> Future<Null>?
+ return InterfaceType(
+ _coreTypes.futureClass, Nullability.nullable, [typeArgument]);
+ } else if (futureOr.declaredNullability == Nullability.nullable &&
+ typeArgument.nullability == Nullability.nullable) {
+ // FutureOr<T?>? --> FutureOr<T?>
+ return futureOr.withDeclaredNullability(Nullability.nonNullable);
+ }
+ // The following is not part of the normalization spec but this is a
+ // convenient place to perform this change of nullability consistently. This
+ // only applies at compile-time and is not needed in the runtime version of
+ // the FutureOr normalization.
+ // FutureOr<T%>% --> FutureOr<T%>
+ //
+ // If the type argument has undetermined nullability the CFE propagates
+ // it to the FutureOr type as well. In this case we can represent the
+ // FutureOr type without any nullability wrappers and rely on the runtime to
+ // handle the nullability of the instantiated type appropriately.
+ if (futureOr.declaredNullability == Nullability.undetermined &&
+ typeArgument.declaredNullability == Nullability.undetermined) {
+ return futureOr.withDeclaredNullability(Nullability.nonNullable);
+ }
+ return null;
+ }
+}