Commit a4a225bd authored by Xavier Thompson's avatar Xavier Thompson

Use MRO to resolve ambiguous cypclass method calls instead of hiding overriden methods

parent 6b95d3ad
......@@ -400,7 +400,8 @@ def inject_acthon_interfaces(self):
"ActhonResultInterface", result_scope, "ActhonResultInterface", (PyrexTypes.cy_object_type,),
lock_mode="nolock", activable=False)
result_scope.type = result_type
#result_type.set_scope is a little bit overkill here, because parent_type is only used when doing scope inheritance
#result_type.set_scope is required because parent_type is used when doing scope inheritance
result_type.set_scope(result_scope)
result_entry = self.declare("ActhonResultInterface", "ActhonResultInterface", result_type, None, "extern")
result_entry.is_type = 1
......@@ -457,6 +458,7 @@ def inject_acthon_interfaces(self):
acthon_message_type = message_type = PyrexTypes.CypClassType(
"ActhonMessageInterface", message_scope, "ActhonMessageInterface", (PyrexTypes.cy_object_type,),
lock_mode="nolock", activable=False)
message_type.set_scope(message_scope)
message_scope.type = message_type
# cypclass ActhonSyncInterface(CyObject):
......@@ -470,6 +472,7 @@ def inject_acthon_interfaces(self):
acthon_sync_type = sync_type = PyrexTypes.CypClassType(
"ActhonSyncInterface", sync_scope, "ActhonSyncInterface", (PyrexTypes.cy_object_type,),
lock_mode="nolock", activable=False)
sync_type.set_scope(sync_scope)
sync_scope.type = sync_type
sync_entry = self.declare("ActhonSyncInterface", "ActhonSyncInterface", sync_type, None, "extern")
sync_entry.is_type = 1
......@@ -538,6 +541,7 @@ def inject_acthon_interfaces(self):
acthon_queue_type = queue_type = PyrexTypes.CypClassType(
"ActhonQueueInterface", queue_scope, "ActhonQueueInterface", (PyrexTypes.cy_object_type,),
lock_mode="nolock", activable=False)
queue_type.set_scope(queue_scope)
queue_scope.type = queue_type
queue_entry = self.declare("ActhonQueueInterface", "ActhonQueueInterface", queue_type, self, "extern")
queue_entry.is_type = 1
......@@ -574,6 +578,7 @@ def inject_acthon_interfaces(self):
acthon_activable_type = activable_type = PyrexTypes.CypClassType(
"ActhonActivableClass", activable_scope, "ActhonActivableClass", (PyrexTypes.cy_object_type,),
lock_mode="nolock", activable=False)
activable_type.set_scope(activable_scope)
activable_entry = self.declare("ActhonActivableClass", None, activable_type, "ActhonActivableClass", "extern")
activable_entry.is_type = 1
......
......@@ -4591,13 +4591,15 @@ def best_match(arg_types, functions, pos=None, env=None, args=None):
bad_types.append((func, error_mesg))
break
else:
possibilities.append((score, index, func)) # so we can sort it
from .Symtab import Entry
mro_score = func.mro_index if isinstance(func, Entry) else 0
possibilities.append((mro_score, score, index, func)) # so we can sort it
if possibilities:
possibilities.sort()
if len(possibilities) > 1:
score1 = possibilities[0][0]
score2 = possibilities[1][0]
score1 = possibilities[0][:2]
score2 = possibilities[1][:2]
if score1 == score2:
if pos is not None:
error(pos, "ambiguous overloaded method")
......
......@@ -142,6 +142,8 @@ class Entry(object):
#
# is_default boolean This entry is a compiler-generated default and
# is not user-defined (e.g default contructor)
# mro_index integer The index of the type where this entry was originally
# declared in the mro of the cypclass where it is now
# TODO: utility_code and utility_code_definition serves the same purpose...
......@@ -229,6 +231,7 @@ class Entry(object):
self.cf_references = []
self.inner_entries = []
self.defining_entry = self
self.mro_index = 0
def __repr__(self):
return "%s(<%x>, name=%s, type=%s)" % (type(self).__name__, id(self), self.name, self.type)
......@@ -317,6 +320,7 @@ class Scope(object):
# is_closure_scope boolean Is a closure scope
# is_passthrough boolean Outer scope is passed directly
# is_cpp_class_scope boolean Is a C++ class scope
# is_cyp_class_scope boolean Is a C++ class scope of a cypclass
# is_property_scope boolean Is a extension type property scope
# scope_prefix string Disambiguator for C names
# in_cinclude boolean Suppress C declaration code
......@@ -334,6 +338,7 @@ class Scope(object):
is_genexpr_scope = 0
is_passthrough = 0
is_cpp_class_scope = 0
is_cyp_class_scope = 0
is_property_scope = 0
is_module_scope = 0
is_internal = 0
......@@ -473,7 +478,7 @@ class Scope(object):
key = entry
return self.tracked_entries.get(key, None)
def declare(self, name, cname, type, pos, visibility, shadow = 0, is_type = 0, create_wrapper = 0):
def declare(self, name, cname, type, pos, visibility, shadow = 0, is_type = 0, create_wrapper = 0, from_type = None):
# Create new entry, and add to dictionary if
# name is not None. Reports a warning if already
# declared.
......@@ -499,20 +504,17 @@ class Scope(object):
ccp_override_allowed = False
# If we're not in a cypclass, any inherited method is visible
# until overloaded by a method with the same signature
if not self.is_cyp_class_scope:
if alt_entry.is_inherited:
if self.is_cyp_class_scope:
# allow default constructor or __alloc__ to be redeclared by user
if alt_entry.is_default:
previous_alternative_indices.append(index)
cpp_override_allowed = True
ccp_override_allowed = True
# In a cypclass, only the predeclared default constructor and __alloc__ are 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).
# The default entry is overriden when there is a subsequent entry with a compatible signature.
elif alt_entry.is_default:
# Any inherited method is visible
# until overloaded by a method with the same signature
if alt_entry.is_inherited:
previous_alternative_indices.append(index)
ccp_override_allowed = True
cpp_override_allowed = True
if cpp_override_allowed:
# C++ function/method overrides with different signatures are ok.
......@@ -535,9 +537,13 @@ class Scope(object):
elif visibility != 'ignore':
error(pos, "'%s' redeclared " % name)
old_entry.already_declared_here()
entry = Entry(name, cname, type, pos = pos)
if from_type and self.is_cyp_class_scope:
entry.mro_index = self.parent_type.mro().index(from_type)
entry.in_cinclude = self.in_cinclude
entry.create_wrapper = create_wrapper
if name:
entry.qualified_name = self.qualify_name(name)
......@@ -2565,7 +2571,6 @@ class CppClassScope(Scope):
# Namespace of a C++ class.
is_cpp_class_scope = 1
is_cyp_class_scope = 0
default_constructor = None
type = None
......@@ -2725,7 +2730,7 @@ class CppClassScope(Scope):
def declare_cfunction(self, name, type, pos,
cname=None, visibility='extern', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False, inheriting=0):
defining=0, modifiers=(), utility_code=None, overridable=False):
reify = self.type.is_cyp_class and self.type.activable
class_name = self.name.split('::')[-1]
if name in (class_name, '__init__') and cname is None:
......@@ -2746,13 +2751,6 @@ class CppClassScope(Scope):
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,
type.has_varargs, type.optional_arg_count,
getattr(type, 'op_arg_struct', None))
......@@ -2803,13 +2801,6 @@ class CppClassScope(Scope):
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.")
# 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,
defining=defining,
cname=cname, visibility=visibility)
......@@ -2881,9 +2872,9 @@ class CppClassScope(Scope):
for base_entry in base_scope.var_entries:
base_entry_type = base_entry.type
#constructor/destructor is not inherited
if base_entry.name == "<del>"\
or base_entry.name in ("<constructor>", "<alloc>", "<active_self>", "__activate__"):
if base_entry.name == "<del>" or base_entry.name in ("<constructor>", "<alloc>", "<active_self>", "__activate__"):
continue
elif base_entry.name == "<init>" and not self.lookup_here("__new__"):
wrapper_entry = self.declare_constructor_wrapper(base_entry_type.args, base_entry.pos,
defining=1, has_varargs = base_entry_type.has_varargs,
......@@ -2916,7 +2907,7 @@ class CppClassScope(Scope):
return_type=base_entry_type.return_type)
wrapper_entry.is_inherited = 1
entry = self.declare(base_entry.name, base_entry.cname, base_entry_type, base_entry.pos, 'extern')
entry = self.declare(base_entry.name, base_entry.cname, base_entry_type, base_entry.pos, 'extern', from_type = base_class)
entry.is_variable = 1
entry.is_inherited = 1
entry.is_cfunction = base_entry.is_cfunction
......
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