| | 412 | _bindImpArgs |
| | 413 | definition as INamedNode? |
| | 414 | type as IType? |
| | 415 | |
| | 416 | if _definition is nil or _type is nil |
| | 417 | # handle foo.bar() where this is the `bar()` part |
| | 418 | if not dotNode.left.didBindImp |
| | 419 | assert dotNode.left.hasError, dotNode.left |
| | 420 | # we get here for Cobra code like "obj.foo.bar(x)" where "foo" is not found |
| | 421 | _type = .compiler.passThroughType |
| | 422 | return # TODO: I don't think I want a return here |
| | 423 | |
| | 424 | definition = _inferDefinitionAndType(dotNode, definition, out type) |
| | 425 | assert type |
| | 426 | if definition inherits IType # Box, IVar or GenericParam |
| | 427 | _transformToPostCallExprOnType(dotNode, definition) |
| | 428 | return |
| | 429 | |
| | 430 | if definition |
| | 431 | # definition should never be a Box, IVar or GenericParam as those cases would be handled by the transformation to PostCallExpr above |
| | 432 | # there is a FallThroughException down below that would report this if it happened |
| | 433 | if definition inherits BoxMember |
| | 434 | if not .hasError |
| | 435 | args = _args |
| | 436 | if definition inherits MemberOverload |
| | 437 | # Note that overloads can have different return types (even if more than just the return type is needed to distinguish them). Example: Math. |
| | 438 | # Plenty of info here: |
| | 439 | # http://www.google.com/search?hl=en&q=C%23+overloaded+method+resolution |
| | 440 | # TODO: handle type inference for generic members. See the C# spec for details. |
| | 441 | |
| | 442 | winner = _computeBestOverload(definition, args) |
| | 443 | sharp'definition = winner' |
| | 444 | type = winner.resultType |
| | 445 | else |
| | 446 | type = definition.resultType |
| | 447 | if not _didVariArgs(definition) |
| | 448 | definition = _checkGenerics(definition, inout type) |
| | 449 | if not definition.hasParams |
| | 450 | _checkNoArgs(definition, args) |
| | 451 | else |
| | 452 | params = definition.params |
| | 453 | if _checkParamsCount(definition, args, params) |
| | 454 | _checkArgsAssignable(definition, args, params) |
| | 455 | else |
| | 456 | throw FallThroughException(definition) |
| | 457 | if type is nil, type = .compiler.passThroughType |
| | 458 | _definition = definition |
| | 459 | if _definition, _definition.isUsed = true |
| | 460 | _type = type |
| | 461 | assert _type, _definition |
| | 462 | if .args.count == 0 and .hasParens |
| | 463 | if (.definition inherits Method or .definition inherits MemberOverload) and not dotNode.isImplicit |
| | 464 | .compiler.warning(this, 'Unnecessary parentheses. You can remove them.') |
| | 465 | else |
| | 466 | for arg in _args |
| | 467 | if arg inherits AssignExpr |
| | 468 | .recordError('Cannot make assignments in arguments. This syntax is reserved in the future for keyword arguments.') |
| | 469 | |
| | 470 | ## _bindImp support |
| | 471 | |
| | 472 | def _setGenericArgTypes |
| | 473 | _genericArgTypes = List<of IType>() |
| | 474 | num = 1 |
| | 475 | for typeProxy in _genericArgProxies |
| | 476 | try |
| | 477 | _genericArgTypes.add(typeProxy.realType) |
| | 478 | catch ne as NodeException |
| | 479 | ne.prefixMessage('For "[_name]" type arg [num]: ') |
| | 480 | .compiler.recordError(ne) |
| | 481 | num += 1 |
| | 482 | # TODO: Check types are compatible with call |
| | 483 | |
| | 484 | def _transformToEnumDecl(dotNode as DotExpr, enumDefi as EnumDecl) |
| | 485 | # Foo.EnumType(MemberA, MemberB) |
| | 486 | # Change to an EnumCallExpr |
| | 487 | # Roll up Foo.Bar.Baz() into one EnumCallExpr |
| | 488 | transformTarget = dotNode |
| | 489 | while transformTarget.superNode inherits DotExpr |
| | 490 | transformTarget = transformTarget.superNode to DotExpr |
| | 491 | enumCall = EnumCallExpr(.token, _name, _args, enumDefi).bindImp |
| | 492 | _type = enumCall.type to IType |
| | 493 | transformTarget._transformTo(enumCall) |
| | 494 | |
| | 495 | def _inferDefinitionAndType(dotNode as DotExpr, definition as INamedNode?, type as out IType?) as INamedNode? |
| | 496 | type = nil |
| | 497 | possibleDefinition = dotNode.left.memberForName(_name) |
| | 498 | if possibleDefinition is nil |
| | 499 | lrt = dotNode.left.receiverType |
| | 500 | if lrt.isDynamic |
| | 501 | type = .compiler.nilableDynamicType |
| | 502 | else if lrt is .compiler.passThroughType |
| | 503 | if _name == 'toString' |
| | 504 | type = .compiler.stringType |
| | 505 | else |
| | 506 | type = .compiler.passThroughType |
| | 507 | else |
| | 508 | left = dotNode.left |
| | 509 | suggs = left.suggestionsForBadMemberName(_name) |
| | 510 | isBoxAccess = left inherits IdentifierExpr and (left to IdentifierExpr).definition inherits Box # like "Console.writeLine" where the receiver is a literal class or struct reference |
| | 511 | whoseTypeIs = if(isBoxAccess, '', ' whose type is "[left.receiverType.name]"') |
| | 512 | .throwError('Cannot find a definition for "[_name]" in "[left.toCobraSource]"[whoseTypeIs].[_suggestionsMessage(suggs)]') |
| | 513 | else |
| | 514 | if not possibleDefinition.isCallable |
| | 515 | .throwError('Cannot call "[_name]" because it is a "[possibleDefinition.englishName]".') |
| | 516 | definition = possibleDefinition |
| | 517 | type = possibleDefinition.resultType |
| | 518 | return definition |
| | 519 | |
| | 520 | def _transformToPostCallExprOnType(dotNode, definition as IType?) |
| | 521 | # for Foo.Bar() where Bar is a box/type |
| | 522 | # Change to a PostCallExpr on the type 'Foo.Bar' |
| | 523 | |
| | 524 | # Roll up Foo.Bar.Baz() into one PostCallExpr |
| | 525 | transformTarget = dotNode |
| | 526 | while transformTarget.superNode inherits DotExpr |
| | 527 | transformTarget = transformTarget.superNode to DotExpr |
| | 528 | |
| | 529 | postCall = PostCallExpr(.token, TypeExpr(.token, definition), .args).bindImp |
| | 530 | _type = postCall.type to IType |
| | 531 | transformTarget._transformTo(postCall) |
| | 532 | |
| | 533 | def _bindImpArgs |
| 444 | | definition as INamedNode? |
| 445 | | type as IType? |
| 446 | | |
| 447 | | if _definition is nil or _type is nil |
| 448 | | checkName = _name |
| 449 | | while checkName.startsWith('_') |
| 450 | | checkName = checkName.substring(1) |
| 451 | | # handle foo.bar() where this is the `bar()` part |
| 452 | | if not dotNode.left.didBindImp |
| 453 | | assert dotNode.left.hasError, dotNode.left |
| 454 | | # we get here for Cobra code like "obj.foo.bar(x)" where "foo" is not found |
| 455 | | _type = .compiler.passThroughType |
| 456 | | return # TODO: I don't think I want a return here |
| 457 | | possibleDefinition = dotNode.left.memberForName(_name) |
| 458 | | if possibleDefinition is nil |
| 459 | | lrt = dotNode.left.receiverType |
| 460 | | if lrt.isDynamic |
| 461 | | type = .compiler.nilableDynamicType |
| 462 | | else if lrt is .compiler.passThroughType |
| 463 | | if _name == 'toString' |
| 464 | | type = .compiler.stringType |
| | 546 | def _computeBestOverload(definition as MemberOverload, args as List<of Expr>) as BoxMember |
| | 547 | # I cooked up the algorithm below as a quick way to fix some bugs caused by the previous |
| | 548 | # implementation of um, doing nothing. |
| | 549 | # But this likely needs to be rewritten. |
| | 550 | candidates = [] |
| | 551 | # handle generic arguments to the method |
| | 552 | genericArgTypes = .genericArgTypes |
| | 553 | if genericArgTypes and genericArgTypes.count |
| | 554 | members = List<of BoxMember>() |
| | 555 | for member as BoxMember? in definition.members |
| | 556 | if member inherits Method |
| | 557 | if member.containsGenericParameters |
| | 558 | if member.genericParams.count == genericArgTypes.count |
| | 559 | member = member.constructedMethodWith(genericArgTypes to !) |
| | 560 | else |
| | 561 | member = nil |
| | 562 | if member, members.add(member) |
| | 563 | else |
| | 564 | members = definition.members |
| | 565 | |
| | 566 | for member in members |
| | 567 | score = -1000 |
| | 568 | if member.params.count == args.count |
| | 569 | score = 0 |
| | 570 | i = 0 |
| | 571 | for param in member.params |
| | 572 | arg = args[i] |
| | 573 | if not arg.didBindImp |
| | 574 | trace arg |
| | 575 | trace arg.hasError |
| | 576 | if arg.type == param.type |
| | 577 | score += 20 |
| | 578 | else if arg.canBeAssignedTo(param.type) |
| | 579 | score += 10 |
| | 580 | else if arg.type inherits NilableType and (arg.type to NilableType).theWrappedType.isAssignableTo(param.type) |
| | 581 | # Cobra's code and data flow analysis sometimes leaves us with a nilable type that's not actually nil anymore |
| | 582 | # due to an assignment, possibly wrapped in an if statement. Eventually this will be corrected, but for now |
| | 583 | # compensate here. |
| | 584 | score += 1 |
| 466 | | type = .compiler.passThroughType |
| | 586 | score -= 100 |
| | 587 | i += 1 |
| | 588 | # print 'candidate:', score, member.name, member.serialNum, Utils.join(', ', (for param in member.params get param.type.name)) |
| | 589 | candidates.add([score, member]) |
| | 590 | |
| | 591 | maxScore = -10_000 |
| | 592 | winner = nil to BoxMember? |
| | 593 | for pair in candidates |
| | 594 | if pair[0] to int > maxScore |
| | 595 | maxScore = pair[0] to int |
| | 596 | winner = pair[1] to BoxMember |
| | 597 | if false |
| | 598 | print |
| | 599 | trace .token.fileName |
| | 600 | trace maxScore, _name |
| | 601 | trace .token.toTechString |
| | 602 | trace winner |
| | 603 | print 'args:' |
| | 604 | for arg in args |
| | 605 | print ' [arg]' |
| | 606 | print 'params:' |
| | 607 | for param in winner.params |
| | 608 | print ' [param]' |
| | 609 | print 'overloads:' |
| | 610 | for member in definition.members |
| | 611 | print ' [member]' |
| | 612 | # print 'winner:', score, winner |
| | 613 | assert winner |
| | 614 | return winner to ! |
| | 615 | |
| | 616 | def _didVariArgs(definition as BoxMember) as bool |
| | 617 | hasVari = false |
| | 618 | for param in definition.params |
| | 619 | if param.type inherits VariType |
| | 620 | hasVari = true |
| | 621 | break |
| | 622 | if hasVari |
| | 623 | # TODO handle variable number of args (4) |
| | 624 | return true |
| | 625 | else |
| | 626 | return false |
| | 627 | |
| | 628 | def _checkGenerics(definition as BoxMember, type as inout IType?) as BoxMember |
| | 629 | if .genericArgTypes and .genericArgTypes.count |
| | 630 | if definition inherits Method |
| | 631 | definition = definition.constructedMethodWith(.genericArgTypes to !) |
| | 632 | type = definition.resultType |
| | 633 | else |
| | 634 | .throwError('Cannot pass type arguments to "[definition.name]" because it is a [definition.getType.name].') # @@ _definition.englishName; also might need this error in other locations |
| | 635 | return definition |
| | 636 | |
| | 637 | def _checkNoArgs(definition as BoxMember, args as List<of Expr>) |
| | 638 | if .name == 'toString' |
| | 639 | # HACK |
| | 640 | # this enables someFloat.toString('0.0') |
| | 641 | # can remove when primitives know their CLR types and look up their methods |
| | 642 | pass |
| | 643 | else if args.count |
| | 644 | .throwError('The method "[definition.name]" is expecting 0 arguments, but [args.count] are being supplied in this call.') |
| | 645 | |
| | 646 | def _checkParamsCount(definition as BoxMember, args as List<of Expr>, params as List<of Param>) as bool |
| | 647 | if args.count <> params.count |
| | 648 | if _name=='toString' # TODO because many structs like Decimal have a toString() overload which cannot currently be expressed in SystemInterfaces.cobra |
| | 649 | return false |
| | 650 | else |
| | 651 | .throwError('The method "[definition.name]" is expecting [params.count] argument[Utils.plural(params)], but [args.count] are being supplied in this call.') |
| | 652 | return true |
| | 653 | |
| | 654 | def _checkArgsAssignable(definition as BoxMember, args as List<of Expr>, params as List<of Param>) |
| | 655 | for i in args.count |
| | 656 | arg = args[i] |
| | 657 | param = params[i] |
| | 658 | if arg.hasError |
| | 659 | break |
| | 660 | if arg inherits AssignExpr # assignments in arguments have special treatment |
| | 661 | break |
| | 662 | assert arg.didBindImp, arg |
| | 663 | assert param.didBindInt, param |
| | 664 | if arg.canBeAssignedTo(param.type) |
| | 665 | arg.contextType = param.type |
| | 666 | else |
| | 667 | if false |
| | 668 | print |
| | 669 | print '<> definition = [definition]' |
| | 670 | print '<> arg = ' stop |
| | 671 | arg.writeDeepString |
| | 672 | print '<> arg.type =', arg.type |
| | 673 | print '<> param = ' stop |
| | 674 | param.writeDeepString |
| | 675 | print '<> param.type =', param.type |
| | 676 | print '<> param.ifInheritsStack.count =', param.ifInheritsStack.count |
| | 677 | if arg.type inherits NilableType and not param.type inherits NilableType and (arg.type to NilableType).theWrappedType.isAssignableTo(param.type) |
| | 678 | .throwError('Argument [i+1] of method "[_name]" expects a non-nilable type ([param.type.name]), but the call is supplying a nilable type ([arg.type.name]).') |
| 468 | | left = dotNode.left |
| 469 | | suggs = left.suggestionsForBadMemberName(_name) |
| 470 | | isBoxAccess = left inherits IdentifierExpr and (left to IdentifierExpr).definition inherits Box # like "Console.writeLine" where the receiver is a literal class or struct reference |
| 471 | | whoseTypeIs = if(isBoxAccess, '', ' whose type is "[left.receiverType.name]"') |
| 472 | | .throwError('Cannot find a definition for "[_name]" in "[left.toCobraSource]"[whoseTypeIs].[_suggestionsMessage(suggs)]') |
| 473 | | else |
| 474 | | if not possibleDefinition.isCallable |
| 475 | | .throwError('Cannot call "[_name]" because it is a "[possibleDefinition.englishName]".') |
| 476 | | definition = possibleDefinition |
| 477 | | type = possibleDefinition.resultType |
| 478 | | assert type |
| 479 | | # assert definition |
| 480 | | if definition inherits IType |
| 481 | | # Foo.Bar() where Bar is a box/type |
| 482 | | # Change to a PostCallExpr on the type 'Foo.Bar' |
| 483 | | |
| 484 | | # Roll up Foo.Bar.Baz() into one PostCallExpr |
| 485 | | transformTarget = dotNode |
| 486 | | while transformTarget.superNode inherits DotExpr |
| 487 | | transformTarget = transformTarget.superNode to DotExpr |
| 488 | | |
| 489 | | postCall = PostCallExpr(.token, TypeExpr(.token, definition), .args).bindImp |
| 490 | | _type = postCall.type to IType |
| 491 | | transformTarget._transformTo(postCall) |
| 492 | | return |
| 493 | | if definition |
| 494 | | # definition should never be a Box, IVar or GenericParam as those cases would be handled by the transformation to PostCallExpr above |
| 495 | | # there is a FallThroughException down below that would report this if it happened |
| 496 | | if definition inherits BoxMember |
| 497 | | if not .hasError |
| 498 | | args = _args |
| 499 | | if definition inherits MemberOverload |
| 500 | | # Note that overloads can have different return types (even if more than just the return type is needed to distinguish them). Example: Math. |
| 501 | | # Plenty of info here: |
| 502 | | # http://www.google.com/search?hl=en&q=C%23+overloaded+method+resolution |
| 503 | | |
| 504 | | # TODO: handle type inference for generic members. See the C# spec for details. |
| 505 | | |
| 506 | | # I cooked up the algorithm below as a quick way to fix some bugs caused by the previous implementation of um, doing nothing. |
| 507 | | # But this likely needs to be rewritten. |
| 508 | | candidates = [] |
| 509 | | |
| 510 | | # handle generic arguments to the method |
| 511 | | genericArgTypes = .genericArgTypes |
| 512 | | if genericArgTypes and genericArgTypes.count |
| 513 | | members = List<of BoxMember>() |
| 514 | | for member as BoxMember? in definition.members |
| 515 | | if member inherits Method |
| 516 | | if member.containsGenericParameters |
| 517 | | if member.genericParams.count == genericArgTypes.count |
| 518 | | member = member.constructedMethodWith(genericArgTypes to !) |
| 519 | | else |
| 520 | | member = nil |
| 521 | | if member, members.add(member) |
| 522 | | else |
| 523 | | members = definition.members |
| 524 | | |
| 525 | | for member in members |
| 526 | | if member.params.count <> args.count |
| 527 | | score = -1000 |
| 528 | | else |
| 529 | | score = 0 |
| 530 | | i = 0 |
| 531 | | for param in member.params |
| 532 | | arg = args[i] |
| 533 | | if not arg.didBindImp |
| 534 | | trace arg |
| 535 | | trace arg.hasError |
| 536 | | if arg.type == param.type |
| 537 | | score += 20 |
| 538 | | else if arg.canBeAssignedTo(param.type) |
| 539 | | score += 10 |
| 540 | | else if arg.type inherits NilableType and (arg.type to NilableType).theWrappedType.isAssignableTo(param.type) |
| 541 | | # Cobra's code and data flow analysis sometimes leaves us with a nilable type that's not actually nil anymore |
| 542 | | # due to an assignment, possibly wrapped in an if statement. Eventually this will be corrected, but for now |
| 543 | | # compensate here. |
| 544 | | score += 1 |
| 545 | | else |
| 546 | | score -= 100 |
| 547 | | i += 1 |
| 548 | | # print 'candidate:', score, member.name, member.serialNum, Utils.join(', ', (for param in member.params get param.type.name)) |
| 549 | | candidates.add([score, member]) |
| 550 | | maxScore = -10_000 |
| 551 | | winner = nil to BoxMember? |
| 552 | | for pair in candidates |
| 553 | | if pair[0] to int > maxScore |
| 554 | | maxScore = pair[0] to int |
| 555 | | winner = pair[1] to BoxMember |
| 556 | | if false |
| 557 | | print |
| 558 | | trace .token.fileName |
| 559 | | trace maxScore, _name |
| 560 | | trace .token.toTechString |
| 561 | | trace winner |
| 562 | | print 'args:' |
| 563 | | for arg in args |
| 564 | | print ' [arg]' |
| 565 | | print 'params:' |
| 566 | | for param in winner.params |
| 567 | | print ' [param]' |
| 568 | | print 'overloads:' |
| 569 | | for member in definition.members |
| 570 | | print ' [member]' |
| 571 | | |