| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, 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 library kernel.type_checker; |
| 5 |
| 6 import 'ast.dart'; |
| 7 import 'class_hierarchy.dart'; |
| 8 import 'core_types.dart'; |
| 9 import 'type_algebra.dart'; |
| 10 import 'type_environment.dart'; |
| 11 |
| 12 /// Performs strong-mode type checking on the kernel IR. |
| 13 /// |
| 14 /// A concrete subclass of [TypeChecker] must implement [checkAssignable] and |
| 15 /// [fail] in order to deal with subtyping requirements and error handling. |
| 16 abstract class TypeChecker { |
| 17 final CoreTypes coreTypes; |
| 18 final ClassHierarchy hierarchy; |
| 19 TypeEnvironment environment; |
| 20 |
| 21 TypeChecker(this.coreTypes, this.hierarchy) { |
| 22 environment = new TypeEnvironment(coreTypes, hierarchy); |
| 23 } |
| 24 |
| 25 void checkProgram(Program program) { |
| 26 for (var library in program.libraries) { |
| 27 if (library.importUri.scheme == 'dart') continue; |
| 28 for (var class_ in library.classes) { |
| 29 hierarchy.forEachOverridePair(class_, |
| 30 (Member ownMember, Member superMember, bool isSetter) { |
| 31 checkOverride(class_, ownMember, superMember, isSetter); |
| 32 }); |
| 33 } |
| 34 } |
| 35 var visitor = new TypeCheckingVisitor(this, environment); |
| 36 for (var library in program.libraries) { |
| 37 if (library.importUri.scheme == 'dart') continue; |
| 38 for (var class_ in library.classes) { |
| 39 environment.thisType = class_.thisType; |
| 40 for (var field in class_.fields) { |
| 41 visitor.visitField(field); |
| 42 } |
| 43 for (var constructor in class_.constructors) { |
| 44 visitor.visitConstructor(constructor); |
| 45 } |
| 46 for (var procedure in class_.procedures) { |
| 47 visitor.visitProcedure(procedure); |
| 48 } |
| 49 } |
| 50 environment.thisType = null; |
| 51 for (var procedure in library.procedures) { |
| 52 visitor.visitProcedure(procedure); |
| 53 } |
| 54 for (var field in library.fields) { |
| 55 visitor.visitField(field); |
| 56 } |
| 57 } |
| 58 } |
| 59 |
| 60 DartType getterType(Class host, Member member) { |
| 61 var hostType = hierarchy.getClassAsInstanceOf(host, member.enclosingClass); |
| 62 var substitution = Substitution.fromSupertype(hostType); |
| 63 return substitution.substituteType(member.getterType); |
| 64 } |
| 65 |
| 66 DartType setterType(Class host, Member member) { |
| 67 var hostType = hierarchy.getClassAsInstanceOf(host, member.enclosingClass); |
| 68 var substitution = Substitution.fromSupertype(hostType); |
| 69 return substitution.substituteType(member.setterType, contravariant: true); |
| 70 } |
| 71 |
| 72 void checkOverride( |
| 73 Class host, Member ownMember, Member superMember, bool isSetter) { |
| 74 if (isSetter) { |
| 75 checkAssignable(ownMember, setterType(host, superMember), |
| 76 setterType(host, ownMember)); |
| 77 } else { |
| 78 checkAssignable(ownMember, getterType(host, ownMember), |
| 79 getterType(host, superMember)); |
| 80 } |
| 81 } |
| 82 |
| 83 /// Check that [from] is a subtype of [to]. |
| 84 /// |
| 85 /// [where] is an AST node indicating roughly where the check is required. |
| 86 void checkAssignable(TreeNode where, DartType from, DartType to); |
| 87 |
| 88 /// Indicates that type checking failed. |
| 89 void fail(TreeNode where, String message); |
| 90 } |
| 91 |
| 92 class TypeCheckingVisitor |
| 93 implements |
| 94 ExpressionVisitor<DartType>, |
| 95 StatementVisitor<Null>, |
| 96 MemberVisitor<Null>, |
| 97 InitializerVisitor<Null> { |
| 98 final TypeChecker checker; |
| 99 final TypeEnvironment environment; |
| 100 |
| 101 CoreTypes get coreTypes => environment.coreTypes; |
| 102 ClassHierarchy get hierarchy => environment.hierarchy; |
| 103 Class get currentClass => environment.thisType.classNode; |
| 104 |
| 105 TypeCheckingVisitor(this.checker, this.environment); |
| 106 |
| 107 void checkAssignable(TreeNode where, DartType from, DartType to) { |
| 108 checker.checkAssignable(where, from, to); |
| 109 } |
| 110 |
| 111 void checkAssignableExpression(Expression from, DartType to) { |
| 112 checker.checkAssignable(from, visitExpression(from), to); |
| 113 } |
| 114 |
| 115 void fail(TreeNode node, String message) { |
| 116 checker.fail(node, message); |
| 117 } |
| 118 |
| 119 DartType visitExpression(Expression node) => node.accept(this); |
| 120 |
| 121 void visitStatement(Statement node) { |
| 122 node.accept(this); |
| 123 } |
| 124 |
| 125 void visitInitializer(Initializer node) { |
| 126 node.accept(this); |
| 127 } |
| 128 |
| 129 defaultMember(Member node) => throw 'Unused'; |
| 130 |
| 131 DartType defaultBasicLiteral(BasicLiteral node) { |
| 132 return defaultExpression(node); |
| 133 } |
| 134 |
| 135 DartType defaultExpression(Expression node) { |
| 136 throw 'Unexpected expression ${node.runtimeType}'; |
| 137 } |
| 138 |
| 139 defaultStatement(Statement node) { |
| 140 throw 'Unexpected statement ${node.runtimeType}'; |
| 141 } |
| 142 |
| 143 defaultInitializer(Initializer node) { |
| 144 throw 'Unexpected initializer ${node.runtimeType}'; |
| 145 } |
| 146 |
| 147 visitField(Field node) { |
| 148 if (node.initializer != null) { |
| 149 checkAssignableExpression(node.initializer, node.type); |
| 150 } |
| 151 } |
| 152 |
| 153 visitConstructor(Constructor node) { |
| 154 environment.returnType = null; |
| 155 environment.yieldType = null; |
| 156 node.initializers.forEach(visitInitializer); |
| 157 handleFunctionNode(node.function); |
| 158 } |
| 159 |
| 160 visitProcedure(Procedure node) { |
| 161 environment.returnType = node.function.returnType; |
| 162 environment.yieldType = _getYieldType(node.function); |
| 163 handleFunctionNode(node.function); |
| 164 } |
| 165 |
| 166 void handleFunctionNode(FunctionNode node) { |
| 167 node.positionalParameters |
| 168 .skip(node.requiredParameterCount) |
| 169 .forEach(handleOptionalParameter); |
| 170 node.namedParameters.forEach(handleOptionalParameter); |
| 171 if (node.body != null) { |
| 172 visitStatement(node.body); |
| 173 } |
| 174 } |
| 175 |
| 176 void handleNestedFunctionNode(FunctionNode node) { |
| 177 var oldReturn = environment.returnType; |
| 178 var oldYield = environment.yieldType; |
| 179 environment.returnType = node.returnType; |
| 180 environment.yieldType = _getYieldType(node); |
| 181 handleFunctionNode(node); |
| 182 environment.returnType = oldReturn; |
| 183 environment.yieldType = oldYield; |
| 184 } |
| 185 |
| 186 void handleOptionalParameter(VariableDeclaration parameter) { |
| 187 if (parameter.initializer != null) { |
| 188 checkAssignableExpression(parameter.initializer, parameter.type); |
| 189 } |
| 190 } |
| 191 |
| 192 Substitution getReceiverType( |
| 193 TreeNode access, Expression receiver, Member member) { |
| 194 var type = visitExpression(receiver); |
| 195 Class superclass = member.enclosingClass; |
| 196 if (superclass.supertype == null) { |
| 197 return Substitution.empty; // Members on Object are always accessible. |
| 198 } |
| 199 while (type is TypeParameterType) { |
| 200 type = (type as TypeParameterType).parameter.bound; |
| 201 } |
| 202 if (type is BottomType) { |
| 203 // The bottom type is a subtype of all types, so it should be allowed. |
| 204 return Substitution.bottomForClass(superclass); |
| 205 } |
| 206 if (type is InterfaceType) { |
| 207 // The receiver type should implement the interface declaring the member. |
| 208 var upcastType = hierarchy.getTypeAsInstanceOf(type, superclass); |
| 209 if (upcastType != null) { |
| 210 return Substitution.fromInterfaceType(upcastType); |
| 211 } |
| 212 } |
| 213 if (type is FunctionType && superclass == coreTypes.functionClass) { |
| 214 assert(type.typeParameters.isEmpty); |
| 215 return Substitution.empty; |
| 216 } |
| 217 // Note that we do not allow 'dynamic' here. Dynamic calls should not |
| 218 // have a declared interface target. |
| 219 fail(access, '$member is not accessible on a receiver of type $type'); |
| 220 return Substitution.bottomForClass(superclass); // Continue type checking. |
| 221 } |
| 222 |
| 223 Substitution getSuperReceiverType(Member member) { |
| 224 return Substitution.fromSupertype( |
| 225 hierarchy.getClassAsInstanceOf(currentClass, member.enclosingClass)); |
| 226 } |
| 227 |
| 228 DartType handleCall(Arguments arguments, FunctionNode function, |
| 229 {Substitution receiver: Substitution.empty, |
| 230 List<TypeParameter> typeParameters}) { |
| 231 typeParameters ??= function.typeParameters; |
| 232 if (arguments.positional.length < function.requiredParameterCount) { |
| 233 fail(arguments, 'Too few positional arguments'); |
| 234 return const BottomType(); |
| 235 } |
| 236 if (arguments.positional.length > function.positionalParameters.length) { |
| 237 fail(arguments, 'Too many positional arguments'); |
| 238 return const BottomType(); |
| 239 } |
| 240 if (arguments.types.length != typeParameters.length) { |
| 241 fail(arguments, 'Wrong number of type arguments'); |
| 242 return const BottomType(); |
| 243 } |
| 244 var instantiation = Substitution.fromPairs(typeParameters, arguments.types); |
| 245 var substitution = Substitution.combine(receiver, instantiation); |
| 246 for (int i = 0; i < typeParameters.length; ++i) { |
| 247 var argument = arguments.types[i]; |
| 248 var bound = substitution.substituteType(typeParameters[i].bound); |
| 249 checkAssignable(arguments, argument, bound); |
| 250 } |
| 251 for (int i = 0; i < arguments.positional.length; ++i) { |
| 252 var expectedType = substitution.substituteType( |
| 253 function.positionalParameters[i].type, |
| 254 contravariant: true); |
| 255 checkAssignableExpression(arguments.positional[i], expectedType); |
| 256 } |
| 257 for (int i = 0; i < arguments.named.length; ++i) { |
| 258 var argument = arguments.named[i]; |
| 259 bool found = false; |
| 260 for (int j = 0; j < function.namedParameters.length; ++j) { |
| 261 if (argument.name == function.namedParameters[j].name) { |
| 262 var expectedType = substitution.substituteType( |
| 263 function.namedParameters[j].type, |
| 264 contravariant: true); |
| 265 checkAssignableExpression(argument.value, expectedType); |
| 266 found = true; |
| 267 break; |
| 268 } |
| 269 } |
| 270 if (!found) { |
| 271 fail(argument.value, 'Unexpected named parameter: ${argument.name}'); |
| 272 return const BottomType(); |
| 273 } |
| 274 } |
| 275 return substitution.substituteType(function.returnType); |
| 276 } |
| 277 |
| 278 DartType _getYieldType(FunctionNode function) { |
| 279 switch (function.asyncMarker) { |
| 280 case AsyncMarker.Sync: |
| 281 case AsyncMarker.Async: |
| 282 return null; |
| 283 |
| 284 case AsyncMarker.SyncStar: |
| 285 case AsyncMarker.AsyncStar: |
| 286 Class container = function.asyncMarker == AsyncMarker.SyncStar |
| 287 ? coreTypes.iterableClass |
| 288 : coreTypes.streamClass; |
| 289 DartType returnType = function.returnType; |
| 290 if (returnType is InterfaceType && returnType.classNode == container) { |
| 291 return returnType.typeArguments.single; |
| 292 } |
| 293 return const DynamicType(); |
| 294 |
| 295 case AsyncMarker.SyncYielding: |
| 296 return function.returnType; |
| 297 |
| 298 default: |
| 299 throw 'Unexpected async marker: ${function.asyncMarker}'; |
| 300 } |
| 301 } |
| 302 |
| 303 @override |
| 304 DartType visitAsExpression(AsExpression node) { |
| 305 visitExpression(node.operand); |
| 306 return node.type; |
| 307 } |
| 308 |
| 309 @override |
| 310 DartType visitAwaitExpression(AwaitExpression node) { |
| 311 return environment.unfutureType(visitExpression(node.operand)); |
| 312 } |
| 313 |
| 314 @override |
| 315 DartType visitBoolLiteral(BoolLiteral node) { |
| 316 return environment.boolType; |
| 317 } |
| 318 |
| 319 @override |
| 320 DartType visitConditionalExpression(ConditionalExpression node) { |
| 321 checkAssignableExpression(node.condition, environment.boolType); |
| 322 if (node.staticType == null) { |
| 323 var thenType = visitExpression(node.then); |
| 324 var otherwiseType = visitExpression(node.otherwise); |
| 325 if (thenType is BottomType) return otherwiseType; |
| 326 if (otherwiseType is BottomType) return thenType; |
| 327 return const DynamicType(); |
| 328 } else { |
| 329 checkAssignableExpression(node.then, node.staticType); |
| 330 checkAssignableExpression(node.otherwise, node.staticType); |
| 331 return node.staticType; |
| 332 } |
| 333 } |
| 334 |
| 335 @override |
| 336 DartType visitConstructorInvocation(ConstructorInvocation node) { |
| 337 Constructor target = node.target; |
| 338 Arguments arguments = node.arguments; |
| 339 Class class_ = target.enclosingClass; |
| 340 handleCall(arguments, target.function, |
| 341 typeParameters: class_.typeParameters); |
| 342 return new InterfaceType(target.enclosingClass, arguments.types); |
| 343 } |
| 344 |
| 345 @override |
| 346 DartType visitDirectMethodInvocation(DirectMethodInvocation node) { |
| 347 return handleCall(node.arguments, node.target.function, |
| 348 receiver: getReceiverType(node, node.receiver, node.target)); |
| 349 } |
| 350 |
| 351 @override |
| 352 DartType visitDirectPropertyGet(DirectPropertyGet node) { |
| 353 var receiver = getReceiverType(node, node.receiver, node.target); |
| 354 return receiver.substituteType(node.target.getterType); |
| 355 } |
| 356 |
| 357 @override |
| 358 DartType visitDirectPropertySet(DirectPropertySet node) { |
| 359 var receiver = getReceiverType(node, node.receiver, node.target); |
| 360 var value = visitExpression(node.value); |
| 361 checkAssignable(node, value, |
| 362 receiver.substituteType(node.target.setterType, contravariant: true)); |
| 363 return value; |
| 364 } |
| 365 |
| 366 @override |
| 367 DartType visitDoubleLiteral(DoubleLiteral node) { |
| 368 return environment.doubleType; |
| 369 } |
| 370 |
| 371 @override |
| 372 DartType visitFunctionExpression(FunctionExpression node) { |
| 373 handleNestedFunctionNode(node.function); |
| 374 return node.function.functionType; |
| 375 } |
| 376 |
| 377 @override |
| 378 DartType visitIntLiteral(IntLiteral node) { |
| 379 return environment.intType; |
| 380 } |
| 381 |
| 382 @override |
| 383 DartType visitInvalidExpression(InvalidExpression node) { |
| 384 return const BottomType(); |
| 385 } |
| 386 |
| 387 @override |
| 388 DartType visitIsExpression(IsExpression node) { |
| 389 visitExpression(node.operand); |
| 390 return environment.boolType; |
| 391 } |
| 392 |
| 393 @override |
| 394 DartType visitLet(Let node) { |
| 395 var value = visitExpression(node.variable.initializer); |
| 396 if (node.variable.type is DynamicType) { |
| 397 node.variable.type = value; |
| 398 } |
| 399 return visitExpression(node.body); |
| 400 } |
| 401 |
| 402 @override |
| 403 DartType visitListLiteral(ListLiteral node) { |
| 404 for (var item in node.expressions) { |
| 405 checkAssignableExpression(item, node.typeArgument); |
| 406 } |
| 407 return environment.literalListType(node.typeArgument); |
| 408 } |
| 409 |
| 410 @override |
| 411 DartType visitLogicalExpression(LogicalExpression node) { |
| 412 checkAssignableExpression(node.left, environment.boolType); |
| 413 checkAssignableExpression(node.right, environment.boolType); |
| 414 return environment.boolType; |
| 415 } |
| 416 |
| 417 @override |
| 418 DartType visitMapLiteral(MapLiteral node) { |
| 419 for (var entry in node.entries) { |
| 420 checkAssignableExpression(entry.key, node.keyType); |
| 421 checkAssignableExpression(entry.value, node.valueType); |
| 422 } |
| 423 return environment.literalMapType(node.keyType, node.valueType); |
| 424 } |
| 425 |
| 426 DartType handleDynamicCall(DartType receiver, Arguments arguments) { |
| 427 arguments.positional.forEach(visitExpression); |
| 428 arguments.named.forEach((NamedExpression n) => visitExpression(n.value)); |
| 429 return const DynamicType(); |
| 430 } |
| 431 |
| 432 DartType handleFunctionCall( |
| 433 TreeNode access, FunctionType function, Arguments arguments) { |
| 434 if (function.requiredParameterCount > arguments.positional.length) { |
| 435 fail(access, 'Too few positional arguments'); |
| 436 return const BottomType(); |
| 437 } |
| 438 if (function.positionalParameters.length < arguments.positional.length) { |
| 439 fail(access, 'Too many positional arguments'); |
| 440 return const BottomType(); |
| 441 } |
| 442 if (function.typeParameters.length != arguments.types.length) { |
| 443 fail(access, 'Wrong number of type arguments'); |
| 444 return const BottomType(); |
| 445 } |
| 446 var instantiation = |
| 447 Substitution.fromPairs(function.typeParameters, arguments.types); |
| 448 for (int i = 0; i < arguments.positional.length; ++i) { |
| 449 var expectedType = instantiation.substituteType( |
| 450 function.positionalParameters[i], |
| 451 contravariant: true); |
| 452 checkAssignableExpression(arguments.positional[i], expectedType); |
| 453 } |
| 454 for (int i = 0; i < arguments.named.length; ++i) { |
| 455 var argument = arguments.named[i]; |
| 456 var expectedType = function.namedParameters[argument.name]; |
| 457 if (expectedType == null) { |
| 458 fail(argument.value, 'Unexpected named parameter: ${argument.name}'); |
| 459 } else { |
| 460 checkAssignableExpression(argument.value, |
| 461 instantiation.substituteType(expectedType, contravariant: true)); |
| 462 } |
| 463 } |
| 464 return instantiation.substituteType(function.returnType); |
| 465 } |
| 466 |
| 467 @override |
| 468 DartType visitMethodInvocation(MethodInvocation node) { |
| 469 var target = node.interfaceTarget; |
| 470 if (target == null) { |
| 471 var receiver = visitExpression(node.receiver); |
| 472 return (node.name.name == 'call' && receiver is FunctionType) |
| 473 ? handleFunctionCall(node, receiver, node.arguments) |
| 474 : handleDynamicCall(receiver, node.arguments); |
| 475 } else if (environment.isOverloadedArithmeticOperator(target)) { |
| 476 assert(node.arguments.positional.length == 1); |
| 477 var receiver = visitExpression(node.receiver); |
| 478 var argument = visitExpression(node.arguments.positional[0]); |
| 479 return environment.getTypeOfOverloadedArithmetic(receiver, argument); |
| 480 } else { |
| 481 return handleCall(node.arguments, target.function, |
| 482 receiver: getReceiverType(node, node.receiver, node.interfaceTarget)); |
| 483 } |
| 484 } |
| 485 |
| 486 @override |
| 487 DartType visitPropertyGet(PropertyGet node) { |
| 488 if (node.interfaceTarget == null) { |
| 489 visitExpression(node.receiver); |
| 490 return const DynamicType(); |
| 491 } else { |
| 492 var receiver = getReceiverType(node, node.receiver, node.interfaceTarget); |
| 493 return receiver.substituteType(node.interfaceTarget.getterType); |
| 494 } |
| 495 } |
| 496 |
| 497 @override |
| 498 DartType visitPropertySet(PropertySet node) { |
| 499 var value = visitExpression(node.value); |
| 500 if (node.interfaceTarget != null) { |
| 501 var receiver = getReceiverType(node, node.receiver, node.interfaceTarget); |
| 502 checkAssignable( |
| 503 node.value, |
| 504 value, |
| 505 receiver.substituteType(node.interfaceTarget.setterType, |
| 506 contravariant: true)); |
| 507 } else { |
| 508 visitExpression(node.receiver); |
| 509 } |
| 510 return value; |
| 511 } |
| 512 |
| 513 @override |
| 514 DartType visitNot(Not node) { |
| 515 visitExpression(node.operand); |
| 516 return environment.boolType; |
| 517 } |
| 518 |
| 519 @override |
| 520 DartType visitNullLiteral(NullLiteral node) { |
| 521 return const BottomType(); |
| 522 } |
| 523 |
| 524 @override |
| 525 DartType visitRethrow(Rethrow node) { |
| 526 return const BottomType(); |
| 527 } |
| 528 |
| 529 @override |
| 530 DartType visitStaticGet(StaticGet node) { |
| 531 return node.target.getterType; |
| 532 } |
| 533 |
| 534 @override |
| 535 DartType visitStaticInvocation(StaticInvocation node) { |
| 536 return handleCall(node.arguments, node.target.function); |
| 537 } |
| 538 |
| 539 @override |
| 540 DartType visitStaticSet(StaticSet node) { |
| 541 var value = visitExpression(node.value); |
| 542 checkAssignable(node.value, value, node.target.setterType); |
| 543 return value; |
| 544 } |
| 545 |
| 546 @override |
| 547 DartType visitStringConcatenation(StringConcatenation node) { |
| 548 node.expressions.forEach(visitExpression); |
| 549 return environment.stringType; |
| 550 } |
| 551 |
| 552 @override |
| 553 DartType visitStringLiteral(StringLiteral node) { |
| 554 return environment.stringType; |
| 555 } |
| 556 |
| 557 @override |
| 558 DartType visitSuperMethodInvocation(SuperMethodInvocation node) { |
| 559 if (node.interfaceTarget == null) { |
| 560 return handleDynamicCall(environment.thisType, node.arguments); |
| 561 } else { |
| 562 return handleCall(node.arguments, node.interfaceTarget.function, |
| 563 receiver: getSuperReceiverType(node.interfaceTarget)); |
| 564 } |
| 565 } |
| 566 |
| 567 @override |
| 568 DartType visitSuperPropertyGet(SuperPropertyGet node) { |
| 569 if (node.interfaceTarget == null) { |
| 570 return const DynamicType(); |
| 571 } else { |
| 572 var receiver = getSuperReceiverType(node.interfaceTarget); |
| 573 return receiver.substituteType(node.interfaceTarget.getterType); |
| 574 } |
| 575 } |
| 576 |
| 577 @override |
| 578 DartType visitSuperPropertySet(SuperPropertySet node) { |
| 579 var value = visitExpression(node.value); |
| 580 if (node.interfaceTarget != null) { |
| 581 var receiver = getSuperReceiverType(node.interfaceTarget); |
| 582 checkAssignable( |
| 583 node.value, |
| 584 value, |
| 585 receiver.substituteType(node.interfaceTarget.setterType, |
| 586 contravariant: true)); |
| 587 } |
| 588 return value; |
| 589 } |
| 590 |
| 591 @override |
| 592 DartType visitSymbolLiteral(SymbolLiteral node) { |
| 593 return environment.symbolType; |
| 594 } |
| 595 |
| 596 @override |
| 597 DartType visitThisExpression(ThisExpression node) { |
| 598 return environment.thisType; |
| 599 } |
| 600 |
| 601 @override |
| 602 DartType visitThrow(Throw node) { |
| 603 visitExpression(node.expression); |
| 604 return const BottomType(); |
| 605 } |
| 606 |
| 607 @override |
| 608 DartType visitTypeLiteral(TypeLiteral node) { |
| 609 return environment.typeType; |
| 610 } |
| 611 |
| 612 @override |
| 613 DartType visitVariableGet(VariableGet node) { |
| 614 return node.promotedType ?? node.variable.type; |
| 615 } |
| 616 |
| 617 @override |
| 618 DartType visitVariableSet(VariableSet node) { |
| 619 var value = visitExpression(node.value); |
| 620 checkAssignable(node.value, value, node.variable.type); |
| 621 return value; |
| 622 } |
| 623 |
| 624 @override |
| 625 visitAssertStatement(AssertStatement node) { |
| 626 visitExpression(node.condition); |
| 627 if (node.message != null) { |
| 628 visitExpression(node.message); |
| 629 } |
| 630 } |
| 631 |
| 632 @override |
| 633 visitBlock(Block node) { |
| 634 node.statements.forEach(visitStatement); |
| 635 } |
| 636 |
| 637 @override |
| 638 visitBreakStatement(BreakStatement node) {} |
| 639 |
| 640 @override |
| 641 visitContinueSwitchStatement(ContinueSwitchStatement node) {} |
| 642 |
| 643 @override |
| 644 visitDoStatement(DoStatement node) { |
| 645 visitStatement(node.body); |
| 646 checkAssignableExpression(node.condition, environment.boolType); |
| 647 } |
| 648 |
| 649 @override |
| 650 visitEmptyStatement(EmptyStatement node) {} |
| 651 |
| 652 @override |
| 653 visitExpressionStatement(ExpressionStatement node) { |
| 654 visitExpression(node.expression); |
| 655 } |
| 656 |
| 657 @override |
| 658 visitForInStatement(ForInStatement node) { |
| 659 var iterable = visitExpression(node.iterable); |
| 660 // TODO(asgerf): Store interface targets on for-in loops or desugar them, |
| 661 // instead of doing the ad-hoc resolution here. |
| 662 if (node.isAsync) { |
| 663 checkAssignable(node, getStreamElementType(iterable), node.variable.type); |
| 664 } else { |
| 665 checkAssignable( |
| 666 node, getIterableElementType(iterable), node.variable.type); |
| 667 } |
| 668 visitStatement(node.body); |
| 669 } |
| 670 |
| 671 static final Name iteratorName = new Name('iterator'); |
| 672 static final Name nextName = new Name('next'); |
| 673 |
| 674 DartType getIterableElementType(DartType iterable) { |
| 675 if (iterable is InterfaceType) { |
| 676 var iteratorGetter = |
| 677 hierarchy.getInterfaceMember(iterable.classNode, iteratorName); |
| 678 if (iteratorGetter == null) return const DynamicType(); |
| 679 var iteratorType = Substitution |
| 680 .fromInterfaceType(iterable) |
| 681 .substituteType(iteratorGetter.getterType); |
| 682 if (iteratorType is InterfaceType) { |
| 683 var nextGetter = |
| 684 hierarchy.getInterfaceMember(iteratorType.classNode, nextName); |
| 685 if (nextGetter == null) return const DynamicType(); |
| 686 return Substitution |
| 687 .fromInterfaceType(iteratorType) |
| 688 .substituteType(nextGetter.getterType); |
| 689 } |
| 690 } |
| 691 return const DynamicType(); |
| 692 } |
| 693 |
| 694 DartType getStreamElementType(DartType stream) { |
| 695 if (stream is InterfaceType) { |
| 696 var asStream = |
| 697 hierarchy.getTypeAsInstanceOf(stream, coreTypes.streamClass); |
| 698 if (asStream == null) return const DynamicType(); |
| 699 return asStream.typeArguments.single; |
| 700 } |
| 701 return const DynamicType(); |
| 702 } |
| 703 |
| 704 @override |
| 705 visitForStatement(ForStatement node) { |
| 706 node.variables.forEach(visitVariableDeclaration); |
| 707 if (node.condition != null) { |
| 708 checkAssignableExpression(node.condition, environment.boolType); |
| 709 } |
| 710 node.updates.forEach(visitExpression); |
| 711 visitStatement(node.body); |
| 712 } |
| 713 |
| 714 @override |
| 715 visitFunctionDeclaration(FunctionDeclaration node) { |
| 716 handleNestedFunctionNode(node.function); |
| 717 } |
| 718 |
| 719 @override |
| 720 visitIfStatement(IfStatement node) { |
| 721 checkAssignableExpression(node.condition, environment.boolType); |
| 722 visitStatement(node.then); |
| 723 if (node.otherwise != null) { |
| 724 visitStatement(node.otherwise); |
| 725 } |
| 726 } |
| 727 |
| 728 @override |
| 729 visitInvalidStatement(InvalidStatement node) {} |
| 730 |
| 731 @override |
| 732 visitLabeledStatement(LabeledStatement node) { |
| 733 visitStatement(node.body); |
| 734 } |
| 735 |
| 736 @override |
| 737 visitReturnStatement(ReturnStatement node) { |
| 738 if (node.expression != null) { |
| 739 if (environment.returnType == null) { |
| 740 fail(node, 'Return of a value from void method'); |
| 741 } else { |
| 742 checkAssignableExpression(node.expression, environment.returnType); |
| 743 } |
| 744 } |
| 745 } |
| 746 |
| 747 @override |
| 748 visitSwitchStatement(SwitchStatement node) { |
| 749 visitExpression(node.expression); |
| 750 for (var switchCase in node.cases) { |
| 751 switchCase.expressions.forEach(visitExpression); |
| 752 visitStatement(switchCase.body); |
| 753 } |
| 754 } |
| 755 |
| 756 @override |
| 757 visitTryCatch(TryCatch node) { |
| 758 visitStatement(node.body); |
| 759 for (var catchClause in node.catches) { |
| 760 visitStatement(catchClause.body); |
| 761 } |
| 762 } |
| 763 |
| 764 @override |
| 765 visitTryFinally(TryFinally node) { |
| 766 visitStatement(node.body); |
| 767 visitStatement(node.finalizer); |
| 768 } |
| 769 |
| 770 @override |
| 771 visitVariableDeclaration(VariableDeclaration node) { |
| 772 if (node.initializer != null) { |
| 773 checkAssignableExpression(node.initializer, node.type); |
| 774 } |
| 775 } |
| 776 |
| 777 @override |
| 778 visitWhileStatement(WhileStatement node) { |
| 779 checkAssignableExpression(node.condition, environment.boolType); |
| 780 visitStatement(node.body); |
| 781 } |
| 782 |
| 783 @override |
| 784 visitYieldStatement(YieldStatement node) { |
| 785 checkAssignableExpression(node.expression, environment.yieldType); |
| 786 } |
| 787 |
| 788 @override |
| 789 visitFieldInitializer(FieldInitializer node) { |
| 790 checkAssignableExpression(node.value, node.field.type); |
| 791 } |
| 792 |
| 793 @override |
| 794 visitRedirectingInitializer(RedirectingInitializer node) { |
| 795 handleCall(node.arguments, node.target.function, |
| 796 typeParameters: const <TypeParameter>[]); |
| 797 } |
| 798 |
| 799 @override |
| 800 visitSuperInitializer(SuperInitializer node) { |
| 801 handleCall(node.arguments, node.target.function, |
| 802 typeParameters: const <TypeParameter>[]); |
| 803 } |
| 804 |
| 805 @override |
| 806 visitLocalInitializer(LocalInitializer node) { |
| 807 visitVariableDeclaration(node.variable); |
| 808 } |
| 809 |
| 810 @override |
| 811 visitInvalidInitializer(InvalidInitializer node) {} |
| 812 } |
| OLD | NEW |