[ddk] Modify _isInterfaceSubtype to consider variance annotations during runtime.

Change-Id: Idc1b553d24d76bfe52d51204aae0e0334f210fd0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121910
Reviewed-by: Leaf Petersen <leafp@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
This commit is contained in:
Kallen Tu 2019-10-23 21:48:17 +00:00 committed by commit-bot@chromium.org
parent 700765a4d7
commit ef742aa6f0
8 changed files with 329 additions and 4 deletions

View file

@ -1037,9 +1037,23 @@ bool _isInterfaceSubtype(t1, t2) => JS('', '''(() => {
if (typeArguments1.length != typeArguments2.length) {
$assertFailed();
}
let variances = $getGenericArgVariances($t1);
for (let i = 0; i < typeArguments1.length; ++i) {
if (!$_isSubtype(typeArguments1[i], typeArguments2[i])) {
return false;
// When using implicit variance, variances will be undefined and
// considered covariant.
if (variances === void 0 || variances[i] == ${Variance.covariant}) {
if (!$_isSubtype(typeArguments1[i], typeArguments2[i])) {
return false;
}
} else if (variances[i] == ${Variance.contravariant}) {
if (!$_isSubtype(typeArguments2[i], typeArguments1[i])) {
return false;
}
} else if (variances[i] == ${Variance.invariant}) {
if (!$_isSubtype(typeArguments1[i], typeArguments2[i]) ||
!$_isSubtype(typeArguments2[i], typeArguments1[i])) {
return false;
}
}
}
return true;

View file

@ -1198,9 +1198,23 @@ bool _isInterfaceSubtype(t1, t2) => JS('', '''(() => {
if (typeArguments1.length != typeArguments2.length) {
$assertFailed();
}
let variances = $getGenericArgVariances($t1);
for (let i = 0; i < typeArguments1.length; ++i) {
if (!$_isSubtype(typeArguments1[i], typeArguments2[i])) {
return false;
// When using implicit variance, variances will be undefined and
// considered covariant.
if (variances === void 0 || variances[i] == ${Variance.covariant}) {
if (!$_isSubtype(typeArguments1[i], typeArguments2[i])) {
return false;
}
} else if (variances[i] == ${Variance.contravariant}) {
if (!$_isSubtype(typeArguments2[i], typeArguments1[i])) {
return false;
}
} else if (variances[i] == ${Variance.invariant}) {
if (!$_isSubtype(typeArguments1[i], typeArguments2[i]) ||
!$_isSubtype(typeArguments2[i], typeArguments1[i])) {
return false;
}
}
}
return true;

View file

@ -5,4 +5,5 @@
# Sections in this file should contain "$compiler == dartdevc" or dartdevk.
[ $compiler == dartdevc ]
variance_subtype_test: SkipByDesign # Not supported by analyzer based DDC.
variance_test: SkipByDesign # Not supported by analyzer based DDC.

View file

@ -0,0 +1,148 @@
// Copyright (c) 2019, 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.
// Tests runtime subtyping with explicit variance modifiers.
// SharedOptions=--enable-experiment=variance
import 'dart:async';
import 'runtime_utils.dart';
class Upper {}
class Middle extends Upper {}
class Lower extends Middle {}
class Covariant<out T> {}
class Contravariant<in T> {}
class Invariant<inout T> {}
class LegacyCovariant<T> {}
void main() {
// Covariant<Lower> <: Covariant<Middle>
checkProperSubtype(generic1(Covariant, Lower), generic1(Covariant, Middle));
// Covariant<Middle> <: Covariant<Middle>
checkSubtype(generic1(Covariant, Middle), generic1(Covariant, Middle));
// Contravariant<Upper> <: Contravariant<Middle>
checkProperSubtype(generic1(Contravariant, Upper), generic1(Contravariant, Middle));
// Contravariant<Middle> <: Contravariant<Middle>
checkSubtype(generic1(Contravariant, Middle), generic1(Contravariant, Middle));
// Invariant<Middle> <: Invariant<Middle>
checkSubtype(generic1(Invariant, Middle), generic1(Invariant, Middle));
// Invariant<dynamic> <:> Invariant<Object>
checkSubtype(generic1(Invariant, dynamic), generic1(Invariant, Object));
checkSubtype(generic1(Invariant, Object), generic1(Invariant, dynamic));
// Invariant<FutureOr<dynamic>> <:> Invariant<dynamic>
checkSubtype(generic1(Invariant, generic1(FutureOr, dynamic)), generic1(Invariant, dynamic));
checkSubtype(generic1(Invariant, dynamic), generic1(Invariant, generic1(FutureOr, dynamic)));
// Invariant<FutureOr<Null>> <:> Invariant<Future<Null>>
checkSubtype(generic1(Invariant, generic1(FutureOr, Null)), generic1(Invariant, generic1(Future, Null)));
checkSubtype(generic1(Invariant, generic1(Future, Null)), generic1(Invariant, generic1(FutureOr, Null)));
// LegacyCovariant<Lower> <: LegacyCovariant<Middle>
checkProperSubtype(generic1(LegacyCovariant, Lower), generic1(LegacyCovariant, Middle));
// List<Covariant<Lower>> <: Iterable<Covariant<Middle>>
checkProperSubtype(generic1(List, generic1(Covariant, Lower)), generic1(Iterable, generic1(Covariant, Middle)));
// List<Contravariant<Upper>> <: Iterable<Contravariant<Middle>>
checkProperSubtype(generic1(List, generic1(Contravariant, Upper)), generic1(Iterable, generic1(Contravariant, Middle)));
// List<Invariant<Middle>> <: Iterable<Invariant<Middle>>
checkProperSubtype(generic1(List, generic1(Invariant, Middle)), generic1(Iterable, generic1(Invariant, Middle)));
// List<LegacyCovariant<Lower>> <: Iterable<LegacyCovariant<Middle>>
checkProperSubtype(generic1(List, generic1(LegacyCovariant, Lower)), generic1(Iterable, generic1(LegacyCovariant, Middle)));
// String -> Covariant<Lower> <: String -> Covariant<Middle>
checkProperSubtype(function1(generic1(Covariant, Lower), String), function1(generic1(Covariant, Middle), String));
// Covariant<Upper> -> String <: Covariant<Middle> -> String
checkProperSubtype(function1(String, generic1(Covariant, Upper)), function1(String, generic1(Covariant, Middle)));
// String -> Contravariant<Upper> <: String -> Contravariant<Middle>
checkProperSubtype(function1(generic1(Contravariant, Upper), String), function1(generic1(Contravariant, Middle), String));
// Contravariant<Lower> -> String <: Contravariant<Middle> -> String
checkProperSubtype(function1(String, generic1(Contravariant, Lower)), function1(String, generic1(Contravariant, Middle)));
// String -> Invariant<Middle> <: String -> Invariant<Middle>
checkSubtype(function1(generic1(Invariant, Middle), String), function1(generic1(Invariant, Middle), String));
// Invariant<Middle> -> String <: Invariant<Middle> -> String
checkSubtype(function1(String, generic1(Invariant, Middle)), function1(String, generic1(Invariant, Middle)));
// String -> LegacyCovariant<Lower> <: String -> LegacyCovariant<Middle>
checkProperSubtype(function1(generic1(LegacyCovariant, Lower), String), function1(generic1(LegacyCovariant, Middle), String));
// LegacyCovariant<Upper> -> String <: LegacyCovariant<Middle> -> String
checkProperSubtype(function1(String, generic1(LegacyCovariant, Upper)), function1(String, generic1(LegacyCovariant, Middle)));
// Covariant<Upper> </: Covariant<Middle>
checkSubtypeFailure(generic1(Covariant, Upper), generic1(Covariant, Middle));
// Contravariant<Lower> </: Contravariant<Middle>
checkSubtypeFailure(generic1(Contravariant, Lower), generic1(Contravariant, Middle));
// Invariant<Upper> </: Invariant<Middle>
checkSubtypeFailure(generic1(Invariant, Upper), generic1(Invariant, Middle));
// Invariant<Lower> </: Invariant<Middle>
checkSubtypeFailure(generic1(Invariant, Lower), generic1(Invariant, Middle));
// LegacyCovariant<Upper> </: LegacyCovariant<Middle>
checkSubtypeFailure(generic1(LegacyCovariant, Upper), generic1(LegacyCovariant, Middle));
// List<Covariant<Upper>> </: Iterable<Covariant<Middle>>
checkSubtypeFailure(generic1(List, generic1(Covariant, Upper)), generic1(Iterable, generic1(Covariant, Middle)));
// List<Contravariant<Lower>> </: Iterable<Contravariant<Middle>>
checkSubtypeFailure(generic1(List, generic1(Contravariant, Lower)), generic1(Iterable, generic1(Contravariant, Middle)));
// List<Invariant<Upper>> </: Iterable<Invariant<Middle>>
checkSubtypeFailure(generic1(List, generic1(Invariant, Upper)), generic1(Iterable, generic1(Invariant, Middle)));
// List<Invariant<Lower>> </: Iterable<Invariant<Middle>>
checkSubtypeFailure(generic1(List, generic1(Invariant, Lower)), generic1(Iterable, generic1(Invariant, Middle)));
// List<LegacyCovariant<Upper>> </: Iterable<LegacyCovariant<Middle>>
checkSubtypeFailure(generic1(List, generic1(LegacyCovariant, Upper)), generic1(Iterable, generic1(LegacyCovariant, Middle)));
// String -> Covariant<Upper> </: String -> Covariant<Middle>
checkSubtypeFailure(function1(generic1(Covariant, Upper), String), function1(generic1(Covariant, Middle), String));
// Covariant<Lower> -> String </: Covariant<Middle> -> String
checkSubtypeFailure(function1(String, generic1(Covariant, Lower)), function1(String, generic1(Covariant, Middle)));
// String -> Contravariant<Lower> </: String -> Contravariant<Middle>
checkSubtypeFailure(function1(generic1(Contravariant, Lower), String), function1(generic1(Contravariant, Middle), String));
// Contravariant<Upper> -> String </: Contravariant<Middle> -> String
checkSubtypeFailure(function1(String, generic1(Contravariant, Upper)), function1(String, generic1(Contravariant, Middle)));
// String -> Invariant<Upper> </: String -> Invariant<Middle>
checkSubtypeFailure(function1(generic1(Invariant, Upper), String), function1(generic1(Invariant, Middle), String));
// Invariant<Upper> -> String </: Invariant<Middle> -> String
checkSubtypeFailure(function1(String, generic1(Invariant, Upper)), function1(String, generic1(Invariant, Middle)));
// String -> Invariant<Lower> </: String -> Invariant<Middle>
checkSubtypeFailure(function1(generic1(Invariant, Lower), String), function1(generic1(Invariant, Middle), String));
// Invariant<Lower> -> String <: Invariant<Middle> -> String
checkSubtypeFailure(function1(String, generic1(Invariant, Lower)), function1(String, generic1(Invariant, Middle)));
// String -> LegacyCovariant<Upper> </: String -> LegacyCovariant<Middle>
checkSubtypeFailure(function1(generic1(LegacyCovariant, Upper), String), function1(generic1(LegacyCovariant, Middle), String));
// LegacyCovariant<Lower> -> String </: LegacyCovariant<Middle> -> String
checkSubtypeFailure(function1(String, generic1(LegacyCovariant, Lower)), function1(String, generic1(LegacyCovariant, Middle)));
}

View file

@ -11,6 +11,7 @@ const_double_in_int_op_test/ii6: Skip # Triple shift
extension_methods/*: SkipByDesign # Analyzer DDC is expected to be turned down before releasing extension methods.
large_class_declaration_test: Slow
nnbd/*: Skip
variance/*: SkipByDesign # Analyzer DDC is expected to be turned down before releasing variance.
[ $compiler == dartdevk && !$checked ]
assertion_initializer_const_error2_test/*: SkipByDesign # DDC does not support non-checked mode.

View file

@ -6,6 +6,8 @@
// SharedOptions=--enable-experiment=variance
import "package:expect/expect.dart";
class Contravariant<in T> {}
class Upper {}
@ -58,14 +60,23 @@ void testCall(Iterable<Contravariant<Lower>> x) {}
main() {
A a = new A();
Expect.type<Contravariant<Middle>>(a.method1());
Expect.type<Contravariant<Lower>>(a.method1());
Expect.notType<Contravariant<Upper>>(a.method1());
a.method2(new Contravariant<Middle>());
a.method2(new Contravariant<Upper>());
B b = new B();
Expect.type<Contravariant<Upper>>(b.method1());
Expect.type<Contravariant<Middle>>(b.method1());
Expect.type<Contravariant<Lower>>(b.method1());
b.method2(new Contravariant<Lower>());
b.method2(new Contravariant<Middle>());
C c = new C();
Expect.type<Contravariant<Middle>>(c.method1());
Expect.type<Contravariant<Lower>>(c.method1());
Expect.notType<Contravariant<Upper>>(c.method1());
c.method2(new Contravariant<Middle>());
c.method2(new Contravariant<Upper>());
@ -73,12 +84,19 @@ main() {
D<Contravariant<Middle>> dMiddle = new D<Contravariant<Middle>>();
E e = new E();
Expect.type<D<Contravariant<Upper>>>(e.method1());
Expect.type<D<Contravariant<Middle>>>(e.method1());
F f = new F();
Expect.type<D<Contravariant<Middle>>>(e.method1());
Iterable<Contravariant<Lower>> iterableLower = [new Contravariant<Lower>()];
List<Contravariant<Middle>> listMiddle = [new Contravariant<Middle>()];
iterableLower = listMiddle;
testCall(listMiddle);
Expect.subtype<Contravariant<Upper>, Contravariant<Middle>>();
Expect.subtype<Contravariant<Middle>, Contravariant<Middle>>();
Expect.notSubtype<Contravariant<Lower>, Contravariant<Middle>>();
}

View file

@ -6,6 +6,10 @@
// SharedOptions=--enable-experiment=variance
import 'dart:async';
import "package:expect/expect.dart";
class Invariant<inout T> {}
class Upper {}
@ -38,22 +42,129 @@ class D {
}
}
class E {
Invariant<dynamic> method1() {
return new Invariant<dynamic>();
}
void method2(Invariant<Object> x) {}
}
class F extends E {
@override
Invariant<Object> method1() {
return new Invariant<Object>();
}
@override
void method2(Invariant<dynamic> x) {}
}
class G {
Invariant<dynamic> method1() {
return new Invariant<dynamic>();
}
void method2(Invariant<FutureOr<dynamic>> x) {}
}
class H extends G {
@override
Invariant<FutureOr<dynamic>> method1() {
return new Invariant<FutureOr<dynamic>>();
}
@override
void method2(Invariant<dynamic> x) {}
}
class I {
Invariant<FutureOr<Null>> method1() {
return new Invariant<FutureOr<Null>>();
}
void method2(Invariant<Future<Null>> x) {}
}
class J extends I {
@override
Invariant<Future<Null>> method1() {
return new Invariant<Future<Null>>();
}
@override
void method2(Invariant<FutureOr<Null>> x) {}
}
void testCall(Iterable<Invariant<Middle>> x) {}
main() {
A a = new A();
Expect.type<Invariant<Middle>>(a.method1());
Expect.notType<Invariant<Upper>>(a.method1());
Expect.notType<Invariant<Lower>>(a.method1());
a.method2(new Invariant<Middle>());
B b = new B();
Expect.type<Invariant<Middle>>(b.method1());
Expect.notType<Invariant<Upper>>(b.method1());
Expect.notType<Invariant<Lower>>(b.method1());
b.method2(new Invariant<Middle>());
C<Invariant<Middle>> c = new C<Invariant<Middle>>();
D d = new D();
Expect.type<C<Invariant<Middle>>>(d.method1());
E e = new E();
Expect.type<Invariant<dynamic>>(e.method1());
e.method2(new Invariant<Object>());
// Invariant<dynamic> <:> Invariant<Object>
F f = new F();
Expect.type<Invariant<Object>>(f.method1());
Expect.type<Invariant<dynamic>>(f.method1());
f.method2(new Invariant<Object>());
f.method2(new Invariant<dynamic>());
G g = new G();
Expect.type<Invariant<dynamic>>(g.method1());
g.method2(new Invariant<FutureOr<dynamic>>());
// Invariant<FutureOr<dynamic>> <:> Invariant<dynamic>
H h = new H();
Expect.type<Invariant<FutureOr<dynamic>>>(h.method1());
Expect.type<Invariant<dynamic>>(h.method1());
h.method2(new Invariant<FutureOr<dynamic>>());
h.method2(new Invariant<dynamic>());
I i = new I();
Expect.type<Invariant<FutureOr<Null>>>(i.method1());
i.method2(new Invariant<Future<Null>>());
// Invariant<FutureOr<Null>> <:> Invariant<Future<Null>>
J j = new J();
Expect.type<Invariant<FutureOr<Null>>>(j.method1());
Expect.type<Invariant<Future<Null>>>(j.method1());
j.method2(new Invariant<FutureOr<Null>>());
j.method2(new Invariant<Future<Null>>());
Iterable<Invariant<Middle>> iterableMiddle = [new Invariant<Middle>()];
List<Invariant<Middle>> listMiddle = [new Invariant<Middle>()];
iterableMiddle = listMiddle;
testCall(listMiddle);
Expect.subtype<Invariant<Middle>, Invariant<Middle>>();
Expect.notSubtype<Invariant<Lower>, Invariant<Middle>>();
Expect.notSubtype<Invariant<Upper>, Invariant<Middle>>();
Expect.subtype<Invariant<dynamic>, Invariant<Object>>();
Expect.subtype<Invariant<Object>, Invariant<dynamic>>();
Expect.subtype<Invariant<FutureOr<dynamic>>, Invariant<dynamic>>();
Expect.subtype<Invariant<dynamic>, Invariant<FutureOr<dynamic>>>();
Expect.subtype<Invariant<FutureOr<Null>>, Invariant<Future<Null>>>();
Expect.subtype<Invariant<Future<Null>>, Invariant<FutureOr<Null>>>();
}

View file

@ -6,6 +6,8 @@
// SharedOptions=--enable-experiment=variance
import "package:expect/expect.dart";
class Covariant<out T> {}
class Upper {}
@ -58,14 +60,23 @@ void testCall(Iterable<Covariant<Middle>> x) {}
main() {
A a = new A();
Expect.type<Covariant<Middle>>(a.method1());
Expect.type<Covariant<Upper>>(a.method1());
Expect.notType<Covariant<Lower>>(a.method1());
a.method2(new Covariant<Middle>());
a.method2(new Covariant<Lower>());
B b = new B();
Expect.type<Covariant<Upper>>(b.method1());
Expect.type<Covariant<Middle>>(b.method1());
Expect.type<Covariant<Lower>>(b.method1());
b.method2(new Covariant<Upper>());
b.method2(new Covariant<Middle>());
C c = new C();
Expect.type<Covariant<Middle>>(c.method1());
Expect.type<Covariant<Upper>>(c.method1());
Expect.notType<Covariant<Lower>>(c.method1());
c.method2(new Covariant<Middle>());
c.method2(new Covariant<Lower>());
@ -73,12 +84,19 @@ main() {
D<Covariant<Middle>> dMiddle = new D<Covariant<Middle>>();
E e = new E();
Expect.type<D<Covariant<Lower>>>(e.method1());
Expect.type<D<Covariant<Middle>>>(e.method1());
F f = new F();
Expect.type<D<Covariant<Middle>>>(f.method1());
Iterable<Covariant<Middle>> iterableMiddle = [new Covariant<Middle>()];
List<Covariant<Lower>> listLower = [new Covariant<Lower>()];
iterableMiddle = listLower;
testCall(listLower);
Expect.subtype<Covariant<Lower>, Covariant<Middle>>();
Expect.subtype<Covariant<Middle>, Covariant<Middle>>();
Expect.notSubtype<Covariant<Upper>, Covariant<Middle>>();
}