Index: tests/language_strong/covariant_subtyping_test.dart |
diff --git a/tests/language_strong/covariant_subtyping_test.dart b/tests/language_strong/covariant_subtyping_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d7ffbc5f49f3b86cad0d85314272526649bab438 |
--- /dev/null |
+++ b/tests/language_strong/covariant_subtyping_test.dart |
@@ -0,0 +1,225 @@ |
+// Copyright (c) 2017, 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. |
+import 'package:expect/expect.dart'; |
+ |
+bool isTypeError(e) => e is TypeError; |
+ |
+class Fields<T> { |
+ T x; |
+ T _y; |
+ T _z; |
+ |
+ m() { |
+ _y = x; |
+ } |
+ |
+ n(Fields<T> c) { |
+ c._z = x; |
+ } |
+} |
+ |
+testField() { |
+ Fields<Object> c = new Fields<int>(); |
+ Expect.throws(() { |
+ c.x = 'hello'; |
+ }, isTypeError); |
+ |
+ Fields<dynamic> d = new Fields<int>(); |
+ Expect.throws(() { |
+ c.x = 'hello'; |
+ }, isTypeError); |
+} |
+ |
+testPrivateFields() { |
+ Fields<Object> c = new Fields<int>()..x = 42; |
+ c.m(); |
+ Expect.equals(c._y, 42); |
+ |
+ Fields<Object> c2 = new Fields<String>()..x = 'hi'; |
+ c2.n(c2); |
+ Expect.equals(c2._z, 'hi'); |
+ Expect.throws(() { |
+ c.n(c2); |
+ }, isTypeError); |
+ Expect.equals(c2._z, 'hi'); |
+} |
+ |
+class NumBounds<T extends num> { |
+ bool m(T t) => t.isNegative; |
+} |
+ |
+class MethodTakesNum extends NumBounds<int> { |
+ bool m(num obj) => obj.isNegative; // does not need check |
+} |
+ |
+class MethodTakesInt extends NumBounds<int> { |
+ bool m(int obj) => obj.isNegative; // needs a check |
+} |
+ |
+testClassBounds() { |
+ NumBounds<num> d = new MethodTakesNum(); |
+ Expect.equals(d.m(-1.1), true); |
+ d = new MethodTakesInt(); |
+ Expect.throws(() { |
+ d.m(-1.1); |
+ }, isTypeError); |
+} |
+ |
+typedef void F<T>(T t); |
+typedef G<T> = void Function<S extends T>(S s); |
+ |
+class FnChecks<T> { |
+ F<T> f; |
+ G<T> g; |
+ T _t; |
+ T getT() => _t; |
+ F<T> setterForT() { |
+ return (T t) { |
+ _t = t; |
+ }; |
+ } |
+} |
+ |
+testReturnOfFunctionType() { |
+ FnChecks<int> cInt = new FnChecks<int>(); |
+ FnChecks<Object> cObj = cInt; |
+ Expect.throws(() => cObj.setterForT(), isTypeError); |
+ Expect.throws(() => (cObj.setterForT() as F<Object>), isTypeError); |
+ FnChecks<dynamic> cDyn = cInt; |
+ cDyn.setterForT(); // allowed fuzzy arrow |
+ Expect.throws(() => cDyn.setterForT()('hi'), isTypeError); // dcall throws |
+ cInt.setterForT()(42); |
+ Expect.equals(cObj.getT(), 42); |
+} |
+ |
+testFieldOfFunctionType() { |
+ FnChecks<Object> c = new FnChecks<String>()..f = (String b) {}; |
+ Expect.throws(() { |
+ F<Object> f = c.f; |
+ }, isTypeError); |
+ Expect.throws(() { |
+ Object f = c.f; |
+ }, isTypeError); |
+ Expect.throws(() => c.f, isTypeError); |
+ Expect.throws(() => c.f(42), isTypeError); |
+ Expect.throws(() => c.f('hi'), isTypeError); |
+ FnChecks<String> cStr = c; |
+ cStr.f('hi'); |
+ FnChecks<dynamic> cDyn = c; |
+ cDyn.f; // allowed fuzzy arrow |
+ Expect.throws(() => cDyn.f(42), isTypeError); // dcall throws |
+} |
+ |
+testFieldOfGenericFunctionType() { |
+ FnChecks<Object> c = new FnChecks<num>() |
+ ..g = <S extends num>(S s) => s.isNegative; |
+ |
+ Expect.throws(() { |
+ G<Object> g = c.g; |
+ }, isTypeError); |
+ Expect.throws(() { |
+ var g = c.g; |
+ }, isTypeError); |
+ Expect.throws(() { |
+ c.g<String>('hi'); |
+ }, isTypeError); |
+ Expect.throws(() { |
+ c.g<int>(42); |
+ }, isTypeError); |
+ FnChecks<num> cNum = c; |
+ cNum.g(42); |
+} |
+ |
+class Base { |
+ int _t = 0; |
+ add(int t) { |
+ _t += t; |
+ } |
+} |
+ |
+abstract class I<T> { |
+ add(T t); |
+} |
+ |
+class ExtendsBase extends Base implements I<int> {} |
+ |
+class MixinBase extends Object with Base implements I<int> {} |
+ |
+class MixinBase2 = Object with Base implements I<int>; |
+ |
+testMixinApplication() { |
+ I<Object> i = new ExtendsBase(); |
+ I<Object> j = new MixinBase(); |
+ I<Object> k = new MixinBase2(); |
+ Expect.throws(() { |
+ i.add('hi'); |
+ }, isTypeError); |
+ Expect.throws(() { |
+ j.add('hi'); |
+ }, isTypeError); |
+ // TODO(jmesserly): this should also throw. It does not because DDC's |
+ // technique for generating mixin aliases (mixin applications of the form |
+ // `class X = Object with Y /* optional implements */;`) does not allow |
+ // adding any methods in the class. The normal technique of generating |
+ // a method that performs the check and then calls `super` will not work, |
+ // because there is no superclass to call. We will need some sort of |
+ // special case code to implement this, perhaps move the original |
+ // method to a symbol, then generate a wrapper with the original method name, |
+ // that checks and calls it. |
+ k.add('hi'); |
+} |
+ |
+abstract class GenericAdd<T> { |
+ add<S extends T>(S t); |
+} |
+ |
+class GenericAdder implements GenericAdd<num> { |
+ num _t = 0; |
+ add<T extends num>(T t) { |
+ _t = t; |
+ } |
+} |
+ |
+testGenericMethodBounds() { |
+ GenericAdd<Object> i = new GenericAdder(); |
+ // TODO(jmesserly): should generic method bounds use a different error type? |
+ // This seems wrong. Also this Error type is not exposed from dart:core. |
+ Expect.throws(() { |
+ i.add('hi'); |
+ }, (e) => '${e.runtimeType}'.startsWith('StrongModeError')); |
+ Expect.throws(() { |
+ i.add<String>(null); |
+ }, (e) => '${e.runtimeType}'.startsWith('StrongModeError')); |
+ i.add(null); |
+ i.add(42); |
+} |
+ |
+class ClassF<T> { |
+ T x; |
+ void call(T t) { |
+ x = t; |
+ } |
+} |
+ |
+testCallMethod() { |
+ ClassF<int> cc = new ClassF<int>(); |
+ ClassF<Object> ca = cc; // An upcast, per covariance. |
+ F<Object> f = ca; |
+ Expect.equals(f.runtimeType.toString(), 'ClassF<int>'); |
+ Expect.throws(() { |
+ f(new Object()); |
+ }, isTypeError); |
+} |
+ |
+main() { |
+ testField(); |
+ testPrivateFields(); |
+ testClassBounds(); |
+ testReturnOfFunctionType(); |
+ testFieldOfFunctionType(); |
+ testFieldOfGenericFunctionType(); |
+ testMixinApplication(); |
+ testGenericMethodBounds(); |
+ testCallMethod(); |
+} |