Commit fd1a431b authored by da-woods's avatar da-woods Committed by GitHub

Call destructors for structs in C++ (GH-3228)

In C++ mode, structs can contain C++ classes. Therefore structs
should have their destructors called to ensure any class
contained is also destructed.

Also, a bit more thorough about ensuring constructor is generated where necessary.

Closes GH-3226.
parent 74357d8a
...@@ -1408,8 +1408,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1408,8 +1408,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if scope.is_internal: if scope.is_internal:
# internal classes (should) never need None inits, normal zeroing will do # internal classes (should) never need None inits, normal zeroing will do
py_attrs = [] py_attrs = []
cpp_class_attrs = [entry for entry in scope.var_entries cpp_constructable_attrs = [entry for entry in scope.var_entries
if entry.type.is_cpp_class] if entry.type.needs_cpp_construction]
cinit_func_entry = scope.lookup_here("__cinit__") cinit_func_entry = scope.lookup_here("__cinit__")
if cinit_func_entry and not cinit_func_entry.is_special: if cinit_func_entry and not cinit_func_entry.is_special:
...@@ -1443,7 +1443,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1443,7 +1443,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
need_self_cast = (type.vtabslot_cname or need_self_cast = (type.vtabslot_cname or
(py_buffers or memoryview_slices or py_attrs) or (py_buffers or memoryview_slices or py_attrs) or
cpp_class_attrs) cpp_constructable_attrs)
if need_self_cast: if need_self_cast:
code.putln("%s;" % scope.parent_type.declaration_code("p")) code.putln("%s;" % scope.parent_type.declaration_code("p"))
if base_type: if base_type:
...@@ -1513,7 +1513,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1513,7 +1513,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.vtabslot_cname, type.vtabslot_cname,
struct_type_cast, type.vtabptr_cname)) struct_type_cast, type.vtabptr_cname))
for entry in cpp_class_attrs: for entry in cpp_constructable_attrs:
code.putln("new((void*)&(p->%s)) %s();" % ( code.putln("new((void*)&(p->%s)) %s();" % (
entry.cname, entry.type.empty_declaration_code())) entry.cname, entry.type.empty_declaration_code()))
...@@ -1583,10 +1583,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1583,10 +1583,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
dict_slot = None dict_slot = None
_, (py_attrs, _, memoryview_slices) = scope.get_refcounted_entries() _, (py_attrs, _, memoryview_slices) = scope.get_refcounted_entries()
cpp_class_attrs = [entry for entry in scope.var_entries cpp_destructable_attrs = [entry for entry in scope.var_entries
if entry.type.is_cpp_class] if entry.type.needs_cpp_construction]
if py_attrs or cpp_class_attrs or memoryview_slices or weakref_slot or dict_slot: if py_attrs or cpp_destructable_attrs or memoryview_slices or weakref_slot or dict_slot:
self.generate_self_cast(scope, code) self.generate_self_cast(scope, code)
if not is_final_type: if not is_final_type:
...@@ -1630,7 +1630,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1630,7 +1630,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if dict_slot: if dict_slot:
code.putln("if (p->__dict__) PyDict_Clear(p->__dict__);") code.putln("if (p->__dict__) PyDict_Clear(p->__dict__);")
for entry in cpp_class_attrs: for entry in cpp_destructable_attrs:
code.putln("__Pyx_call_destructor(p->%s);" % entry.cname) code.putln("__Pyx_call_destructor(p->%s);" % entry.cname)
for entry in (py_attrs + memoryview_slices): for entry in (py_attrs + memoryview_slices):
......
...@@ -194,7 +194,8 @@ class PyrexType(BaseType): ...@@ -194,7 +194,8 @@ class PyrexType(BaseType):
# is_pythran_expr boolean Is Pythran expr # is_pythran_expr boolean Is Pythran expr
# is_numpy_buffer boolean Is Numpy array buffer # is_numpy_buffer boolean Is Numpy array buffer
# has_attributes boolean Has C dot-selectable attributes # has_attributes boolean Has C dot-selectable attributes
# needs_refcounting boolean Needs code to be generated similar to incref/gotref/decref. # needs_cpp_construction boolean Needs C++ constructor and destructor when used in a cdef class
# needs_refcounting boolean Needs code to be generated similar to incref/gotref/decref.
# Largely used internally. # Largely used internally.
# default_value string Initial value that can be assigned before first user assignment. # default_value string Initial value that can be assigned before first user assignment.
# declaration_value string The value statically assigned on declaration (if any). # declaration_value string The value statically assigned on declaration (if any).
...@@ -262,6 +263,7 @@ class PyrexType(BaseType): ...@@ -262,6 +263,7 @@ class PyrexType(BaseType):
is_pythran_expr = 0 is_pythran_expr = 0
is_numpy_buffer = 0 is_numpy_buffer = 0
has_attributes = 0 has_attributes = 0
needs_cpp_construction = 0
needs_refcounting = 0 needs_refcounting = 0
default_value = "" default_value = ""
declaration_value = "" declaration_value = ""
...@@ -3495,7 +3497,7 @@ class CStructOrUnionType(CType): ...@@ -3495,7 +3497,7 @@ class CStructOrUnionType(CType):
has_attributes = 1 has_attributes = 1
exception_check = True exception_check = True
def __init__(self, name, kind, scope, typedef_flag, cname, packed=False): def __init__(self, name, kind, scope, typedef_flag, cname, packed=False, in_cpp=False):
self.name = name self.name = name
self.cname = cname self.cname = cname
self.kind = kind self.kind = kind
...@@ -3510,6 +3512,7 @@ class CStructOrUnionType(CType): ...@@ -3510,6 +3512,7 @@ class CStructOrUnionType(CType):
self._convert_to_py_code = None self._convert_to_py_code = None
self._convert_from_py_code = None self._convert_from_py_code = None
self.packed = packed self.packed = packed
self.needs_cpp_construction = self.is_struct and in_cpp
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
if self._convert_to_py_code is False: if self._convert_to_py_code is False:
...@@ -3680,6 +3683,7 @@ class CppClassType(CType): ...@@ -3680,6 +3683,7 @@ class CppClassType(CType):
is_cpp_class = 1 is_cpp_class = 1
has_attributes = 1 has_attributes = 1
needs_cpp_construction = 1
exception_check = True exception_check = True
namespace = None namespace = None
......
...@@ -592,8 +592,10 @@ class Scope(object): ...@@ -592,8 +592,10 @@ class Scope(object):
cname = self.mangle(Naming.type_prefix, name) cname = self.mangle(Naming.type_prefix, name)
entry = self.lookup_here(name) entry = self.lookup_here(name)
if not entry: if not entry:
in_cpp = self.is_cpp()
type = PyrexTypes.CStructOrUnionType( type = PyrexTypes.CStructOrUnionType(
name, kind, scope, typedef_flag, cname, packed) name, kind, scope, typedef_flag, cname, packed,
in_cpp = in_cpp)
entry = self.declare_type(name, type, pos, cname, entry = self.declare_type(name, type, pos, cname,
visibility = visibility, api = api, visibility = visibility, api = api,
defining = scope is not None) defining = scope is not None)
...@@ -2146,7 +2148,7 @@ class CClassScope(ClassScope): ...@@ -2146,7 +2148,7 @@ class CClassScope(ClassScope):
has_pyobject_attrs = False has_pyobject_attrs = False
has_memoryview_attrs = False has_memoryview_attrs = False
has_cpp_class_attrs = False has_cpp_constructable_attrs = False
has_cyclic_pyobject_attrs = False has_cyclic_pyobject_attrs = False
defined = False defined = False
implemented = False implemented = False
...@@ -2234,14 +2236,14 @@ class CClassScope(ClassScope): ...@@ -2234,14 +2236,14 @@ class CClassScope(ClassScope):
cname = punycodify_name(cname, Naming.unicode_structmember_prefix) cname = punycodify_name(cname, Naming.unicode_structmember_prefix)
if type.is_cpp_class and visibility != 'extern': if type.is_cpp_class and visibility != 'extern':
type.check_nullary_constructor(pos) type.check_nullary_constructor(pos)
self.use_utility_code(Code.UtilityCode("#include <new>"))
entry = self.declare(name, cname, type, pos, visibility) entry = self.declare(name, cname, type, pos, visibility)
entry.is_variable = 1 entry.is_variable = 1
self.var_entries.append(entry) self.var_entries.append(entry)
if type.is_memoryviewslice: if type.is_memoryviewslice:
self.has_memoryview_attrs = True self.has_memoryview_attrs = True
elif type.is_cpp_class: elif type.needs_cpp_construction:
self.has_cpp_class_attrs = True self.use_utility_code(Code.UtilityCode("#include <new>"))
self.has_cpp_constructable_attrs = True
elif type.is_pyobject and (self.is_closure_class_scope or name != '__weakref__'): elif type.is_pyobject and (self.is_closure_class_scope or name != '__weakref__'):
self.has_pyobject_attrs = True self.has_pyobject_attrs = True
if (not type.is_builtin_type if (not type.is_builtin_type
......
...@@ -432,7 +432,7 @@ class ConstructorSlot(InternalMethodSlot): ...@@ -432,7 +432,7 @@ class ConstructorSlot(InternalMethodSlot):
if (scope.parent_type.base_type if (scope.parent_type.base_type
and not scope.has_pyobject_attrs and not scope.has_pyobject_attrs
and not scope.has_memoryview_attrs and not scope.has_memoryview_attrs
and not scope.has_cpp_class_attrs and not scope.has_cpp_constructable_attrs
and not (self.slot_name == 'tp_new' and scope.parent_type.vtabslot_cname)): and not (self.slot_name == 'tp_new' and scope.parent_type.vtabslot_cname)):
entry = scope.lookup_here(self.method) if self.method else None entry = scope.lookup_here(self.method) if self.method else None
if not (entry and entry.is_special): if not (entry and entry.is_special):
......
...@@ -130,6 +130,10 @@ def test_value_call(int w): ...@@ -130,6 +130,10 @@ def test_value_call(int w):
del sqr del sqr
cdef struct StructWithEmpty:
Empty empty
def get_destructor_count(): def get_destructor_count():
return destructor_count return destructor_count
...@@ -146,6 +150,15 @@ def test_stack_allocation(int w, int h): ...@@ -146,6 +150,15 @@ def test_stack_allocation(int w, int h):
print rect.method(<int>5) print rect.method(<int>5)
return destructor_count return destructor_count
def test_stack_allocation_in_struct():
"""
>>> d = test_stack_allocation_in_struct()
>>> get_destructor_count() - d
1
"""
cdef StructWithEmpty swe
sizeof(swe.empty) # use it for something
return destructor_count
cdef class EmptyHolder: cdef class EmptyHolder:
cdef Empty empty cdef Empty empty
...@@ -153,6 +166,9 @@ cdef class EmptyHolder: ...@@ -153,6 +166,9 @@ cdef class EmptyHolder:
cdef class AnotherEmptyHolder(EmptyHolder): cdef class AnotherEmptyHolder(EmptyHolder):
cdef Empty another_empty cdef Empty another_empty
cdef class EmptyViaStructHolder:
cdef StructWithEmpty swe
def test_class_member(): def test_class_member():
""" """
>>> test_class_member() >>> test_class_member()
...@@ -183,6 +199,18 @@ def test_derived_class_member(): ...@@ -183,6 +199,18 @@ def test_derived_class_member():
assert destructor_count - start_destructor_count == 2, \ assert destructor_count - start_destructor_count == 2, \
destructor_count - start_destructor_count destructor_count - start_destructor_count
def test_class_in_struct_member():
"""
>>> test_class_in_struct_member()
"""
start_constructor_count = constructor_count
start_destructor_count = destructor_count
e = EmptyViaStructHolder()
#assert constructor_count - start_constructor_count == 1, \
# constructor_count - start_constructor_count
del e
assert destructor_count - start_destructor_count == 1, \
destructor_count - start_destructor_count
cdef class TemplateClassMember: cdef class TemplateClassMember:
cdef vector[int] x cdef vector[int] x
......
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