Commit 88812dee authored by Xavier Thompson's avatar Xavier Thompson

Make methods declared in subclasses hide all identically named inherited methods

parent a2ce7ddc
...@@ -478,19 +478,36 @@ class Scope(object): ...@@ -478,19 +478,36 @@ class Scope(object):
if not self.in_cinclude and cname and re.match("^_[_A-Z]+$", cname): if not self.in_cinclude and cname and re.match("^_[_A-Z]+$", cname):
# See https://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html#Reserved-Names # See https://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html#Reserved-Names
warning(pos, "'%s' is a reserved name in C." % cname, -1) warning(pos, "'%s' is a reserved name in C." % cname, -1)
entries = self.entries entries = self.entries
# The index of an inherited c method among the overloaded alternatives in a cpp class
old_index = -1 old_index = -1
# The index of the predeclared default constructor among the alternative constructors in a cypclass
cpp_no_args_constructor_index = -1
if name and name in entries and not shadow: if name and name in entries and not shadow:
old_entry = entries[name] old_entry = entries[name]
# Reject redeclared C++ functions only if they have the same type signature. # Reject redeclared C++ functions only if they have the same type signature.
cpp_override_allowed = False cpp_override_allowed = False
if type.is_cfunction and old_entry.type.is_cfunction and self.is_cpp(): if type.is_cfunction and old_entry.type.is_cfunction and self.is_cpp():
for index, alt_entry in enumerate(old_entry.all_alternatives()): for index, alt_entry in enumerate(old_entry.all_alternatives()):
if type.compatible_signature_with(alt_entry.type): if type.compatible_signature_with(alt_entry.type):
if alt_entry.is_inherited:
old_index = index # If we're not in a cypclass, keep the same behaviour as before
if not (self.type and self.type.is_cyp_class):
if alt_entry.is_inherited:
old_index = index
# In a cypclass, only the predeclared default constructor is allowed to be redeclared.
# We don't have to deal with inherited entries because they are hidden as soon as the subclass has a method
# of the same name, regardless of the exact signature (this is also C++ behavior by default)
elif name == '<constructor>' and (not type.args or len(type.args) == type.optional_arg_count):
# Cython pre-declares the no-args constructor - allow later user definitions.
cpp_no_args_constructor_index = index
cpp_override_allowed = True cpp_override_allowed = True
break break
else: else:
cpp_override_allowed = True cpp_override_allowed = True
...@@ -499,7 +516,7 @@ class Scope(object): ...@@ -499,7 +516,7 @@ class Scope(object):
# C++ function/method overrides with different signatures are ok. # C++ function/method overrides with different signatures are ok.
pass pass
elif self.is_cpp_class_scope and entries[name].is_inherited: elif self.is_cpp_class_scope and entries[name].is_inherited:
# Likewise ignore inherited classes. # Likewise ignore inherited methods with identical signatures
pass pass
elif visibility == 'extern': elif visibility == 'extern':
# Silenced outside of "cdef extern" blocks, until we have a safe way to # Silenced outside of "cdef extern" blocks, until we have a safe way to
...@@ -513,16 +530,32 @@ class Scope(object): ...@@ -513,16 +530,32 @@ class Scope(object):
entry.create_wrapper = create_wrapper entry.create_wrapper = create_wrapper
if name: if name:
entry.qualified_name = self.qualify_name(name) entry.qualified_name = self.qualify_name(name)
if not shadow: if not shadow:
if name in entries and self.is_cpp_class_scope and type.is_cfunction: if name in entries and self.is_cpp_class_scope and type.is_cfunction:
if old_index > -1:
if old_index > 0: # If we're not in a cypclass, keep the same behaviour as before
entries[name].overloaded_alternatives[old_index-1] = entry if not (self.type and self.type.is_cyp_class):
if old_index > -1:
if old_index > 0:
entries[name].overloaded_alternatives[old_index-1] = entry
else:
entry.overloaded_alternatives = entries[name].overloaded_alternatives
entries[name] = entry
else: else:
entries[name].overloaded_alternatives.append(entry)
# In a cypclass, only the predeclared default constructor might need to be replaced
else:
if cpp_no_args_constructor_index == 0:
entry.overloaded_alternatives = entries[name].overloaded_alternatives entry.overloaded_alternatives = entries[name].overloaded_alternatives
entries[name] = entry entries[name] = entry
else: elif cpp_no_args_constructor_index > 0:
entries[name].overloaded_alternatives.append(entry) entries[name].overloaded_alternatives[cpp_no_args_constructor_index-1] = entry
else:
entries[name].overloaded_alternatives.append(entry)
else: else:
entries[name] = entry entries[name] = entry
...@@ -690,7 +723,7 @@ class Scope(object): ...@@ -690,7 +723,7 @@ class Scope(object):
wrapper_name = "<constructor>" wrapper_name = "<constructor>"
wrapper_entry = scope.declare(wrapper_name, wrapper_cname, wrapper_type, pos, visibility) wrapper_entry = scope.declare(wrapper_name, wrapper_cname, wrapper_type, pos, visibility)
wrapper_type.entry = wrapper_entry wrapper_type.entry = wrapper_entry
wrapper_entry.is_inherited = 1 # wrapper_entry.is_inherited = 1
wrapper_entry.is_cfunction = 1 wrapper_entry.is_cfunction = 1
wrapper_entry.func_cname = "%s::%s" % (entry.type.empty_declaration_code(), wrapper_cname) wrapper_entry.func_cname = "%s::%s" % (entry.type.empty_declaration_code(), wrapper_cname)
...@@ -700,7 +733,7 @@ class Scope(object): ...@@ -700,7 +733,7 @@ class Scope(object):
alloc_name = "<alloc>" alloc_name = "<alloc>"
alloc_entry = scope.declare(alloc_name, alloc_cname, alloc_type, pos, visibility) alloc_entry = scope.declare(alloc_name, alloc_cname, alloc_type, pos, visibility)
alloc_type.entry = alloc_entry alloc_type.entry = alloc_entry
alloc_entry.is_inherited = 1 # alloc_entry.is_inherited = 1
alloc_entry.is_cfunction = 1 alloc_entry.is_cfunction = 1
# is_builtin_cmethod is currently only used in cclass, so it should be safe # is_builtin_cmethod is currently only used in cclass, so it should be safe
# to use it here to distinguish between implicit default __alloc__ and user-defined one # to use it here to distinguish between implicit default __alloc__ and user-defined one
...@@ -2637,7 +2670,7 @@ class CppClassScope(Scope): ...@@ -2637,7 +2670,7 @@ class CppClassScope(Scope):
def declare_cfunction(self, name, type, pos, def declare_cfunction(self, name, type, pos,
cname=None, visibility='extern', api=0, in_pxd=0, cname=None, visibility='extern', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False): defining=0, modifiers=(), utility_code=None, overridable=False, inheriting=0):
reify = self.type.is_cyp_class and self.type.activable reify = self.type.is_cyp_class and self.type.activable
class_name = self.name.split('::')[-1] class_name = self.name.split('::')[-1]
if name in (class_name, '__init__') and cname is None: if name in (class_name, '__init__') and cname is None:
...@@ -2657,6 +2690,14 @@ class CppClassScope(Scope): ...@@ -2657,6 +2690,14 @@ class CppClassScope(Scope):
type.args = [maybe_ref(arg) for arg in type.args] type.args = [maybe_ref(arg) for arg in type.args]
if self.type.is_cyp_class and not self.lookup_here("__new__"): if self.type.is_cyp_class and not self.lookup_here("__new__"):
# In cypclasses, inherited methods are all hidden as soon as a
# method with the same name is declared in the subclass
if not inheriting:
prev_constructor = self.lookup_here("<constructor>")
if prev_constructor and prev_constructor.is_inherited:
del self.entries["<constructor>"]
self.declare_constructor_wrapper(type.args, pos, defining, self.declare_constructor_wrapper(type.args, pos, defining,
type.has_varargs, type.optional_arg_count, type.has_varargs, type.optional_arg_count,
getattr(type, 'op_arg_struct', None)) getattr(type, 'op_arg_struct', None))
...@@ -2732,8 +2773,14 @@ class CppClassScope(Scope): ...@@ -2732,8 +2773,14 @@ class CppClassScope(Scope):
if base_entry and not base_entry.type.nogil: 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(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.") error(base_entry.pos, "Base constructor defined here.")
# The previous entries management is now done directly in Scope.declare
#prev_entry = self.lookup_here(name) # In cypclasses, inherited methods are all hidden as soon as a
# method with the same name is declared in the subclass
if self.type.is_cyp_class and not inheriting:
prev_entry = self.lookup_here(name)
if prev_entry and prev_entry.is_inherited:
del self.entries[name]
entry = self.declare_var(name, type, pos, entry = self.declare_var(name, type, pos,
defining=defining, defining=defining,
cname=cname, visibility=visibility) cname=cname, visibility=visibility)
...@@ -2814,7 +2861,8 @@ class CppClassScope(Scope): ...@@ -2814,7 +2861,8 @@ class CppClassScope(Scope):
base_entry.pos, base_entry.cname, base_entry.pos, base_entry.cname,
base_entry.visibility, api=0, base_entry.visibility, api=0,
modifiers=base_entry.func_modifiers, modifiers=base_entry.func_modifiers,
utility_code=base_entry.utility_code) utility_code=base_entry.utility_code,
inheriting=1)
entry.is_inherited = 1 entry.is_inherited = 1
for base_entry in base_scope.type_entries + base_scope.inherited_type_entries: for base_entry in base_scope.type_entries + base_scope.inherited_type_entries:
if base_entry.name not in base_templates: if base_entry.name not in base_templates:
......
...@@ -55,15 +55,15 @@ def test_base_new_keyword(): ...@@ -55,15 +55,15 @@ def test_base_new_keyword():
print o.first() print o.first()
print o.second() print o.second()
def test_derived_twoargs_construction(): # def test_derived_twoargs_construction():
""" # """
>>> test_derived_twoargs_construction() # >>> test_derived_twoargs_construction()
42 # 42
4 # 4
""" # """
cdef TemplatedDerived[int] o = TemplatedDerived[int](42, 4) # cdef TemplatedDerived[int] o = TemplatedDerived[int](42, 4)
print o.first() # print o.first()
print o.second() # print o.second()
def test_derived_threeargs_construction(): def test_derived_threeargs_construction():
""" """
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment