Commit 593e4420 authored by Xavier Thompson's avatar Xavier Thompson

Make cypclasses inherit from ancestors in MRO order

parent 931de505
......@@ -2117,13 +2117,6 @@ if VALUE is not None:
return property
class ComputeMROTransform(CythonTransform):
def visit_CppClassNode(self, node):
if node.cypclass:
node.entry.type.compute_mro()
return node
class CalculateQualifiedNamesTransform(EnvTransform):
"""
Calculate and store the '__qualname__' and the global
......
......@@ -142,7 +142,6 @@ def create_pipeline(context, mode, exclude_classes=()):
from .Visitor import PrintTree
from .ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
from .ParseTreeTransforms import ForwardDeclareTypes, InjectGilHandling, AnalyseDeclarationsTransform
from .ParseTreeTransforms import ComputeMROTransform
from .ParseTreeTransforms import AnalyseExpressionsTransform, FindInvalidUseOfFusedTypes
from .ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
from .ParseTreeTransforms import TrackNumpyAttributes, InterpretCompilerDirectives, TransformBuiltinMethods
......@@ -197,7 +196,6 @@ def create_pipeline(context, mode, exclude_classes=()):
ForwardDeclareTypes(context),
InjectGilHandling(),
AnalyseDeclarationsTransform(context),
ComputeMROTransform(context),
AutoTestDictTransform(context),
EmbedSignature(context),
ReplacePropertyNode(context),
......
......@@ -3956,7 +3956,7 @@ def compute_mro_generic(cls):
class CypClassType(CppClassType):
# lock_mode string (tri-state: "nolock"/"checklock"/"autolock")
# mro [CppClassType] or None The Method Resolution Order of this cypclass according to Python
# _mro [CppClassType] or None The Method Resolution Order of this cypclass according to Python
is_cyp_class = 1
......@@ -3964,27 +3964,27 @@ class CypClassType(CppClassType):
CppClassType.__init__(self, name, scope, cname, base_classes, templates, template_type, nogil)
self.lock_mode = lock_mode if lock_mode else "autolock"
self.activable = activable
self.mro = None
self._mro = None
# compute the MRO for this cypclass
# the mro is also computed for bases when needed
# Return the MRO for this cypclass
# Compute all the mro needed when a previous computation is not available
# based on https://mail.python.org/pipermail/python-dev/2002-October/029176.html
def compute_mro(self):
if self.mro is not None:
return self.mro
def mro(self):
if self._mro is not None:
return self._mro
if self.base_classes is None:
self.mro = [self]
return self.mro
self._mro = [self]
return self._mro
inputs = [[self]]
for base in self.base_classes:
if base.is_cyp_class:
base_mro = base.compute_mro()
base_mro = base.mro()
else:
base_mro = compute_mro_generic(base)
inputs.append(base_mro)
inputs.append(self.base_classes)
self.mro = mro_C3_merge(inputs)
return self.mro
self._mro = mro_C3_merge(inputs)
return self._mro
def empty_declaration_code(self):
if self._empty_declaration is None:
......
......@@ -710,14 +710,24 @@ class Scope(object):
entry.already_declared_here()
def declare_inherited_attributes(entry, base_classes):
for base_class in base_classes:
def examine_base(base_class):
if base_class is PyrexTypes.error_type or base_class is PyrexTypes.cy_object_type:
continue
return False
if base_class.scope is None:
error(pos, "Cannot inherit from incomplete type")
else:
declare_inherited_attributes(entry, base_class.base_classes)
entry.type.scope.declare_inherited_cpp_attributes(base_class)
return False
return True
if cypclass:
for base_class in reversed(entry.type.mro()):
if examine_base(base_class):
entry.type.scope.declare_inherited_cyp_attributes(base_class)
else:
for base_class in base_classes:
if examine_base(base_class):
declare_inherited_attributes(entry, base_class.base_classes)
entry.type.scope.declare_inherited_cpp_attributes(base_class)
if scope:
entry.type.set_scope(scope)
declare_inherited_attributes(entry, base_classes)
......@@ -2891,6 +2901,79 @@ class CppClassScope(Scope):
entry.is_inherited = 1
self.inherited_type_entries.append(entry)
def declare_inherited_cyp_attributes(self, base_class):
base_scope = base_class.scope
template_type = base_class
while getattr(template_type, 'template_type', None):
template_type = template_type.template_type
if getattr(template_type, 'templates', None):
base_templates = [T.name for T in template_type.templates]
else:
base_templates = ()
# Declare entries for all the C++ attributes of an
# inherited type, with cnames modified appropriately
# to work with this type.
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__"):
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,
optional_arg_count = base_entry_type.optional_arg_count,
op_arg_struct = getattr(base_entry_type, 'op_arg_struct', None),
return_type=self.parent_type)
wrapper_entry.is_inherited = 1
elif base_entry.name == "__new__":
# Rewrite first argument for __new__
alloc_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(self.parent_type, [], nogil=1))
alloc_arg = PyrexTypes.CFuncTypeArg(base_entry.type.args[0].name, alloc_type,
base_entry.type.args[0].pos, cname=base_entry.type.args[0].cname)
base_entry_type = PyrexTypes.CFuncType(base_entry_type.return_type,
[alloc_arg] + base_entry_type.args[1:], nogil=1,
has_varargs=base_entry_type.has_varargs,
optional_arg_count=base_entry_type.optional_arg_count)
if hasattr(base_entry.type, 'op_arg_struct'):
base_entry_type.op_arg_struct = base_entry.type.op_arg_struct
base_entry_type.original_alloc_type = base_entry.type.original_alloc_type
if base_entry.name in self.entries:
del self.entries[base_entry.name]
del self.entries["<constructor>"]
elif "<init>" in self.entries:
del self.entries["<constructor>"]
wrapper_entry = self.declare_constructor_wrapper(base_entry_type.args[1:],
base_entry.pos, defining=1,
has_varargs = base_entry_type.has_varargs,
optional_arg_count = base_entry_type.optional_arg_count,
op_arg_struct = getattr(base_entry_type, 'op_arg_struct', None),
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.is_variable = 1
entry.is_inherited = 1
entry.is_cfunction = base_entry.is_cfunction
if entry.is_cfunction:
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,
base_entry.pos, base_entry.cname,
base_entry.visibility, api=0,
modifiers=base_entry.func_modifiers,
utility_code=base_entry.utility_code,
inheriting=1)
entry.is_inherited = 1
for base_entry in base_scope.type_entries:
if base_entry.name not in base_templates:
entry = self.declare_type(base_entry.name, base_entry.type,
base_entry.pos, base_entry.cname,
base_entry.visibility, defining=0)
entry.is_inherited = 1
self.inherited_type_entries.append(entry)
def specialize(self, values, type_entry):
scope = CppClassScope(self.name, self.outer_scope)
scope.type = type_entry
......
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