Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dev_compiler.src.checker.rules; | 5 library dev_compiler.src.checker.rules; |
| 6 | 6 |
| 7 import 'package:analyzer/src/generated/ast.dart'; | 7 import 'package:analyzer/src/generated/ast.dart'; |
| 8 import 'package:analyzer/src/generated/element.dart'; | 8 import 'package:analyzer/src/generated/element.dart'; |
| 9 import 'package:analyzer/src/generated/resolver.dart'; | 9 import 'package:analyzer/src/generated/resolver.dart'; |
| 10 | 10 |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 155 FunctionType callType = manager.lookupMemberType(t, "call"); | 155 FunctionType callType = manager.lookupMemberType(t, "call"); |
| 156 return callType; | 156 return callType; |
| 157 } | 157 } |
| 158 return null; | 158 return null; |
| 159 } | 159 } |
| 160 | 160 |
| 161 /// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC | 161 /// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC |
| 162 /// checker to determine whether f1 would be a subtype of f2 if the return | 162 /// checker to determine whether f1 would be a subtype of f2 if the return |
| 163 /// type of f1 is set to match f2's return type. | 163 /// type of f1 is set to match f2's return type. |
| 164 bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2, | 164 bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2, |
| 165 {bool ignoreReturn: false}) { | 165 {bool dynamicIsBottom: false, bool ignoreReturn: false}) { |
| 166 final r1s = f1.normalParameterTypes; | 166 final r1s = f1.normalParameterTypes; |
| 167 final o1s = f1.optionalParameterTypes; | 167 final o1s = f1.optionalParameterTypes; |
| 168 final n1s = f1.namedParameterTypes; | 168 final n1s = f1.namedParameterTypes; |
| 169 final r2s = f2.normalParameterTypes; | 169 final r2s = f2.normalParameterTypes; |
| 170 final o2s = f2.optionalParameterTypes; | 170 final o2s = f2.optionalParameterTypes; |
| 171 final n2s = f2.namedParameterTypes; | 171 final n2s = f2.namedParameterTypes; |
| 172 final ret1 = ignoreReturn ? f2.returnType : f1.returnType; | 172 final ret1 = ignoreReturn ? f2.returnType : f1.returnType; |
| 173 final ret2 = f2.returnType; | 173 final ret2 = f2.returnType; |
| 174 | 174 |
| 175 // A -> B <: C -> D if C <: A and | 175 // A -> B <: C -> D if C <: A and |
| 176 // either D is void or B <: D | 176 // either D is void or B <: D |
| 177 if (!ret2.isVoid && !isSubTypeOf(ret1, ret2)) return false; | 177 if (!ret2.isVoid && !isSubTypeOf(ret1, ret2)) return false; |
| 178 | 178 |
| 179 // Reject if one has named and the other has optional | 179 // Reject if one has named and the other has optional |
| 180 if (n1s.length > 0 && o2s.length > 0) return false; | 180 if (n1s.length > 0 && o2s.length > 0) return false; |
| 181 if (n2s.length > 0 && o1s.length > 0) return false; | 181 if (n2s.length > 0 && o1s.length > 0) return false; |
| 182 | 182 |
| 183 // f2 has named parameters | 183 // f2 has named parameters |
| 184 if (n2s.length > 0) { | 184 if (n2s.length > 0) { |
| 185 // Check that every named parameter in f2 has a match in f1 | 185 // Check that every named parameter in f2 has a match in f1 |
| 186 for (String k2 in n2s.keys) { | 186 for (String k2 in n2s.keys) { |
| 187 if (!n1s.containsKey(k2)) return false; | 187 if (!n1s.containsKey(k2)) return false; |
| 188 if (!isSubTypeOf(n2s[k2], n1s[k2])) return false; | 188 if (!isSubTypeOf(n2s[k2], n1s[k2], dynamicIsBottom: true)) return false; |
| 189 } | 189 } |
| 190 } | 190 } |
| 191 // If we get here, we either have no named parameters, | 191 // If we get here, we either have no named parameters, |
| 192 // or else the named parameters match and we have no optional | 192 // or else the named parameters match and we have no optional |
| 193 // parameters | 193 // parameters |
| 194 | 194 |
| 195 // If f1 has more required parameters, reject | 195 // If f1 has more required parameters, reject |
| 196 if (r1s.length > r2s.length) return false; | 196 if (r1s.length > r2s.length) return false; |
| 197 | 197 |
| 198 // If f2 has more required + optional parameters, reject | 198 // If f2 has more required + optional parameters, reject |
| 199 if (r2s.length + o2s.length > r1s.length + o1s.length) return false; | 199 if (r2s.length + o2s.length > r1s.length + o1s.length) return false; |
| 200 | 200 |
| 201 // The parameter lists must look like the following at this point | 201 // The parameter lists must look like the following at this point |
| 202 // where rrr is a region of required, and ooo is a region of optionals. | 202 // where rrr is a region of required, and ooo is a region of optionals. |
| 203 // f1: rrr ooo ooo ooo | 203 // f1: rrr ooo ooo ooo |
| 204 // f2: rrr rrr ooo | 204 // f2: rrr rrr ooo |
| 205 int rr = r1s.length; // required in both | 205 int rr = r1s.length; // required in both |
| 206 int or = r2s.length - r1s.length; // optional in f1, required in f2 | 206 int or = r2s.length - r1s.length; // optional in f1, required in f2 |
| 207 int oo = o2s.length; // optional in both | 207 int oo = o2s.length; // optional in both |
| 208 | 208 |
| 209 for (int i = 0; i < rr; ++i) { | 209 for (int i = 0; i < rr; ++i) { |
| 210 if (!isSubTypeOf(r2s[i], r1s[i])) return false; | 210 if (!isSubTypeOf(r2s[i], r1s[i], dynamicIsBottom: true)) return false; |
| 211 } | 211 } |
| 212 for (int i = 0, j = rr; i < or; ++i, ++j) { | 212 for (int i = 0, j = rr; i < or; ++i, ++j) { |
| 213 if (!isSubTypeOf(r2s[j], o1s[i])) return false; | 213 if (!isSubTypeOf(r2s[j], o1s[i], dynamicIsBottom: true)) return false; |
| 214 } | 214 } |
| 215 for (int i = or, j = 0; i < oo; ++i, ++j) { | 215 for (int i = or, j = 0; i < oo; ++i, ++j) { |
| 216 if (!isSubTypeOf(o2s[j], o1s[i])) return false; | 216 if (!isSubTypeOf(o2s[j], o1s[i], dynamicIsBottom: true)) return false; |
| 217 } | 217 } |
| 218 return true; | 218 return true; |
| 219 } | 219 } |
| 220 | 220 |
| 221 bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) { | 221 bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) { |
| 222 if (i1 == i2) return true; | 222 if (i1 == i2) return true; |
| 223 | 223 |
| 224 if (i1.element == i2.element) { | 224 if (i1.element == i2.element) { |
| 225 List<DartType> tArgs1 = i1.typeArguments; | 225 List<DartType> tArgs1 = i1.typeArguments; |
| 226 List<DartType> tArgs2 = i2.typeArguments; | 226 List<DartType> tArgs2 = i2.typeArguments; |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 253 if (_isInterfaceSubTypeOf(parent, i2)) return true; | 253 if (_isInterfaceSubTypeOf(parent, i2)) return true; |
| 254 } | 254 } |
| 255 | 255 |
| 256 for (final parent in i1.mixins) { | 256 for (final parent in i1.mixins) { |
| 257 if (_isInterfaceSubTypeOf(parent, i2)) return true; | 257 if (_isInterfaceSubTypeOf(parent, i2)) return true; |
| 258 } | 258 } |
| 259 | 259 |
| 260 return false; | 260 return false; |
| 261 } | 261 } |
| 262 | 262 |
| 263 bool isSubTypeOf(DartType t1, DartType t2) { | 263 bool isSubTypeOf(DartType t1, DartType t2, {bool dynamicIsBottom: false}) { |
| 264 if (t1 == t2) return true; | 264 if (t1 == t2) return true; |
| 265 | 265 |
| 266 if (t2.isDynamic) return !dynamicIsBottom; | |
| 267 // In negative state, dynamic acts as bottom as well as top, but in a | |
|
Leaf
2015/03/18 22:57:25
This comment I think is left over from the polarit
vsm
2015/03/18 23:40:32
Done.
| |
| 268 // positive (normal) state, we disallow this subtyping. | |
| 269 if (t1.isDynamic) return dynamicIsBottom; | |
| 270 | |
| 266 // Null can be assigned to anything non-primitive. | 271 // Null can be assigned to anything non-primitive. |
| 267 // FIXME: Can this be anything besides null? | 272 // FIXME: Can this be anything besides null? |
| 268 if (t1.isBottom) { | 273 if (t1.isBottom) { |
| 269 // Return false iff t2 *may* be a primitive type. | 274 // Return false iff t2 *may* be a primitive type. |
| 270 return !maybeNonNullableType(t2); | 275 return !maybeNonNullableType(t2); |
| 271 } | 276 } |
| 272 if (t2.isBottom) return false; | 277 if (t2.isBottom) return false; |
| 273 | 278 |
| 274 if (t2.isDynamic) return true; | |
| 275 if (t1.isDynamic) return false; | |
| 276 | |
| 277 // Trivially true for non-primitives. | 279 // Trivially true for non-primitives. |
| 278 if (t2 == provider.objectType) return true; | 280 if (t2 == provider.objectType) return true; |
| 279 if (t1 == provider.objectType) return false; | 281 if (t1 == provider.objectType) return false; |
| 280 | 282 |
| 281 // S <: T where S is a type variable | 283 // S <: T where S is a type variable |
| 282 // T is not dynamic or object (handled above) | 284 // T is not dynamic or object (handled above) |
| 283 // S != T (handled above) | 285 // S != T (handled above) |
| 284 // So only true if bound of S is S' and | 286 // So only true if bound of S is S' and |
| 285 // S' <: T | 287 // S' <: T |
| 286 if (t1 is TypeParameterType) { | 288 if (t1 is TypeParameterType) { |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 489 return t.isDynamic || t.isDartCoreFunction; | 491 return t.isDynamic || t.isDartCoreFunction; |
| 490 } | 492 } |
| 491 | 493 |
| 492 /// Returns `true` if the expression is a dynamic function call or method | 494 /// Returns `true` if the expression is a dynamic function call or method |
| 493 /// invocation. | 495 /// invocation. |
| 494 bool isDynamicCall(Expression call) { | 496 bool isDynamicCall(Expression call) { |
| 495 if (options.ignoreTypes) return true; | 497 if (options.ignoreTypes) return true; |
| 496 var t = getStaticType(call); | 498 var t = getStaticType(call); |
| 497 // TODO(jmesserly): fix handling of types with `call` methods. These are not | 499 // TODO(jmesserly): fix handling of types with `call` methods. These are not |
| 498 // FunctionType, but they also aren't dynamic calls. | 500 // FunctionType, but they also aren't dynamic calls. |
| 499 return t.isDynamic || t.isDartCoreFunction || t is! FunctionType; | 501 if (t.isDynamic || t.isDartCoreFunction || t is! FunctionType) { |
| 502 return true; | |
| 503 } | |
| 504 // Dynamic as the parameter type is treated as bottom. A function with | |
| 505 // a dynamic parameter type requires a dynamic call in general. | |
| 506 // However, as an optimization, if we have an original definition, we know | |
| 507 // dynamic is reified as Object - in this case a regular call is fine. | |
| 508 if (call is SimpleIdentifier) { | |
|
Leaf
2015/03/18 22:57:25
This could probably be done for at least some pref
vsm
2015/03/18 23:40:32
The "call" expression is the method name (not the
| |
| 509 var element = call.staticElement; | |
| 510 if (element is FunctionElement || element is MethodElement) { | |
| 511 // An original declaration. | |
| 512 return false; | |
| 513 } | |
| 514 } | |
| 515 var ft = t as FunctionType; | |
| 516 for (var parameterType in ft.normalParameterTypes) { | |
| 517 if (parameterType.isDynamic) return true; | |
| 518 } | |
| 519 for (var parameterType in ft.optionalParameterTypes) { | |
| 520 if (parameterType.isDynamic) return true; | |
| 521 } | |
| 522 for (var parameterType in ft.namedParameterTypes.values) { | |
| 523 if (parameterType.isDynamic) return true; | |
| 524 } | |
| 525 return false; | |
| 500 } | 526 } |
| 501 } | 527 } |
| OLD | NEW |