Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(527)

Side by Side Diff: pkg/analyzer/lib/src/task/strong/rules.dart

Issue 1507933002: Refactor strong mode to remove duplicate TypeRules (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
6 // refactored to fit into analyzer. 6 // refactored to fit into analyzer.
7 library analyzer.src.task.strong.rules; 7 library analyzer.src.task.strong.rules;
8 8
9 import 'package:analyzer/src/generated/ast.dart'; 9 import 'package:analyzer/src/generated/ast.dart';
10 import 'package:analyzer/src/generated/element.dart'; 10 import 'package:analyzer/src/generated/element.dart';
11 import 'package:analyzer/src/generated/resolver.dart'; 11 import 'package:analyzer/src/generated/resolver.dart';
12 12
13 import 'info.dart'; 13 import 'info.dart';
14 14
15 // TODO(jmesserly): this entire file needs to be removed in favor of TypeSystem. 15 // TODO(jmesserly): move this to another file or rename this one.
16
17 final _objectMap = new Expando('providerToObjectMap');
18 Map<String, DartType> getObjectMemberMap(TypeProvider typeProvider) {
19 var map = _objectMap[typeProvider] as Map<String, DartType>;
20 if (map == null) {
21 map = <String, DartType>{};
22 _objectMap[typeProvider] = map;
23 var objectType = typeProvider.objectType;
24 var element = objectType.element;
25 // Only record methods (including getters) with no parameters. As parameter s are contravariant wrt
26 // type, using Object's version may be too strict.
27 // Add instance methods.
28 element.methods.where((method) => !method.isStatic).forEach((method) {
29 map[method.name] = method.type;
30 });
31 // Add getters.
32 element.accessors
33 .where((member) => !member.isStatic && member.isGetter)
34 .forEach((member) {
35 map[member.name] = member.type.returnType;
36 });
37 }
38 return map;
39 }
40
41 class TypeRules {
42 final TypeProvider provider;
43
44 /// Map of fields / properties / methods on Object.
45 final Map<String, DartType> objectMembers;
46
47 DownwardsInference inferrer;
48
49 TypeRules(TypeProvider provider)
50 : provider = provider,
51 objectMembers = getObjectMemberMap(provider) {
52 inferrer = new DownwardsInference(this);
53 }
54
55 /// Given a type t, if t is an interface type with a call method
56 /// defined, return the function type for the call method, otherwise
57 /// return null.
58 FunctionType getCallMethodType(DartType t) {
59 if (t is InterfaceType) {
60 return t.lookUpMethod("call", null)?.type;
61 }
62 return null;
63 }
64
65 /// Given an expression, return its type assuming it is
66 /// in the caller position of a call (that is, accounting
67 /// for the possibility of a call method). Returns null
68 /// if expression is not statically callable.
69 FunctionType getTypeAsCaller(Expression applicand) {
70 var t = getStaticType(applicand);
71 if (t is InterfaceType) {
72 return getCallMethodType(t);
73 }
74 if (t is FunctionType) return t;
75 return null;
76 }
77
78 /// Gets the expected return type of the given function [body], either from
79 /// a normal return/yield, or from a yield*.
80 DartType getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
81 FunctionType functionType;
82 var parent = body.parent;
83 if (parent is Declaration) {
84 functionType = elementType(parent.element);
85 } else {
86 assert(parent is FunctionExpression);
87 functionType = getStaticType(parent);
88 }
89
90 var type = functionType.returnType;
91
92 InterfaceType expectedType = null;
93 if (body.isAsynchronous) {
94 if (body.isGenerator) {
95 // Stream<T> -> T
96 expectedType = provider.streamType;
97 } else {
98 // Future<T> -> T
99 // TODO(vsm): Revisit with issue #228.
100 expectedType = provider.futureType;
101 }
102 } else {
103 if (body.isGenerator) {
104 // Iterable<T> -> T
105 expectedType = provider.iterableType;
106 } else {
107 // T -> T
108 return type;
109 }
110 }
111 if (yieldStar) {
112 if (type.isDynamic) {
113 // Ensure it's at least a Stream / Iterable.
114 return expectedType.substitute4([provider.dynamicType]);
115 } else {
116 // Analyzer will provide a separate error if expected type
117 // is not compatible with type.
118 return type;
119 }
120 }
121 if (type.isDynamic) {
122 return type;
123 } else if (type is InterfaceType && type.element == expectedType.element) {
124 return type.typeArguments[0];
125 } else {
126 // Malformed type - fallback on analyzer error.
127 return null;
128 }
129 }
130
131 DartType getStaticType(Expression expr) {
132 return expr.staticType ?? provider.dynamicType;
133 }
134
135 bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
136 if (t.isDynamic && dynamicIsBottom) return true;
137 // TODO(vsm): We need direct support for non-nullability in DartType.
138 // This should check on "true/nonnullable" Bottom
139 if (t.isBottom) return true;
140 return false;
141 }
142
143 bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
144 if (t.isDynamic && !dynamicIsBottom) return true;
145 if (t.isObject) return true;
146 return false;
147 }
148
149 bool _anyParameterType(FunctionType ft, bool predicate(DartType t)) {
150 return ft.normalParameterTypes.any(predicate) ||
151 ft.optionalParameterTypes.any(predicate) ||
152 ft.namedParameterTypes.values.any(predicate);
153 }
154
155 // TODO(leafp): Revisit this.
156 bool isGroundType(DartType t) {
157 if (t is TypeParameterType) return false;
158 if (_isTop(t)) return true;
159
160 if (t is FunctionType) {
161 if (!_isTop(t.returnType) ||
162 _anyParameterType(t, (pt) => !_isBottom(pt, dynamicIsBottom: true))) {
163 return false;
164 } else {
165 return true;
166 }
167 }
168
169 if (t is InterfaceType) {
170 var typeArguments = t.typeArguments;
171 for (var typeArgument in typeArguments) {
172 if (!_isTop(typeArgument)) return false;
173 }
174 return true;
175 }
176
177 // We should not see any other type aside from malformed code.
178 return false;
179 }
180
181 /// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC
182 /// checker to determine whether f1 would be a subtype of f2 if the return
183 /// type of f1 is set to match f2's return type.
184 // [fuzzyArrows] indicates whether or not the f1 and f2 should be
185 // treated as fuzzy arrow types (and hence dynamic parameters to f2 treated as
186 // bottom).
187 bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2,
188 {bool fuzzyArrows: true, bool ignoreReturn: false}) {
189 final r1s = f1.normalParameterTypes;
190 final o1s = f1.optionalParameterTypes;
191 final n1s = f1.namedParameterTypes;
192 final r2s = f2.normalParameterTypes;
193 final o2s = f2.optionalParameterTypes;
194 final n2s = f2.namedParameterTypes;
195 final ret1 = ignoreReturn ? f2.returnType : f1.returnType;
196 final ret2 = f2.returnType;
197
198 // A -> B <: C -> D if C <: A and
199 // either D is void or B <: D
200 if (!ret2.isVoid && !isSubTypeOf(ret1, ret2)) return false;
201
202 // Reject if one has named and the other has optional
203 if (n1s.length > 0 && o2s.length > 0) return false;
204 if (n2s.length > 0 && o1s.length > 0) return false;
205
206 // f2 has named parameters
207 if (n2s.length > 0) {
208 // Check that every named parameter in f2 has a match in f1
209 for (String k2 in n2s.keys) {
210 if (!n1s.containsKey(k2)) return false;
211 if (!isSubTypeOf(n2s[k2], n1s[k2],
212 dynamicIsBottom: fuzzyArrows)) return false;
213 }
214 }
215 // If we get here, we either have no named parameters,
216 // or else the named parameters match and we have no optional
217 // parameters
218
219 // If f1 has more required parameters, reject
220 if (r1s.length > r2s.length) return false;
221
222 // If f2 has more required + optional parameters, reject
223 if (r2s.length + o2s.length > r1s.length + o1s.length) return false;
224
225 // The parameter lists must look like the following at this point
226 // where rrr is a region of required, and ooo is a region of optionals.
227 // f1: rrr ooo ooo ooo
228 // f2: rrr rrr ooo
229 int rr = r1s.length; // required in both
230 int or = r2s.length - r1s.length; // optional in f1, required in f2
231 int oo = o2s.length; // optional in both
232
233 for (int i = 0; i < rr; ++i) {
234 if (!isSubTypeOf(r2s[i], r1s[i],
235 dynamicIsBottom: fuzzyArrows)) return false;
236 }
237 for (int i = 0, j = rr; i < or; ++i, ++j) {
238 if (!isSubTypeOf(r2s[j], o1s[i],
239 dynamicIsBottom: fuzzyArrows)) return false;
240 }
241 for (int i = or, j = 0; i < oo; ++i, ++j) {
242 if (!isSubTypeOf(o2s[j], o1s[i],
243 dynamicIsBottom: fuzzyArrows)) return false;
244 }
245 return true;
246 }
247
248 bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) {
249 if (i1 == i2) return true;
250
251 if (i1.element == i2.element) {
252 List<DartType> tArgs1 = i1.typeArguments;
253 List<DartType> tArgs2 = i2.typeArguments;
254
255 // TODO(leafp): Verify that this is always true
256 // Do raw types get filled in?
257 assert(tArgs1.length == tArgs2.length);
258
259 for (int i = 0; i < tArgs1.length; i++) {
260 DartType t1 = tArgs1[i];
261 DartType t2 = tArgs2[i];
262 if (!isSubTypeOf(t1, t2)) return false;
263 }
264 return true;
265 }
266
267 if (i2.isDartCoreFunction) {
268 if (i1.element.getMethod("call") != null) return true;
269 }
270
271 if (i1 == provider.objectType) return false;
272
273 if (_isInterfaceSubTypeOf(i1.superclass, i2)) return true;
274
275 for (final parent in i1.interfaces) {
276 if (_isInterfaceSubTypeOf(parent, i2)) return true;
277 }
278
279 for (final parent in i1.mixins) {
280 if (_isInterfaceSubTypeOf(parent, i2)) return true;
281 }
282
283 return false;
284 }
285
286 bool isSubTypeOf(DartType t1, DartType t2, {bool dynamicIsBottom: false}) {
287 if (t1 == t2) return true;
288
289 // Trivially true.
290 if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
291 _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
292 return true;
293 }
294
295 // Trivially false.
296 if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
297 _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
298 return false;
299 }
300
301 // The null type is a subtype of any nullable type, which is all Dart types.
302 // TODO(vsm): Note, t1.isBottom still allows for null confusingly.
303 // _isBottom(t1) does not necessarily imply t1.isBottom if there are
304 // nonnullable types in the system.
305 if (t1.isBottom) {
306 return true;
307 }
308
309 // S <: T where S is a type variable
310 // T is not dynamic or object (handled above)
311 // S != T (handled above)
312 // So only true if bound of S is S' and
313 // S' <: T
314 if (t1 is TypeParameterType) {
315 DartType bound = t1.element.bound;
316 if (bound == null) return false;
317 return isSubTypeOf(bound, t2);
318 }
319
320 if (t2 is TypeParameterType) {
321 return false;
322 }
323
324 if (t1.isVoid || t2.isVoid) {
325 return false;
326 }
327
328 if (t2.isDartCoreFunction) {
329 if (t1 is FunctionType) return true;
330 if (t1.element is ClassElement) {
331 if ((t1.element as ClassElement).getMethod("call") != null) return true;
332 }
333 }
334
335 // "Traditional" name-based subtype check.
336 if (t1 is InterfaceType && t2 is InterfaceType) {
337 return _isInterfaceSubTypeOf(t1, t2);
338 }
339
340 if (t1 is! FunctionType && t2 is! FunctionType) return false;
341
342 if (t1 is InterfaceType && t2 is FunctionType) {
343 var callType = getCallMethodType(t1);
344 if (callType == null) return false;
345 return isFunctionSubTypeOf(callType, t2);
346 }
347
348 if (t1 is FunctionType && t2 is InterfaceType) {
349 return false;
350 }
351
352 // Functions
353 // Note: it appears under the hood all Dart functions map to a class /
354 // hidden type that:
355 // (a) subtypes Object (an internal _FunctionImpl in the VM)
356 // (b) implements Function
357 // (c) provides standard Object members (hashCode, toString)
358 // (d) contains private members (corresponding to _FunctionImpl?)
359 // (e) provides a call method to handle the actual function invocation
360 //
361 // The standard Dart subtyping rules are structural in nature. I.e.,
362 // bivariant on arguments and return type.
363 //
364 // The below tries for a more traditional subtyping rule:
365 // - covariant on return type
366 // - contravariant on parameters
367 // - 'sensible' (?) rules on optional and/or named params
368 // but doesn't properly mix with class subtyping. I suspect Java 8 lambdas
369 // essentially map to dynamic (and rely on invokedynamic) due to similar
370 // issues.
371 return isFunctionSubTypeOf(t1 as FunctionType, t2 as FunctionType);
372 }
373
374 bool isAssignable(DartType t1, DartType t2) {
375 return isSubTypeOf(t1, t2);
376 }
377
378 // Produce a coercion which coerces something of type fromT
379 // to something of type toT.
380 // Returns the error coercion if the types cannot be coerced
381 // according to our current criteria.
382 Coercion _coerceTo(DartType fromT, DartType toT) {
383 // We can use anything as void
384 if (toT.isVoid) return Coercion.identity(toT);
385
386 // fromT <: toT, no coercion needed
387 if (isSubTypeOf(fromT, toT)) return Coercion.identity(toT);
388
389 // TODO(vsm): We can get rid of the second clause if we disallow
390 // all sideways casts - see TODO below.
391 // -------
392 // Note: a function type is never assignable to a class per the Dart
393 // spec - even if it has a compatible call method. We disallow as
394 // well for consistency.
395 if ((fromT is FunctionType && getCallMethodType(toT) != null) ||
396 (toT is FunctionType && getCallMethodType(fromT) != null)) {
397 return Coercion.error();
398 }
399
400 // Downcast if toT <: fromT
401 if (isSubTypeOf(toT, fromT)) return Coercion.cast(fromT, toT);
402
403 // TODO(vsm): Once we have generic methods, we should delete this
404 // workaround. These sideways casts are always ones we warn about
405 // - i.e., we think they are likely to fail at runtime.
406 // -------
407 // Downcast if toT <===> fromT
408 // The intention here is to allow casts that are sideways in the restricted
409 // type system, but allowed in the regular dart type system, since these
410 // are likely to succeed. The canonical example is List<dynamic> and
411 // Iterable<T> for some concrete T (e.g. Object). These are unrelated
412 // in the restricted system, but List<dynamic> <: Iterable<T> in dart.
413 if (fromT.isAssignableTo(toT)) {
414 return Coercion.cast(fromT, toT);
415 }
416
417 return Coercion.error();
418 }
419
420 StaticInfo checkAssignment(Expression expr, DartType toT) {
421 final fromT = getStaticType(expr);
422 final Coercion c = _coerceTo(fromT, toT);
423 if (c is Identity) return null;
424 if (c is CoercionError) return new StaticTypeError(this, expr, toT);
425 var reason = null;
426
427 var errors = <String>[];
428
429 var ok = inferrer.inferExpression(expr, toT, errors);
430 if (ok) return InferredType.create(this, expr, toT);
431 reason = (errors.isNotEmpty) ? errors.first : null;
432
433 if (c is Cast) return DownCast.create(this, expr, c, reason: reason);
434 assert(false);
435 return null;
436 }
437
438 DartType elementType(Element e) {
439 if (e == null) {
440 // Malformed code - just return dynamic.
441 return provider.dynamicType;
442 }
443 return (e as dynamic).type;
444 }
445
446 bool _isLibraryPrefix(Expression node) =>
447 node is SimpleIdentifier && node.staticElement is PrefixElement;
448
449 /// Returns `true` if the target expression is dynamic.
450 bool isDynamicTarget(Expression node) {
451 if (node == null) return false;
452
453 if (_isLibraryPrefix(node)) return false;
454
455 // Null type happens when we have unknown identifiers, like a dart: import
456 // that doesn't resolve.
457 var type = node.staticType;
458 return type == null || type.isDynamic;
459 }
460
461 /// Returns `true` if the expression is a dynamic function call or method
462 /// invocation.
463 bool isDynamicCall(Expression call) {
464 var ft = getTypeAsCaller(call);
465 // TODO(leafp): This will currently return true if t is Function
466 // This is probably the most correct thing to do for now, since
467 // this code is also used by the back end. Maybe revisit at some
468 // point?
469 if (ft == null) return true;
470 // Dynamic as the parameter type is treated as bottom. A function with
471 // a dynamic parameter type requires a dynamic call in general.
472 // However, as an optimization, if we have an original definition, we know
473 // dynamic is reified as Object - in this case a regular call is fine.
474 if (call is SimpleIdentifier) {
475 var element = call.staticElement;
476 if (element is FunctionElement || element is MethodElement) {
477 // An original declaration.
478 return false;
479 }
480 }
481
482 return _anyParameterType(ft, (pt) => pt.isDynamic);
483 }
484 }
485 16
486 class DownwardsInference { 17 class DownwardsInference {
Leaf 2015/12/08 00:24:40 This code is super close to dead. I'm pretty sure
Jennifer Messerly 2015/12/08 01:06:32 Ah yes! good catch. Done!
487 final TypeRules rules; 18 final TypeSystem rules;
488 19
489 DownwardsInference(this.rules); 20 DownwardsInference(this.rules);
490 21
491 /// Called for each list literal which gets inferred 22 /// Called for each list literal which gets inferred
492 void annotateListLiteral(ListLiteral e, List<DartType> targs) {} 23 void annotateListLiteral(ListLiteral e, List<DartType> targs) {}
493 24
494 /// Called for each map literal which gets inferred 25 /// Called for each map literal which gets inferred
495 void annotateMapLiteral(MapLiteral e, List<DartType> targs) {} 26 void annotateMapLiteral(MapLiteral e, List<DartType> targs) {}
496 27
497 /// Called for each new/const which gets inferred 28 /// Called for each new/const which gets inferred
498 void annotateInstanceCreationExpression( 29 void annotateInstanceCreationExpression(
499 InstanceCreationExpression e, List<DartType> targs) {} 30 InstanceCreationExpression e, List<DartType> targs) {}
500 31
501 /// Called for cast from dynamic required for inference to succeed 32 /// Called for cast from dynamic required for inference to succeed
502 void annotateCastFromDynamic(Expression e, DartType t) {} 33 void annotateCastFromDynamic(Expression e, DartType t) {}
503 34
504 /// Called for each function expression return type inferred 35 /// Called for each function expression return type inferred
505 void annotateFunctionExpression(FunctionExpression e, DartType returnType) {} 36 void annotateFunctionExpression(FunctionExpression e, DartType returnType) {}
506 37
507 /// Downward inference 38 /// Downward inference
508 bool inferExpression(Expression e, DartType t, List<String> errors) { 39 bool inferExpression(Expression e, DartType t, List<String> errors) {
509 // Don't cast top level expressions, only sub-expressions 40 // Don't cast top level expressions, only sub-expressions
510 return _inferExpression(e, t, errors, cast: false); 41 return _inferExpression(e, t, errors, cast: false);
511 } 42 }
512 43
513 /// Downward inference 44 /// Downward inference
514 bool _inferExpression(Expression e, DartType t, List<String> errors, 45 bool _inferExpression(Expression e, DartType t, List<String> errors,
515 {cast: true}) { 46 {cast: true}) {
516 if (rules.isSubTypeOf(rules.getStaticType(e), t)) return true; 47 DartType staticType = e.staticType ?? DynamicTypeImpl.instance;
517 if (cast && rules.getStaticType(e).isDynamic) { 48 if (rules.isSubtypeOf(staticType, t)) {
49 return true;
50 }
51 if (cast && staticType.isDynamic) {
518 annotateCastFromDynamic(e, t); 52 annotateCastFromDynamic(e, t);
519 return true; 53 return true;
520 } 54 }
521 errors.add("$e cannot be typed as $t"); 55 errors.add("$e cannot be typed as $t");
522 return false; 56 return false;
523 } 57 }
524 } 58 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/task/strong/info.dart ('k') | pkg/analyzer/test/src/task/strong/checker_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698