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