OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 // Some examples of how the dart type system/checked mode interacts with dart ty
pe unsoundness. | |
6 // Suppose we were to "trust types" in the following sense: if the program passe
s the dart | |
7 // type checker, assume that all checks implied by checked mode would have passe
d, and | |
8 // compile under that assumption. What does that buy you? These examples are i
ntended to | |
9 // illustrate why this is less than you might think. This file has no static er
rors or | |
10 // warnings, and runs fine in checked mode, but it demonstrates lots of ways in
which | |
11 // nonetheless we can produce no such method errors, or sideways casts, etc. | |
12 | |
13 // A simple class | |
14 class A { | |
15 int x = 0; | |
16 } | |
17 | |
18 // Overriding x with an assignable type (supertype) is ok | |
19 class B extends A { | |
20 num x = 1.0; | |
21 int y = 1; | |
22 } | |
23 | |
24 // Contain a B | |
25 class S0 { | |
26 B field; | |
27 } | |
28 | |
29 // Override to an A | |
30 class S1 extends S0 { | |
31 A field; | |
32 S1(A this.field); | |
33 } | |
34 | |
35 // If we assume that s has type S0, can we assume that s.field has type B? No. | |
36 void nsm0(S0 s) { | |
37 // Dynamic dispatch required. | |
38 // With global subclass information, the compiler could potentially get rid | |
39 // of some of the checks here when bad overrides as above don't happen. | |
40 try { | |
41 s.field.y; | |
42 } on NoSuchMethodError catch (r) { | |
43 print("Oops, no field y in something of type B"); | |
44 } | |
45 } | |
46 | |
47 // If we assume that f returns a B, can we assume that B.y exists? No. | |
48 void nsm1(B f()) { | |
49 // Dynamic dispatch required. | |
50 // Even with global subclass info, the compiler can't do anything here, since | |
51 // this relies on screwy subtyping rather than screwy field overrides. | |
52 try { | |
53 f().y; | |
54 } on NoSuchMethodError catch (r) { | |
55 print("Oops, no field y in something of type B (extra badness)"); | |
56 } | |
57 } | |
58 | |
59 // If we assume that s is an S1, can we assume that s.field is an A? No. | |
60 void doubleForInt0(S1 s) { | |
61 // Dynamic dispatch required. | |
62 // With global subclass info we can possibly know when we need to emit careful
code | |
63 // here, but we certainly can't just generate a simple unconditional primitive
operation | |
64 // (even ignoring nulls) | |
65 s.field.x + 1; | |
66 if (s.field.x is double) { | |
67 print("Oops, got double for int"); | |
68 } | |
69 } | |
70 | |
71 // Can we assume that f returns an int (or a subtype of an int)? No. | |
72 // Can we assume that f returns an int (or subtype or a supertype of an int)? No
. | |
73 void doubleForInt1(int f()) { | |
74 // Dynamic dispatch required. | |
75 // Here, even with global subclass info we're still stuck - we can't really | |
76 // ever trust the return types on closures. Note that int and double aren't | |
77 // even assignment compatible in this case. | |
78 f() + 1; | |
79 if (f() is double) { | |
80 print("Oops, got double for int (extra badness)"); | |
81 } | |
82 } | |
83 | |
84 class C extends A {} | |
85 | |
86 // Can we assume that f returns something on the same branch of the inheritance
chain as B? No. | |
87 void sidewaysCast(B f()) { | |
88 // Just to emphasize the point, here's a more general case where the static ty
pe | |
89 // isn't even on the same branch of the subtype hierarchy as the dynamic type. | |
90 if (f() is C) { | |
91 print( | |
92 "Static type of expression is B, but runtime type is C (neither sub nor
supertype)"); | |
93 } | |
94 if (f() is B) { | |
95 // Do something here. This code doesn't get reached.... unless the compiler
optimizes | |
96 // based on trusting types and eliminates the is check, in which case we ma
y suddenly | |
97 // execute different code. | |
98 } | |
99 } | |
100 | |
101 // If we assume that x has type List<B>, what can we assume about the type of th
e elements? Nothing. | |
102 void generic0(List<B> x) { | |
103 // What can we assume about the elements of this array if we trust types? | |
104 // Absolutely nothing, whatsoever. | |
105 if (x[0] is int) { | |
106 print( | |
107 "Parameter type is List<B> but actually contains an int (completely unre
lated type)"); | |
108 } | |
109 } | |
110 | |
111 void main() { | |
112 nsm0(new S1(new A())); | |
113 nsm1(() => new A()); | |
114 doubleForInt0(new S1(new B())); | |
115 doubleForInt1(() { | |
116 num b = 1.0; | |
117 return b; | |
118 }); | |
119 sidewaysCast(() { | |
120 A a = new C(); | |
121 return a; | |
122 }); | |
123 generic0(<dynamic>[3]); | |
124 } | |
OLD | NEW |