Commit a36eab03 authored by Xavier Thompson's avatar Xavier Thompson

Make unambiguous static cast to defining base in MRO emulation forwarding methods

parent c0fbc598
......@@ -992,7 +992,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
constructor = scope.lookup_here("<constructor>")
for constructor_alternative in constructor.all_alternatives():
code.putln("static %s;" % constructor_alternative.type.declaration_code(constructor_alternative.cname))
self.generate_cyp_class_mro_method_resolution(scope, code)
self.generate_cyp_class_mro_method_resolution(type, code)
elif constructor or py_attrs:
if constructor:
for constructor_alternative in constructor.all_alternatives():
......@@ -1082,12 +1082,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.globalstate.use_utility_code(
UtilityCode.load("CyObjects", "CyObjects.cpp", proto_block="utility_code_proto_before_types"))
def generate_cyp_class_mro_method_resolution(self, scope, code):
def generate_cyp_class_mro_method_resolution(self, type, code):
"""
Generate overriding methods in derived cypclasses to forward calls to the correct method according
to the MRO, regardless of the type of the pointer to the object through which the call is made.
In other words: emulate Python MRO lookup rules using only C++ virtual methods.
"""
scope = type.scope
inherited_methods = [
e for entry in scope.entries.values() for e in entry.all_alternatives()
if e.is_cfunction
......@@ -1117,7 +1118,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
return_code = "" if e.type.return_type.is_void else "return "
resolution = e.from_type.empty_declaration_code()
body = "%s%s::%s(%s);" % (return_code, resolution, e.cname, ", ".join(arg_names))
cast_code = "this"
cast_path = type.left_path_to_base(e.from_type)
for base_class in cast_path:
cast_code = "static_cast<%s *>(%s)" % (base_class.empty_declaration_code(), cast_code)
body = "%s%s->%s::%s(%s);" % (return_code, cast_code, resolution, e.cname, ", ".join(arg_names))
code.putln("virtual %s%s {%s}" % (modifiers, header, body))
if inherited_methods:
......
......@@ -3966,6 +3966,7 @@ class CypClassType(CppClassType):
self.lock_mode = lock_mode if lock_mode else "autolock"
self.activable = activable
self._mro = None
self._left_path_to_base = {}
# Return the MRO for this cypclass
# Compute all the mro needed when a previous computation is not available
......@@ -3987,6 +3988,39 @@ class CypClassType(CppClassType):
self._mro = mro_C3_merge(inputs)
return self._mro
# Depth first, left first search for the given base while keeping track of the path
def left_path_to_base(self, base):
if base is self:
return []
try:
return self._left_path_to_base[base]
except KeyError:
pass
stack = []
path_down = {}
stack.append((self, None))
while stack:
current, path_child = stack.pop()
if current in path_down:
continue
path_down[current] = path_child
if current is base:
break
if hasattr(current, "base_classes") and current.base_classes:
# reverse to visit left bases first
for parent in reversed(current.base_classes):
stack.append((parent, current))
path = None
if base in path_down:
path = [base]
path_class = path_down[base]
while path_class is not self:
path.append(path_class)
path_class = path_down[path_class]
path.reverse()
self._left_path_to_base[base] = path
return path
def empty_declaration_code(self):
if self._empty_declaration is None:
self._empty_declaration = self.declaration_code('', deref=1)
......
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