Wiki

Ticket #257: infer-out-param.patch

File infer-out-param.patch, 8.4 KB (added by hopscc, 14 years ago)
  • Source/Expr.cobra

     
    561561                _transformToEnumDecl(dotNode, enumDefi) 
    562562                return 
    563563 
    564         hasErrors = _bindImpArgs 
     564        needInferOutArgs = false 
     565        hasErrors = _bindImpArgs(out needInferOutArgs) 
    565566        definition as IMember? 
    566567        type as IType? 
    567568 
     
    586587                    if not .hasError and not hasErrors 
    587588                        args = _args 
    588589                        if definition inherits MemberOverload 
     590                            #TODO: Correct this dependency; Need args bindImp done to calc bestOverload,  
     591                            # but need best overload to infer (undefined out) variable type 
     592                            if needInferOutArgs  
     593                                moDefn = definition.members[0] # not guaranteed to have the best param list  
     594                                if moDefn.hasParams 
     595                                    params = moDefn.params 
     596                                    _inferOutArgs(args, params, true)    
     597                                     
    589598                            # Note that overloads can have different return types (even if more than just the return type is needed to distinguish them). Example: Math. 
    590599                            # Plenty of info here: 
    591600                            # http://www.google.com/search?hl=en&q=C%23+overloaded+method+resolution 
     
    602611                                else 
    603612                                    params = definition.params 
    604613                                    if _checkParamsCount(definition, args, params) 
     614                                        if needInferOutArgs, _inferOutArgs(args, params, false)  
    605615                                        _checkArgsAssignable(definition, args, params) 
    606616                        _checkInOutArguments 
    607617                else 
     
    681691        _type = postCall.type 
    682692        transformTarget._transformTo(postCall) 
    683693 
    684     def _bindImpArgs as bool 
     694    def _undefinedOutArg(arg as Expr) as bool 
     695        if arg.direction == Direction.Out   
     696            if arg inherits IdentifierExpr 
     697                if not arg.findDefinition  
     698                    return true 
     699        return false 
     700                 
     701    #do bindImp on callExprs args.  
     702    #Defer binding on undefined out args but flag that inference needed (later see _bindImp) 
     703    def _bindImpArgs(needInferOutArgs as out bool) as bool 
    685704        hasErrors = false 
     705        needInferOutArgs = false 
    686706        num = 1 
    687707        for arg in List<of Expr>(_args) 
    688708            try 
    689709                if arg inherits AssignExpr 
    690710                    arg.right.bindImp  # 'x=y' has special treatment in arguments 
    691711                else 
    692                     boundArg = arg.bindImp 
    693                     if boundArg is not arg  # transformation 
    694                         _args[num-1] = boundArg 
    695                     assert boundArg.didBindImp 
     712                    if needInferOutArgs == false  and _undefinedOutArg(arg) 
     713                        #print 'No explicit prior definition for out-arg [arg.name]'     
     714                        needInferOutArgs = true 
     715                        continue 
     716                    _bindImpArg(num, arg) 
    696717            catch ne as NodeException 
    697718                ne.prefixMessage('For "[_name]" arg [num]: ') 
    698719                .compiler.recordError(ne) 
     
    700721            num += 1 
    701722        return hasErrors 
    702723 
     724    def _bindImpArg(num as int, arg as Expr) 
     725        boundArg = arg.bindImp 
     726        if boundArg is not arg  # transformation 
     727            _args[num-1] = boundArg 
     728        assert boundArg.didBindImp 
     729 
    703730    def _didVariArgs(definition as BoxMember) as bool 
    704731        hasVari = false 
    705732        for param in definition.params 
     
    729756                .throwError('The method "[definition.name]" is expecting [params.count] argument[Utils.plural(params)], but [args.count] are being supplied in this call.') 
    730757        return true 
    731758                 
     759    def _inferOutArgs(args as IList<of Expr>, params as IList<of Param>, warn as bool)  
     760        num = 0 
     761        for arg in args 
     762            if _undefinedOutArg(arg) 
     763                #inferredType = .compiler.nilableType(params[num].type) # always nilable? (e.g. dict.tryGetValue) 
     764                inferredType = params[num].type   
     765                newDefn = LocalVar(arg.token, inferredType).bindAll to LocalVar  
     766                .compiler.codeMemberStack.peek.addLocal(newDefn) 
     767                (arg to IdentifierExpr).setDefinition(newDefn) 
     768                arg.type = newDefn.type 
     769                _bindImpArg(num, arg) 
     770                # the warning below is temporary till we sort out arg.bindImp vs calculating Best overload 
     771                if warn  
     772                    sugg = 'You may want to disambiguate this by declaring the variable explicitly.' 
     773                    .compiler.warning(this, 'Cobra has implicitly declared a variable "[newDefn.name] as [newDefn.type.name]" for an undefined out parameter variable but the type inferred may not be correct as the method called is overloaded. [sugg]') 
     774                     
     775            num += 1     
     776             
    732777    def _checkArgsAssignable(definition as BoxMember, args as IList<of Expr>, params as IList<of Param>)  
    733778        for i in args.count 
    734779            arg = args[i] 
     
    10251070    def _bindImp is override 
    10261071        base._bindImp 
    10271072        if _definition is nil 
     1073            _definition = .findDefinition  
    10281074            canBeUndottedMember = _name.canBeUndottedMemberName 
    1029             if canBeUndottedMember 
    1030                 # assert .compiler.boxStack.count TODO: identifier expr is being used by PostCallExpr which is used for attribute calls 
    1031                 _definition = .compiler.symbolForName(_name, false) 
    1032             else 
    1033                 # local var ref: foo 
    1034                 if .compiler.codeMemberStack.count  # could be: var _x = y   or: has foo 
    1035                     _definition = .compiler.findLocal(_name) 
    1036                 if _definition is nil 
    1037                     _definition  = .compiler.symbolForName(_name, false, true)  # 3rd party DLLs have lowercase class names like iConnection 
    10381075            if _definition is nil and not canBeUndottedMember 
    10391076                if _superNode inherits BinaryOpExpr 
    10401077                    if _superNode.right is this 
     
    10591096                    .throwUnknownIdError 
    10601097                    throw FallThroughException() 
    10611098 
     1099    def findDefinition as INamedNode? 
     1100        definition as INamedNode? 
     1101        canBeUndottedMember = _name.canBeUndottedMemberName 
     1102        if canBeUndottedMember 
     1103            # assert .compiler.boxStack.count TODO: identifier expr is being used by PostCallExpr which is used for attribute calls 
     1104            definition = .compiler.symbolForName(_name, false) 
     1105        else 
     1106            # local var ref: foo 
     1107            if .compiler.codeMemberStack.count  # could be: var _x = y   or: has foo 
     1108                definition = .compiler.findLocal(_name) 
     1109            if definition is nil 
     1110                definition  = .compiler.symbolForName(_name, false, true)  # 3rd party DLLs have lowercase class names like iConnection 
     1111        return definition        
     1112 
    10621113    def bindImp as dynamic 
    10631114        base.bindImp 
    10641115        if _definition and not (.superNode inherits AbstractAssignExpr and .binarySuperNode.left is this) 
  • Tests/300-type-inference/610-out-param.cobra

     
     1# Implicit creation of an undefined out param variable. 
     2# The warnings generated below are intended to be a temporary situation 
     3class DefaultOut 
     4     
     5    var _x = '' 
     6     
     7    def main 
     8        # User method 
     9        x = .getX( true, out s0) 
     10        assert x 
     11        assert s0 == '.BigWalla' 
     12        x =.getX( false, out s0) 
     13        assert not x  
     14        assert s0 == '.' 
     15     
     16        s1='something Ignored' 
     17        x = .getX( true, out s1) 
     18        assert x  
     19        assert s1 =='.BigWalla' 
     20        x =.getX( false, out s1) 
     21        assert not x  
     22        assert s1 == '.' 
     23     
     24        # (nilable) variable in a  system method 
     25        d= {'a':'A1', 'b':'B2', 'c':'See3'} 
     26        #result as String? 
     27        x = d.tryGetValue('b', out result) 
     28        assert x  
     29        assert result =='B2' 
     30        x = d.tryGetValue('d', out result) 
     31        assert not x 
     32        assert not result   #nil 
     33        result = nil 
     34     
     35        # out to a method Parameter 
     36        r = '' 
     37        .testParam(d, r) 
     38        assert r == '' 
     39     
     40        # out to a field 
     41        assert _x == '' 
     42        ok = d.tryGetValue('a', out _x) 
     43        assert ok 
     44        assert _x == 'A1' 
     45         
     46        # out in an overloaded method successfully 
     47        ok = int.tryParse('986', out iVal)  # .warning. implicitly declared a variable 
     48        assert ok 
     49        assert iVal == 986 
     50     
     51        # out in an overloaded method unsuccessfully 
     52        ok = int.tryParse('noway', out iVal1) # .warning. implicitly declared a variable 
     53        assert not ok 
     54        assert iVal1 == 0 
     55     
     56         
     57    def getX( flag as bool, z as  out String) as bool 
     58        z='.' 
     59        if flag 
     60            z = z + 'BigWalla' 
     61            return true 
     62        return false 
     63         
     64    def testParam(d as Dictionary<of String, String>, x as String) 
     65        ok = d.tryGetValue('a', out x) 
     66        assert ok 
     67        assert x == 'A1' 
     68         
  • Developer/IntermediateReleaseNotes.text

     
    6666 
    6767* Structs have implicit parameterless initializers and do not allow explicit ones to be declared. 
    6868 
     69* First use of a variable as an out parameter in a method call implicitly declares the variable: ticket 257 
    6970 
    7071================================================================================ 
    7172Library