From: Craig Citro Date: Tue, 9 Feb 2010 18:03:52 +0000 (-0800) Subject: Fix trac #506 -- error with overloading, pops up in cpp_classes X-Git-Tag: 0.13.beta0~349^2~8 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=34741cc4178c0740355dc9c3bbf2ae8325e7e3bc;p=cython.git Fix trac #506 -- error with overloading, pops up in cpp_classes --- diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index af0a01dd..1be454f7 100755 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -2227,49 +2227,78 @@ def is_promotion(src_type, dst_type): def best_match(args, functions, pos=None): """ - Finds the best function to be called - Error if no function fits the call or an ambiguity is find (two or more possible functions) + Given a list args of arguments and a list of functions, choose one + to call which seems to be the "best" fit for this list of arguments. + This function is used, e.g., when deciding which overloaded method + to dispatch for C++ classes. + + We first eliminate functions based on arity, and if only one + function has the correct arity, we return it. Otherwise, we weight + functions based on how much work must be done to convert the + arguments, with the following priorities: + * identical types or pointers to identical types + * promotions + * non-Python types + That is, we prefer functions where no arguments need converted, + and failing that, functions where only promotions are required, and + so on. + + If no function is deemed a good fit, or if two or more functions have + the same weight, we return None (as there is no best match). If pos + is not None, we also generate an error. """ # TODO: args should be a list of types, not a list of Nodes. actual_nargs = len(args) - possibilities = [] - bad_types = 0 - from_type = None - target_type = None + + candidates = [] + errors = [] for func in functions: + error_mesg = "" func_type = func.type if func_type.is_ptr: func_type = func_type.base_type # Check function type if not func_type.is_cfunction: if not func_type.is_error and pos is not None: - error(pos, "Calling non-function type '%s'" % func_type) - return None + error_mesg = "Calling non-function type '%s'" % func_type + errors.append((func, error_mesg)) + continue # Check no. of args max_nargs = len(func_type.args) min_nargs = max_nargs - func_type.optional_arg_count - if actual_nargs < min_nargs \ - or (not func_type.has_varargs and actual_nargs > max_nargs): - if max_nargs == min_nargs and not func_type.has_varargs: - expectation = max_nargs - elif actual_nargs < min_nargs: - expectation = "at least %s" % min_nargs - else: - expectation = "at most %s" % max_nargs - error_str = "Call with wrong number of arguments (expected %s, got %s)" \ - % (expectation, actual_nargs) - continue - if len(functions) == 1: - # Optimize the most common case of no overloading... - return func + if actual_nargs < min_nargs or \ + (not func_type.has_varargs and actual_nargs > max_nargs): + if max_nargs == min_nargs and not func_type.has_varargs: + expectation = max_nargs + elif actual_nargs < min_nargs: + expectation = "at least %s" % min_nargs + else: + expectation = "at most %s" % max_nargs + error_mesg = "Call with wrong number of arguments (expected %s, got %s)" \ + % (expectation, actual_nargs) + errors.append((func, error_mesg)) + continue + candidates.append((func, func_type)) + + # Optimize the most common case of no overloading... + if len(candidates) == 1: + return candidates[0] + elif len(candidates) == 0: + if len(errors) == 1 and pos is not None: + error(pos, errors[0][1]) + return None + + possibilities = [] + bad_types = [] + for func, func_type in candidates: score = [0,0,0] for i in range(min(len(args), len(func_type.args))): src_type = args[i].type dst_type = func_type.args[i].type if dst_type.assignable_from(src_type): if src_type == dst_type or (dst_type.is_reference and \ - src_type == dst_type.base_type) or \ - dst_type.same_as(src_type): + src_type == dst_type.base_type) \ + or dst_type.same_as(src_type): pass # score 0 elif is_promotion(src_type, dst_type): score[2] += 1 @@ -2278,13 +2307,13 @@ def best_match(args, functions, pos=None): else: score[0] += 1 else: - bad_types = func - from_type = src_type - target_type = dst_type + error_mesg = "Invalid conversion from '%s' to '%s'"%(src_type, + dst_type) + bad_types.append((func, error_mesg)) break else: possibilities.append((score, func)) # so we can sort it - if len(possibilities): + if possibilities: possibilities.sort() if len(possibilities) > 1 and possibilities[0][0] == possibilities[1][0]: if pos is not None: @@ -2292,10 +2321,10 @@ def best_match(args, functions, pos=None): return None return possibilities[0][1] if pos is not None: - if bad_types: - error(pos, "Invalid conversion from '%s' to '%s'" % (from_type, target_type)) + if len(bad_types) == 1: + error(pos, bad_types[0][1]) else: - error(pos, error_str) + error(pos, "no suitable method found") return None