From c0e500d45f4329162af1ac9c7eea266e80881667 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Sat, 13 Sep 2008 15:10:38 -0700 Subject: [PATCH] Fix cpdef cross-module dispatching bug, greatly simplify (also simplify optional args) --- Cython/Compiler/ExprNodes.py | 9 ++++--- Cython/Compiler/Naming.py | 1 - Cython/Compiler/Nodes.py | 49 +++++++++++++++++++++++++---------- Cython/Compiler/PyrexTypes.py | 4 ++- Cython/Compiler/Symtab.py | 23 +++++----------- 5 files changed, 50 insertions(+), 36 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 703e1980..9e7e9729 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1887,6 +1887,9 @@ class SimpleCallNode(CallNode): arg_code = actual_arg.result_as(formal_arg.type) arg_list_code.append(arg_code) + if func_type.is_overridable: + arg_list_code.append(str(int(self.wrapper_call or self.function.entry.is_unbound_cmethod))) + if func_type.optional_arg_count: if expected_nargs == actual_nargs: optional_args = 'NULL' @@ -1898,9 +1901,9 @@ class SimpleCallNode(CallNode): arg_list_code.append(actual_arg.result_code) result = "%s(%s)" % (self.function.result_code, join(arg_list_code, ", ")) - if self.wrapper_call or \ - self.function.entry.is_unbound_cmethod and self.function.entry.type.is_overridable: - result = "(%s = 1, %s)" % (Naming.skip_dispatch_cname, result) +# if self.wrapper_call or \ +# self.function.entry.is_unbound_cmethod and self.function.entry.type.is_overridable: +# result = "(%s = 1, %s)" % (Naming.skip_dispatch_cname, result) return result def generate_result_code(self, code): diff --git a/Cython/Compiler/Naming.py b/Cython/Compiler/Naming.py index 47ec6be3..13d15c45 100644 --- a/Cython/Compiler/Naming.py +++ b/Cython/Compiler/Naming.py @@ -76,7 +76,6 @@ print_function_kwargs = pyrex_prefix + "print_kwargs" cleanup_cname = pyrex_prefix + "module_cleanup" pymoduledef_cname = pyrex_prefix + "moduledef" optional_args_cname = pyrex_prefix + "optional_args" -no_opt_args = pyrex_prefix + "no_opt_args" import_star = pyrex_prefix + "import_star" import_star_set = pyrex_prefix + "import_star_set" cur_scope_cname = pyrex_prefix + "cur_scope" diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 2008f921..99804684 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1027,7 +1027,7 @@ class FuncDefNode(StatNode, BlockNode): code.exit_cfunc_scope() if self.py_func: self.py_func.generate_function_definitions(env, code) - self.generate_optarg_wrapper_function(env, code) + self.generate_wrapper_functions(code) def declare_argument(self, env, arg): if arg.type.is_void: @@ -1036,7 +1036,8 @@ class FuncDefNode(StatNode, BlockNode): error(arg.pos, "Argument type '%s' is incomplete" % arg.type) return env.declare_arg(arg.name, arg.type, arg.pos) - def generate_optarg_wrapper_function(self, env, code): + + def generate_wrapper_functions(self, code): pass def generate_execution_code(self, code): @@ -1093,6 +1094,7 @@ class CFuncDefNode(FuncDefNode): # with_gil boolean Acquire GIL around body # type CFuncType # py_func wrapper for calling from Python + # overridable whether or not this is a cpdef function child_attrs = ["base_type", "declarator", "body", "py_func"] @@ -1188,21 +1190,22 @@ class CFuncDefNode(FuncDefNode): if self.overridable: self.py_func.analyse_expressions(env) - def generate_function_header(self, code, with_pymethdef, with_opt_args = 1): + def generate_function_header(self, code, with_pymethdef, with_opt_args = 1, with_dispatch = 1, cname = None): arg_decls = [] type = self.type visibility = self.entry.visibility for arg in type.args[:len(type.args)-type.optional_arg_count]: arg_decls.append(arg.declaration_code()) + if with_dispatch and self.overridable: + arg_decls.append(PyrexTypes.c_int_type.declaration_code(Naming.skip_dispatch_cname)) if type.optional_arg_count and with_opt_args: arg_decls.append(type.op_arg_struct.declaration_code(Naming.optional_args_cname)) if type.has_varargs: arg_decls.append("...") if not arg_decls: arg_decls = ["void"] - cname = self.entry.func_cname - if not with_opt_args: - cname += Naming.no_opt_args + if cname is None: + cname = self.entry.func_cname entity = type.function_header_code(cname, string.join(arg_decls, ", ")) if visibility == 'public': dll_linkage = "DL_EXPORT" @@ -1279,20 +1282,38 @@ class CFuncDefNode(FuncDefNode): def caller_will_check_exceptions(self): return self.entry.type.exception_check - - def generate_optarg_wrapper_function(self, env, code): - if self.type.optional_arg_count and \ - self.type.original_sig and not self.type.original_sig.optional_arg_count: + + def generate_wrapper_functions(self, code): + # If the C signature of a function has changed, we need to generate + # wrappers to put in the slots here. + k = 0 + entry = self.entry + func_type = entry.type + while entry.prev_entry is not None: + k += 1 + entry = entry.prev_entry + entry.func_cname = "%s%swrap_%s" % (self.entry.func_cname, Naming.pyrex_prefix, k) code.putln() - self.generate_function_header(code, 0, with_opt_args = 0) + self.generate_function_header(code, + 0, + with_dispatch = entry.type.is_overridable, + with_opt_args = entry.type.optional_arg_count, + cname = entry.func_cname) if not self.return_type.is_void: code.put('return ') args = self.type.args arglist = [arg.cname for arg in args[:len(args)-self.type.optional_arg_count]] - arglist.append('NULL') + if entry.type.is_overridable: + arglist.append(Naming.skip_dispatch_cname) + elif func_type.is_overridable: + arglist.append('0') + if entry.type.optional_arg_count: + arglist.append(Naming.optional_args_cname) + elif func_type.optional_arg_count: + arglist.append('NULL') code.putln('%s(%s);' % (self.entry.func_cname, ', '.join(arglist))) code.putln('}') - + class PyArgDeclNode(Node): # Argument which must be a Python object (used @@ -2070,7 +2091,7 @@ class OverrideCheckNode(StatNode): else: self_arg = "((PyObject *)%s)" % self.args[0].cname code.putln("/* Check if called by wrapper */") - code.putln("if (unlikely(%s)) %s = 0;" % (Naming.skip_dispatch_cname, Naming.skip_dispatch_cname)) + code.putln("if (unlikely(%s)) ;" % Naming.skip_dispatch_cname) code.putln("/* Check if overriden in Python */") if self.py_func.is_module_scope: code.putln("else {") diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 2561d9c7..263abb57 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -732,7 +732,7 @@ class CFuncType(CType): return 1 if not other_type.is_cfunction: return 0 - if not self.is_overridable and other_type.is_overridable: + if self.is_overridable != other_type.is_overridable: return 0 nargs = len(self.args) if nargs != len(other_type.args): @@ -846,6 +846,8 @@ class CFuncType(CType): for arg in self.args[:len(self.args)-self.optional_arg_count]: arg_decl_list.append( arg.type.declaration_code("", for_display, pyrex = pyrex)) + if self.is_overridable: + arg_decl_list.append("int %s" % Naming.skip_dispatch_cname) if self.optional_arg_count: arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname)) if self.has_varargs: diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index fd691a7c..9687af61 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -138,6 +138,7 @@ class Entry: utility_code = None is_overridable = 0 buffer_aux = None + prev_entry = None def __init__(self, name, cname, type, pos = None, init = None): self.name = name @@ -277,7 +278,7 @@ class Scope: if name and dict.has_key(name): if visibility == 'extern': warning(pos, "'%s' redeclared " % name, 0) - else: + elif visibility != 'ignore': error(pos, "'%s' redeclared " % name) entry = Entry(name, cname, type, pos = pos) entry.in_cinclude = self.in_cinclude @@ -1411,22 +1412,8 @@ class CClassScope(ClassScope): if type.same_c_signature_as(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil: pass elif type.compatible_signature_with(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil: - if type.optional_arg_count and not type.original_sig.optional_arg_count: - # Need to put a wrapper taking no optional arguments - # into the method table. - wrapper_func_cname = self.mangle(Naming.func_prefix, name) + Naming.no_opt_args - wrapper_func_name = name + Naming.no_opt_args - if entry.type.optional_arg_count: - old_entry = self.lookup_here(wrapper_func_name) - old_entry.func_cname = wrapper_func_cname - else: - entry.func_cname = wrapper_func_cname - entry.name = wrapper_func_name - entry = self.add_cfunction(name, type, pos, cname or name, visibility) - defining = 1 - entry.type = type -# if type.narrower_c_signature_than(entry.type, as_cmethod = 1): -# entry.type = type + entry = self.add_cfunction(name, type, pos, cname or name, visibility='ignore') + defining = 1 else: error(pos, "Signature not compatible with previous declaration") error(entry.pos, "Previous declaration is here") @@ -1442,8 +1429,10 @@ class CClassScope(ClassScope): def add_cfunction(self, name, type, pos, cname, visibility): # Add a cfunction entry without giving it a func_cname. + prev_entry = self.lookup_here(name) entry = ClassScope.add_cfunction(self, name, type, pos, cname, visibility) entry.is_cmethod = 1 + entry.prev_entry = prev_entry return entry def declare_property(self, name, doc, pos): -- 2.26.2