Commit fc1aef57 authored by Stefan Behnel's avatar Stefan Behnel

allow external 'cpdef cfunc' overrides in .pxd files

parent ab91ae72
......@@ -1723,19 +1723,18 @@ class NameNode(AtomicExprNode):
def analyse_target_types(self, env):
self.analyse_entry(env, is_target=True)
if (not self.is_lvalue() and self.entry.is_cfunction and
self.entry.fused_cfunction and self.entry.as_variable):
# We need this for the fused 'def' TreeFragment
self.entry = self.entry.as_variable
self.type = self.entry.type
if self.entry.is_cfunction and self.entry.as_variable:
if self.entry.is_overridable or not self.is_lvalue() and self.entry.fused_cfunction:
# We need this for assigning to cpdef names and for the fused 'def' TreeFragment
self.entry = self.entry.as_variable
self.type = self.entry.type
if self.type.is_const:
error(self.pos, "Assignment to const '%s'" % self.name)
if self.type.is_reference:
error(self.pos, "Assignment to reference '%s'" % self.name)
if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue '%s'"
% self.name)
error(self.pos, "Assignment to non-lvalue '%s'" % self.name)
self.type = PyrexTypes.error_type
self.entry.used = 1
if self.entry.type.is_buffer:
......@@ -1859,9 +1858,14 @@ class NameNode(AtomicExprNode):
return True
def is_lvalue(self):
return self.entry.is_variable and \
not self.entry.type.is_array and \
return (
self.entry.is_variable and
not self.entry.type.is_array and
not self.entry.is_readonly
) or (
self.entry.is_cfunction and
self.entry.is_overridable
)
def is_addressable(self):
return self.entry.is_variable and not self.type.is_memoryviewslice
......
......@@ -1294,10 +1294,9 @@ class CVarDefNode(StatNode):
return
if type.is_cfunction:
self.entry = dest_scope.declare_cfunction(name, type, declarator.pos,
cname = cname, visibility = self.visibility, in_pxd = self.in_pxd,
api = self.api, modifiers = self.modifiers)
cname=cname, visibility=self.visibility, in_pxd=self.in_pxd,
api=self.api, modifiers=self.modifiers, overridable=self.overridable)
if self.entry is not None:
self.entry.is_overridable = self.overridable
self.entry.directive_locals = copy.copy(self.directive_locals)
if 'staticmethod' in env.directives:
type.is_static_method = True
......@@ -2256,8 +2255,9 @@ class CFuncDefNode(FuncDefNode):
type.is_static_method = self.is_static_method
self.entry = env.declare_cfunction(
name, type, self.pos,
cname = cname, visibility = self.visibility, api = self.api,
defining = self.body is not None, modifiers = self.modifiers)
cname=cname, visibility=self.visibility, api=self.api,
defining=self.body is not None, modifiers=self.modifiers,
overridable=self.overridable)
self.entry.inline_func_in_pxd = self.inline_in_pxd
self.return_type = type.return_type
if self.return_type.is_array and self.visibility != 'extern':
......
......@@ -681,8 +681,8 @@ class Scope(object):
self.pyfunc_entries.append(entry)
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None):
cname=None, visibility='private', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
# Add an entry for a C function.
if not cname:
if visibility != 'private' or api:
......@@ -693,6 +693,9 @@ class Scope(object):
if entry:
if visibility != 'private' and visibility != entry.visibility:
warning(pos, "Function '%s' previously declared as '%s'" % (name, entry.visibility), 1)
if overridable != entry.is_overridable:
warning(pos, "Function '%s' previously declared as '%s'" % (
name, 'cpdef' if overridable else 'cdef'), 1)
if not entry.type.same_as(type):
if visibility == 'extern' and entry.visibility == 'extern':
can_override = False
......@@ -718,6 +721,7 @@ class Scope(object):
else:
entry = self.add_cfunction(name, type, pos, cname, visibility, modifiers)
entry.func_cname = cname
entry.is_overridable = overridable
if in_pxd and visibility != 'extern':
entry.defined_in_pxd = 1
if api:
......@@ -731,6 +735,13 @@ class Scope(object):
if utility_code:
assert not entry.utility_code, "duplicate utility code definition in entry %s (%s)" % (name, cname)
entry.utility_code = utility_code
if overridable:
# names of cpdef functions can be used as variables and can be assigned to
var_entry = Entry(name, cname, py_object_type) # FIXME: cname?
var_entry.is_variable = 1
var_entry.is_pyglobal = 1
var_entry.scope = entry.scope
entry.as_variable = var_entry
type.entry = entry
return entry
......@@ -889,13 +900,12 @@ class BuiltinScope(Scope):
else:
warning(pos, "undeclared name not builtin: %s" % name, 2)
def declare_builtin_cfunction(self, name, type, cname, python_equiv = None,
utility_code = None):
def declare_builtin_cfunction(self, name, type, cname, python_equiv=None, utility_code=None):
# If python_equiv == "*", the Python equivalent has the same name
# as the entry, otherwise it has the name specified by python_equiv.
name = EncodedString(name)
entry = self.declare_cfunction(name, type, None, cname, visibility='extern',
utility_code = utility_code)
utility_code=utility_code)
if python_equiv:
if python_equiv == "*":
python_equiv = name
......@@ -1212,8 +1222,8 @@ class ModuleScope(Scope):
return entry
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None):
cname=None, visibility='private', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
# Add an entry for a C function.
if not cname:
if visibility == 'extern' or (visibility == 'public' and defining):
......@@ -1230,8 +1240,9 @@ class ModuleScope(Scope):
entry.func_cname = cname
entry = Scope.declare_cfunction(
self, name, type, pos,
cname = cname, visibility = visibility, api = api, in_pxd = in_pxd,
defining = defining, modifiers = modifiers, utility_code = utility_code)
cname=cname, visibility=visibility, api=api, in_pxd=in_pxd,
defining=defining, modifiers=modifiers, utility_code=utility_code,
overridable=overridable)
return entry
def declare_global(self, name, pos):
......@@ -1670,8 +1681,8 @@ class StructOrUnionScope(Scope):
return entry
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = ()): # currently no utility code ...
cname=None, visibility='private', api=0, in_pxd=0,
defining=0, modifiers=()): # currently no utility code ...
return self.declare_var(name, type, pos,
cname=cname, visibility=visibility)
......@@ -1940,8 +1951,8 @@ class CClassScope(ClassScope):
return entry
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None):
cname=None, visibility='private', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
if get_special_method_signature(name) and not self.parent_type.is_builtin_type:
error(pos, "Special methods must be declared with 'def', not 'cdef'")
args = type.args
......@@ -1976,8 +1987,7 @@ class CClassScope(ClassScope):
error(pos,
"C method '%s' not previously declared in definition part of"
" extension type" % name)
entry = self.add_cfunction(name, type, pos, cname,
visibility, modifiers)
entry = self.add_cfunction(name, type, pos, cname, visibility, modifiers)
if defining:
entry.func_cname = self.mangle(Naming.func_prefix, name)
entry.utility_code = utility_code
......@@ -2007,7 +2017,7 @@ class CClassScope(ClassScope):
# equivalent that must be accessible to support bound methods
name = EncodedString(name)
entry = self.declare_cfunction(name, type, None, cname, visibility='extern',
utility_code = utility_code)
utility_code=utility_code)
var_entry = Entry(name, name, py_object_type)
var_entry.is_variable = 1
var_entry.is_builtin = 1
......@@ -2144,8 +2154,8 @@ class CppClassScope(Scope):
(self.default_constructor, self.default_constructor))
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'extern', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None):
cname=None, visibility='extern', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
if name in (self.name.split('::')[-1], '__init__') and cname is None:
self.check_base_default_constructor(pos)
cname = self.type.cname
......@@ -2184,9 +2194,9 @@ class CppClassScope(Scope):
for base_entry in base_scope.cfunc_entries:
entry = self.declare_cfunction(base_entry.name, base_entry.type,
base_entry.pos, base_entry.cname,
base_entry.visibility, 0,
modifiers = base_entry.func_modifiers,
utility_code = base_entry.utility_code)
base_entry.visibility, api=0,
modifiers=base_entry.func_modifiers,
utility_code=base_entry.utility_code)
entry.is_inherited = 1
def specialize(self, values, type_entry):
......@@ -2205,7 +2215,7 @@ class CppClassScope(Scope):
e.type.specialize(values),
e.pos,
e.cname,
utility_code = e.utility_code)
utility_code=e.utility_code)
else:
scope.declare_var(entry.name,
entry.type.specialize(values),
......
cdef extern from "math.h":
cpdef double pxd_sqrt "sqrt"(double)
"""
>>> pxd_sqrt(9)
3.0
"""
import cython
if not cython.compiled:
from math import sqrt as pxd_sqrt
@cython.test_assert_path_exists('//SimpleCallNode/NameNode[@type.is_pyobject = False]')
def call_pxd_sqrt(x):
"""
>>> call_pxd_sqrt(9)
3.0
"""
return pxd_sqrt(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