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

Side by Side Diff: frog/leg/typechecker.dart

Issue 9873021: Move frog/leg to lib/compiler/implementation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 9 months 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
« no previous file with comments | « frog/leg/tree_validator.dart ('k') | frog/leg/universe.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 class TypeCheckerTask extends CompilerTask {
6 TypeCheckerTask(Compiler compiler) : super(compiler);
7 String get name() => "Type checker";
8
9 static final bool LOG_FAILURES = false;
10
11 void check(Node tree, TreeElements elements) {
12 measure(() {
13 Visitor visitor =
14 new TypeCheckerVisitor(compiler, elements, compiler.types);
15 try {
16 tree.accept(visitor);
17 } catch (CancelTypeCheckException e) {
18 if (LOG_FAILURES) {
19 // Do not warn about unimplemented features; log message instead.
20 compiler.log("'${e.node}': ${e.reason}");
21 }
22 }
23 });
24 }
25 }
26
27 interface Type {
28 SourceString get name();
29 Element get element();
30 }
31
32 class TypeVariableType implements Type {
33 final SourceString name;
34 Element element;
35 TypeVariableType(this.name, [this.element]);
36 }
37
38 /**
39 * A statement type tracks whether a statement returns or may return.
40 */
41 class StatementType implements Type {
42 final String stringName;
43 Element get element() => null;
44
45 SourceString get name() => new SourceString(stringName);
46
47 const StatementType(this.stringName);
48
49 static final RETURNING = const StatementType('<returning>');
50 static final NOT_RETURNING = const StatementType('<not returning>');
51 static final MAYBE_RETURNING = const StatementType('<maybe returning>');
52
53 /** Combine the information about two control-flow edges that are joined. */
54 StatementType join(StatementType other) {
55 return (this === other) ? this : MAYBE_RETURNING;
56 }
57
58 String toString() => stringName;
59 }
60
61 class SimpleType implements Type {
62 final SourceString name;
63 final Element element;
64
65 const SimpleType(SourceString this.name, Element this.element);
66
67 String toString() => name.slowToString();
68 }
69
70 class FunctionType implements Type {
71 final Element element;
72 final Type returnType;
73 final Link<Type> parameterTypes;
74
75 const FunctionType(Type this.returnType, Link<Type> this.parameterTypes,
76 Element this.element);
77
78 toString() {
79 StringBuffer sb = new StringBuffer();
80 bool first = true;
81 sb.add('(');
82 parameterTypes.printOn(sb, ', ');
83 sb.add(') -> ${returnType}');
84 return sb.toString();
85 }
86
87 SourceString get name() => const SourceString('Function');
88 }
89
90 class Types {
91 static final VOID = const SourceString('void');
92 static final INT = const SourceString('int');
93 static final DOUBLE = const SourceString('double');
94 static final DYNAMIC = const SourceString('Dynamic');
95 static final STRING = const SourceString('String');
96 static final BOOL = const SourceString('bool');
97 static final OBJECT = const SourceString('Object');
98 static final LIST = const SourceString('List');
99
100 final SimpleType voidType;
101 final SimpleType dynamicType;
102
103 Types() : this.with(new LibraryElement(new Script(null, null)));
104 Types.with(LibraryElement library)
105 : voidType = new SimpleType(VOID, new ClassElement(VOID, library)),
106 dynamicType = new SimpleType(DYNAMIC, new ClassElement(DYNAMIC, library));
107
108 Type lookup(SourceString s) {
109 if (VOID == s) {
110 return voidType;
111 } else if (DYNAMIC == s || s.stringValue === 'var') {
112 return dynamicType;
113 }
114 return null;
115 }
116
117 /** Returns true if t is a subtype of s */
118 bool isSubtype(Type t, Type s) {
119 if (t === s || t === dynamicType || s === dynamicType ||
120 s.name == OBJECT) return true;
121 if (t is SimpleType) {
122 if (s is !SimpleType) return false;
123 ClassElement tc = t.element;
124 for (Link<Type> supertypes = tc.allSupertypes;
125 supertypes != null && !supertypes.isEmpty();
126 supertypes = supertypes.tail) {
127 Type supertype = supertypes.head;
128 if (supertype.element === s.element) return true;
129 }
130 return false;
131 } else if (t is FunctionType) {
132 if (s is !FunctionType) return false;
133 FunctionType tf = t;
134 FunctionType sf = s;
135 Link<Type> tps = tf.parameterTypes;
136 Link<Type> sps = sf.parameterTypes;
137 while (!tps.isEmpty() && !sps.isEmpty()) {
138 if (!isAssignable(tps.head, sps.head)) return false;
139 tps = tps.tail;
140 sps = sps.tail;
141 }
142 if (!tps.isEmpty() || !sps.isEmpty()) return false;
143 if (!isAssignable(sf.returnType, tf.returnType)) return false;
144 return true;
145 } else {
146 throw 'internal error: unknown type kind';
147 }
148 }
149
150 bool isAssignable(Type r, Type s) {
151 return isSubtype(r, s) || isSubtype(s, r);
152 }
153 }
154
155 class CancelTypeCheckException {
156 final Node node;
157 final String reason;
158
159 CancelTypeCheckException(this.node, this.reason);
160 }
161
162 Type lookupType(SourceString name, Compiler compiler, types) {
163 Type t = types.lookup(name);
164 if (t !== null) return t;
165 Element element = compiler.coreLibrary.find(name);
166 if (element !== null && element.kind === ElementKind.CLASS) {
167 return element.computeType(compiler);
168 }
169 return null;
170 }
171
172 class TypeCheckerVisitor implements Visitor<Type> {
173 final Compiler compiler;
174 final TreeElements elements;
175 Node lastSeenNode;
176 final Types types;
177
178 Type expectedReturnType;
179 ClassElement currentClass;
180
181 Type intType;
182 Type doubleType;
183 Type boolType;
184 Type stringType;
185 Type objectType;
186 Type listType;
187
188 TypeCheckerVisitor(Compiler this.compiler, TreeElements this.elements,
189 Types this.types) {
190 intType = lookupType(Types.INT, compiler, types);
191 doubleType = lookupType(Types.DOUBLE, compiler, types);
192 boolType = lookupType(Types.BOOL, compiler, types);
193 stringType = lookupType(Types.STRING, compiler, types);
194 objectType = lookupType(Types.OBJECT, compiler, types);
195 listType = lookupType(Types.LIST, compiler, types);
196 }
197
198 Type fail(node, [reason]) {
199 String message = 'cannot type-check';
200 if (reason !== null) {
201 message = '$message: $reason';
202 }
203 throw new CancelTypeCheckException(node, message);
204 }
205
206 reportTypeWarning(Node node, MessageKind kind, [List arguments = const []]) {
207 compiler.reportWarning(node, new TypeWarning(kind, arguments));
208 }
209
210 Type analyzeNonVoid(Node node) {
211 Type type = analyze(node);
212 if (type == types.voidType) {
213 reportTypeWarning(node, MessageKind.VOID_EXPRESSION);
214 }
215 return type;
216 }
217
218 Type analyzeWithDefault(Node node, Type defaultValue) {
219 return node !== null ? analyze(node) : defaultValue;
220 }
221
222 Type analyze(Node node) {
223 if (node == null) {
224 final String error = 'internal error: unexpected node: null';
225 if (lastSeenNode != null) {
226 fail(null, error);
227 } else {
228 compiler.cancel(error);
229 }
230 } else {
231 lastSeenNode = node;
232 }
233 Type result = node.accept(this);
234 // TODO(karlklose): record type?
235 if (result === null) {
236 fail(node, 'internal error: type is null');
237 }
238 return result;
239 }
240
241 /**
242 * Check if a value of type t can be assigned to a variable,
243 * parameter or return value of type s.
244 */
245 checkAssignable(Node node, Type s, Type t) {
246 if (!types.isAssignable(s, t)) {
247 reportTypeWarning(node, MessageKind.NOT_ASSIGNABLE, [s, t]);
248 }
249 }
250
251 checkCondition(Expression condition) {
252 checkAssignable(condition, boolType, analyze(condition));
253 }
254
255 Type visitBlock(Block node) {
256 return analyze(node.statements);
257 }
258
259 Type visitClassNode(ClassNode node) {
260 fail(node);
261 }
262
263 Type visitDoWhile(DoWhile node) {
264 StatementType bodyType = analyze(node.body);
265 checkCondition(node.condition);
266 return bodyType.join(StatementType.NOT_RETURNING);
267 }
268
269 Type visitExpressionStatement(ExpressionStatement node) {
270 analyze(node.expression);
271 return StatementType.NOT_RETURNING;
272 }
273
274 /** Dart Programming Language Specification: 11.5.1 For Loop */
275 Type visitFor(For node) {
276 analyzeWithDefault(node.initializer, StatementType.NOT_RETURNING);
277 checkCondition(node.condition);
278 analyzeWithDefault(node.update, StatementType.NOT_RETURNING);
279 StatementType bodyType = analyze(node.body);
280 return bodyType.join(StatementType.NOT_RETURNING);
281 }
282
283 Type visitFunctionDeclaration(FunctionDeclaration node) {
284 analyze(node.function);
285 return StatementType.NOT_RETURNING;
286 }
287
288 Type visitFunctionExpression(FunctionExpression node) {
289 Type type;
290 Type returnType;
291 Type previousType;
292 final FunctionElement element = elements[node];
293 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR ||
294 element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
295 type = types.dynamicType;
296 returnType = types.voidType;
297 } else {
298 FunctionType functionType = computeType(element);
299 returnType = functionType.returnType;
300 type = functionType;
301 }
302 Type previous = expectedReturnType;
303 expectedReturnType = returnType;
304 if (element.isMember()) currentClass = element.enclosingElement;
305 StatementType bodyType = analyze(node.body);
306 if (returnType != types.voidType && returnType != types.dynamicType
307 && bodyType != StatementType.RETURNING) {
308 MessageKind kind;
309 if (bodyType == StatementType.MAYBE_RETURNING) {
310 kind = MessageKind.MAYBE_MISSING_RETURN;
311 } else {
312 kind = MessageKind.MISSING_RETURN;
313 }
314 reportTypeWarning(node.name, kind);
315 }
316 expectedReturnType = previous;
317 return type;
318 }
319
320 Type visitIdentifier(Identifier node) {
321 if (node.isThis()) {
322 return currentClass.computeType(compiler);
323 } else {
324 fail(node, 'internal error: unexpected identifier');
325 }
326 }
327
328 Type visitIf(If node) {
329 checkCondition(node.condition);
330 StatementType thenType = analyze(node.thenPart);
331 StatementType elseType = node.hasElsePart ? analyze(node.elsePart)
332 : StatementType.NOT_RETURNING;
333 return thenType.join(elseType);
334 }
335
336 Type visitLoop(Loop node) {
337 fail(node, 'internal error');
338 }
339
340 Type lookupMethodType(Node node, ClassElement classElement,
341 SourceString name) {
342 Element member = classElement.lookupLocalMember(name);
343 if (member === null) {
344 classElement.ensureResolved(compiler);
345 for (Link<Type> supertypes = classElement.allSupertypes;
346 !supertypes.isEmpty();
347 supertypes = supertypes.tail) {
348 ClassElement lookupTarget = supertypes.head.element;
349 member = lookupTarget.lookupLocalMember(name);
350 if (member !== null) return computeType(member);
351 }
352 }
353 if (member !== null && member.kind == ElementKind.FUNCTION) {
354 return computeType(member);
355 }
356 reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND,
357 [classElement.name, name]);
358 return types.dynamicType;
359 }
360
361 Link<Type> analyzeArguments(Link<Node> arguments) {
362 LinkBuilder<Type> builder = new LinkBuilder<Type>();
363 while(!arguments.isEmpty()) {
364 builder.addLast(analyze(arguments.head));
365 arguments = arguments.tail;
366 }
367 return builder.toLink();
368 }
369
370 Type visitSend(Send node) {
371 if (Elements.isClosureSend(node, elements)) {
372 // TODO(karlklose): Finish implementation.
373 return types.dynamicType;
374 }
375
376 Identifier selector = node.selector.asIdentifier();
377 String name = selector.source.stringValue;
378
379 if (node.isOperator && name === 'is') {
380 analyze(node.receiver);
381 return boolType;
382 } else if (node.isOperator) {
383 final Node firstArgument = node.receiver;
384 final Type firstArgumentType = analyze(node.receiver);
385 final arguments = node.arguments;
386 final Node secondArgument = arguments.isEmpty() ? null : arguments.head;
387 final Type secondArgumentType = analyzeWithDefault(secondArgument, null);
388
389 if (name === '+' || name === '=' || name === '-'
390 || name === '*' || name === '/' || name === '%'
391 || name === '~/' || name === '|' || name ==='&'
392 || name === '^' || name === '~'|| name === '<<'
393 || name === '>>' || name === '[]') {
394 return types.dynamicType;
395 } else if (name === '<' || name === '>' || name === '<='
396 || name === '>=' || name === '==' || name === '!='
397 || name === '===' || name === '!==') {
398 return boolType;
399 } else if (name === '||' || name === '&&' || name === '!') {
400 checkAssignable(firstArgument, boolType, firstArgumentType);
401 if (!arguments.isEmpty()) {
402 // TODO(karlklose): check number of arguments in validator.
403 checkAssignable(secondArgument, boolType, secondArgumentType);
404 }
405 return boolType;
406 }
407 fail(selector, 'unexpected operator ${name}');
408
409 } else if (node.isPropertyAccess) {
410 if (node.receiver !== null) fail(node, 'cannot handle fields');
411 Element element = elements[node];
412 if (element === null) fail(node.selector, 'unresolved property');
413 return computeType(element);
414
415 } else if (node.isFunctionObjectInvocation) {
416 fail(node.receiver, 'function object invocation unimplemented');
417
418 } else {
419 Link<Type> argumentTypes = analyzeArguments(node.arguments);
420 FunctionType funType;
421 if (node.receiver !== null) {
422 Type receiverType = analyze(node.receiver);
423 if (receiverType === types.dynamicType) return types.dynamicType;
424 if (receiverType === null) {
425 fail(node.receiver, 'receivertype is null');
426 }
427 if (receiverType.element.kind !== ElementKind.CLASS) {
428 fail(node.receiver, 'receivertype is not a class');
429 }
430 ClassElement classElement = receiverType.element;
431 // TODO(karlklose): substitute type arguments.
432 Type memberType =
433 lookupMethodType(selector, classElement, selector.source);
434 if (memberType === types.dynamicType) return types.dynamicType;
435 if (memberType is !FunctionType) {
436 fail(node, 'can only handle function types');
437 }
438 funType = memberType;
439 } else {
440 Element element = elements[node];
441 if (element === null) {
442 fail(node, 'unresolved ${node.selector}');
443 } else if (element.kind === ElementKind.FUNCTION) {
444 funType = computeType(element);
445 } else if (element.kind === ElementKind.FOREIGN) {
446 return types.dynamicType;
447 } else {
448 fail(node, 'unexpected element kind ${element.kind}');
449 }
450 }
451 Link<Type> parameterTypes = funType.parameterTypes;
452 Link<Node> argumentNodes = node.arguments;
453 while (!argumentTypes.isEmpty() && !parameterTypes.isEmpty()) {
454 checkAssignable(argumentNodes.head, parameterTypes.head,
455 argumentTypes.head);
456 argumentTypes = argumentTypes.tail;
457 parameterTypes = parameterTypes.tail;
458 argumentNodes = argumentNodes.tail;
459 }
460 if (!argumentTypes.isEmpty()) {
461 reportTypeWarning(argumentNodes.head, MessageKind.ADDITIONAL_ARGUMENT);
462 } else if (!parameterTypes.isEmpty()) {
463 reportTypeWarning(node, MessageKind.MISSING_ARGUMENT,
464 [parameterTypes.head]);
465 }
466 return funType.returnType;
467 }
468 }
469
470 visitSendSet(SendSet node) {
471 Identifier selector = node.selector;
472 final name = node.assignmentOperator.source.stringValue;
473 if (name === '++' || name === '--') {
474 final Element element = elements[node.selector];
475 final Type receiverType = computeType(element);
476 // TODO(karlklose): this should be the return type instead of int.
477 return node.isPrefix ? intType : receiverType;
478 } else {
479 Type targetType = computeType(elements[node]);
480 Node value = node.arguments.head;
481 checkAssignable(value, targetType, analyze(value));
482 return targetType;
483 }
484 }
485
486 Type visitLiteralInt(LiteralInt node) {
487 return intType;
488 }
489
490 Type visitLiteralDouble(LiteralDouble node) {
491 return doubleType;
492 }
493
494 Type visitLiteralBool(LiteralBool node) {
495 return boolType;
496 }
497
498 Type visitLiteralString(LiteralString node) {
499 return stringType;
500 }
501
502 Type visitStringJuxtaposition(StringJuxtaposition node) {
503 analyze(node.first);
504 analyze(node.second);
505 return stringType;
506 }
507
508 Type visitLiteralNull(LiteralNull node) {
509 return types.dynamicType;
510 }
511
512 Type visitNewExpression(NewExpression node) {
513 return analyze(node.send.selector);
514 }
515
516 Type visitLiteralList(LiteralList node) {
517 return listType;
518 }
519
520 Type visitNodeList(NodeList node) {
521 Type type = StatementType.NOT_RETURNING;
522 bool reportedDeadCode = false;
523 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
524 Type nextType = analyze(link.head);
525 if (type == StatementType.RETURNING) {
526 if (!reportedDeadCode) {
527 reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE);
528 reportedDeadCode = true;
529 }
530 } else if (type == StatementType.MAYBE_RETURNING){
531 if (nextType == StatementType.RETURNING) {
532 type = nextType;
533 }
534 } else {
535 type = nextType;
536 }
537 }
538 return type;
539 }
540
541 Type visitOperator(Operator node) {
542 fail(node, 'internal error');
543 }
544
545 /** Dart Programming Language Specification: 11.10 Return */
546 Type visitReturn(Return node) {
547 final expression = node.expression;
548 final isVoidFunction = (expectedReturnType === types.voidType);
549
550 // Executing a return statement return e; [...] It is a static type warning
551 // if the type of e may not be assigned to the declared return type of the
552 // immediately enclosing function.
553 if (expression !== null) {
554 final expressionType = analyze(expression);
555 if (isVoidFunction
556 && !types.isAssignable(expressionType, types.voidType)) {
557 reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID,
558 [expressionType]);
559 } else {
560 checkAssignable(expression, expectedReturnType, expressionType);
561 }
562
563 // Let f be the function immediately enclosing a return statement of the
564 // form 'return;' It is a static warning if both of the following conditions
565 // hold:
566 // - f is not a generative constructor.
567 // - The return type of f may not be assigned to void.
568 } else if (!types.isAssignable(expectedReturnType, types.voidType)) {
569 reportTypeWarning(node, MessageKind.RETURN_NOTHING, [expectedReturnType]);
570 }
571 return StatementType.RETURNING;
572 }
573
574 Type visitThrow(Throw node) {
575 if (node.expression !== null) analyze(node.expression);
576 return StatementType.RETURNING;
577 }
578
579 Type computeType(Element element) {
580 if (element === null) return types.dynamicType;
581 Type result = element.computeType(compiler);
582 return (result !== null) ? result : types.dynamicType;
583 }
584
585 Type visitTypeAnnotation(TypeAnnotation node) {
586 if (node.typeName === null) return types.dynamicType;
587 Identifier identifier = node.typeName.asIdentifier();
588 if (identifier === null) {
589 fail(node.typeName, 'library prefix not implemented');
590 }
591 // TODO(ahe): Why wasn't this resolved by the resolver?
592 Type type = lookupType(identifier.source, compiler, types);
593 if (type === null) {
594 // The type name cannot be resolved, but the resolver
595 // already gave a warning, so we continue checking.
596 return types.dynamicType;
597 }
598 return type;
599 }
600
601 visitTypeVariable(TypeVariable node) {
602 return types.dynamicType;
603 }
604
605 Type visitVariableDefinitions(VariableDefinitions node) {
606 Type type = analyzeWithDefault(node.type, types.dynamicType);
607 if (type == types.voidType) {
608 reportTypeWarning(node.type, MessageKind.VOID_VARIABLE);
609 type = types.dynamicType;
610 }
611 for (Link<Node> link = node.definitions.nodes; !link.isEmpty();
612 link = link.tail) {
613 Node initialization = link.head;
614 compiler.ensure(initialization is Identifier
615 || initialization is Send);
616 if (initialization is Send) {
617 Type initializer = analyzeNonVoid(link.head);
618 checkAssignable(node, type, initializer);
619 }
620 }
621 return StatementType.NOT_RETURNING;
622 }
623
624 Type visitWhile(While node) {
625 checkCondition(node.condition);
626 StatementType bodyType = analyze(node.body);
627 return bodyType.join(StatementType.NOT_RETURNING);
628 }
629
630 Type visitParenthesizedExpression(ParenthesizedExpression node) {
631 return analyze(node.expression);
632 }
633
634 Type visitConditional(Conditional node) {
635 checkCondition(node.condition);
636 Type thenType = analyzeNonVoid(node.thenExpression);
637 Type elseType = analyzeNonVoid(node.elseExpression);
638 if (types.isSubtype(thenType, elseType)) {
639 return thenType;
640 } else if (types.isSubtype(elseType, thenType)) {
641 return elseType;
642 } else {
643 return objectType;
644 }
645 }
646
647 Type visitModifiers(Modifiers node) {}
648
649 visitStringInterpolation(StringInterpolation node) {
650 node.visitChildren(this);
651 return stringType;
652 }
653
654 visitStringInterpolationPart(StringInterpolationPart node) {
655 node.visitChildren(this);
656 return stringType;
657 }
658
659 visitEmptyStatement(EmptyStatement node) {
660 return StatementType.NOT_RETURNING;
661 }
662
663 visitBreakStatement(BreakStatement node) {
664 return StatementType.NOT_RETURNING;
665 }
666
667 visitContinueStatement(ContinueStatement node) {
668 return StatementType.NOT_RETURNING;
669 }
670
671 visitForInStatement(ForInStatement node) {
672 analyze(node.expression);
673 StatementType bodyType = analyze(node.body);
674 return bodyType.join(StatementType.NOT_RETURNING);
675 }
676
677 visitLabeledStatement(LabeledStatement node) {
678 return node.statement.accept(this);
679 }
680
681 visitLiteralMap(LiteralMap node) {
682 fail(node);
683 }
684
685 visitLiteralMapEntry(LiteralMapEntry node) {
686 fail(node);
687 }
688
689 visitNamedArgument(NamedArgument node) {
690 fail(node, 'named argument not implemented');
691 }
692
693 visitSwitchStatement(SwitchStatement node) {
694 fail(node);
695 }
696
697 visitSwitchCase(SwitchCase node) {
698 fail(node);
699 }
700
701 visitTryStatement(TryStatement node) {
702 fail(node, 'unimplemented');
703 }
704
705 visitScriptTag(ScriptTag node) {
706 fail(node);
707 }
708
709 visitCatchBlock(CatchBlock node) {
710 fail(node);
711 }
712
713 visitTypedef(Typedef node) {
714 fail(node);
715 }
716 }
OLDNEW
« no previous file with comments | « frog/leg/tree_validator.dart ('k') | frog/leg/universe.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698