--- contrib/tools/cython_py2/Cython/Compiler/ModuleNode.py (index) +++ contrib/tools/cython_py2/Cython/Compiler/ModuleNode.py (working tree) @@ -1072,6 +1072,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln(" #pragma pack(pop)") code.putln("#endif") + def generate_cpp_constructor_code(self, arg_decls, arg_names, is_implementing, py_attrs, constructor, type, code): + if is_implementing: + code.putln("%s(%s) {" % (type.cname, ", ".join(arg_decls))) + needs_gil = py_attrs or (constructor and not constructor.type.nogil) + if needs_gil: + code.put_ensure_gil() + if py_attrs: + for attr in py_attrs: + code.put_init_var_to_py_none(attr, nanny=False) + if constructor: + code.putln("%s(%s);" % (constructor.cname, ", ".join(arg_names))) + if needs_gil: + code.put_release_ensured_gil() + code.putln("}") + else: + code.putln("%s(%s);" % (type.cname, ", ".join(arg_decls))) + def generate_cpp_class_definition(self, entry, code): code.mark_pos(entry.pos) type = entry.type @@ -1099,7 +1116,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): if attr.type.is_cfunction and attr.type.is_static_method: code.put("static ") elif attr.name == "": - constructor = attr + constructor = scope.lookup_here("") elif attr.name == "": destructor = attr elif attr.type.is_cfunction: @@ -1107,35 +1124,28 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): has_virtual_methods = True code.putln("%s;" % attr.type.declaration_code(attr.cname)) is_implementing = 'init_module' in code.globalstate.parts + if constructor or py_attrs: if constructor: + for constructor_alternative in constructor.all_alternatives(): + arg_decls = [] + arg_names = [] + for arg in constructor_alternative.type.original_args[ + :len(constructor_alternative.type.args)-constructor_alternative.type.optional_arg_count]: + arg_decls.append(arg.declaration_code()) + arg_names.append(arg.cname) + if constructor_alternative.type.optional_arg_count: + arg_decls.append(constructor_alternative.type.op_arg_struct.declaration_code(Naming.optional_args_cname)) + arg_names.append(Naming.optional_args_cname) + if not arg_decls: + default_constructor = True + arg_decls = [] + self.generate_cpp_constructor_code(arg_decls, arg_names, is_implementing, py_attrs, constructor_alternative, type, code) + else: arg_decls = [] arg_names = [] - for arg in constructor.type.original_args[ - :len(constructor.type.args)-constructor.type.optional_arg_count]: - arg_decls.append(arg.declaration_code()) - arg_names.append(arg.cname) - if constructor.type.optional_arg_count: - arg_decls.append(constructor.type.op_arg_struct.declaration_code(Naming.optional_args_cname)) - arg_names.append(Naming.optional_args_cname) - if not arg_decls: - arg_decls = ["void"] - else: - arg_decls = ["void"] - arg_names = [] - if is_implementing: - code.putln("%s(%s) {" % (type.cname, ", ".join(arg_decls))) - if py_attrs: - code.put_ensure_gil() - for attr in py_attrs: - code.put_init_var_to_py_none(attr, nanny=False) - if constructor: - code.putln("%s(%s);" % (constructor.cname, ", ".join(arg_names))) - if py_attrs: - code.put_release_ensured_gil() - code.putln("}") - else: - code.putln("%s(%s);" % (type.cname, ", ".join(arg_decls))) + self.generate_cpp_constructor_code(arg_decls, arg_names, is_implementing, py_attrs, constructor, type, code) + if destructor or py_attrs or has_virtual_methods: if has_virtual_methods: code.put("virtual ") --- contrib/tools/cython_py2/Cython/Compiler/Symtab.py (index) +++ contrib/tools/cython_py2/Cython/Compiler/Symtab.py (working tree) @@ -510,6 +510,66 @@ class Scope(object): yield self.in_c_type_context = old_c_type_context + def handle_already_declared_name(self, name, cname, type, pos, visibility): + """ + Returns an entry or None + + If it returns an entry, it makes sense for "declare" to keep using that + entry and not to declare its own. + + May be overridden (e.g. for builtin scope, + which always allows redeclarations) + """ + entry = None + entries = self.entries + old_entry = entries[name] + + # Reject redeclared C++ functions only if they have a compatible type signature. + cpp_override_allowed = False + if type.is_cfunction and old_entry.type.is_cfunction and self.is_cpp(): + # If we redefine a C++ class method which is either inherited + # or automatically generated (base constructor), then it's fine. + # Otherwise, we shout. + for alt_entry in old_entry.all_alternatives(): + if type.compatible_signature_with(alt_entry.type): + if name == '' and not type.args: + # Cython pre-declares the no-args constructor - allow later user definitions. + cpp_override_allowed = True + elif alt_entry.is_inherited: + # Note that we can override an inherited method with a compatible but not exactly equal signature, as in C++. + cpp_override_allowed = True + if cpp_override_allowed: + # A compatible signature doesn't mean the exact same signature, + # so we're taking the new signature for the entry. + alt_entry.type = type + alt_entry.is_inherited = False + # Updating the entry attributes which can be modified in the method redefinition. + alt_entry.cname = cname + alt_entry.pos = pos + entry = alt_entry + break + else: + cpp_override_allowed = True + + if cpp_override_allowed: + # C++ function/method overrides with different signatures are ok. + pass + elif entries[name].is_inherited: + # Likewise ignore inherited classes. + pass + else: + if visibility == 'extern': + # Silenced outside of "cdef extern" blocks, until we have a safe way to + # prevent pxd-defined cpdef functions from ending up here. + warning(pos, "'%s' redeclared " % name, 1 if self.in_cinclude else 0) + elif visibility != 'ignore': + error(pos, "'%s' redeclared " % name) + self.entries[name].already_declared_here() + return None + + return entry + + def declare(self, name, cname, type, pos, visibility, shadow = 0, is_type = 0, create_wrapper = 0): # Create new entry, and add to dictionary if # name is not None. Reports a warning if already @@ -521,45 +581,22 @@ class Scope(object): warning(pos, "'%s' is a reserved name in C." % cname, -1) entries = self.entries - if name and name in entries and not shadow and not self.is_builtin_scope: - old_entry = entries[name] + entry = None + if name and name in entries and not shadow: + entry = self.handle_already_declared_name(name, cname, type, pos, visibility) - # Reject redeclared C++ functions only if they have the same type signature. - cpp_override_allowed = False - if type.is_cfunction and old_entry.type.is_cfunction and self.is_cpp(): - for alt_entry in old_entry.all_alternatives(): - if type == alt_entry.type: - if name == '' and not type.args: - # Cython pre-declares the no-args constructor - allow later user definitions. - cpp_override_allowed = True - break - else: - cpp_override_allowed = True - - if cpp_override_allowed: - # C++ function/method overrides with different signatures are ok. - pass - elif self.is_cpp_class_scope and entries[name].is_inherited: - # Likewise ignore inherited classes. - pass - elif visibility == 'extern': - # Silenced outside of "cdef extern" blocks, until we have a safe way to - # prevent pxd-defined cpdef functions from ending up here. - warning(pos, "'%s' redeclared " % name, 1 if self.in_cinclude else 0) - elif visibility != 'ignore': - error(pos, "'%s' redeclared " % name) - entries[name].already_declared_here() - entry = Entry(name, cname, type, pos = pos) - entry.in_cinclude = self.in_cinclude - entry.create_wrapper = create_wrapper - if name: - entry.qualified_name = self.qualify_name(name) -# if name in entries and self.is_cpp(): -# entries[name].overloaded_alternatives.append(entry) -# else: -# entries[name] = entry - if not shadow: - entries[name] = entry + if not entry: + entry = Entry(name, cname, type, pos = pos) + entry.in_cinclude = self.in_cinclude + entry.create_wrapper = create_wrapper + if name: + entry.qualified_name = self.qualify_name(name) + if not shadow: + if name in entries and self.is_cpp() and type.is_cfunction and not entries[name].is_cmethod: + # Which means: function or cppclass method is already present + entries[name].overloaded_alternatives.append(entry) + else: + entries[name] = entry if type.is_memoryviewslice: entry.init = type.default_value @@ -1270,6 +1307,10 @@ class BuiltinScope(Scope): def builtin_scope(self): return self + def handle_already_declared_name(self, name, cname, type, pos, visibility): + # Overriding is OK in the builtin scope + return None + const_counter = 1 # As a temporary solution for compiling code in pxds @@ -2705,18 +2746,21 @@ class CppClassScope(Scope): self._reject_pytyping_modifiers(pos, pytyping_modifiers) entry = self.lookup_here(name) if defining and entry is not None: - if entry.type.same_as(type): + if type.is_cfunction: + entry = self.declare(name, cname, type, pos, visibility) + elif entry.type.same_as(type): # Fix with_gil vs nogil. entry.type = entry.type.with_with_gil(type.with_gil) - elif type.is_cfunction and type.compatible_signature_with(entry.type): - entry.type = type else: error(pos, "Function signature does not match previous declaration") else: entry = self.declare(name, cname, type, pos, visibility) + if type.is_cfunction and not defining: + entry.is_inherited = 1 entry.is_variable = 1 - if type.is_cfunction and self.type: - if not self.type.get_fused_types(): + if type.is_cfunction: + entry.is_cfunction = 1 + if self.type and not self.type.get_fused_types(): entry.func_cname = "%s::%s" % (self.type.empty_declaration_code(), cname) if name != "this" and (defining or name != ""): self.var_entries.append(entry) @@ -2750,12 +2794,10 @@ class CppClassScope(Scope): if base_entry and not base_entry.type.nogil: error(pos, "Constructor cannot be called without GIL unless all base constructors can also be called without GIL") error(base_entry.pos, "Base constructor defined here.") - prev_entry = self.lookup_here(name) + # The previous entries management is now done directly in Scope.declare entry = self.declare_var(name, type, pos, defining=defining, cname=cname, visibility=visibility) - if prev_entry and not defining: - entry.overloaded_alternatives = prev_entry.all_alternatives() entry.utility_code = utility_code type.entry = entry return entry @@ -2783,6 +2825,9 @@ class CppClassScope(Scope): base_entry.type, None, 'extern') entry.is_variable = 1 entry.is_inherited = 1 + if base_entry.is_cfunction: + entry.is_cfunction = 1 + entry.func_cname = base_entry.func_cname self.inherited_var_entries.append(entry) for base_entry in base_scope.cfunc_entries: entry = self.declare_cfunction(base_entry.name, base_entry.type, @@ -2824,6 +2869,13 @@ class CppClassScope(Scope): return scope + def lookup_here(self, name): + if name == "__init__": + name = "" + elif name == "__dealloc__": + name = "" + return super(CppClassScope, self).lookup_here(name) + class CppScopedEnumScope(Scope): # Namespace of a ScopedEnum