Commit 888bc4a4 authored by Stefan Behnel's avatar Stefan Behnel

Make sure we always use the correct post-analysed bases, mkw and metaclass in...

Make sure we always use the correct post-analysed bases, mkw and metaclass in Python class creation by making the PyClassDefNode own it and other nodes only refer to it, rather than risking stale references in subnodes.
(Alternatively, ownership could be distributed across the subnodes, but it's a bit unclear how, and how to refer to the other subnodes then.)
Closes #3338.
parent e83ff762
...@@ -14,6 +14,9 @@ Cython Changelog ...@@ -14,6 +14,9 @@ Cython Changelog
* Double reference free in ``__class__`` cell handling for ``super()`` calls. * Double reference free in ``__class__`` cell handling for ``super()`` calls.
(Github issue #3246) (Github issue #3246)
* Compile error when using ``*args`` as Python class bases.
(Github issue #3338)
* Import failure in IPython 7.11. * Import failure in IPython 7.11.
(Github issue #3297) (Github issue #3297)
......
...@@ -8938,12 +8938,11 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -8938,12 +8938,11 @@ class ClassNode(ExprNode, ModuleNameMixin):
# a name, tuple of bases and class dictionary. # a name, tuple of bases and class dictionary.
# #
# name EncodedString Name of the class # name EncodedString Name of the class
# bases ExprNode Base class tuple # class_def_node PyClassDefNode PyClassDefNode defining this class
# dict ExprNode Class dict (not owned by this node)
# doc ExprNode or None Doc string # doc ExprNode or None Doc string
# module_name EncodedString Name of defining module # module_name EncodedString Name of defining module
subexprs = ['bases', 'doc'] subexprs = ['doc']
type = py_object_type type = py_object_type
is_temp = True is_temp = True
...@@ -8952,7 +8951,6 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -8952,7 +8951,6 @@ class ClassNode(ExprNode, ModuleNameMixin):
return py_object_type return py_object_type
def analyse_types(self, env): def analyse_types(self, env):
self.bases = self.bases.analyse_types(env)
if self.doc: if self.doc:
self.doc = self.doc.analyse_types(env) self.doc = self.doc.analyse_types(env)
self.doc = self.doc.coerce_to_pyobject(env) self.doc = self.doc.coerce_to_pyobject(env)
...@@ -8965,12 +8963,13 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -8965,12 +8963,13 @@ class ClassNode(ExprNode, ModuleNameMixin):
gil_message = "Constructing Python class" gil_message = "Constructing Python class"
def generate_result_code(self, code): def generate_result_code(self, code):
class_def_node = self.class_def_node
cname = code.intern_identifier(self.name) cname = code.intern_identifier(self.name)
if self.doc: if self.doc:
code.put_error_if_neg(self.pos, code.put_error_if_neg(self.pos,
'PyDict_SetItem(%s, %s, %s)' % ( 'PyDict_SetItem(%s, %s, %s)' % (
self.dict.py_result(), class_def_node.dict.py_result(),
code.intern_identifier( code.intern_identifier(
StringEncoding.EncodedString("__doc__")), StringEncoding.EncodedString("__doc__")),
self.doc.py_result())) self.doc.py_result()))
...@@ -8979,8 +8978,8 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -8979,8 +8978,8 @@ class ClassNode(ExprNode, ModuleNameMixin):
code.putln( code.putln(
'%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % ( '%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
self.result(), self.result(),
self.bases.py_result(), class_def_node.bases.py_result(),
self.dict.py_result(), class_def_node.dict.py_result(),
cname, cname,
qualname, qualname,
py_mod_name, py_mod_name,
...@@ -8994,8 +8993,8 @@ class Py3ClassNode(ExprNode): ...@@ -8994,8 +8993,8 @@ class Py3ClassNode(ExprNode):
# a name, tuple of bases and class dictionary. # a name, tuple of bases and class dictionary.
# #
# name EncodedString Name of the class # name EncodedString Name of the class
# dict ExprNode Class dict (not owned by this node)
# module_name EncodedString Name of defining module # module_name EncodedString Name of defining module
# class_def_node PyClassDefNode PyClassDefNode defining this class
# calculate_metaclass bool should call CalculateMetaclass() # calculate_metaclass bool should call CalculateMetaclass()
# allow_py2_metaclass bool should look for Py2 metaclass # allow_py2_metaclass bool should look for Py2 metaclass
...@@ -9018,12 +9017,10 @@ class Py3ClassNode(ExprNode): ...@@ -9018,12 +9017,10 @@ class Py3ClassNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c")) code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c"))
cname = code.intern_identifier(self.name) cname = code.intern_identifier(self.name)
if self.mkw: class_def_node = self.class_def_node
mkw = self.mkw.py_result() mkw = class_def_node.mkw.py_result() if class_def_node.mkw else 'NULL'
else: if class_def_node.metaclass:
mkw = 'NULL' metaclass = class_def_node.metaclass.py_result()
if self.metaclass:
metaclass = self.metaclass.py_result()
else: else:
metaclass = "((PyObject*)&__Pyx_DefaultClassType)" metaclass = "((PyObject*)&__Pyx_DefaultClassType)"
code.putln( code.putln(
...@@ -9031,8 +9028,8 @@ class Py3ClassNode(ExprNode): ...@@ -9031,8 +9028,8 @@ class Py3ClassNode(ExprNode):
self.result(), self.result(),
metaclass, metaclass,
cname, cname,
self.bases.py_result(), class_def_node.bases.py_result(),
self.dict.py_result(), class_def_node.dict.py_result(),
mkw, mkw,
self.calculate_metaclass, self.calculate_metaclass,
self.allow_py2_metaclass, self.allow_py2_metaclass,
...@@ -9043,8 +9040,7 @@ class Py3ClassNode(ExprNode): ...@@ -9043,8 +9040,7 @@ class Py3ClassNode(ExprNode):
class PyClassMetaclassNode(ExprNode): class PyClassMetaclassNode(ExprNode):
# Helper class holds Python3 metaclass object # Helper class holds Python3 metaclass object
# #
# bases ExprNode Base class tuple (not owned by this node) # class_def_node PyClassDefNode PyClassDefNode defining this class
# mkw ExprNode Class keyword arguments (not owned by this node)
subexprs = [] subexprs = []
...@@ -9057,38 +9053,38 @@ class PyClassMetaclassNode(ExprNode): ...@@ -9057,38 +9053,38 @@ class PyClassMetaclassNode(ExprNode):
return True return True
def generate_result_code(self, code): def generate_result_code(self, code):
if self.mkw: bases = self.class_def_node.bases
mkw = self.class_def_node.mkw
if mkw:
code.globalstate.use_utility_code( code.globalstate.use_utility_code(
UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c")) UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c"))
call = "__Pyx_Py3MetaclassGet(%s, %s)" % ( call = "__Pyx_Py3MetaclassGet(%s, %s)" % (
self.bases.result(), bases.result(),
self.mkw.result()) mkw.result())
else: else:
code.globalstate.use_utility_code( code.globalstate.use_utility_code(
UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c")) UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c"))
call = "__Pyx_CalculateMetaclass(NULL, %s)" % ( call = "__Pyx_CalculateMetaclass(NULL, %s)" % (
self.bases.result()) bases.result())
code.putln( code.putln(
"%s = %s; %s" % ( "%s = %s; %s" % (
self.result(), call, self.result(), call,
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
class PyClassNamespaceNode(ExprNode, ModuleNameMixin): class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
# Helper class holds Python3 namespace object # Helper class holds Python3 namespace object
# #
# All this are not owned by this node # All this are not owned by this node
# metaclass ExprNode Metaclass object # class_def_node PyClassDefNode PyClassDefNode defining this class
# bases ExprNode Base class tuple
# mkw ExprNode Class keyword arguments
# doc ExprNode or None Doc string (owned) # doc ExprNode or None Doc string (owned)
subexprs = ['doc'] subexprs = ['doc']
def analyse_types(self, env): def analyse_types(self, env):
if self.doc: if self.doc:
self.doc = self.doc.analyse_types(env) self.doc = self.doc.analyse_types(env).coerce_to_pyobject(env)
self.doc = self.doc.coerce_to_pyobject(env)
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
return self return self
...@@ -9100,23 +9096,16 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin): ...@@ -9100,23 +9096,16 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
cname = code.intern_identifier(self.name) cname = code.intern_identifier(self.name)
py_mod_name = self.get_py_mod_name(code) py_mod_name = self.get_py_mod_name(code)
qualname = self.get_py_qualified_name(code) qualname = self.get_py_qualified_name(code)
if self.doc: class_def_node = self.class_def_node
doc_code = self.doc.result() null = "(PyObject *) NULL"
else: doc_code = self.doc.result() if self.doc else null
doc_code = '(PyObject *) NULL' mkw = class_def_node.mkw.py_result() if class_def_node.mkw else null
if self.mkw: metaclass = class_def_node.metaclass.py_result() if class_def_node.metaclass else null
mkw = self.mkw.py_result()
else:
mkw = '(PyObject *) NULL'
if self.metaclass:
metaclass = self.metaclass.py_result()
else:
metaclass = "(PyObject *) NULL"
code.putln( code.putln(
"%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % ( "%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
self.result(), self.result(),
metaclass, metaclass,
self.bases.result(), class_def_node.bases.result(),
cname, cname,
qualname, qualname,
mkw, mkw,
......
...@@ -4503,26 +4503,22 @@ class PyClassDefNode(ClassDefNode): ...@@ -4503,26 +4503,22 @@ class PyClassDefNode(ClassDefNode):
pass # no base classes => no inherited metaclass pass # no base classes => no inherited metaclass
else: else:
self.metaclass = ExprNodes.PyClassMetaclassNode( self.metaclass = ExprNodes.PyClassMetaclassNode(
pos, mkw=mkdict, bases=self.bases) pos, class_def_node=self)
needs_metaclass_calculation = False needs_metaclass_calculation = False
else: else:
needs_metaclass_calculation = True needs_metaclass_calculation = True
self.dict = ExprNodes.PyClassNamespaceNode( self.dict = ExprNodes.PyClassNamespaceNode(
pos, name=name, doc=doc_node, pos, name=name, doc=doc_node, class_def_node=self)
metaclass=self.metaclass, bases=self.bases, mkw=self.mkw)
self.classobj = ExprNodes.Py3ClassNode( self.classobj = ExprNodes.Py3ClassNode(
pos, name=name, pos, name=name, class_def_node=self, doc=doc_node,
bases=self.bases, dict=self.dict, doc=doc_node,
metaclass=self.metaclass, mkw=self.mkw,
calculate_metaclass=needs_metaclass_calculation, calculate_metaclass=needs_metaclass_calculation,
allow_py2_metaclass=allow_py2_metaclass) allow_py2_metaclass=allow_py2_metaclass)
else: else:
# no bases, no metaclass => old style class creation # no bases, no metaclass => old style class creation
self.dict = ExprNodes.DictNode(pos, key_value_pairs=[]) self.dict = ExprNodes.DictNode(pos, key_value_pairs=[])
self.classobj = ExprNodes.ClassNode( self.classobj = ExprNodes.ClassNode(
pos, name=name, pos, name=name, class_def_node=self, doc=doc_node)
bases=bases, dict=self.dict, doc=doc_node)
self.target = ExprNodes.NameNode(pos, name=name) self.target = ExprNodes.NameNode(pos, name=name)
self.class_cell = ExprNodes.ClassCellInjectorNode(self.pos) self.class_cell = ExprNodes.ClassCellInjectorNode(self.pos)
...@@ -4540,7 +4536,7 @@ class PyClassDefNode(ClassDefNode): ...@@ -4540,7 +4536,7 @@ class PyClassDefNode(ClassDefNode):
visibility='private', visibility='private',
module_name=None, module_name=None,
class_name=self.name, class_name=self.name,
bases=self.classobj.bases or ExprNodes.TupleNode(self.pos, args=[]), bases=self.bases or ExprNodes.TupleNode(self.pos, args=[]),
decorators=self.decorators, decorators=self.decorators,
body=self.body, body=self.body,
in_pxd=False, in_pxd=False,
...@@ -4564,6 +4560,10 @@ class PyClassDefNode(ClassDefNode): ...@@ -4564,6 +4560,10 @@ class PyClassDefNode(ClassDefNode):
args=[class_result]) args=[class_result])
self.decorators = None self.decorators = None
self.class_result = class_result self.class_result = class_result
if self.bases:
self.bases.analyse_declarations(env)
if self.mkw:
self.mkw.analyse_declarations(env)
self.class_result.analyse_declarations(env) self.class_result.analyse_declarations(env)
self.target.analyse_target_declaration(env) self.target.analyse_target_declaration(env)
cenv = self.create_scope(env) cenv = self.create_scope(env)
...@@ -4574,10 +4574,10 @@ class PyClassDefNode(ClassDefNode): ...@@ -4574,10 +4574,10 @@ class PyClassDefNode(ClassDefNode):
def analyse_expressions(self, env): def analyse_expressions(self, env):
if self.bases: if self.bases:
self.bases = self.bases.analyse_expressions(env) self.bases = self.bases.analyse_expressions(env)
if self.metaclass:
self.metaclass = self.metaclass.analyse_expressions(env)
if self.mkw: if self.mkw:
self.mkw = self.mkw.analyse_expressions(env) self.mkw = self.mkw.analyse_expressions(env)
if self.metaclass:
self.metaclass = self.metaclass.analyse_expressions(env)
self.dict = self.dict.analyse_expressions(env) self.dict = self.dict.analyse_expressions(env)
self.class_result = self.class_result.analyse_expressions(env) self.class_result = self.class_result.analyse_expressions(env)
cenv = self.scope cenv = self.scope
......
...@@ -24,3 +24,22 @@ def cond_if_bases(x): ...@@ -24,3 +24,22 @@ def cond_if_bases(x):
class PyClass(A if x else B): class PyClass(A if x else B):
p = 5 p = 5
return PyClass return PyClass
def make_subclass(*bases):
"""
>>> cls = make_subclass(list)
>>> issubclass(cls, list) or cls.__mro__
True
>>> class Cls(object): pass
>>> cls = make_subclass(Cls, list)
>>> issubclass(cls, list) or cls.__mro__
True
>>> issubclass(cls, Cls) or cls.__mro__
True
"""
# GH-3338
class MadeClass(*bases):
pass
return MadeClass
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