|
5 | 5 | import 'dart:math'; |
6 | 6 | import 'package:expect/expect.dart'; |
7 | 7 |
|
| 8 | +Type getType<T>() => T; |
| 9 | + |
8 | 10 | void testInstantiateToBounds() { |
9 | 11 | f<T extends num, U extends T>() => [T, U]; |
10 | 12 | g<T extends List<U>, U extends int>() => [T, U]; |
11 | | - h<T extends num, U extends T>(T x, U y) => h.runtimeType.toString(); |
| 13 | + h<T extends U, U extends num>(T x, U y) => [T, U]; |
12 | 14 |
|
| 15 | + // Check that instantiate to bounds creates the correct type arguments |
| 16 | + // during dynamic calls. |
13 | 17 | Expect.listEquals([num, num], (f as dynamic)()); |
14 | | - Expect.equals('List<int>|int', (g as dynamic)().join('|')); |
15 | | - Expect.equals('<T extends num, U extends T>(T, U) => String', |
16 | | - (h as dynamic)(null, null)); |
| 18 | + Expect.listEquals([getType<List<int>>(), int], (g as dynamic)()); |
| 19 | + Expect.listEquals([num, num], (h as dynamic)(null, null)); |
17 | 20 |
|
| 21 | + // Check that when instantiate to bounds creates a super-bounded type argument |
| 22 | + // during a dynamic call, an error is thrown. |
18 | 23 | i<T extends Iterable<T>>() => null; |
19 | 24 | j<T extends Iterable<S>, S extends T>() => null; |
20 | | - Expect.throws( |
21 | | - () => (i as dynamic)(), (e) => '$e'.contains('Instantiate to bounds')); |
22 | | - Expect.throws( |
23 | | - () => (j as dynamic)(), (e) => '$e'.contains('Instantiate to bounds')); |
| 25 | + Expect.throwsTypeError(() => (i as dynamic)(), "Super bounded type argument"); |
| 26 | + Expect.throwsTypeError(() => (j as dynamic)(), "Super bounded type argument"); |
24 | 27 | } |
25 | 28 |
|
26 | 29 | void testChecksBound() { |
27 | 30 | f<T extends num>(T x) => x; |
28 | | - Expect.equals((f as dynamic)(42), 42); |
29 | | - Expect.throws(() => (f as dynamic)('42')); |
30 | | - |
31 | | - msg(t1, t2, tf) => Expect.throws(() => (f as dynamic)<Object>(42), |
32 | | - (e) => '$e' == 'type `Object` does not extend `num` of `T'); |
33 | | - |
34 | 31 | g<T extends U, U extends num>(T x, U y) => x; |
| 32 | + |
| 33 | + // Check that arguments are checked against the correct types when instantiate |
| 34 | + // to bounds produces a type argument during a dynamic call. |
| 35 | + Expect.equals((f as dynamic)(42), 42); |
35 | 36 | Expect.equals((g as dynamic)(42.0, 100), 42.0); |
36 | | - Expect.throws(() => (g as dynamic)('hi', 100)); |
37 | | - Expect.throws(() => (g as dynamic)<double, int>(42.0, 100), |
38 | | - (e) => '$e' == 'type `double` does not extend `int` of `T`.'); |
| 37 | + Expect.throwsTypeError(() => (f as dynamic)('42'), "Argument check"); |
| 38 | + Expect.throwsTypeError(() => (g as dynamic)('hi', 100), "Argument check"); |
39 | 39 |
|
40 | | - Expect.throws(() => (g as dynamic)<num, Object>(42.0, 100), |
41 | | - (e) => '$e' == 'type `Object` does not extend `num` of `U`.'); |
| 40 | + // Check that an actual type argument is checked against the bound during a |
| 41 | + // dynamic call. |
| 42 | + Expect.equals((f as dynamic)<int>(42), 42); |
| 43 | + Expect.equals((g as dynamic)<double, num>(42.0, 100), 42.0); |
| 44 | + Expect.throwsTypeError(() => (g as dynamic)<double, int>(42.0, 100), |
| 45 | + "Type argument bounds check"); |
| 46 | + Expect.throwsTypeError( |
| 47 | + () => (f as dynamic)<Object>(42), "Type argument bounds check"); |
| 48 | + Expect.throwsTypeError(() => (g as dynamic)<double, int>(42.0, 100), |
| 49 | + "Type argument bounds check"); |
| 50 | + Expect.throwsTypeError(() => (g as dynamic)<num, Object>(42.0, 100), |
| 51 | + "Type argument bounds check"); |
42 | 52 | } |
43 | 53 |
|
44 | | -typedef G<U> = T Function<T extends U>(T x); |
| 54 | +typedef G<U> = num Function<T extends U>(T x); |
45 | 55 |
|
46 | | -void testSubtype() { |
47 | | - f<T extends num>(T x) => x + 2; |
| 56 | +typedef F<U> = Object Function<T extends U>(T x); |
48 | 57 |
|
| 58 | +void testSubtype() { |
| 59 | + num f<T extends num>(T x) => x + 2; |
49 | 60 | dynamic d = f; |
50 | | - Expect.equals(d(40.0), 42.0); |
51 | | - Expect.equals((f as G<int>)(40), 42); |
52 | | - Expect.equals((d as G<int>)(40), 42); |
53 | | - Expect.equals((f as G<double>)(40.0), 42.0); |
54 | | - Expect.equals((d as G<double>)(40.0), 42.0); |
55 | | - |
56 | | - d as G<Null>; |
57 | | - Expect.throws(() => d as G); |
58 | | - Expect.throws(() => d as G<Object>); |
59 | | - Expect.throws(() => d as G<String>); |
| 61 | + |
| 62 | + // Check that casting to an equal generic function type works |
| 63 | + Expect.equals((f as G<num>)(40), 42); |
| 64 | + Expect.equals((d as G<num>)(40), 42); |
| 65 | + |
| 66 | + // Check that casting to a more general generic function type works |
| 67 | + Expect.equals((f as F<num>)(40), 42); |
| 68 | + Expect.equals((d as F<num>)(40), 42); |
| 69 | + |
| 70 | + // Check that casting to a generic function with more specific bounds fails |
| 71 | + Expect.throwsCastError( |
| 72 | + () => (f as G<int>), "Generic functions are invariant"); |
| 73 | + Expect.throwsCastError( |
| 74 | + () => (d as G<int>), "Generic functions are invariant"); |
| 75 | + Expect.throwsCastError( |
| 76 | + () => (f as G<double>), "Generic functions are invariant"); |
| 77 | + Expect.throwsCastError( |
| 78 | + () => (d as G<double>), "Generic functions are invariant"); |
| 79 | + Expect.throwsCastError( |
| 80 | + () => (f as G<Null>), "Generic functions are invariant"); |
| 81 | + Expect.throwsCastError( |
| 82 | + () => (d as G<Null>), "Generic functions are invariant"); |
| 83 | + |
| 84 | + // Check that casting to a generic function with a more general bound fails |
| 85 | + Expect.throwsCastError( |
| 86 | + () => (f as G<Object>), "Generic functions are invariant"); |
| 87 | + Expect.throwsCastError( |
| 88 | + () => (d as G<Object>), "Generic functions are invariant"); |
| 89 | + |
| 90 | + // Check that casting to a generic function with an unrelated bound fails |
| 91 | + Expect.throwsCastError( |
| 92 | + () => (f as G<String>), "Generic functions are invariant"); |
| 93 | + Expect.throwsCastError( |
| 94 | + () => (d as G<String>), "Generic functions are invariant"); |
60 | 95 | } |
61 | 96 |
|
62 | 97 | void testToString() { |
63 | | - // TODO(jmesserly): I don't think the cast on `y` should be required. |
64 | | - num f<T extends num, U extends T>(T x, U y) => min(x, y as num); |
| 98 | + num f<T extends num, U extends T>(T x, U y) => min(x, y); |
65 | 99 | num g<T, U>(T x, U y) => max(x as num, y as num); |
66 | 100 | String h<T, U>(T x, U y) => h.runtimeType.toString(); |
67 | | - Expect.equals( |
68 | | - f.runtimeType.toString(), '<T extends num, U extends T>(T, U) => num'); |
69 | | - Expect.equals(g.runtimeType.toString(), '<T, U>(T, U) => num'); |
70 | | - Expect.equals(h(42, 123.0), '<T, U>(T, U) => String'); |
| 101 | + |
| 102 | + // Check that generic method types are printed in a reasonable way |
| 103 | + Expect.isTrue( |
| 104 | + new RegExp(r'<(\w+) extends num, (\w+) extends \1>\(\1, \2\) => num') |
| 105 | + .hasMatch(f.runtimeType.toString())); |
| 106 | + Expect.isTrue(new RegExp(r'<(\w+), (\w+)>\(\1, \2\) => num') |
| 107 | + .hasMatch(g.runtimeType.toString())); |
| 108 | + Expect.isTrue( |
| 109 | + new RegExp(r'<(\w+), (\w+)>\(\1, \2\) => String').hasMatch(h(42, 123.0))); |
71 | 110 | } |
72 | 111 |
|
73 | 112 | main() { |
74 | | - testInstantiateToBounds(); |
75 | | - testToString(); |
76 | | - testChecksBound(); |
77 | | - testSubtype(); |
| 113 | + testInstantiateToBounds(); //# 01: ok |
| 114 | + testToString(); //# 02: ok |
| 115 | + testChecksBound(); //# 03: ok |
| 116 | + testSubtype(); //# 04: ok |
78 | 117 | } |
0 commit comments