[dart2js] Add Rti specializer for `is FutureOr<T>`. In particular, this fixes some type tests involving records. `(1, true) as FutureOr<(int, bool)>` was failing because the Rti for the record literal was _Record_2 rather than +(int, bool). We want to avoid constructing the full record Rti, so instead we can have the specializer for FutureOr directly delegate to its union components. Fixes: #51910 Change-Id: I3716882aab0a519ab35803d87fabe570b7e27655 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/292242 Reviewed-by: Stephen Adams <sra@google.com> Commit-Queue: Mayank Patke <fishythefish@google.com>
diff --git a/sdk/lib/_internal/js_shared/lib/rti.dart b/sdk/lib/_internal/js_shared/lib/rti.dart index 30876a3..88cce4b 100644 --- a/sdk/lib/_internal/js_shared/lib/rti.dart +++ b/sdk/lib/_internal/js_shared/lib/rti.dart
@@ -946,6 +946,10 @@ if (isTopType(testRti)) { return _finishIsFn(testRti, object, RAW_DART_FUNCTION_REF(_isTop)); } + if (Rti._getKind(testRti) == Rti.kindQuestion) { + return _finishIsFn(testRti, object, + RAW_DART_FUNCTION_REF(_generalNullableIsTestImplementation)); + } // `o is T*` generally behaves like `o is T`. // The exceptions are `Object*` (handled above) and `Never*` @@ -960,6 +964,10 @@ ? Rti._getStarArgument(testRti) : testRti; + if (Rti._getKind(unstarred) == Rti.kindFutureOr) { + return _finishIsFn(testRti, object, RAW_DART_FUNCTION_REF(_isFutureOr)); + } + var isFn = _simpleSpecializedIsTest(unstarred); if (isFn != null) { return _finishIsFn(testRti, object, isFn); @@ -987,9 +995,6 @@ testRti, object, RAW_DART_FUNCTION_REF(_isTestViaProperty)); } // fall through to general implementation. - } else if (Rti._getKind(testRti) == Rti.kindQuestion) { - return _finishIsFn(testRti, object, - RAW_DART_FUNCTION_REF(_generalNullableIsTestImplementation)); } else if (Rti._getKind(unstarred) == Rti.kindRecord) { isFn = _recordSpecializedIsTest(unstarred); return _finishIsFn(testRti, object, isFn); @@ -1212,6 +1217,14 @@ // Specializations can be placed on Rti objects as the _as and _is // 'methods'. They can also be called directly called from generated code. +/// Specialization for `is FutureOr<T>`. +/// Called from generated code via Rti `_is` method. +bool _isFutureOr(Object? object) { + Rti testRti = _Utils.asRti(JS('', 'this')); + return Rti._isCheck(Rti._getFutureOrArgument(testRti), object) || + Rti._isCheck(Rti._getFutureFromFutureOr(_theUniverse(), testRti), object); +} + /// Specialization for 'is Object'. /// Called from generated code via Rti `_is` method. bool _isObject(Object? object) { @@ -3080,6 +3093,9 @@ return true; } + // Record Type/Record: + if (sKind == Rti.kindRecord && isRecordInterfaceType(t)) return true; + // Positional Function Types + Named Function Types: // TODO(fishythefish): Disallow JavaScriptFunction as a subtype of function // types using features inaccessible from JavaScript. @@ -3120,15 +3136,7 @@ return _isInterfaceSubtype(universe, s, sEnv, t, tEnv); } - // Records - // - // TODO(50081): Reference rules to updated specification - // https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md#rules - - // Record Type/Record: - if (sKind == Rti.kindRecord && isRecordInterfaceType(t)) return true; - - // Record Type/Record Type: + // Record Types: if (sKind == Rti.kindRecord && tKind == Rti.kindRecord) { return _isRecordSubtype(universe, s, sEnv, t, tEnv); }