| 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 |