Commit 65a97690 authored by scoder's avatar scoder

Merge pull request #59 from vitek/_final_methods

Final and inline method support
parents 278cc338 7f22634d
......@@ -3844,6 +3844,8 @@ class AttributeNode(ExprNode):
#print "...obj_code =", obj_code ###
if self.entry and self.entry.is_cmethod:
if obj.type.is_extension_type and not self.entry.is_builtin_cmethod:
if self.entry.final_func_cname:
return self.entry.final_func_cname
return "((struct %s *)%s%s%s)->%s" % (
obj.type.vtabstruct_cname, obj_code, self.op,
obj.type.vtabslot_cname, self.member)
......
......@@ -457,6 +457,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_typeobj_predeclaration(entry, code)
self.generate_exttype_vtable_struct(entry, code)
self.generate_exttype_vtabptr_declaration(entry, code)
self.generate_exttype_final_methods_declaration(entry, code)
def generate_declarations_for_modules(self, env, modules, globalstate):
typecode = globalstate['type_declarations']
......@@ -991,6 +992,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.vtabstruct_cname,
type.vtabptr_cname))
def generate_exttype_final_methods_declaration(self, entry, code):
if not entry.used:
return
code.mark_pos(entry.pos)
# Generate final methods prototypes
type = entry.type
for method_entry in entry.type.scope.cfunc_entries:
if not method_entry.is_inherited and method_entry.final_func_cname:
declaration = method_entry.type.declaration_code(
method_entry.final_func_cname)
if entry.func_modifiers:
modifiers = "%s " % ' '.join(entry.func_modifiers).upper()
else:
modifiers = ''
code.putln("static %s%s;" % (modifiers, declaration))
def generate_objstruct_predeclaration(self, type, code):
if not type.scope:
return
......
......@@ -1751,7 +1751,8 @@ class CFuncDefNode(FuncDefNode):
self.entry.as_variable = self.py_func.entry
# Reset scope entry the above cfunction
env.entries[name] = self.entry
if not env.is_module_scope or Options.lookup_module_cpdef:
if (not self.entry.is_final_cmethod and
(not env.is_module_scope or Options.lookup_module_cpdef)):
self.override = OverrideCheckNode(self.pos, py_func = self.py_func)
self.body = StatListNode(self.pos, stats=[self.override, self.body])
self.create_local_scope(env)
......@@ -2243,7 +2244,11 @@ class DefNode(FuncDefNode):
#print "DefNode.declare_pyfunction:", self.name, "in", env ###
name = self.name
entry = env.lookup_here(name)
if entry and entry.type.is_cfunction and not entry.is_builtin_cmethod and not self.is_wrapper:
if entry:
if entry.is_final_cmethod and not env.parent_type.is_final_type:
error(self.pos, "Only final type could have final cpdef method")
if (entry.type.is_cfunction and not entry.is_builtin_cmethod
and not self.is_wrapper):
warning(self.pos, "Overriding cdef method with def method.", 5)
entry = env.declare_pyfunction(name, self.pos, allow_redefine=not self.is_wrapper)
self.entry = entry
......@@ -3503,7 +3508,7 @@ class CClassDefNode(ClassDefNode):
error(self.pos, "Base class '%s' of type '%s' is incomplete" % (
self.base_class_name, self.class_name))
elif base_class_entry.type.scope and base_class_entry.type.scope.directives and \
base_class_entry.type.scope.directives['final']:
base_class_entry.type.is_final_type:
error(self.pos, "Base class '%s' of type '%s' is final" % (
self.base_class_name, self.class_name))
elif base_class_entry.type.is_builtin_type and \
......
......@@ -141,7 +141,7 @@ for key, val in directive_defaults.items():
directive_scopes = { # defaults to available everywhere
# 'module', 'function', 'class', 'with statement'
'final' : ('cclass',), # add 'method' in the future
'final' : ('cclass', 'function'),
'internal' : ('cclass',),
'autotestdict' : ('module',),
'autotestdict.all' : ('module',),
......
......@@ -567,7 +567,8 @@ class PxdPostParse(CythonTransform, SkipDeclarations):
err = None # allow these slots
if isinstance(node, Nodes.CFuncDefNode):
if u'inline' in node.modifiers and self.scope_type == 'pxd':
if (u'inline' in node.modifiers and
self.scope_type in ('pxd', 'cclass')):
node.inline_in_pxd = True
if node.visibility != 'private':
err = self.ERR_NOGO_WITH_INLINE % node.visibility
......@@ -1943,9 +1944,9 @@ class CreateClosureClasses(CythonTransform):
objstruct_cname='__pyx_Generator_object',
typeobj_cname='__pyx_Generator_type',
pos=pos, defining=True, implementing=True)
entry.type.is_final_type = True
klass = entry.type.scope
klass.is_internal = True
klass.directives = {'final': True}
body_type = PyrexTypes.create_typedef_type('generator_body',
PyrexTypes.c_void_ptr_type,
......@@ -2041,11 +2042,11 @@ class CreateClosureClasses(CythonTransform):
entry = target_module_scope.declare_c_class(
name=as_name, pos=node.pos, defining=True,
implementing=True, base_type=base_type)
entry.type.is_final_type = True
func_scope.scope_class = entry
class_scope = entry.type.scope
class_scope.is_internal = True
class_scope.directives = {'final': True}
if from_closure:
assert cscope.is_closure_scope
......
......@@ -39,6 +39,7 @@ class PyrexType(BaseType):
#
# is_pyobject boolean Is a Python object type
# is_extension_type boolean Is a Python extension type
# is_final_type boolean Is a final extension type
# is_numeric boolean Is a C numeric type
# is_int boolean Is a C integer type
# is_float boolean Is a C floating point type
......@@ -90,6 +91,7 @@ class PyrexType(BaseType):
is_pyobject = 0
is_unspecified = 0
is_extension_type = 0
is_final_type = 0
is_builtin_type = 0
is_numeric = 0
is_int = 0
......
......@@ -70,6 +70,8 @@ class Entry(object):
# is_cmethod boolean Is a C method of an extension type
# is_builtin_cmethod boolean Is a C method of a builtin type (implies is_cmethod)
# is_unbound_cmethod boolean Is an unbound C method of an extension type
# is_final_cmethod boolean Is non-overridable C method
# is_inline_cmethod boolean Is inlined C method
# is_anonymous boolean Is a anonymous pyfunction entry
# is_type boolean Is a type definition
# is_cclass boolean Is an extension class
......@@ -139,6 +141,8 @@ class Entry(object):
is_cmethod = 0
is_builtin_cmethod = False
is_unbound_cmethod = 0
is_final_cmethod = 0
is_inline_cmethod = 0
is_anonymous = 0
is_type = 0
is_cclass = 0
......@@ -157,6 +161,7 @@ class Entry(object):
is_readonly = 0
func_cname = None
func_modifiers = []
final_func_cname = None
doc = None
as_variable = None
xdecref_cleanup = 0
......@@ -829,7 +834,7 @@ class BuiltinScope(Scope):
scope = CClassScope(name, outer_scope=None, visibility='extern')
scope.directives = {}
if name == 'bool':
scope.directives['final'] = True
type.is_final_type = True
type.set_scope(scope)
self.type_names[name] = 1
entry = self.declare_type(name, type, None, visibility='extern')
......@@ -1245,6 +1250,9 @@ class ModuleScope(Scope):
error(pos, "Type object name differs from previous declaration")
type.typeobj_cname = typeobj_cname
if self.directives.get('final'):
entry.type.is_final_type = True
# cdef classes are always exported, but we need to set it to
# distinguish between unused Cython utility code extension classes
entry.used = True
......@@ -1775,7 +1783,7 @@ class CClassScope(ClassScope):
# Otherwise, subtypes may choose to override the
# method, but the optimisation would prevent the
# subtype method from being called.
if not self.directives['final']:
if not self.parent_type.is_final_type:
return None
return entry
......@@ -1798,7 +1806,9 @@ class CClassScope(ClassScope):
if defining and entry.func_cname:
error(pos, "'%s' already defined" % name)
#print "CClassScope.declare_cfunction: checking signature" ###
if type.same_c_signature_as(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil:
if entry.is_final_cmethod:
error(pos, "Overriding final methods is not allowed")
elif type.same_c_signature_as(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil:
pass
elif type.compatible_signature_with(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil:
entry = self.add_cfunction(name, type, pos, cname or name, visibility='ignore', modifiers=modifiers)
......@@ -1816,6 +1826,12 @@ class CClassScope(ClassScope):
if defining:
entry.func_cname = self.mangle(Naming.func_prefix, name)
entry.utility_code = utility_code
if u'inline' in modifiers:
entry.is_inline_cmethod = True
if (self.parent_type.is_final_type or entry.is_inline_cmethod or
self.directives.get('final')):
entry.is_final_cmethod = True
entry.final_func_cname = entry.func_cname
return entry
def add_cfunction(self, name, type, pos, cname, visibility, modifiers):
......@@ -1874,6 +1890,12 @@ class CClassScope(ClassScope):
base_entry.pos, cname,
base_entry.visibility, base_entry.func_modifiers)
entry.is_inherited = 1
if base_entry.is_final_cmethod:
entry.is_final_cmethod = True
entry.is_inline_cmethod = base_entry.is_inline_cmethod
if (self.parent_scope == base_scope.parent_scope or
entry.is_inline_cmethod):
entry.final_func_cname = base_entry.final_func_cname
if is_builtin:
entry.is_builtin_cmethod = True
entry.as_variable = var_entry
......
......@@ -343,7 +343,7 @@ class TypeFlagsSlot(SlotDescriptor):
def slot_code(self, scope):
value = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER"
if not scope.directives['final']:
if not scope.parent_type.is_final_type:
value += "|Py_TPFLAGS_BASETYPE"
if scope.needs_gc():
value += "|Py_TPFLAGS_HAVE_GC"
......
# mode: error
cimport cython
cdef class BaseClass:
@cython.final
cdef cdef_method(self):
pass
@cython.final
cpdef cpdef_method(self):
pass
cdef class SubType(BaseClass):
cdef cdef_method(self):
pass
_ERRORS = """
11:10: Only final type could have final cpdef method
16:9: Overriding final methods is not allowed
"""
cdef class TestInlineMethod(object):
cdef inline int cdef_inline_method(self):
return 0
# mode: run
# ticket: 474
cimport cython
cdef class TestInlineMethod(object):
"""
>>> test = TestInlineMethod()
>>> test.test_cdef_method()
0
"""
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
@cython.test_assert_path_exists("//AttributeNode[@entry.is_inline_cmethod=True]")
def test_cdef_method(self):
return self.cdef_inline_method()
cdef class Subtyping(TestInlineMethod):
"""
>>> test = Subtyping()
>>> test.test_cdef_subtyping()
0
"""
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
@cython.test_assert_path_exists("//AttributeNode[@entry.is_inline_cmethod=True]")
def test_cdef_subtyping(self):
return self.cdef_inline_method()
# mode: run
# ticket: 568
cimport cython
@cython.final
cdef class FinalType(object):
"""
>>> obj = FinalType()
>>> obj.test_cdef()
>>> obj.test_cpdef()
"""
@cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
cdef cdef_method(self):
pass
@cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
@cython.test_fail_if_path_exists("//CFuncDefNode//OverrideCheckNode")
cpdef cpdef_method(self):
pass
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
def test_cdef(self):
self.cdef_method()
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
def test_cpdef(self):
self.cpdef_method()
cdef class BaseTypeWithFinalMethods(object):
"""
>>> obj = BaseTypeWithFinalMethods()
>>> obj.test_cdef()
"""
@cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
@cython.final
cdef cdef_method(self):
pass
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
def test_cdef(self):
self.cdef_method()
cdef class SubType(BaseTypeWithFinalMethods):
"""
>>> obj = SubType()
>>> obj.test_cdef()
"""
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
def test_cdef(self):
self.cdef_method()
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