[ddc] Fix bug in FutureOr type normalization

The correct normalization should only normalize FutureOr<Never>
when Never is non-nullable.

Change-Id: I592f3a4856c219b33a8f1ac8377567a956e1148c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/261000
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
Reviewed-by: Erik Ernst <eernst@google.com>
This commit is contained in:
Nicholas Shahan 2022-10-03 20:09:38 +00:00 committed by Commit Queue
parent 118f628ecf
commit da79baa962
6 changed files with 65 additions and 5 deletions

View file

@ -4337,7 +4337,9 @@ AbstractBool _typeTest(
bool _nullIs(DartType /*!*/ type) =>
dartTypes.isStrongTopType(type) ||
type is LegacyType &&
(type.baseType.isObject || type.baseType is NeverType) ||
(type.baseType.isObject ||
type.baseType is NeverType ||
_nullIs(type.baseType)) ||
type is NullableType ||
type is FutureOrType && _nullIs(type.typeArgument) ||
type.isNull;

View file

@ -59,7 +59,7 @@ class _FutureOrNormalizer extends ReplacementVisitor {
? Nullability.legacy
: Nullability.nonNullable;
return typeArgument.withDeclaredNullability(nullability);
} else if (typeArgument is NeverType) {
} else if (typeArgument == const NeverType.nonNullable()) {
// FutureOr<Never> --> Future<Never>
return InterfaceType(
_coreTypes.futureClass, futureOr.nullability, [typeArgument]);

View file

@ -382,9 +382,12 @@ class LegacyType extends DartType {
@JSExportName('is')
bool is_T(obj) {
if (obj == null) {
// Object and Never are the only legacy types that should return true if
// Object and Never are trivially legacy types that should return true if
// obj is `null`.
return _equalType(type, Object) || _equalType(type, Never);
if (_equalType(type, Object) || _equalType(type, Never)) return true;
return _isFutureOr(type) &&
JS<bool>('!', '#[0].is(#)', getGenericArgs(type), obj);
}
return JS<bool>('!', '#.is(#)', type, obj);
}
@ -1383,7 +1386,7 @@ external Type legacyTypeRep<T>();
@notNull
bool _isFutureOr(type) {
var genericClass = getGenericClass(type);
return JS<bool>('!', '# && # === #', genericClass, genericClass,
return JS<bool>('!', '!!# && # === #', genericClass, genericClass,
getGenericClassStatic<FutureOr>());
}

View file

@ -933,6 +933,7 @@ bool _nullIs(Rti testRti) {
isLegacyObjectType(testRti) ||
_Utils.isIdentical(testRti, LEGACY_TYPE_REF<Never>()) ||
kind == Rti.kindQuestion ||
kind == Rti.kindStar && _nullIs(Rti._getStarArgument(testRti)) ||
kind == Rti.kindFutureOr && _nullIs(Rti._getFutureOrArgument(testRti)) ||
isNullType(testRti);
}

View file

@ -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.
// @dart=2.7
import 'dart:async';
import 'package:expect/expect.dart';
import 'future_or_never_normalization_test.dart';
@pragma('dart2js:noInline')
bool legacyTypeTest<T>(dynamic val) => val is T;
@pragma('dart2js:noInline')
bool legacyFutureOrTypeTest<T>(dynamic val) => val is FutureOr<T>;
void weakTests() {
Expect.isTrue(typeTest<FutureOr<Never>>(null));
Expect.isTrue(futureOrTypeTest<Never>(null));
Expect.isTrue(legacyFutureOrTypeTest<Never>(null));
}

View file

@ -0,0 +1,31 @@
// 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.
// Requirements=nnbd-weak
import 'dart:async';
import 'package:expect/expect.dart';
import 'future_or_never_normalization_legacy_lib.dart';
// Tests to ensure normalization of various forms of FutureOr<Never> include
// or exclude null properly.
@pragma('dart2js:noInline')
bool typeTest<T>(dynamic val) => val is T;
@pragma('dart2js:noInline')
bool futureOrTypeTest<T>(dynamic val) => val is FutureOr<T>;
void main() {
Expect.isTrue(typeTest<FutureOr<Never?>>(null));
Expect.isTrue(futureOrTypeTest<Never?>(null));
Expect.isTrue(legacyFutureOrTypeTest<Never?>(null));
Expect.isFalse(typeTest<FutureOr<Never>>(null));
Expect.isFalse(legacyTypeTest<FutureOr<Never>>(null));
Expect.isFalse(futureOrTypeTest<Never>(null));
Expect.isTrue(legacyFutureOrTypeTest<Never>(null));
weakTests();
}