diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart index 6ea2bbfeab4..a5e9561a37e 100644 --- a/pkg/vm/lib/transformations/type_flow/types.dart +++ b/pkg/vm/lib/transformations/type_flow/types.dart @@ -843,8 +843,16 @@ class ConcreteType extends Type implements Comparable { if (rhs is FutureOrType) { if (typeHierarchy.isSubtype( cls.classNode, typeHierarchy.coreTypes.futureClass)) { + Type typeArg; + if (typeArgs == null) { + typeArg = const UnknownType(); + } else { + final interfaceOffset = typeHierarchy.genericInterfaceOffsetFor( + cls.classNode, typeHierarchy.coreTypes.futureClass); + typeArg = typeArgs[interfaceOffset]; + } final RuntimeType lhs = - typeArgs == null ? RuntimeType(DynamicType(), null) : typeArgs[0]; + typeArg is RuntimeType ? typeArg : RuntimeType(DynamicType(), null); return lhs.isSubtypeOfRuntimeType( typeHierarchy, runtimeType.typeArgs[0]); } else { diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart new file mode 100644 index 00000000000..26bfac0389f --- /dev/null +++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2021, 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. + +// Regression test for https://github.com/flutter/flutter/issues/81068. +// Verifies that TFA doesn't crash when handling a class which +// implements Future and another generic class and a cast to FutureOr. + +import 'dart:async'; + +class A {} + +class B extends A implements Future { + noSuchMethod(Invocation i) => throw 'Not implemented'; +} + +B createB() => B(); + +void main() { + print(createB() as FutureOr); +} diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart.expect new file mode 100644 index 00000000000..41a1a345b50 --- /dev/null +++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart.expect @@ -0,0 +1,30 @@ +library #lib; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +import "dart:async"; + +abstract class A extends core::Object { + synthetic constructor •() → self::A* + : super core::Object::•() + ; +} +class B extends self::A implements asy::Future { + synthetic constructor •() → self::B* + : super self::A::•() + ; +[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method noSuchMethod(core::Invocation* i) → dynamic + return throw "Not implemented"; +[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] no-such-method-forwarder method then((self::B::T*) →* FutureOr* onValue, {core::Function* onError = #C1}) → asy::Future* + return [@vm.direct-call.metadata=#lib::B.noSuchMethod] [@vm.inferred-type.metadata=! (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#C2, 0, core::List::unmodifiable([@vm.inferred-type.metadata=dart.core::_GrowableList] core::_GrowableList::_literal1(self::B::then::R*)), core::List::unmodifiable([@vm.inferred-type.metadata=dart.core::_GrowableList] core::_GrowableList::_literal1(onValue)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable({#C3: onError}))) as{TypeError,ForDynamic} asy::Future*; +[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] no-such-method-forwarder method catchError(core::Function* onError, {[@vm.inferred-type.metadata=dart.core::Null? (value: null)] (core::Object*) →* core::bool* test = #C1}) → asy::Future* + return [@vm.direct-call.metadata=#lib::B.noSuchMethod] [@vm.inferred-type.metadata=! (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#C4, 0, #C5, core::List::unmodifiable([@vm.inferred-type.metadata=dart.core::_GrowableList] core::_GrowableList::_literal1(onError)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable({#C6: test}))) as{TypeError,ForDynamic} asy::Future*; +[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] no-such-method-forwarder method whenComplete(() →* FutureOr* action) → asy::Future* + return [@vm.direct-call.metadata=#lib::B.noSuchMethod] [@vm.inferred-type.metadata=! (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#C7, 0, #C5, core::List::unmodifiable([@vm.inferred-type.metadata=dart.core::_GrowableList] core::_GrowableList::_literal1(action)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable(#C9))) as{TypeError,ForDynamic} asy::Future*; +} +static method createB() → self::B* + return new self::B::•(); +static method main() → void { + core::print([@vm.inferred-type.metadata=#lib::B] self::createB() as FutureOr*); +}