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

Side by Side Diff: pkg/compiler/lib/src/typechecker.dart

Issue 693183006: Revert "Move dart2js from sdk/lib/_internal/compiler to pkg/compiler" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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 part of dart2js;
6
7 class TypeCheckerTask extends CompilerTask {
8 TypeCheckerTask(Compiler compiler) : super(compiler);
9 String get name => "Type checker";
10
11 void check(TreeElements elements) {
12 AstElement element = elements.analyzedElement;
13 compiler.withCurrentElement(element, () {
14 measure(() {
15 Node tree = element.node;
16 TypeCheckerVisitor visitor =
17 new TypeCheckerVisitor(compiler, elements, compiler.types);
18 if (element.isField) {
19 visitor.analyzingInitializer = true;
20 }
21 tree.accept(visitor);
22 });
23 });
24 }
25 }
26
27 /**
28 * Class used to report different warnings for differrent kinds of members.
29 */
30 class MemberKind {
31 static const MemberKind METHOD = const MemberKind("method");
32 static const MemberKind OPERATOR = const MemberKind("operator");
33 static const MemberKind GETTER = const MemberKind("getter");
34 static const MemberKind SETTER = const MemberKind("setter");
35
36 final String name;
37
38 const MemberKind(this.name);
39
40 String toString() => name;
41 }
42
43 /**
44 * [ElementAccess] represents the access of [element], either as a property
45 * access or invocation.
46 */
47 abstract class ElementAccess {
48 Element get element;
49
50 DartType computeType(Compiler compiler);
51
52 /// Returns [: true :] if the element can be access as an invocation.
53 bool isCallable(Compiler compiler) {
54 if (element != null && element.isAbstractField) {
55 AbstractFieldElement abstractFieldElement = element;
56 if (abstractFieldElement.getter == null) {
57 // Setters cannot be invoked as function invocations.
58 return false;
59 }
60 }
61 return compiler.types.isAssignable(
62 computeType(compiler), compiler.functionClass.computeType(compiler));
63 }
64 }
65
66 /// An access of a instance member.
67 class MemberAccess extends ElementAccess {
68 final MemberSignature member;
69
70 MemberAccess(MemberSignature this.member);
71
72 Element get element => member.declarations.first.element;
73
74 DartType computeType(Compiler compiler) => member.type;
75
76 String toString() => 'MemberAccess($member)';
77 }
78
79 /// An access of an unresolved element.
80 class DynamicAccess implements ElementAccess {
81 const DynamicAccess();
82
83 Element get element => null;
84
85 DartType computeType(Compiler compiler) => const DynamicType();
86
87 bool isCallable(Compiler compiler) => true;
88
89 String toString() => 'DynamicAccess';
90 }
91
92 /// An access of the `assert` method.
93 class AssertAccess implements ElementAccess {
94 const AssertAccess();
95
96 Element get element => null;
97
98 DartType computeType(Compiler compiler) {
99 return new FunctionType.synthesized(
100 const VoidType(),
101 <DartType>[const DynamicType()]);
102 }
103
104 bool isCallable(Compiler compiler) => true;
105
106 String toString() => 'AssertAccess';
107 }
108
109 /**
110 * An access of a resolved top-level or static property or function, or an
111 * access of a resolved element through [:this:].
112 */
113 class ResolvedAccess extends ElementAccess {
114 final Element element;
115
116 ResolvedAccess(Element this.element) {
117 assert(element != null);
118 }
119
120 DartType computeType(Compiler compiler) {
121 if (element.isGetter) {
122 FunctionType functionType = element.computeType(compiler);
123 return functionType.returnType;
124 } else if (element.isSetter) {
125 FunctionType functionType = element.computeType(compiler);
126 if (functionType.parameterTypes.length != 1) {
127 // TODO(johnniwinther,karlklose): this happens for malformed static
128 // setters. Treat them the same as instance members.
129 return const DynamicType();
130 }
131 return functionType.parameterTypes.first;
132 } else {
133 return element.computeType(compiler);
134 }
135 }
136
137 String toString() => 'ResolvedAccess($element)';
138 }
139
140 /// An access to a promoted variable.
141 class PromotedAccess extends ElementAccess {
142 final VariableElement element;
143 final DartType type;
144
145 PromotedAccess(VariableElement this.element, DartType this.type) {
146 assert(element != null);
147 assert(type != null);
148 }
149
150 DartType computeType(Compiler compiler) => type;
151
152 String toString() => 'PromotedAccess($element,$type)';
153 }
154
155 /**
156 * An access of a resolved top-level or static property or function, or an
157 * access of a resolved element through [:this:].
158 */
159 class TypeAccess extends ElementAccess {
160 final DartType type;
161 TypeAccess(DartType this.type) {
162 assert(type != null);
163 }
164
165 Element get element => type.element;
166
167 DartType computeType(Compiler compiler) => type;
168
169 String toString() => 'TypeAccess($type)';
170 }
171
172 /**
173 * An access of a type literal.
174 */
175 class TypeLiteralAccess extends ElementAccess {
176 final DartType type;
177
178 TypeLiteralAccess(this.type) {
179 assert(type != null);
180 }
181
182 Element get element => type.element;
183
184 DartType computeType(Compiler compiler) => compiler.typeClass.rawType;
185
186 String toString() => 'TypeLiteralAccess($type)';
187 }
188
189
190 /// An access to the 'call' method of a function type.
191 class FunctionCallAccess implements ElementAccess {
192 final Element element;
193 final DartType type;
194
195 const FunctionCallAccess(this.element, this.type);
196
197 DartType computeType(Compiler compiler) => type;
198
199 bool isCallable(Compiler compiler) => true;
200
201 String toString() => 'FunctionAccess($element, $type)';
202 }
203
204
205 /// An is-expression that potentially promotes a variable.
206 class TypePromotion {
207 final Send node;
208 final VariableElement variable;
209 final DartType type;
210 final List<TypePromotionMessage> messages = <TypePromotionMessage>[];
211
212 TypePromotion(this.node, this.variable, this.type);
213
214 bool get isValid => messages.isEmpty;
215
216 TypePromotion copy() {
217 return new TypePromotion(node, variable, type)..messages.addAll(messages);
218 }
219
220 void addHint(Spannable spannable, MessageKind kind, [Map arguments]) {
221 messages.add(new TypePromotionMessage(api.Diagnostic.HINT,
222 spannable, kind, arguments));
223 }
224
225 void addInfo(Spannable spannable, MessageKind kind, [Map arguments]) {
226 messages.add(new TypePromotionMessage(api.Diagnostic.INFO,
227 spannable, kind, arguments));
228 }
229
230 String toString() {
231 return 'Promote ${variable} to ${type}${isValid ? '' : ' (invalid)'}';
232 }
233 }
234
235 /// A hint or info message attached to a type promotion.
236 class TypePromotionMessage {
237 api.Diagnostic diagnostic;
238 Spannable spannable;
239 MessageKind messageKind;
240 Map messageArguments;
241
242 TypePromotionMessage(this.diagnostic, this.spannable, this.messageKind,
243 [this.messageArguments]);
244 }
245
246 class TypeCheckerVisitor extends Visitor<DartType> {
247 final Compiler compiler;
248 final TreeElements elements;
249 final Types types;
250
251 Node lastSeenNode;
252 DartType expectedReturnType;
253 AsyncMarker currentAsyncMarker = AsyncMarker.SYNC;
254
255 final ClassElement currentClass;
256
257 InterfaceType thisType;
258 InterfaceType superType;
259
260 Link<DartType> cascadeTypes = const Link<DartType>();
261
262 bool analyzingInitializer = false;
263
264 DartType intType;
265 DartType doubleType;
266 DartType boolType;
267 DartType stringType;
268 DartType objectType;
269 DartType listType;
270
271 Map<Node, List<TypePromotion>> shownTypePromotionsMap =
272 new Map<Node, List<TypePromotion>>();
273
274 Map<VariableElement, Link<TypePromotion>> typePromotionsMap =
275 new Map<VariableElement, Link<TypePromotion>>();
276
277 Set<TypePromotion> reportedTypePromotions = new Set<TypePromotion>();
278
279 void showTypePromotion(Node node, TypePromotion typePromotion) {
280 List<TypePromotion> shownTypePromotions =
281 shownTypePromotionsMap.putIfAbsent(node, () => <TypePromotion>[]);
282 shownTypePromotions.add(typePromotion);
283 }
284
285 void registerKnownTypePromotion(TypePromotion typePromotion) {
286 VariableElement variable = typePromotion.variable;
287 Link<TypePromotion> knownTypes =
288 typePromotionsMap.putIfAbsent(variable,
289 () => const Link<TypePromotion>());
290 typePromotionsMap[variable] = knownTypes.prepend(typePromotion);
291 }
292
293 void unregisterKnownTypePromotion(TypePromotion typePromotion) {
294 VariableElement variable = typePromotion.variable;
295 Link<TypePromotion> knownTypes = typePromotionsMap[variable].tail;
296 if (knownTypes.isEmpty) {
297 typePromotionsMap.remove(variable);
298 } else {
299 typePromotionsMap[variable] = knownTypes;
300 }
301 }
302
303 List<TypePromotion> getShownTypePromotionsFor(Node node) {
304 List<TypePromotion> shownTypePromotions = shownTypePromotionsMap[node];
305 return shownTypePromotions != null ? shownTypePromotions : const [];
306 }
307
308 TypePromotion getKnownTypePromotion(VariableElement element) {
309 Link<TypePromotion> promotions = typePromotionsMap[element];
310 if (promotions != null) {
311 while (!promotions.isEmpty) {
312 TypePromotion typePromotion = promotions.head;
313 if (typePromotion.isValid) {
314 return typePromotion;
315 }
316 promotions = promotions.tail;
317 }
318 }
319 return null;
320 }
321
322 DartType getKnownType(VariableElement element) {
323 TypePromotion typePromotion = getKnownTypePromotion(element);
324 if (typePromotion != null) return typePromotion.type;
325 return element.type;
326 }
327
328 TypeCheckerVisitor(this.compiler, TreeElements elements, this.types)
329 : this.elements = elements,
330 currentClass = elements.analyzedElement != null
331 ? elements.analyzedElement.enclosingClass : null {
332 intType = compiler.intClass.computeType(compiler);
333 doubleType = compiler.doubleClass.computeType(compiler);
334 boolType = compiler.boolClass.computeType(compiler);
335 stringType = compiler.stringClass.computeType(compiler);
336 objectType = compiler.objectClass.computeType(compiler);
337 listType = compiler.listClass.computeType(compiler);
338
339 if (currentClass != null) {
340 thisType = currentClass.thisType;
341 superType = currentClass.supertype;
342 }
343 }
344
345 LibraryElement get currentLibrary => elements.analyzedElement.library;
346
347 reportTypeWarning(Spannable spannable, MessageKind kind,
348 [Map arguments = const {}]) {
349 compiler.reportWarning(spannable, kind, arguments);
350 }
351
352 reportTypeInfo(Spannable spannable, MessageKind kind,
353 [Map arguments = const {}]) {
354 compiler.reportInfo(spannable, kind, arguments);
355 }
356
357 reportTypePromotionHint(TypePromotion typePromotion) {
358 if (!reportedTypePromotions.contains(typePromotion)) {
359 reportedTypePromotions.add(typePromotion);
360 for (TypePromotionMessage message in typePromotion.messages) {
361 switch (message.diagnostic) {
362 case api.Diagnostic.HINT:
363 compiler.reportHint(message.spannable,
364 message.messageKind,
365 message.messageArguments);
366 break;
367 case api.Diagnostic.INFO:
368 compiler.reportInfo(message.spannable,
369 message.messageKind,
370 message.messageArguments);
371 break;
372 }
373 }
374 }
375 }
376
377 // TODO(karlklose): remove these functions.
378 DartType unhandledExpression() => const DynamicType();
379
380 DartType analyzeNonVoid(Node node) {
381 DartType type = analyze(node);
382 if (type.isVoid) {
383 reportTypeWarning(node, MessageKind.VOID_EXPRESSION);
384 }
385 return type;
386 }
387
388 DartType analyzeWithDefault(Node node, DartType defaultValue) {
389 return node != null ? analyze(node) : defaultValue;
390 }
391
392 /// If [inInitializer] is true, assignment should be interpreted as write to
393 /// a field and not to a setter.
394 DartType analyze(Node node, {bool inInitializer: false}) {
395 if (node == null) {
396 final String error = 'Unexpected node: null';
397 if (lastSeenNode != null) {
398 compiler.internalError(lastSeenNode, error);
399 } else {
400 compiler.internalError(elements.analyzedElement, error);
401 }
402 } else {
403 lastSeenNode = node;
404 }
405 bool previouslyInitializer = analyzingInitializer;
406 analyzingInitializer = inInitializer;
407 DartType result = node.accept(this);
408 analyzingInitializer = previouslyInitializer;
409 if (result == null) {
410 compiler.internalError(node, 'Type is null.');
411 }
412 return result;
413 }
414
415 void checkTypePromotion(Node node, TypePromotion typePromotion,
416 {bool checkAccesses: false}) {
417 VariableElement variable = typePromotion.variable;
418 String variableName = variable.name;
419 List<Node> potentialMutationsIn =
420 elements.getPotentialMutationsIn(node, variable);
421 if (!potentialMutationsIn.isEmpty) {
422 typePromotion.addHint(typePromotion.node,
423 MessageKind.POTENTIAL_MUTATION,
424 {'variableName': variableName, 'shownType': typePromotion.type});
425 for (Node mutation in potentialMutationsIn) {
426 typePromotion.addInfo(mutation,
427 MessageKind.POTENTIAL_MUTATION_HERE,
428 {'variableName': variableName});
429 }
430 }
431 List<Node> potentialMutationsInClosures =
432 elements.getPotentialMutationsInClosure(variable);
433 if (!potentialMutationsInClosures.isEmpty) {
434 typePromotion.addHint(typePromotion.node,
435 MessageKind.POTENTIAL_MUTATION_IN_CLOSURE,
436 {'variableName': variableName, 'shownType': typePromotion.type});
437 for (Node mutation in potentialMutationsInClosures) {
438 typePromotion.addInfo(mutation,
439 MessageKind.POTENTIAL_MUTATION_IN_CLOSURE_HERE,
440 {'variableName': variableName});
441 }
442 }
443 if (checkAccesses) {
444 List<Node> accesses = elements.getAccessesByClosureIn(node, variable);
445 List<Node> mutations = elements.getPotentialMutations(variable);
446 if (!accesses.isEmpty && !mutations.isEmpty) {
447 typePromotion.addHint(typePromotion.node,
448 MessageKind.ACCESSED_IN_CLOSURE,
449 {'variableName': variableName, 'shownType': typePromotion.type});
450 for (Node access in accesses) {
451 typePromotion.addInfo(access,
452 MessageKind.ACCESSED_IN_CLOSURE_HERE,
453 {'variableName': variableName});
454 }
455 for (Node mutation in mutations) {
456 typePromotion.addInfo(mutation,
457 MessageKind.POTENTIAL_MUTATION_HERE,
458 {'variableName': variableName});
459 }
460 }
461 }
462 }
463
464 /// Show type promotions from [left] and [right] in [node] given that the
465 /// promoted variables are not potentially mutated in [right].
466 void reshowTypePromotions(Node node, Node left, Node right) {
467 for (TypePromotion typePromotion in getShownTypePromotionsFor(left)) {
468 typePromotion = typePromotion.copy();
469 checkTypePromotion(right, typePromotion);
470 showTypePromotion(node, typePromotion);
471 }
472
473 for (TypePromotion typePromotion in getShownTypePromotionsFor(right)) {
474 typePromotion = typePromotion.copy();
475 checkTypePromotion(right, typePromotion);
476 showTypePromotion(node, typePromotion);
477 }
478 }
479
480 /// Analyze [node] in the context of the known types shown in [context].
481 DartType analyzeInPromotedContext(Node context, Node node) {
482 Link<TypePromotion> knownForNode = const Link<TypePromotion>();
483 for (TypePromotion typePromotion in getShownTypePromotionsFor(context)) {
484 typePromotion = typePromotion.copy();
485 checkTypePromotion(node, typePromotion, checkAccesses: true);
486 knownForNode = knownForNode.prepend(typePromotion);
487 registerKnownTypePromotion(typePromotion);
488 }
489
490 final DartType type = analyze(node);
491
492 while (!knownForNode.isEmpty) {
493 unregisterKnownTypePromotion(knownForNode.head);
494 knownForNode = knownForNode.tail;
495 }
496
497 return type;
498 }
499
500 /**
501 * Check if a value of type [from] can be assigned to a variable, parameter or
502 * return value of type [to]. If `isConst == true`, an error is emitted in
503 * checked mode, otherwise a warning is issued.
504 */
505 bool checkAssignable(Spannable spannable, DartType from, DartType to,
506 {bool isConst: false}) {
507 if (!types.isAssignable(from, to)) {
508 if (compiler.enableTypeAssertions && isConst) {
509 compiler.reportError(spannable, MessageKind.NOT_ASSIGNABLE,
510 {'fromType': from, 'toType': to});
511 } else {
512 reportTypeWarning(spannable, MessageKind.NOT_ASSIGNABLE,
513 {'fromType': from, 'toType': to});
514 }
515 return false;
516 }
517 return true;
518 }
519
520 checkCondition(Expression condition) {
521 checkAssignable(condition, analyze(condition), boolType);
522 }
523
524 void pushCascadeType(DartType type) {
525 cascadeTypes = cascadeTypes.prepend(type);
526 }
527
528 DartType popCascadeType() {
529 DartType type = cascadeTypes.head;
530 cascadeTypes = cascadeTypes.tail;
531 return type;
532 }
533
534 DartType visitBlock(Block node) {
535 return analyze(node.statements);
536 }
537
538 DartType visitCascade(Cascade node) {
539 analyze(node.expression);
540 return popCascadeType();
541 }
542
543 DartType visitCascadeReceiver(CascadeReceiver node) {
544 DartType type = analyze(node.expression);
545 pushCascadeType(type);
546 return type;
547 }
548
549 DartType visitDoWhile(DoWhile node) {
550 analyze(node.body);
551 checkCondition(node.condition);
552 return const StatementType();
553 }
554
555 DartType visitExpressionStatement(ExpressionStatement node) {
556 Expression expression = node.expression;
557 analyze(expression);
558 return const StatementType();
559 }
560
561 /** Dart Programming Language Specification: 11.5.1 For Loop */
562 DartType visitFor(For node) {
563 if (node.initializer != null) {
564 analyze(node.initializer);
565 }
566 if (node.condition != null) {
567 checkCondition(node.condition);
568 }
569 if (node.update != null) {
570 analyze(node.update);
571 }
572 return analyze(node.body);
573 }
574
575 DartType visitFunctionDeclaration(FunctionDeclaration node) {
576 analyze(node.function);
577 return const StatementType();
578 }
579
580 DartType visitFunctionExpression(FunctionExpression node) {
581 DartType type;
582 DartType returnType;
583 DartType previousType;
584 final FunctionElement element = elements.getFunctionDefinition(node);
585 assert(invariant(node, element != null,
586 message: 'FunctionExpression with no element'));
587 if (Elements.isUnresolved(element)) return const DynamicType();
588 if (identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR) ||
589 identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR_BODY)) {
590 type = const DynamicType();
591 returnType = const VoidType();
592
593 element.functionSignature.forEachParameter((ParameterElement parameter) {
594 if (parameter.isInitializingFormal) {
595 InitializingFormalElement fieldParameter = parameter;
596 checkAssignable(parameter, parameter.type,
597 fieldParameter.fieldElement.computeType(compiler));
598 }
599 });
600 if (node.initializers != null) {
601 analyze(node.initializers, inInitializer: true);
602 }
603 } else {
604 FunctionType functionType = element.computeType(compiler);
605 returnType = functionType.returnType;
606 type = functionType;
607 }
608 DartType previousReturnType = expectedReturnType;
609 expectedReturnType = returnType;
610 AsyncMarker previousAsyncMarker = currentAsyncMarker;
611 currentAsyncMarker = element.asyncMarker;
612 analyze(node.body);
613 expectedReturnType = previousReturnType;
614 currentAsyncMarker = previousAsyncMarker;
615 return type;
616 }
617
618 DartType visitIdentifier(Identifier node) {
619 if (node.isThis()) {
620 return thisType;
621 } else if (node.isSuper()) {
622 return superType;
623 } else {
624 Element element = elements[node];
625 assert(invariant(node, element != null,
626 message: 'Missing element for identifier'));
627 assert(invariant(node, element.isVariable ||
628 element.isParameter ||
629 element.isField,
630 message: 'Unexpected context element ${element}'));
631 return element.computeType(compiler);
632 }
633 }
634
635 DartType visitIf(If node) {
636 Expression condition = node.condition.expression;
637 Statement thenPart = node.thenPart;
638
639 checkCondition(node.condition);
640 analyzeInPromotedContext(condition, thenPart);
641 if (node.elsePart != null) {
642 analyze(node.elsePart);
643 }
644 return const StatementType();
645 }
646
647 void checkPrivateAccess(Node node, Element element, String name) {
648 if (name != null &&
649 isPrivateName(name) &&
650 element.library != currentLibrary) {
651 reportTypeWarning(
652 node,
653 MessageKind.PRIVATE_ACCESS,
654 {'name': name,
655 'libraryName': element.library.getLibraryOrScriptName()});
656 }
657
658 }
659
660 ElementAccess lookupMember(Node node, DartType receiverType, String name,
661 MemberKind memberKind, Element receiverElement,
662 {bool lookupClassMember: false}) {
663 if (receiverType.treatAsDynamic) {
664 return const DynamicAccess();
665 }
666
667 Name memberName = new Name(name, currentLibrary,
668 isSetter: memberKind == MemberKind.SETTER);
669
670 // Compute the unaliased type of the first non type variable bound of
671 // [type].
672 DartType computeUnaliasedBound(DartType type) {
673 DartType originalType = type;
674 while (identical(type.kind, TypeKind.TYPE_VARIABLE)) {
675 TypeVariableType variable = type;
676 type = variable.element.bound;
677 if (type == originalType) {
678 type = compiler.objectClass.rawType;
679 }
680 }
681 if (type.isMalformed) {
682 return const DynamicType();
683 }
684 return type.unalias(compiler);
685 }
686
687 // Compute the interface type of [type]. For type variable it is the
688 // interface type of the bound, for function types and typedefs it is the
689 // `Function` type.
690 InterfaceType computeInterfaceType(DartType type) {
691 if (type.isFunctionType) {
692 type = compiler.functionClass.rawType;
693 }
694 assert(invariant(node, type.isInterfaceType,
695 message: "unexpected type kind ${type.kind}."));
696 return type;
697 }
698
699 // Lookup the class or interface member [name] in [interface].
700 MemberSignature lookupMemberSignature(Name name, InterfaceType interface) {
701 MembersCreator.computeClassMembersByName(
702 compiler, interface.element, name.text);
703 return lookupClassMember || analyzingInitializer
704 ? interface.lookupClassMember(name)
705 : interface.lookupInterfaceMember(name);
706 }
707
708 // Compute the access of [name] on [type]. This function takes the special
709 // 'call' method into account.
710 ElementAccess getAccess(Name name,
711 DartType unaliasedBound, InterfaceType interface) {
712 MemberSignature member = lookupMemberSignature(memberName, interface);
713 if (member != null) {
714 return new MemberAccess(member);
715 }
716 if (name == const PublicName('call')) {
717 if (unaliasedBound.isFunctionType) {
718 // This is an access the implicit 'call' method of a function type.
719 return new FunctionCallAccess(receiverElement, unaliasedBound);
720 }
721 if (types.isSubtype(interface, compiler.functionClass.rawType)) {
722 // This is an access of the special 'call' method implicitly defined
723 // on 'Function'. This method can be called with any arguments, which
724 // we ensure by giving it the type 'dynamic'.
725 return new FunctionCallAccess(null, const DynamicType());
726 }
727 }
728 return null;
729 }
730
731 DartType unaliasedBound = computeUnaliasedBound(receiverType);
732 if (unaliasedBound.treatAsDynamic) {
733 return new DynamicAccess();
734 }
735 InterfaceType interface = computeInterfaceType(unaliasedBound);
736 ElementAccess access = getAccess(memberName, unaliasedBound, interface);
737 if (access != null) {
738 return access;
739 }
740 if (receiverElement != null &&
741 (receiverElement.isVariable || receiverElement.isParameter)) {
742 Link<TypePromotion> typePromotions = typePromotionsMap[receiverElement];
743 if (typePromotions != null) {
744 while (!typePromotions.isEmpty) {
745 TypePromotion typePromotion = typePromotions.head;
746 if (!typePromotion.isValid) {
747 DartType unaliasedBound = computeUnaliasedBound(typePromotion.type);
748 if (!unaliasedBound.treatAsDynamic) {
749 InterfaceType interface = computeInterfaceType(unaliasedBound);
750 if (getAccess(memberName, unaliasedBound, interface) != null) {
751 reportTypePromotionHint(typePromotion);
752 }
753 }
754 }
755 typePromotions = typePromotions.tail;
756 }
757 }
758 }
759 // We didn't find a member with the correct name. If this lookup is for a
760 // super or redirecting initializer, the resolver has already emitted an
761 // error message. If the target is a proxy, no warning needs to be emitted.
762 // Otherwise, try to emit the most precise warning.
763 if (!interface.element.isProxy && !analyzingInitializer) {
764 bool foundPrivateMember = false;
765 if (memberName.isPrivate) {
766 void findPrivateMember(MemberSignature member) {
767 if (memberName.isSimilarTo(member.name)) {
768 PrivateName privateName = member.name;
769 reportTypeWarning(
770 node,
771 MessageKind.PRIVATE_ACCESS,
772 {'name': name,
773 'libraryName': privateName.library.getLibraryOrScriptName()});
774 foundPrivateMember = true;
775 }
776 }
777 // TODO(johnniwinther): Avoid computation of all class members.
778 MembersCreator.computeAllClassMembers(compiler, interface.element);
779 if (lookupClassMember) {
780 interface.element.forEachClassMember(findPrivateMember);
781 } else {
782 interface.element.forEachInterfaceMember(findPrivateMember);
783 }
784
785 }
786 if (!foundPrivateMember) {
787 switch (memberKind) {
788 case MemberKind.METHOD:
789 reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND,
790 {'className': receiverType.name, 'memberName': name});
791 break;
792 case MemberKind.OPERATOR:
793 reportTypeWarning(node, MessageKind.OPERATOR_NOT_FOUND,
794 {'className': receiverType.name, 'memberName': name});
795 break;
796 case MemberKind.GETTER:
797 if (lookupMemberSignature(memberName.setter, interface) != null) {
798 // A setter is present so warn explicitly about the missing
799 // getter.
800 reportTypeWarning(node, MessageKind.GETTER_NOT_FOUND,
801 {'className': receiverType.name, 'memberName': name});
802 } else {
803 reportTypeWarning(node, MessageKind.MEMBER_NOT_FOUND,
804 {'className': receiverType.name, 'memberName': name});
805 }
806 break;
807 case MemberKind.SETTER:
808 reportTypeWarning(node, MessageKind.SETTER_NOT_FOUND,
809 {'className': receiverType.name, 'memberName': name});
810 break;
811 }
812 }
813 }
814 return const DynamicAccess();
815 }
816
817 DartType lookupMemberType(Node node, DartType type, String name,
818 MemberKind memberKind) {
819 return lookupMember(node, type, name, memberKind, null)
820 .computeType(compiler);
821 }
822
823 void analyzeArguments(Send send, Element element, DartType type,
824 [LinkBuilder<DartType> argumentTypes]) {
825 Link<Node> arguments = send.arguments;
826 DartType unaliasedType = type.unalias(compiler);
827 if (identical(unaliasedType.kind, TypeKind.FUNCTION)) {
828 bool error = false;
829 FunctionType funType = unaliasedType;
830 Iterator<DartType> parameterTypes = funType.parameterTypes.iterator;
831 Iterator<DartType> optionalParameterTypes =
832 funType.optionalParameterTypes.iterator;
833 while (!arguments.isEmpty) {
834 Node argument = arguments.head;
835 NamedArgument namedArgument = argument.asNamedArgument();
836 if (namedArgument != null) {
837 argument = namedArgument.expression;
838 String argumentName = namedArgument.name.source;
839 DartType namedParameterType =
840 funType.getNamedParameterType(argumentName);
841 if (namedParameterType == null) {
842 error = true;
843 // TODO(johnniwinther): Provide better information on the called
844 // function.
845 reportTypeWarning(argument, MessageKind.NAMED_ARGUMENT_NOT_FOUND,
846 {'argumentName': argumentName});
847
848 DartType argumentType = analyze(argument);
849 if (argumentTypes != null) argumentTypes.addLast(argumentType);
850 } else {
851 DartType argumentType = analyze(argument);
852 if (argumentTypes != null) argumentTypes.addLast(argumentType);
853 if (!checkAssignable(argument, argumentType, namedParameterType)) {
854 error = true;
855 }
856 }
857 } else {
858 if (!parameterTypes.moveNext()) {
859 if (!optionalParameterTypes.moveNext()) {
860 error = true;
861 // TODO(johnniwinther): Provide better information on the
862 // called function.
863 reportTypeWarning(argument, MessageKind.ADDITIONAL_ARGUMENT);
864
865 DartType argumentType = analyze(argument);
866 if (argumentTypes != null) argumentTypes.addLast(argumentType);
867 } else {
868 DartType argumentType = analyze(argument);
869 if (argumentTypes != null) argumentTypes.addLast(argumentType);
870 if (!checkAssignable(argument,
871 argumentType,
872 optionalParameterTypes.current)) {
873 error = true;
874 }
875 }
876 } else {
877 DartType argumentType = analyze(argument);
878 if (argumentTypes != null) argumentTypes.addLast(argumentType);
879 if (!checkAssignable(argument, argumentType,
880 parameterTypes.current)) {
881 error = true;
882 }
883 }
884 }
885 arguments = arguments.tail;
886 }
887 if (parameterTypes.moveNext()) {
888 error = true;
889 // TODO(johnniwinther): Provide better information on the called
890 // function.
891 reportTypeWarning(send, MessageKind.MISSING_ARGUMENT,
892 {'argumentType': parameterTypes.current});
893 }
894 if (error) {
895 // TODO(johnniwinther): Improve access to declaring element and handle
896 // synthesized member signatures. Currently function typed instance
897 // members provide no access to there own name.
898 if (element == null) {
899 element = type.element;
900 } else if (type.element.isTypedef) {
901 if (element != null) {
902 reportTypeInfo(element,
903 MessageKind.THIS_IS_THE_DECLARATION,
904 {'name': element.name});
905 }
906 element = type.element;
907 }
908 reportTypeInfo(element, MessageKind.THIS_IS_THE_METHOD);
909 }
910 } else {
911 while(!arguments.isEmpty) {
912 DartType argumentType = analyze(arguments.head);
913 if (argumentTypes != null) argumentTypes.addLast(argumentType);
914 arguments = arguments.tail;
915 }
916 }
917 }
918
919 // Analyze the invocation [node] of [elementAccess].
920 //
921 // If provided [argumentTypes] is filled with the argument types during
922 // analysis.
923 DartType analyzeInvocation(Send node, ElementAccess elementAccess,
924 [LinkBuilder<DartType> argumentTypes]) {
925 DartType type = elementAccess.computeType(compiler);
926 if (elementAccess.isCallable(compiler)) {
927 analyzeArguments(node, elementAccess.element, type, argumentTypes);
928 } else {
929 reportTypeWarning(node, MessageKind.NOT_CALLABLE,
930 {'elementName': elementAccess.element.name});
931 analyzeArguments(node, elementAccess.element, const DynamicType(),
932 argumentTypes);
933 }
934 type = type.unalias(compiler);
935 if (identical(type.kind, TypeKind.FUNCTION)) {
936 FunctionType funType = type;
937 return funType.returnType;
938 } else {
939 return const DynamicType();
940 }
941 }
942
943 /**
944 * Computes the [ElementAccess] for [name] on the [node] possibly using the
945 * [element] provided for [node] by the resolver.
946 */
947 ElementAccess computeAccess(Send node, String name, Element element,
948 MemberKind memberKind,
949 {bool lookupClassMember: false}) {
950 if (element != null && element.isErroneous) {
951 // An error has already been reported for this node.
952 return const DynamicAccess();
953 }
954 if (node.receiver != null) {
955 Element receiverElement = elements[node.receiver];
956 if (receiverElement != null) {
957 if (receiverElement.isPrefix) {
958 assert(invariant(node, element != null,
959 message: 'Prefixed node has no element.'));
960 return computeResolvedAccess(node, name, element, memberKind);
961 }
962 }
963 // e.foo() for some expression e.
964 DartType receiverType = analyze(node.receiver);
965 if (receiverType.treatAsDynamic || receiverType.isVoid) {
966 return const DynamicAccess();
967 }
968 TypeKind receiverKind = receiverType.kind;
969 return lookupMember(node, receiverType, name, memberKind,
970 elements[node.receiver],
971 lookupClassMember: lookupClassMember ||
972 element != null && element.isStatic);
973 } else {
974 return computeResolvedAccess(node, name, element, memberKind);
975 }
976 }
977
978 /**
979 * Computes the [ElementAccess] for [name] on the [node] using the [element]
980 * provided for [node] by the resolver.
981 */
982 ElementAccess computeResolvedAccess(Send node, String name,
983 Element element, MemberKind memberKind) {
984 if (element == null) {
985 // foo() where foo is unresolved.
986 return lookupMember(node, thisType, name, memberKind, null);
987 } else if (element.isErroneous) {
988 // foo() where foo is erroneous.
989 return const DynamicAccess();
990 } else if (element.impliesType) {
991 // The literal `Foo` where Foo is a class, a typedef, or a type variable.
992 if (elements.isTypeLiteral(node)) {
993 return new TypeLiteralAccess(elements.getTypeLiteralType(node));
994 }
995 return createResolvedAccess(node, name, element);
996 } else if (element.isClassMember) {
997 // foo() where foo is a member.
998 return lookupMember(node, thisType, name, memberKind, null,
999 lookupClassMember: element.isStatic);
1000 } else if (element.isFunction) {
1001 // foo() where foo is a method in the same class.
1002 return createResolvedAccess(node, name, element);
1003 } else if (element.isVariable ||
1004 element.isParameter ||
1005 element.isField) {
1006 // foo() where foo is a field in the same class.
1007 return createResolvedAccess(node, name, element);
1008 } else if (element.isGetter || element.isSetter) {
1009 return createResolvedAccess(node, name, element);
1010 } else {
1011 compiler.internalError(element,
1012 'Unexpected element kind ${element.kind}.');
1013 return null;
1014 }
1015 }
1016
1017 ElementAccess createResolvedAccess(Send node, String name,
1018 Element element) {
1019 checkPrivateAccess(node, element, name);
1020 return createPromotedAccess(element);
1021 }
1022
1023 ElementAccess createPromotedAccess(Element element) {
1024 if (element.isVariable || element.isParameter) {
1025 TypePromotion typePromotion = getKnownTypePromotion(element);
1026 if (typePromotion != null) {
1027 return new PromotedAccess(element, typePromotion.type);
1028 }
1029 }
1030 return new ResolvedAccess(element);
1031 }
1032
1033 /**
1034 * Computes the type of the access of [name] on the [node] possibly using the
1035 * [element] provided for [node] by the resolver.
1036 */
1037 DartType computeAccessType(Send node, String name, Element element,
1038 MemberKind memberKind,
1039 {bool lookupClassMember: false}) {
1040 DartType type =
1041 computeAccess(node, name, element, memberKind,
1042 lookupClassMember: lookupClassMember).computeType(compiler);
1043 if (type == null) {
1044 compiler.internalError(node, 'Type is null on access of $name on $node.');
1045 }
1046 return type;
1047 }
1048
1049 /// Compute a version of [shownType] that is more specific that [knownType].
1050 /// This is used to provided better hints when trying to promote a supertype
1051 /// to a raw subtype. For instance trying to promote `Iterable<int>` to `List`
1052 /// we suggest the use of `List<int>`, which would make promotion valid.
1053 DartType computeMoreSpecificType(DartType shownType,
1054 DartType knownType) {
1055 if (knownType.isInterfaceType &&
1056 shownType.isInterfaceType &&
1057 types.isSubtype(shownType.asRaw(), knownType)) {
1058 // For the comments in the block, assume the hierarchy:
1059 // class A<T, V> {}
1060 // class B<S, U> extends A<S, int> {}
1061 // and a promotion from a [knownType] of `A<double, int>` to a
1062 // [shownType] of `B`.
1063 InterfaceType knownInterfaceType = knownType;
1064 ClassElement shownClass = shownType.element;
1065
1066 // Compute `B<double, dynamic>` as the subtype of `A<double, int>` using
1067 // the relation between `A<S, int>` and `A<double, int>`.
1068 MoreSpecificSubtypeVisitor visitor =
1069 new MoreSpecificSubtypeVisitor(compiler);
1070 InterfaceType shownTypeGeneric = visitor.computeMoreSpecific(
1071 shownClass, knownInterfaceType);
1072
1073 if (shownTypeGeneric != null &&
1074 types.isMoreSpecific(shownTypeGeneric, knownType)) {
1075 // This should be the case but we double-check.
1076 // TODO(johnniwinther): Ensure that we don't suggest malbounded types.
1077 return shownTypeGeneric;
1078 }
1079 }
1080 return null;
1081
1082 }
1083
1084 DartType visitSend(Send node) {
1085 if (elements.isAssert(node)) {
1086 return analyzeInvocation(node, const AssertAccess());
1087 }
1088
1089 Element element = elements[node];
1090
1091 if (element != null && element.isConstructor) {
1092 DartType receiverType;
1093 if (node.receiver != null) {
1094 receiverType = analyze(node.receiver);
1095 } else if (node.selector.isSuper()) {
1096 // TODO(johnniwinther): Lookup super-member in class members.
1097 receiverType = superType;
1098 } else {
1099 assert(node.selector.isThis());
1100 receiverType = thisType;
1101 }
1102 DartType constructorType = computeConstructorType(element, receiverType);
1103 analyzeArguments(node, element, constructorType);
1104 return const DynamicType();
1105 }
1106
1107 if (Elements.isClosureSend(node, element)) {
1108 if (element != null) {
1109 // foo() where foo is a local or a parameter.
1110 return analyzeInvocation(node, createPromotedAccess(element));
1111 } else {
1112 // exp() where exp is some complex expression like (o) or foo().
1113 DartType type = analyze(node.selector);
1114 return analyzeInvocation(node, new TypeAccess(type));
1115 }
1116 }
1117
1118 Identifier selector = node.selector.asIdentifier();
1119 String name = selector.source;
1120
1121 if (node.isOperator && identical(name, 'is')) {
1122 analyze(node.receiver);
1123 if (!node.isIsNotCheck) {
1124 Element variable = elements[node.receiver];
1125 if (variable == null) {
1126 // Look for the variable element within parenthesized expressions.
1127 ParenthesizedExpression parentheses =
1128 node.receiver.asParenthesizedExpression();
1129 while (parentheses != null) {
1130 variable = elements[parentheses.expression];
1131 if (variable != null) break;
1132 parentheses = parentheses.expression.asParenthesizedExpression();
1133 }
1134 }
1135
1136 if (variable != null &&
1137 (variable.isVariable || variable.isParameter)) {
1138 DartType knownType = getKnownType(variable);
1139 if (!knownType.isDynamic) {
1140 DartType shownType = elements.getType(node.arguments.head);
1141 TypePromotion typePromotion =
1142 new TypePromotion(node, variable, shownType);
1143 if (!types.isMoreSpecific(shownType, knownType)) {
1144 String variableName = variable.name;
1145 if (!types.isSubtype(shownType, knownType)) {
1146 typePromotion.addHint(node,
1147 MessageKind.NOT_MORE_SPECIFIC_SUBTYPE,
1148 {'variableName': variableName,
1149 'shownType': shownType,
1150 'knownType': knownType});
1151 } else {
1152 DartType shownTypeSuggestion =
1153 computeMoreSpecificType(shownType, knownType);
1154 if (shownTypeSuggestion != null) {
1155 typePromotion.addHint(node,
1156 MessageKind.NOT_MORE_SPECIFIC_SUGGESTION,
1157 {'variableName': variableName,
1158 'shownType': shownType,
1159 'shownTypeSuggestion': shownTypeSuggestion,
1160 'knownType': knownType});
1161 } else {
1162 typePromotion.addHint(node,
1163 MessageKind.NOT_MORE_SPECIFIC,
1164 {'variableName': variableName,
1165 'shownType': shownType,
1166 'knownType': knownType});
1167 }
1168 }
1169 }
1170 showTypePromotion(node, typePromotion);
1171 }
1172 }
1173 }
1174 return boolType;
1175 } if (node.isOperator && identical(name, 'as')) {
1176 analyze(node.receiver);
1177 return elements.getType(node.arguments.head);
1178 } else if (node.isOperator) {
1179 final Node receiver = node.receiver;
1180 final DartType receiverType = analyze(receiver);
1181 if (identical(name, '==') || identical(name, '!=')
1182 // TODO(johnniwinther): Remove these.
1183 || identical(name, '===') || identical(name, '!==')) {
1184 // Analyze argument.
1185 analyze(node.arguments.head);
1186 return boolType;
1187 } else if (identical(name, '||')) {
1188 checkAssignable(receiver, receiverType, boolType);
1189 final Node argument = node.arguments.head;
1190 final DartType argumentType = analyze(argument);
1191 checkAssignable(argument, argumentType, boolType);
1192 return boolType;
1193 } else if (identical(name, '&&')) {
1194 checkAssignable(receiver, receiverType, boolType);
1195 final Node argument = node.arguments.head;
1196
1197 final DartType argumentType =
1198 analyzeInPromotedContext(receiver, argument);
1199
1200 reshowTypePromotions(node, receiver, argument);
1201
1202 checkAssignable(argument, argumentType, boolType);
1203 return boolType;
1204 } else if (identical(name, '!')) {
1205 checkAssignable(receiver, receiverType, boolType);
1206 return boolType;
1207 } else if (identical(name, '?')) {
1208 return boolType;
1209 }
1210 String operatorName = selector.source;
1211 if (identical(name, '-') && node.arguments.isEmpty) {
1212 operatorName = 'unary-';
1213 }
1214 assert(invariant(node,
1215 identical(name, '+') || identical(name, '=') ||
1216 identical(name, '-') || identical(name, '*') ||
1217 identical(name, '/') || identical(name, '%') ||
1218 identical(name, '~/') || identical(name, '|') ||
1219 identical(name, '&') || identical(name, '^') ||
1220 identical(name, '~')|| identical(name, '<<') ||
1221 identical(name, '>>') ||
1222 identical(name, '<') || identical(name, '>') ||
1223 identical(name, '<=') || identical(name, '>=') ||
1224 identical(name, '[]'),
1225 message: 'Unexpected operator $name'));
1226
1227 // TODO(karlklose): handle `void` in expression context by calling
1228 // [analyzeNonVoid] instead of [analyze].
1229 ElementAccess access = receiverType.isVoid ? const DynamicAccess()
1230 : lookupMember(node, receiverType, operatorName,
1231 MemberKind.OPERATOR, null);
1232 LinkBuilder<DartType> argumentTypesBuilder = new LinkBuilder<DartType>();
1233 DartType resultType =
1234 analyzeInvocation(node, access, argumentTypesBuilder);
1235 if (identical(receiverType.element, compiler.intClass)) {
1236 if (identical(name, '+') ||
1237 identical(operatorName, '-') ||
1238 identical(name, '*') ||
1239 identical(name, '%')) {
1240 DartType argumentType = argumentTypesBuilder.toLink().head;
1241 if (identical(argumentType.element, compiler.intClass)) {
1242 return intType;
1243 } else if (identical(argumentType.element, compiler.doubleClass)) {
1244 return doubleType;
1245 }
1246 }
1247 }
1248 return resultType;
1249 } else if (node.isPropertyAccess) {
1250 ElementAccess access =
1251 computeAccess(node, selector.source, element, MemberKind.GETTER);
1252 return access.computeType(compiler);
1253 } else if (node.isFunctionObjectInvocation) {
1254 return unhandledExpression();
1255 } else {
1256 ElementAccess access =
1257 computeAccess(node, selector.source, element, MemberKind.METHOD);
1258 return analyzeInvocation(node, access);
1259 }
1260 }
1261
1262 /// Returns the first type in the list or [:dynamic:] if the list is empty.
1263 DartType firstType(List<DartType> list) {
1264 return list.isEmpty ? const DynamicType() : list.first;
1265 }
1266
1267 /**
1268 * Returns the second type in the list or [:dynamic:] if the list is too
1269 * short.
1270 */
1271 DartType secondType(List<DartType> list) {
1272 return list.length < 2 ? const DynamicType() : list[1];
1273 }
1274
1275 /**
1276 * Checks [: target o= value :] for some operator o, and returns the type
1277 * of the result. This method also handles increment/decrement expressions
1278 * like [: target++ :].
1279 */
1280 DartType checkAssignmentOperator(SendSet node,
1281 String operatorName,
1282 Node valueNode,
1283 DartType value) {
1284 assert(invariant(node, !node.isIndex));
1285 Element setterElement = elements[node];
1286 Element getterElement = elements[node.selector];
1287 Identifier selector = node.selector;
1288 DartType getter = computeAccessType(
1289 node, selector.source, getterElement, MemberKind.GETTER);
1290 DartType setter = computeAccessType(
1291 node, selector.source, setterElement, MemberKind.SETTER);
1292 // [operator] is the type of operator+ or operator- on [target].
1293 DartType operator =
1294 lookupMemberType(node, getter, operatorName, MemberKind.OPERATOR);
1295 if (operator is FunctionType) {
1296 FunctionType operatorType = operator;
1297 // [result] is the type of target o value.
1298 DartType result = operatorType.returnType;
1299 DartType operatorArgument = firstType(operatorType.parameterTypes);
1300 // Check target o value.
1301 bool validValue = checkAssignable(valueNode, value, operatorArgument);
1302 if (validValue || !(node.isPrefix || node.isPostfix)) {
1303 // Check target = result.
1304 checkAssignable(node.assignmentOperator, result, setter);
1305 }
1306 return node.isPostfix ? getter : result;
1307 }
1308 return const DynamicType();
1309 }
1310
1311 /**
1312 * Checks [: base[key] o= value :] for some operator o, and returns the type
1313 * of the result. This method also handles increment/decrement expressions
1314 * like [: base[key]++ :].
1315 */
1316 DartType checkIndexAssignmentOperator(SendSet node,
1317 String operatorName,
1318 Node valueNode,
1319 DartType value) {
1320 assert(invariant(node, node.isIndex));
1321 final DartType base = analyze(node.receiver);
1322 final Node keyNode = node.arguments.head;
1323 final DartType key = analyze(keyNode);
1324
1325 // [indexGet] is the type of operator[] on [base].
1326 DartType indexGet = lookupMemberType(
1327 node, base, '[]', MemberKind.OPERATOR);
1328 if (indexGet is FunctionType) {
1329 FunctionType indexGetType = indexGet;
1330 DartType indexGetKey = firstType(indexGetType.parameterTypes);
1331 // Check base[key].
1332 bool validKey = checkAssignable(keyNode, key, indexGetKey);
1333
1334 // [element] is the type of base[key].
1335 DartType element = indexGetType.returnType;
1336 // [operator] is the type of operator o on [element].
1337 DartType operator = lookupMemberType(
1338 node, element, operatorName, MemberKind.OPERATOR);
1339 if (operator is FunctionType) {
1340 FunctionType operatorType = operator;
1341
1342 // Check base[key] o value.
1343 DartType operatorArgument = firstType(operatorType.parameterTypes);
1344 bool validValue = checkAssignable(valueNode, value, operatorArgument);
1345
1346 // [result] is the type of base[key] o value.
1347 DartType result = operatorType.returnType;
1348
1349 // [indexSet] is the type of operator[]= on [base].
1350 DartType indexSet = lookupMemberType(
1351 node, base, '[]=', MemberKind.OPERATOR);
1352 if (indexSet is FunctionType) {
1353 FunctionType indexSetType = indexSet;
1354 DartType indexSetKey = firstType(indexSetType.parameterTypes);
1355 DartType indexSetValue = secondType(indexSetType.parameterTypes);
1356
1357 if (validKey || indexGetKey != indexSetKey) {
1358 // Only check base[key] on []= if base[key] was valid for [] or
1359 // if the key types differ.
1360 checkAssignable(keyNode, key, indexSetKey);
1361 }
1362 // Check base[key] = result
1363 if (validValue || !(node.isPrefix || node.isPostfix)) {
1364 checkAssignable(node.assignmentOperator, result, indexSetValue);
1365 }
1366 }
1367 return node.isPostfix ? element : result;
1368 }
1369 }
1370 return const DynamicType();
1371 }
1372
1373 visitSendSet(SendSet node) {
1374 Element element = elements[node];
1375 Identifier selector = node.selector;
1376 final name = node.assignmentOperator.source;
1377 if (identical(name, '=')) {
1378 // e1 = value
1379 if (node.isIndex) {
1380 // base[key] = value
1381 final DartType base = analyze(node.receiver);
1382 final Node keyNode = node.arguments.head;
1383 final DartType key = analyze(keyNode);
1384 final Node valueNode = node.arguments.tail.head;
1385 final DartType value = analyze(valueNode);
1386 DartType indexSet = lookupMemberType(
1387 node, base, '[]=', MemberKind.OPERATOR);
1388 if (indexSet is FunctionType) {
1389 FunctionType indexSetType = indexSet;
1390 DartType indexSetKey = firstType(indexSetType.parameterTypes);
1391 checkAssignable(keyNode, key, indexSetKey);
1392 DartType indexSetValue = secondType(indexSetType.parameterTypes);
1393 checkAssignable(node.assignmentOperator, value, indexSetValue);
1394 }
1395 return value;
1396 } else {
1397 // target = value
1398 DartType target;
1399 if (analyzingInitializer) {
1400 // Field declaration `Foo target = value;` or initializer
1401 // `this.target = value`. Lookup the getter `target` in the class
1402 // members.
1403 target = computeAccessType(node, selector.source, element,
1404 MemberKind.GETTER, lookupClassMember: true);
1405 } else {
1406 // Normal assignment `target = value`.
1407 target = computeAccessType(
1408 node, selector.source, element, MemberKind.SETTER);
1409 }
1410 final Node valueNode = node.arguments.head;
1411 final DartType value = analyze(valueNode);
1412 checkAssignable(node.assignmentOperator, value, target);
1413 return value;
1414 }
1415 } else if (identical(name, '++') || identical(name, '--')) {
1416 // e++ or e--
1417 String operatorName = identical(name, '++') ? '+' : '-';
1418 if (node.isIndex) {
1419 // base[key]++, base[key]--, ++base[key], or --base[key]
1420 return checkIndexAssignmentOperator(
1421 node, operatorName, node.assignmentOperator, intType);
1422 } else {
1423 // target++, target--, ++target, or --target
1424 return checkAssignmentOperator(
1425 node, operatorName, node.assignmentOperator, intType);
1426 }
1427 } else {
1428 // e1 o= e2 for some operator o.
1429 String operatorName;
1430 switch (name) {
1431 case '+=': operatorName = '+'; break;
1432 case '-=': operatorName = '-'; break;
1433 case '*=': operatorName = '*'; break;
1434 case '/=': operatorName = '/'; break;
1435 case '%=': operatorName = '%'; break;
1436 case '~/=': operatorName = '~/'; break;
1437 case '&=': operatorName = '&'; break;
1438 case '|=': operatorName = '|'; break;
1439 case '^=': operatorName = '^'; break;
1440 case '<<=': operatorName = '<<'; break;
1441 case '>>=': operatorName = '>>'; break;
1442 default:
1443 compiler.internalError(node, 'Unexpected assignment operator $name.');
1444 }
1445 if (node.isIndex) {
1446 // base[key] o= value for some operator o.
1447 final Node valueNode = node.arguments.tail.head;
1448 final DartType value = analyze(valueNode);
1449 return checkIndexAssignmentOperator(
1450 node, operatorName, valueNode, value);
1451 } else {
1452 // target o= value for some operator o.
1453 final Node valueNode = node.arguments.head;
1454 final DartType value = analyze(valueNode);
1455 return checkAssignmentOperator(node, operatorName, valueNode, value);
1456 }
1457 }
1458 }
1459
1460 DartType visitLiteralInt(LiteralInt node) {
1461 return intType;
1462 }
1463
1464 DartType visitLiteralDouble(LiteralDouble node) {
1465 return doubleType;
1466 }
1467
1468 DartType visitLiteralBool(LiteralBool node) {
1469 return boolType;
1470 }
1471
1472 DartType visitLiteralString(LiteralString node) {
1473 return stringType;
1474 }
1475
1476 DartType visitStringJuxtaposition(StringJuxtaposition node) {
1477 analyze(node.first);
1478 analyze(node.second);
1479 return stringType;
1480 }
1481
1482 DartType visitLiteralNull(LiteralNull node) {
1483 return const DynamicType();
1484 }
1485
1486 DartType visitLiteralSymbol(LiteralSymbol node) {
1487 return compiler.symbolClass.rawType;
1488 }
1489
1490 DartType computeConstructorType(Element constructor, DartType type) {
1491 if (Elements.isUnresolved(constructor)) return const DynamicType();
1492 DartType constructorType = constructor.computeType(compiler);
1493 if (identical(type.kind, TypeKind.INTERFACE)) {
1494 if (constructor.isSynthesized) {
1495 // TODO(johnniwinther): Remove this when synthesized constructors handle
1496 // type variables correctly.
1497 InterfaceType interfaceType = type;
1498 ClassElement receiverElement = interfaceType.element;
1499 while (receiverElement.isMixinApplication) {
1500 receiverElement = receiverElement.supertype.element;
1501 }
1502 constructorType = constructorType.substByContext(
1503 interfaceType.asInstanceOf(receiverElement));
1504 } else {
1505 constructorType = constructorType.substByContext(type);
1506 }
1507 }
1508 return constructorType;
1509 }
1510
1511 DartType visitNewExpression(NewExpression node) {
1512 Element element = elements[node.send];
1513 if (Elements.isUnresolved(element)) return const DynamicType();
1514
1515 checkPrivateAccess(node, element, element.name);
1516
1517 DartType newType = elements.getType(node);
1518 DartType constructorType = computeConstructorType(element, newType);
1519 analyzeArguments(node.send, element, constructorType);
1520 return newType;
1521 }
1522
1523 DartType visitLiteralList(LiteralList node) {
1524 InterfaceType listType = elements.getType(node);
1525 DartType listElementType = firstType(listType.typeArguments);
1526 for (Link<Node> link = node.elements.nodes;
1527 !link.isEmpty;
1528 link = link.tail) {
1529 Node element = link.head;
1530 DartType elementType = analyze(element);
1531 checkAssignable(element, elementType, listElementType,
1532 isConst: node.isConst);
1533 }
1534 return listType;
1535 }
1536
1537 DartType visitNodeList(NodeList node) {
1538 for (Link<Node> link = node.nodes; !link.isEmpty; link = link.tail) {
1539 analyze(link.head, inInitializer: analyzingInitializer);
1540 }
1541 return const StatementType();
1542 }
1543
1544 DartType visitRedirectingFactoryBody(RedirectingFactoryBody node) {
1545 // TODO(lrn): Typecheck the body. It must refer to the constructor
1546 // of a subtype.
1547 return const StatementType();
1548 }
1549
1550 DartType visitRethrow(Rethrow node) {
1551 return const StatementType();
1552 }
1553
1554 /** Dart Programming Language Specification: 11.10 Return */
1555 DartType visitReturn(Return node) {
1556 if (identical(node.beginToken.stringValue, 'native')) {
1557 return const StatementType();
1558 }
1559
1560 final expression = node.expression;
1561 final isVoidFunction = expectedReturnType.isVoid;
1562
1563 // Executing a return statement return e; [...] It is a static type warning
1564 // if the type of e may not be assigned to the declared return type of the
1565 // immediately enclosing function.
1566 if (expression != null) {
1567 final expressionType = analyze(expression);
1568 Element element = elements.analyzedElement;
1569 if (element != null && element.isGenerativeConstructor) {
1570 // The resolver already emitted an error for this expression.
1571 } else if (isVoidFunction
1572 && !types.isAssignable(expressionType, const VoidType())) {
1573 reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID);
1574 } else {
1575 checkAssignable(expression, expressionType, expectedReturnType);
1576 }
1577
1578 // Let f be the function immediately enclosing a return statement of the
1579 // form 'return;' It is a static warning if both of the following conditions
1580 // hold:
1581 // - f is not a generative constructor.
1582 // - The return type of f may not be assigned to void.
1583 } else if (!types.isAssignable(expectedReturnType, const VoidType())) {
1584 reportTypeWarning(node, MessageKind.RETURN_NOTHING,
1585 {'returnType': expectedReturnType});
1586 }
1587 return const StatementType();
1588 }
1589
1590 DartType visitThrow(Throw node) {
1591 // TODO(johnniwinther): Handle reachability.
1592 analyze(node.expression);
1593 return const DynamicType();
1594 }
1595
1596 DartType visitAwait(Await node) {
1597 DartType expressionType = analyze(node.expression);
1598 DartType resultType = expressionType;
1599 if (expressionType is InterfaceType) {
1600 InterfaceType futureType =
1601 expressionType.asInstanceOf(compiler.futureClass);
1602 if (futureType != null) {
1603 resultType = futureType.typeArguments.first;
1604 }
1605 }
1606 return resultType;
1607 }
1608
1609 DartType visitYield(Yield node) {
1610 DartType resultType = analyze(node.expression);
1611 if (!node.hasStar) {
1612 if (currentAsyncMarker.isAsync) {
1613 resultType =
1614 compiler.streamClass.thisType.createInstantiation(
1615 <DartType>[resultType]);
1616 } else {
1617 resultType =
1618 compiler.iterableClass.thisType.createInstantiation(
1619 <DartType>[resultType]);
1620 }
1621 }
1622 checkAssignable(node, resultType, expectedReturnType);
1623 return const StatementType();
1624 }
1625
1626 DartType visitTypeAnnotation(TypeAnnotation node) {
1627 return elements.getType(node);
1628 }
1629
1630 DartType visitVariableDefinitions(VariableDefinitions node) {
1631 DartType type = analyzeWithDefault(node.type, const DynamicType());
1632 if (type.isVoid) {
1633 reportTypeWarning(node.type, MessageKind.VOID_VARIABLE);
1634 type = const DynamicType();
1635 }
1636 for (Link<Node> link = node.definitions.nodes; !link.isEmpty;
1637 link = link.tail) {
1638 Node definition = link.head;
1639 invariant(definition, definition is Identifier || definition is SendSet,
1640 message: 'expected identifier or initialization');
1641 if (definition is SendSet) {
1642 SendSet initialization = definition;
1643 DartType initializer = analyzeNonVoid(initialization.arguments.head);
1644 checkAssignable(initialization.assignmentOperator, initializer, type);
1645 }
1646 }
1647 return const StatementType();
1648 }
1649
1650 DartType visitWhile(While node) {
1651 checkCondition(node.condition);
1652 analyze(node.body);
1653 Expression cond = node.condition.asParenthesizedExpression().expression;
1654 return const StatementType();
1655 }
1656
1657 DartType visitParenthesizedExpression(ParenthesizedExpression node) {
1658 Expression expression = node.expression;
1659 DartType type = analyze(expression);
1660 for (TypePromotion typePromotion in getShownTypePromotionsFor(expression)) {
1661 showTypePromotion(node, typePromotion);
1662 }
1663 return type;
1664 }
1665
1666 DartType visitConditional(Conditional node) {
1667 Expression condition = node.condition;
1668 Expression thenExpression = node.thenExpression;
1669
1670 checkCondition(condition);
1671
1672 DartType thenType = analyzeInPromotedContext(condition, thenExpression);
1673
1674 DartType elseType = analyze(node.elseExpression);
1675 return compiler.types.computeLeastUpperBound(thenType, elseType);
1676 }
1677
1678 visitStringInterpolation(StringInterpolation node) {
1679 node.visitChildren(this);
1680 return stringType;
1681 }
1682
1683 visitStringInterpolationPart(StringInterpolationPart node) {
1684 node.visitChildren(this);
1685 return stringType;
1686 }
1687
1688 visitEmptyStatement(EmptyStatement node) {
1689 return const StatementType();
1690 }
1691
1692 visitBreakStatement(BreakStatement node) {
1693 return const StatementType();
1694 }
1695
1696 visitContinueStatement(ContinueStatement node) {
1697 return const StatementType();
1698 }
1699
1700 visitForIn(ForIn node) {
1701 analyze(node.expression);
1702 analyze(node.body);
1703 return const StatementType();
1704 }
1705
1706 visitLabeledStatement(LabeledStatement node) {
1707 return analyze(node.statement);
1708 }
1709
1710 visitLiteralMap(LiteralMap node) {
1711 InterfaceType mapType = elements.getType(node);
1712 DartType mapKeyType = firstType(mapType.typeArguments);
1713 DartType mapValueType = secondType(mapType.typeArguments);
1714 bool isConst = node.isConst;
1715 for (Link<Node> link = node.entries.nodes;
1716 !link.isEmpty;
1717 link = link.tail) {
1718 LiteralMapEntry entry = link.head;
1719 DartType keyType = analyze(entry.key);
1720 checkAssignable(entry.key, keyType, mapKeyType, isConst: isConst);
1721 DartType valueType = analyze(entry.value);
1722 checkAssignable(entry.value, valueType, mapValueType, isConst: isConst);
1723 }
1724 return mapType;
1725 }
1726
1727 visitNamedArgument(NamedArgument node) {
1728 // Named arguments are visited as part of analyzing invocations of
1729 // unresolved methods. For instance [: foo(a: 42); :] where 'foo' is neither
1730 // found in the enclosing scope nor through lookup on 'this' or
1731 // [: x.foo(b: 42); :] where 'foo' cannot be not found through lookup on
1732 // the static type of 'x'.
1733 return analyze(node.expression);
1734 }
1735
1736 visitSwitchStatement(SwitchStatement node) {
1737 // TODO(johnniwinther): Handle reachability based on reachability of
1738 // switch cases.
1739
1740 DartType expressionType = analyze(node.expression);
1741
1742 // Check that all the case expressions are assignable to the expression.
1743 for (SwitchCase switchCase in node.cases) {
1744 for (Node labelOrCase in switchCase.labelsAndCases) {
1745 CaseMatch caseMatch = labelOrCase.asCaseMatch();
1746 if (caseMatch == null) continue;
1747
1748 DartType caseType = analyze(caseMatch.expression);
1749 checkAssignable(caseMatch, expressionType, caseType);
1750 }
1751
1752 analyze(switchCase);
1753 }
1754
1755 return const StatementType();
1756 }
1757
1758 visitSwitchCase(SwitchCase node) {
1759 return analyze(node.statements);
1760 }
1761
1762 visitTryStatement(TryStatement node) {
1763 // TODO(johnniwinther): Use reachability information of try-block,
1764 // catch-blocks and finally-block to compute the whether the try statement
1765 // is returning.
1766 analyze(node.tryBlock);
1767 for (CatchBlock catchBlock in node.catchBlocks) {
1768 analyze(catchBlock);
1769 }
1770 analyzeWithDefault(node.finallyBlock, null);
1771 return const StatementType();
1772 }
1773
1774 visitCatchBlock(CatchBlock node) {
1775 return analyze(node.block);
1776 }
1777
1778 visitTypedef(Typedef node) {
1779 // Do not typecheck [Typedef] nodes.
1780 }
1781
1782 visitNode(Node node) {
1783 compiler.internalError(node,
1784 'Unexpected node ${node.getObjectDescription()} in the type checker.');
1785 }
1786 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/tree/unparser.dart ('k') | pkg/compiler/lib/src/types/container_type_mask.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698