Chromium Code Reviews| 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.function.positionalParameters | |
|
Kevin Millikin (Google)
2016/11/01 13:03:01
This checks the optional parameters twice, both he
asgerf
2016/11/01 13:59:36
You are right, this was not supposed to be here.
| |
| 157 .skip(node.function.requiredParameterCount) | |
| 158 .forEach(handleOptionalParameter); | |
| 159 node.function.namedParameters.forEach(handleOptionalParameter); | |
| 160 node.initializers.forEach(visitInitializer); | |
| 161 handleFunctionNode(node.function); | |
| 162 } | |
| 163 | |
| 164 visitProcedure(Procedure node) { | |
| 165 environment.returnType = node.function.returnType; | |
| 166 environment.yieldType = _getYieldType(node.function); | |
| 167 handleFunctionNode(node.function); | |
| 168 } | |
| 169 | |
| 170 void handleFunctionNode(FunctionNode node) { | |
| 171 node.positionalParameters | |
| 172 .skip(node.requiredParameterCount) | |
| 173 .forEach(handleOptionalParameter); | |
| 174 node.namedParameters.forEach(handleOptionalParameter); | |
| 175 if (node.body != null) { | |
| 176 visitStatement(node.body); | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 void handleNestedFunctionNode(FunctionNode node) { | |
| 181 var oldReturn = environment.returnType; | |
| 182 var oldYield = environment.yieldType; | |
| 183 environment.returnType = node.returnType; | |
| 184 environment.yieldType = _getYieldType(node); | |
| 185 handleFunctionNode(node); | |
| 186 environment.returnType = oldReturn; | |
| 187 environment.yieldType = oldYield; | |
| 188 } | |
| 189 | |
| 190 void handleOptionalParameter(VariableDeclaration parameter) { | |
| 191 if (parameter.initializer != null) { | |
| 192 checkAssignableExpression(parameter.initializer, parameter.type); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 Substitution getReceiverType( | |
| 197 TreeNode access, Expression receiver, Member member) { | |
| 198 var type = visitExpression(receiver); | |
| 199 Class superclass = member.enclosingClass; | |
| 200 if (superclass.supertype == null) { | |
| 201 return Substitution.empty; // Members on Object are always accessible. | |
| 202 } | |
| 203 while (type is TypeParameterType) { | |
| 204 type = (type as TypeParameterType).parameter.bound; | |
| 205 } | |
| 206 if (type is BottomType) { | |
| 207 // The bottom type is a subtype of all types, so it should be allowed. | |
| 208 return Substitution.bottomForClass(superclass); | |
| 209 } | |
| 210 if (type is InterfaceType) { | |
| 211 // The receiver type should implement the interface declaring the member. | |
| 212 var upcastType = hierarchy.getTypeAsInstanceOf(type, superclass); | |
| 213 if (upcastType != null) { | |
| 214 return Substitution.fromInterfaceType(upcastType); | |
| 215 } | |
| 216 } | |
| 217 if (type is FunctionType && superclass == coreTypes.functionClass) { | |
| 218 assert(type.typeParameters.isEmpty); | |
| 219 return Substitution.empty; | |
| 220 } | |
| 221 // Note that we do not allow 'dynamic' here. Dynamic calls should not | |
| 222 // have a declared interface target. | |
| 223 fail(access, '$member is not accessible on a receiver of type $type'); | |
| 224 return Substitution.bottomForClass(superclass); // Continue type checking. | |
| 225 } | |
| 226 | |
| 227 Substitution getSuperReceiverType(Member member) { | |
| 228 return Substitution.fromSupertype( | |
| 229 hierarchy.getClassAsInstanceOf(currentClass, member.enclosingClass)); | |
| 230 } | |
| 231 | |
| 232 DartType handleCall(Arguments arguments, FunctionNode function, | |
| 233 {Substitution receiver: Substitution.empty, | |
| 234 List<TypeParameter> typeParameters}) { | |
| 235 typeParameters ??= function.typeParameters; | |
| 236 if (arguments.positional.length < function.requiredParameterCount) { | |
| 237 fail(arguments, 'Too few positional arguments'); | |
| 238 return const BottomType(); | |
| 239 } | |
| 240 if (arguments.positional.length > function.positionalParameters.length) { | |
| 241 fail(arguments, 'Too many positional arguments'); | |
| 242 return const BottomType(); | |
| 243 } | |
| 244 if (arguments.types.length != typeParameters.length) { | |
| 245 fail(arguments, 'Wrong number of type arguments'); | |
| 246 return const BottomType(); | |
| 247 } | |
| 248 var instantiation = Substitution.fromPairs(typeParameters, arguments.types); | |
| 249 var substitution = Substitution.combine(receiver, instantiation); | |
| 250 for (int i = 0; i < typeParameters.length; ++i) { | |
| 251 var argument = arguments.types[i]; | |
| 252 var bound = substitution.substituteType(typeParameters[i].bound); | |
| 253 checkAssignable(arguments, argument, bound); | |
| 254 } | |
| 255 for (int i = 0; i < arguments.positional.length; ++i) { | |
| 256 var expectedType = substitution.substituteType( | |
| 257 function.positionalParameters[i].type, | |
| 258 contravariant: true); | |
| 259 checkAssignableExpression(arguments.positional[i], expectedType); | |
| 260 } | |
| 261 for (int i = 0; i < arguments.named.length; ++i) { | |
| 262 var argument = arguments.named[i]; | |
| 263 bool found = false; | |
| 264 for (int j = 0; j < function.namedParameters.length; ++j) { | |
| 265 if (argument.name == function.namedParameters[j].name) { | |
| 266 var expectedType = substitution.substituteType( | |
| 267 function.namedParameters[j].type, | |
| 268 contravariant: true); | |
| 269 checkAssignableExpression(argument.value, expectedType); | |
| 270 found = true; | |
| 271 break; | |
| 272 } | |
| 273 } | |
| 274 if (!found) { | |
| 275 fail(argument.value, 'Unexpected named parameter: ${argument.name}'); | |
| 276 return const BottomType(); | |
| 277 } | |
| 278 } | |
| 279 return substitution.substituteType(function.returnType); | |
| 280 } | |
| 281 | |
| 282 DartType _getYieldType(FunctionNode function) { | |
| 283 switch (function.asyncMarker) { | |
| 284 case AsyncMarker.Sync: | |
| 285 case AsyncMarker.Async: | |
| 286 return null; | |
| 287 | |
| 288 case AsyncMarker.SyncStar: | |
| 289 case AsyncMarker.AsyncStar: | |
| 290 Class container = function.asyncMarker == AsyncMarker.SyncStar | |
| 291 ? coreTypes.iterableClass | |
| 292 : coreTypes.streamClass; | |
| 293 DartType returnType = function.returnType; | |
| 294 if (returnType is InterfaceType && returnType.classNode == container) { | |
| 295 return returnType.typeArguments.single; | |
| 296 } | |
| 297 return const DynamicType(); | |
| 298 | |
| 299 case AsyncMarker.SyncYielding: | |
| 300 return function.returnType; | |
| 301 | |
| 302 default: | |
| 303 throw 'Unexpected async marker: ${function.asyncMarker}'; | |
| 304 } | |
| 305 } | |
| 306 | |
| 307 @override | |
| 308 DartType visitAsExpression(AsExpression node) { | |
| 309 visitExpression(node.operand); | |
| 310 return node.type; | |
| 311 } | |
| 312 | |
| 313 @override | |
| 314 DartType visitAwaitExpression(AwaitExpression node) { | |
| 315 return environment.unfutureType(visitExpression(node.operand)); | |
| 316 } | |
| 317 | |
| 318 @override | |
| 319 DartType visitBlockExpression(BlockExpression node) { | |
| 320 visitStatement(node.body); | |
| 321 return visitExpression(node.value); | |
| 322 } | |
| 323 | |
| 324 @override | |
| 325 DartType visitBoolLiteral(BoolLiteral node) { | |
| 326 return environment.boolType; | |
| 327 } | |
| 328 | |
| 329 @override | |
| 330 DartType visitConditionalExpression(ConditionalExpression node) { | |
| 331 checkAssignableExpression(node.condition, environment.boolType); | |
| 332 if (node.staticType == null) { | |
| 333 var thenType = visitExpression(node.then); | |
| 334 var otherwiseType = visitExpression(node.otherwise); | |
| 335 if (thenType is BottomType) return otherwiseType; | |
| 336 if (otherwiseType is BottomType) return thenType; | |
| 337 return const DynamicType(); | |
| 338 } else { | |
| 339 checkAssignableExpression(node.then, node.staticType); | |
| 340 checkAssignableExpression(node.otherwise, node.staticType); | |
| 341 return node.staticType; | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 @override | |
| 346 DartType visitConstructorInvocation(ConstructorInvocation node) { | |
| 347 Constructor target = node.target; | |
| 348 Arguments arguments = node.arguments; | |
| 349 Class class_ = target.enclosingClass; | |
| 350 handleCall(arguments, target.function, | |
| 351 typeParameters: class_.typeParameters); | |
| 352 return new InterfaceType(target.enclosingClass, arguments.types); | |
| 353 } | |
| 354 | |
| 355 @override | |
| 356 DartType visitDirectMethodInvocation(DirectMethodInvocation node) { | |
| 357 return handleCall(node.arguments, node.target.function, | |
| 358 receiver: getReceiverType(node, node.receiver, node.target)); | |
| 359 } | |
| 360 | |
| 361 @override | |
| 362 DartType visitDirectPropertyGet(DirectPropertyGet node) { | |
| 363 var receiver = getReceiverType(node, node.receiver, node.target); | |
| 364 return receiver.substituteType(node.target.getterType); | |
| 365 } | |
| 366 | |
| 367 @override | |
| 368 DartType visitDirectPropertySet(DirectPropertySet node) { | |
| 369 var receiver = getReceiverType(node, node.receiver, node.target); | |
| 370 var value = visitExpression(node.value); | |
| 371 checkAssignable(node, value, | |
| 372 receiver.substituteType(node.target.setterType, contravariant: true)); | |
| 373 return value; | |
| 374 } | |
| 375 | |
| 376 @override | |
| 377 DartType visitDoubleLiteral(DoubleLiteral node) { | |
| 378 return environment.doubleType; | |
| 379 } | |
| 380 | |
| 381 @override | |
| 382 DartType visitFunctionExpression(FunctionExpression node) { | |
| 383 handleNestedFunctionNode(node.function); | |
| 384 return node.function.functionType; | |
| 385 } | |
| 386 | |
| 387 @override | |
| 388 DartType visitIntLiteral(IntLiteral node) { | |
| 389 return environment.intType; | |
| 390 } | |
| 391 | |
| 392 @override | |
| 393 DartType visitInvalidExpression(InvalidExpression node) { | |
| 394 return const BottomType(); | |
| 395 } | |
| 396 | |
| 397 @override | |
| 398 DartType visitIsExpression(IsExpression node) { | |
| 399 visitExpression(node.operand); | |
| 400 return environment.boolType; | |
| 401 } | |
| 402 | |
| 403 @override | |
| 404 DartType visitLet(Let node) { | |
| 405 var value = visitExpression(node.variable.initializer); | |
| 406 if (node.variable.type is DynamicType) { | |
| 407 node.variable.type = value; | |
| 408 } | |
| 409 return visitExpression(node.body); | |
| 410 } | |
| 411 | |
| 412 @override | |
| 413 DartType visitListLiteral(ListLiteral node) { | |
| 414 for (var item in node.expressions) { | |
| 415 checkAssignableExpression(item, node.typeArgument); | |
| 416 } | |
| 417 return environment.literalListType(node.typeArgument); | |
| 418 } | |
| 419 | |
| 420 @override | |
| 421 DartType visitLogicalExpression(LogicalExpression node) { | |
| 422 checkAssignableExpression(node.left, environment.boolType); | |
| 423 checkAssignableExpression(node.right, environment.boolType); | |
| 424 return environment.boolType; | |
| 425 } | |
| 426 | |
| 427 @override | |
| 428 DartType visitMapLiteral(MapLiteral node) { | |
| 429 for (var entry in node.entries) { | |
| 430 checkAssignableExpression(entry.key, node.keyType); | |
| 431 checkAssignableExpression(entry.value, node.valueType); | |
| 432 } | |
| 433 return environment.literalMapType(node.keyType, node.valueType); | |
| 434 } | |
| 435 | |
| 436 DartType handleDynamicCall(DartType receiver, Arguments arguments) { | |
| 437 arguments.positional.forEach(visitExpression); | |
| 438 arguments.named.forEach((NamedExpression n) => visitExpression(n.value)); | |
| 439 return const DynamicType(); | |
| 440 } | |
| 441 | |
| 442 DartType handleFunctionCall( | |
| 443 TreeNode access, FunctionType function, Arguments arguments) { | |
| 444 if (function.requiredParameterCount > arguments.positional.length) { | |
| 445 fail(access, 'Too few positional arguments'); | |
| 446 return const BottomType(); | |
| 447 } | |
| 448 if (function.positionalParameters.length < arguments.positional.length) { | |
| 449 fail(access, 'Too many positional arguments'); | |
| 450 return const BottomType(); | |
| 451 } | |
| 452 if (function.typeParameters.length != arguments.types.length) { | |
| 453 fail(access, 'Wrong number of type arguments'); | |
| 454 return const BottomType(); | |
| 455 } | |
| 456 var instantiation = | |
| 457 Substitution.fromPairs(function.typeParameters, arguments.types); | |
| 458 for (int i = 0; i < arguments.positional.length; ++i) { | |
| 459 var expectedType = instantiation.substituteType( | |
| 460 function.positionalParameters[i], | |
| 461 contravariant: true); | |
| 462 checkAssignableExpression(arguments.positional[i], expectedType); | |
| 463 } | |
| 464 for (int i = 0; i < arguments.named.length; ++i) { | |
| 465 var argument = arguments.named[i]; | |
| 466 var expectedType = function.namedParameters[argument.name]; | |
| 467 if (expectedType == null) { | |
| 468 fail(argument.value, 'Unexpected named parameter: ${argument.name}'); | |
| 469 } else { | |
| 470 checkAssignableExpression(argument.value, | |
| 471 instantiation.substituteType(expectedType, contravariant: true)); | |
| 472 } | |
| 473 } | |
| 474 return instantiation.substituteType(function.returnType); | |
| 475 } | |
| 476 | |
| 477 @override | |
| 478 DartType visitMethodInvocation(MethodInvocation node) { | |
| 479 var target = node.interfaceTarget; | |
| 480 if (target == null) { | |
| 481 var receiver = visitExpression(node.receiver); | |
| 482 return (node.name.name == 'call' && receiver is FunctionType) | |
| 483 ? handleFunctionCall(node, receiver, node.arguments) | |
| 484 : handleDynamicCall(receiver, node.arguments); | |
| 485 } else if (environment.isOverloadedArithmeticOperator(target)) { | |
| 486 assert(node.arguments.positional.length == 1); | |
| 487 var receiver = visitExpression(node.receiver); | |
| 488 var argument = visitExpression(node.arguments.positional[0]); | |
| 489 return environment.getTypeOfOverloadedArithmetic(receiver, argument); | |
| 490 } else { | |
| 491 return handleCall(node.arguments, target.function, | |
| 492 receiver: getReceiverType(node, node.receiver, node.interfaceTarget)); | |
| 493 } | |
| 494 } | |
| 495 | |
| 496 @override | |
| 497 DartType visitPropertyGet(PropertyGet node) { | |
| 498 if (node.interfaceTarget == null) { | |
| 499 visitExpression(node.receiver); | |
| 500 return const DynamicType(); | |
| 501 } else { | |
| 502 var receiver = getReceiverType(node, node.receiver, node.interfaceTarget); | |
| 503 return receiver.substituteType(node.interfaceTarget.getterType); | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 @override | |
| 508 DartType visitPropertySet(PropertySet node) { | |
| 509 var value = visitExpression(node.value); | |
| 510 if (node.interfaceTarget != null) { | |
| 511 var receiver = getReceiverType(node, node.receiver, node.interfaceTarget); | |
| 512 checkAssignable( | |
| 513 node.value, | |
| 514 value, | |
| 515 receiver.substituteType(node.interfaceTarget.setterType, | |
| 516 contravariant: true)); | |
| 517 } else { | |
| 518 visitExpression(node.receiver); | |
| 519 } | |
| 520 return value; | |
| 521 } | |
| 522 | |
| 523 @override | |
| 524 DartType visitNot(Not node) { | |
| 525 visitExpression(node.operand); | |
| 526 return environment.boolType; | |
| 527 } | |
| 528 | |
| 529 @override | |
| 530 DartType visitNullLiteral(NullLiteral node) { | |
| 531 return const BottomType(); | |
| 532 } | |
| 533 | |
| 534 @override | |
| 535 DartType visitRethrow(Rethrow node) { | |
| 536 return const BottomType(); | |
| 537 } | |
| 538 | |
| 539 @override | |
| 540 DartType visitStaticGet(StaticGet node) { | |
| 541 return node.target.getterType; | |
| 542 } | |
| 543 | |
| 544 @override | |
| 545 DartType visitStaticInvocation(StaticInvocation node) { | |
| 546 return handleCall(node.arguments, node.target.function); | |
| 547 } | |
| 548 | |
| 549 @override | |
| 550 DartType visitStaticSet(StaticSet node) { | |
| 551 var value = visitExpression(node.value); | |
| 552 checkAssignable(node.value, value, node.target.setterType); | |
| 553 return value; | |
| 554 } | |
| 555 | |
| 556 @override | |
| 557 DartType visitStringConcatenation(StringConcatenation node) { | |
| 558 node.expressions.forEach(visitExpression); | |
| 559 return environment.stringType; | |
| 560 } | |
| 561 | |
| 562 @override | |
| 563 DartType visitStringLiteral(StringLiteral node) { | |
| 564 return environment.stringType; | |
| 565 } | |
| 566 | |
| 567 @override | |
| 568 DartType visitSuperMethodInvocation(SuperMethodInvocation node) { | |
| 569 if (node.interfaceTarget == null) { | |
| 570 return handleDynamicCall(environment.thisType, node.arguments); | |
| 571 } else { | |
| 572 return handleCall(node.arguments, node.interfaceTarget.function, | |
| 573 receiver: getSuperReceiverType(node.interfaceTarget)); | |
| 574 } | |
| 575 } | |
| 576 | |
| 577 @override | |
| 578 DartType visitSuperPropertyGet(SuperPropertyGet node) { | |
| 579 if (node.interfaceTarget == null) { | |
| 580 return const DynamicType(); | |
| 581 } else { | |
| 582 var receiver = getSuperReceiverType(node.interfaceTarget); | |
| 583 return receiver.substituteType(node.interfaceTarget.getterType); | |
| 584 } | |
| 585 } | |
| 586 | |
| 587 @override | |
| 588 DartType visitSuperPropertySet(SuperPropertySet node) { | |
| 589 var value = visitExpression(node.value); | |
| 590 if (node.interfaceTarget != null) { | |
| 591 var receiver = getSuperReceiverType(node.interfaceTarget); | |
| 592 checkAssignable( | |
| 593 node.value, | |
| 594 value, | |
| 595 receiver.substituteType(node.interfaceTarget.setterType, | |
| 596 contravariant: true)); | |
| 597 } | |
| 598 return value; | |
| 599 } | |
| 600 | |
| 601 @override | |
| 602 DartType visitSymbolLiteral(SymbolLiteral node) { | |
| 603 return environment.symbolType; | |
| 604 } | |
| 605 | |
| 606 @override | |
| 607 DartType visitThisExpression(ThisExpression node) { | |
| 608 return environment.thisType; | |
| 609 } | |
| 610 | |
| 611 @override | |
| 612 DartType visitThrow(Throw node) { | |
| 613 visitExpression(node.expression); | |
| 614 return const BottomType(); | |
| 615 } | |
| 616 | |
| 617 @override | |
| 618 DartType visitTypeLiteral(TypeLiteral node) { | |
| 619 return environment.typeType; | |
| 620 } | |
| 621 | |
| 622 @override | |
| 623 DartType visitVariableGet(VariableGet node) { | |
| 624 return node.promotedType ?? node.variable.type; | |
| 625 } | |
| 626 | |
| 627 @override | |
| 628 DartType visitVariableSet(VariableSet node) { | |
| 629 var value = visitExpression(node.value); | |
| 630 checkAssignable(node.value, value, node.variable.type); | |
| 631 return value; | |
| 632 } | |
| 633 | |
| 634 @override | |
| 635 visitAssertStatement(AssertStatement node) { | |
| 636 visitExpression(node.condition); | |
| 637 if (node.message != null) { | |
| 638 visitExpression(node.message); | |
| 639 } | |
| 640 } | |
| 641 | |
| 642 @override | |
| 643 visitBlock(Block node) { | |
| 644 node.statements.forEach(visitStatement); | |
| 645 } | |
| 646 | |
| 647 @override | |
| 648 visitBreakStatement(BreakStatement node) {} | |
| 649 | |
| 650 @override | |
| 651 visitContinueSwitchStatement(ContinueSwitchStatement node) {} | |
| 652 | |
| 653 @override | |
| 654 visitDoStatement(DoStatement node) { | |
| 655 visitStatement(node.body); | |
| 656 checkAssignableExpression(node.condition, environment.boolType); | |
| 657 } | |
| 658 | |
| 659 @override | |
| 660 visitEmptyStatement(EmptyStatement node) {} | |
| 661 | |
| 662 @override | |
| 663 visitExpressionStatement(ExpressionStatement node) { | |
| 664 visitExpression(node.expression); | |
| 665 } | |
| 666 | |
| 667 @override | |
| 668 visitForInStatement(ForInStatement node) { | |
| 669 var iterable = visitExpression(node.iterable); | |
| 670 // TODO(asgerf): Store interface targets on for-in loops or desugar them, | |
| 671 // instead of doing the ad-hoc resolution here. | |
| 672 if (node.isAsync) { | |
| 673 checkAssignable(node, getStreamElementType(iterable), node.variable.type); | |
| 674 } else { | |
| 675 checkAssignable( | |
| 676 node, getIterableElementType(iterable), node.variable.type); | |
| 677 } | |
| 678 visitStatement(node.body); | |
| 679 } | |
| 680 | |
| 681 static final Name iteratorName = new Name('iterator'); | |
| 682 static final Name nextName = new Name('next'); | |
| 683 | |
| 684 DartType getIterableElementType(DartType iterable) { | |
| 685 if (iterable is InterfaceType) { | |
| 686 var iteratorGetter = | |
| 687 hierarchy.getInterfaceMember(iterable.classNode, iteratorName); | |
| 688 if (iteratorGetter == null) return const DynamicType(); | |
| 689 var iteratorType = Substitution | |
| 690 .fromInterfaceType(iterable) | |
| 691 .substituteType(iteratorGetter.getterType); | |
| 692 if (iteratorType is InterfaceType) { | |
| 693 var nextGetter = | |
| 694 hierarchy.getInterfaceMember(iteratorType.classNode, nextName); | |
| 695 if (nextGetter == null) return const DynamicType(); | |
| 696 return Substitution | |
| 697 .fromInterfaceType(iteratorType) | |
| 698 .substituteType(nextGetter.getterType); | |
| 699 } | |
| 700 } | |
| 701 return const DynamicType(); | |
| 702 } | |
| 703 | |
| 704 DartType getStreamElementType(DartType stream) { | |
| 705 if (stream is InterfaceType) { | |
| 706 var asStream = | |
| 707 hierarchy.getTypeAsInstanceOf(stream, coreTypes.streamClass); | |
| 708 if (asStream == null) return const DynamicType(); | |
| 709 return asStream.typeArguments.single; | |
| 710 } | |
| 711 return const DynamicType(); | |
| 712 } | |
| 713 | |
| 714 @override | |
| 715 visitForStatement(ForStatement node) { | |
| 716 node.variables.forEach(visitVariableDeclaration); | |
| 717 if (node.condition != null) { | |
| 718 checkAssignableExpression(node.condition, environment.boolType); | |
| 719 } | |
| 720 node.updates.forEach(visitExpression); | |
| 721 visitStatement(node.body); | |
| 722 } | |
| 723 | |
| 724 @override | |
| 725 visitFunctionDeclaration(FunctionDeclaration node) { | |
| 726 handleNestedFunctionNode(node.function); | |
| 727 } | |
| 728 | |
| 729 @override | |
| 730 visitIfStatement(IfStatement node) { | |
| 731 checkAssignableExpression(node.condition, environment.boolType); | |
| 732 visitStatement(node.then); | |
| 733 if (node.otherwise != null) { | |
| 734 visitStatement(node.otherwise); | |
| 735 } | |
| 736 } | |
| 737 | |
| 738 @override | |
| 739 visitInvalidStatement(InvalidStatement node) {} | |
| 740 | |
| 741 @override | |
| 742 visitLabeledStatement(LabeledStatement node) { | |
| 743 visitStatement(node.body); | |
| 744 } | |
| 745 | |
| 746 @override | |
| 747 visitReturnStatement(ReturnStatement node) { | |
| 748 if (node.expression != null) { | |
| 749 if (environment.returnType == null) { | |
| 750 fail(node, 'Return from void method'); | |
|
Kevin Millikin (Google)
2016/11/01 13:03:01
Return a value from void method.
asgerf
2016/11/01 13:59:36
Done.
| |
| 751 } else { | |
| 752 checkAssignableExpression(node.expression, environment.returnType); | |
| 753 } | |
| 754 } | |
| 755 } | |
| 756 | |
| 757 @override | |
| 758 visitSwitchStatement(SwitchStatement node) { | |
| 759 visitExpression(node.expression); | |
| 760 for (var switchCase in node.cases) { | |
| 761 switchCase.expressions.forEach(visitExpression); | |
| 762 visitStatement(switchCase.body); | |
| 763 } | |
| 764 } | |
| 765 | |
| 766 @override | |
| 767 visitTryCatch(TryCatch node) { | |
| 768 visitStatement(node.body); | |
| 769 for (var catchClause in node.catches) { | |
| 770 visitStatement(catchClause.body); | |
| 771 } | |
| 772 } | |
| 773 | |
| 774 @override | |
| 775 visitTryFinally(TryFinally node) { | |
| 776 visitStatement(node.body); | |
| 777 visitStatement(node.finalizer); | |
| 778 } | |
| 779 | |
| 780 @override | |
| 781 visitVariableDeclaration(VariableDeclaration node) { | |
| 782 if (node.initializer != null) { | |
| 783 checkAssignableExpression(node.initializer, node.type); | |
| 784 } | |
| 785 } | |
| 786 | |
| 787 @override | |
| 788 visitWhileStatement(WhileStatement node) { | |
| 789 checkAssignableExpression(node.condition, environment.boolType); | |
| 790 visitStatement(node.body); | |
| 791 } | |
| 792 | |
| 793 @override | |
| 794 visitYieldStatement(YieldStatement node) { | |
| 795 checkAssignableExpression(node.expression, environment.yieldType); | |
| 796 } | |
| 797 | |
| 798 @override | |
| 799 visitFieldInitializer(FieldInitializer node) { | |
| 800 checkAssignableExpression(node.value, node.field.type); | |
| 801 } | |
| 802 | |
| 803 @override | |
| 804 visitRedirectingInitializer(RedirectingInitializer node) { | |
| 805 handleCall(node.arguments, node.target.function, | |
| 806 typeParameters: const <TypeParameter>[]); | |
| 807 } | |
| 808 | |
| 809 @override | |
| 810 visitSuperInitializer(SuperInitializer node) { | |
| 811 handleCall(node.arguments, node.target.function, | |
| 812 typeParameters: const <TypeParameter>[]); | |
| 813 } | |
| 814 | |
| 815 @override | |
| 816 visitLocalInitializer(LocalInitializer node) { | |
| 817 visitVariableDeclaration(node.variable); | |
| 818 } | |
| 819 | |
| 820 @override | |
| 821 visitInvalidInitializer(InvalidInitializer node) {} | |
| 822 } | |
| OLD | NEW |