| OLD | NEW |
| (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 } | |
| OLD | NEW |