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):
if scope.is_internal:
# internal classes (should) never need None inits, normal zeroing will do
py_attrs = []
cpp_class_attrs = [entry for entry in scope.var_entries
if entry.type.is_cpp_class]
cpp_constructable_attrs = [entry for entry in scope.var_entries
if entry.type.needs_cpp_construction]
cinit_func_entry = scope.lookup_here("__cinit__")
if cinit_func_entry and not cinit_func_entry.is_special:
......@@ -1443,7 +1443,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
need_self_cast = (type.vtabslot_cname or
(py_buffers or memoryview_slices or py_attrs) or
cpp_class_attrs)
cpp_constructable_attrs)
if need_self_cast:
code.putln("%s;" % scope.parent_type.declaration_code("p"))
if base_type:
......@@ -1513,7 +1513,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.vtabslot_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();" % (
entry.cname, entry.type.empty_declaration_code()))
......@@ -1583,10 +1583,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
dict_slot = None
_, (py_attrs, _, memoryview_slices) = scope.get_refcounted_entries()
cpp_class_attrs = [entry for entry in scope.var_entries
if entry.type.is_cpp_class]
cpp_destructable_attrs = [entry for entry in scope.var_entries
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)
if not is_final_type:
......@@ -1630,7 +1630,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if dict_slot:
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)
for entry in (py_attrs + memoryview_slices):
......
......@@ -194,7 +194,8 @@ class PyrexType(BaseType):
# is_pythran_expr boolean Is Pythran expr
# is_numpy_buffer boolean Is Numpy array buffer
# 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.
# default_value string Initial value that can be assigned before first user assignment.
# declaration_value string The value statically assigned on declaration (if any).
......@@ -262,6 +263,7 @@ class PyrexType(BaseType):
is_pythran_expr = 0
is_numpy_buffer = 0
has_attributes = 0
needs_cpp_construction = 0
needs_refcounting = 0
default_value = ""
declaration_value = ""
......@@ -3495,7 +3497,7 @@ class CStructOrUnionType(CType):
has_attributes = 1
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.cname = cname
self.kind = kind
......@@ -3510,6 +3512,7 @@ class CStructOrUnionType(CType):
self._convert_to_py_code = None
self._convert_from_py_code = None
self.packed = packed
self.needs_cpp_construction = self.is_struct and in_cpp
def can_coerce_to_pyobject(self, env):
if self._convert_to_py_code is False:
......@@ -3680,6 +3683,7 @@ class CppClassType(CType):
is_cpp_class = 1
has_attributes = 1
needs_cpp_construction = 1
exception_check = True
namespace = None
......
......@@ -592,8 +592,10 @@ class Scope(object):
cname = self.mangle(Naming.type_prefix, name)
entry = self.lookup_here(name)
if not entry:
in_cpp = self.is_cpp()
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,
visibility = visibility, api = api,
defining = scope is not None)
......@@ -2146,7 +2148,7 @@ class CClassScope(ClassScope):
has_pyobject_attrs = False
has_memoryview_attrs = False
has_cpp_class_attrs = False
has_cpp_constructable_attrs = False
has_cyclic_pyobject_attrs = False
defined = False
implemented = False
......@@ -2234,14 +2236,14 @@ class CClassScope(ClassScope):
cname = punycodify_name(cname, Naming.unicode_structmember_prefix)
if type.is_cpp_class and visibility != 'extern':
type.check_nullary_constructor(pos)
self.use_utility_code(Code.UtilityCode("#include <new>"))
entry = self.declare(name, cname, type, pos, visibility)
entry.is_variable = 1
self.var_entries.append(entry)
if type.is_memoryviewslice:
self.has_memoryview_attrs = True
elif type.is_cpp_class:
self.has_cpp_class_attrs = True
elif type.needs_cpp_construction:
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__'):
self.has_pyobject_attrs = True
if (not type.is_builtin_type
......
......@@ -432,7 +432,7 @@ class ConstructorSlot(InternalMethodSlot):
if (scope.parent_type.base_type
and not scope.has_pyobject_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)):
entry = scope.lookup_here(self.method) if self.method else None
if not (entry and entry.is_special):
......
......@@ -130,6 +130,10 @@ def test_value_call(int w):
del sqr
cdef struct StructWithEmpty:
Empty empty
def get_destructor_count():
return destructor_count
......@@ -146,6 +150,15 @@ def test_stack_allocation(int w, int h):
print rect.method(<int>5)
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 Empty empty
......@@ -153,6 +166,9 @@ cdef class EmptyHolder:
cdef class AnotherEmptyHolder(EmptyHolder):
cdef Empty another_empty
cdef class EmptyViaStructHolder:
cdef StructWithEmpty swe
def test_class_member():
"""
>>> test_class_member()
......@@ -183,6 +199,18 @@ def test_derived_class_member():
assert destructor_count - start_destructor_count == 2, \
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 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