| // 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'; |
| |
| 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.throwsTypeError(() => c.x = 'hello'); |
| |
| Fields<dynamic> d = new Fields<int>(); |
| Expect.throwsTypeError(() => c.x = 'hello'); |
| } |
| |
| 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.throwsTypeError(() => c.n(c2)); |
| 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.throwsTypeError(() => d.m(-1.1)); |
| } |
| |
| 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.throwsTypeError(() => cObj.setterForT()); |
| Expect.throwsTypeError(() => (cObj.setterForT() as F<Object>)); |
| FnChecks<dynamic> cDyn = cInt; |
| Expect.throwsTypeError(() => cDyn.setterForT()); |
| Expect.throwsTypeError( |
| () => (cDyn as dynamic).setterForT()('hi')); // dcall throws |
| cInt.setterForT()(42); |
| Expect.equals(cObj.getT(), 42); |
| } |
| |
| testTearoffReturningFunctionType() { |
| FnChecks<int> cInt = new FnChecks<int>(); |
| FnChecks<Object> cObj = cInt; |
| |
| Expect.throwsTypeError( |
| () => cObj.setterForT, 'unsound tear-off throws at runtime'); |
| Expect.equals(cInt.setterForT, cInt.setterForT, 'sound tear-off works'); |
| } |
| |
| testFieldOfFunctionType() { |
| FnChecks<Object> c = new FnChecks<String>()..f = (String b) {}; |
| Expect.throwsTypeError(() { |
| F<Object> f = c.f; |
| }); |
| Expect.throwsTypeError(() { |
| Object f = c.f; |
| }); |
| Expect.throwsTypeError(() => c.f); |
| Expect.throwsTypeError(() => c.f(42)); |
| Expect.throwsTypeError(() => c.f('hi')); |
| FnChecks<String> cStr = c; |
| cStr.f('hi'); |
| FnChecks<dynamic> cDyn = c; |
| Expect.throwsTypeError(() => cDyn.f); |
| Expect.throwsTypeError(() => (cDyn as dynamic).f(42)); // dcall throws |
| } |
| |
| testFieldOfGenericFunctionType() { |
| FnChecks<Object> c = new FnChecks<num>() |
| ..g = <S extends num>(S s) => s.isNegative; |
| |
| Expect.throwsTypeError(() { |
| G<Object> g = c.g; |
| }); |
| Expect.throwsTypeError(() { |
| var g = c.g; |
| }); |
| Expect.throwsTypeError(() => c.g<String>('hi')); |
| Expect.throwsTypeError(() => c.g<int>(42)); |
| 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.throwsTypeError(() => i.add('hi')); |
| Expect.throwsTypeError(() => j.add('hi')); |
| Expect.throwsTypeError(() => k.add('hi')); |
| } |
| |
| class GenericMethodBounds<T> { |
| Type get t => T; |
| GenericMethodBounds<E> foo<E extends T>() => new GenericMethodBounds<E>(); |
| GenericMethodBounds<E> bar<E extends void Function(T)>() => |
| new GenericMethodBounds<E>(); |
| } |
| |
| class GenericMethodBoundsDerived extends GenericMethodBounds<num> { |
| GenericMethodBounds<E> foo<E extends num>() => new GenericMethodBounds<E>(); |
| GenericMethodBounds<E> bar<E extends void Function(num)>() => |
| new GenericMethodBounds<E>(); |
| } |
| |
| GenericMethodBounds<E> Function<E extends T>() genericFunctionWithBounds<T>() { |
| inner<E extends T>() => new GenericMethodBounds<E>(); |
| return inner; |
| } |
| |
| testGenericMethodBounds() { |
| test(GenericMethodBounds<Object> g) { |
| Expect.throwsTypeError(() => g.foo<String>()); |
| Expect.throwsTypeError(() => g.foo()); |
| Expect.equals(g.foo<Null>().t, Null); |
| Expect.equals(g.foo<int>().t, int); |
| Expect.isFalse(g.foo<int>() is GenericMethodBounds<double>); |
| g.bar<Function(Object)>(); |
| dynamic d = g; |
| d.bar<Function(num)>(); |
| Expect.throwsTypeError(() => d.bar<Function(String)>()); |
| Expect.throwsTypeError(() => d.bar<Function(Null)>()); |
| } |
| |
| test(new GenericMethodBounds<num>()); |
| test(new GenericMethodBounds<int>()); |
| test(new GenericMethodBoundsDerived()); |
| test(genericFunctionWithBounds<num>()<int>()); |
| } |
| |
| 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; |
| void f2(Object x) {} |
| Expect.equals(f.runtimeType, f2.runtimeType); |
| Expect.throwsTypeError(() => f(new Object())); |
| } |
| |
| class TearOff<T> { |
| method1(T t) => null; // needs check |
| method2(Function(T) takesT) => null; |
| method3(T Function() returnsT) => null; // needs check |
| method4(Function(Function(T)) takesTakesT) => null; // needs check |
| method5(Function(T Function()) takesReturnsT) => null; |
| method6(Function(T) Function() returnsTakesT) => null; |
| method7(T Function() Function() returnsReturnsT) => null; // needs check |
| } |
| |
| typedef F1 = dynamic Function(Object); |
| typedef F2 = dynamic Function(dynamic Function(int)); |
| typedef F3 = dynamic Function(dynamic Function(int Function())); |
| typedef F4 = dynamic Function(dynamic Function(int) Function()); |
| |
| testTearOffRuntimeType() { |
| expectRTTI(tearoff, type) => Expect.equals(tearoff.runtimeType, type, |
| 'covariant params should reify with Object as their type'); |
| |
| TearOff<num> t = new TearOff<int>(); |
| expectRTTI(t.method1, F1); |
| |
| expectRTTI(t.method2, F2); |
| expectRTTI(t.method3, F1); |
| |
| expectRTTI(t.method4, F1); |
| expectRTTI(t.method5, F3); |
| expectRTTI(t.method6, F4); |
| expectRTTI(t.method7, F1); |
| } |
| |
| main() { |
| testField(); |
| testPrivateFields(); |
| testClassBounds(); |
| testReturnOfFunctionType(); |
| testTearoffReturningFunctionType(); |
| testFieldOfFunctionType(); |
| testFieldOfGenericFunctionType(); |
| testMixinApplication(); |
| testGenericMethodBounds(); |
| testCallMethod(); |
| testTearOffRuntimeType(); |
| } |