mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 20:01:19 +00:00
[vm/aot/tfa] Handle classes which implement Future along with another generic type in a cast to FutureOr
When handling a type check to FutureOr in TFA, there is a case when a ConcreteType is a subtype of Future. In such case, it was assumed that ConcreteType has only 1 type argument. This is not true if class extends/implements/mixes-in Future along with another generic class. This change adds the logic to query offset of Future type arguments in the type arguments of a class and also check if the type argument is known. TEST=pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart Fixes https://github.com/flutter/flutter/issues/81068 Change-Id: I970e649823bafec433fc21a286498acc0126b331 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196546 Auto-Submit: Alexander Markov <alexmarkov@google.com> Commit-Queue: Aske Simon Christensen <askesc@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Aske Simon Christensen <askesc@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
417e3ef225
commit
47eff41aa0
|
@ -843,8 +843,16 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
|||
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 {
|
||||
|
|
|
@ -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<T> {}
|
||||
|
||||
class B<T> extends A<String> implements Future<T> {
|
||||
noSuchMethod(Invocation i) => throw 'Not implemented';
|
||||
}
|
||||
|
||||
B createB<T>() => B<T>();
|
||||
|
||||
void main() {
|
||||
print(createB<int>() as FutureOr<double>);
|
||||
}
|
|
@ -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<T extends core::Object* = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::A<self::A::T*>*
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
class B<T extends core::Object* = dynamic> extends self::A<core::String*> implements asy::Future<self::B::T*> {
|
||||
synthetic constructor •() → self::B<self::B::T*>*
|
||||
: 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<R extends core::Object* = dynamic>((self::B::T*) →* FutureOr<self::B::then::R*>* onValue, {core::Function* onError = #C1}) → asy::Future<self::B::then::R*>*
|
||||
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<core::Type*>([@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::Type*>] core::_GrowableList::_literal1<core::Type*>(self::B::then::R*)), core::List::unmodifiable<dynamic>([@vm.inferred-type.metadata=dart.core::_GrowableList<dynamic>] core::_GrowableList::_literal1<dynamic>(onValue)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol*, dynamic>] core::Map::unmodifiable<core::Symbol*, dynamic>(<core::Symbol*, dynamic>{#C3: onError}))) as{TypeError,ForDynamic} asy::Future<self::B::then::R*>*;
|
||||
[@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<self::B::T*>*
|
||||
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<dynamic>([@vm.inferred-type.metadata=dart.core::_GrowableList<dynamic>] core::_GrowableList::_literal1<dynamic>(onError)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol*, dynamic>] core::Map::unmodifiable<core::Symbol*, dynamic>(<core::Symbol*, dynamic>{#C6: test}))) as{TypeError,ForDynamic} asy::Future<self::B::T*>*;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] no-such-method-forwarder method whenComplete(() →* FutureOr<void>* action) → asy::Future<self::B::T*>*
|
||||
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<dynamic>([@vm.inferred-type.metadata=dart.core::_GrowableList<dynamic>] core::_GrowableList::_literal1<dynamic>(action)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol*, dynamic>] core::Map::unmodifiable<core::Symbol*, dynamic>(#C9))) as{TypeError,ForDynamic} asy::Future<self::B::T*>*;
|
||||
}
|
||||
static method createB<T extends core::Object* = dynamic>() → self::B<dynamic>*
|
||||
return new self::B::•<self::createB::T*>();
|
||||
static method main() → void {
|
||||
core::print([@vm.inferred-type.metadata=#lib::B<?>] self::createB<core::int*>() as FutureOr<core::double*>*);
|
||||
}
|
Loading…
Reference in a new issue