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

Support fused arguments specified by annotation or locals (GH-3391)

1. DefNode.has_fused_arguments was set too early (before
locals/annotations) were evalutated, so function was not treated
as fused.
2. When re-evaluating the specializations of the fused function
it was treated as a redefinition because the locals/annotation was
reapplied over the specialized type.
3. Including annotation as string (required changes to
StringNode.analyse_as_type), and extra tests for fused type defined
as cython.fused_type in the Py file
parent f6bf6aa9
......@@ -1434,11 +1434,7 @@ def _analyse_name_as_type(name, pos, env):
return type
global_entry = env.global_scope().lookup(name)
if global_entry and global_entry.type and (
global_entry.type.is_extension_type
or global_entry.type.is_struct_or_union
or global_entry.type.is_builtin_type
or global_entry.type.is_cpp_class):
if global_entry and global_entry.is_type and global_entry.type:
return global_entry.type
from .TreeFragment import TreeFragment
......
......@@ -877,7 +877,10 @@ class CArgDeclNode(Node):
# inject type declaration from annotations
# this is called without 'env' by AdjustDefByDirectives transform before declaration analysis
if self.annotation and env and env.directives['annotation_typing'] and self.base_type.name is None:
if (self.annotation and env and env.directives['annotation_typing']
# CSimpleBaseTypeNode has a name attribute; CAnalysedBaseTypeNode
# (and maybe other options) doesn't
and getattr(self.base_type, "name", None) is None):
arg_type = self.inject_type_from_annotations(env)
if arg_type is not None:
base_type = arg_type
......@@ -1678,6 +1681,8 @@ class FuncDefNode(StatNode, BlockNode):
return arg
if other_type is None:
error(type_node.pos, "Not a type")
elif other_type.is_fused and any(orig_type.same_as(t) for t in other_type.types):
pass # use specialized rather than fused type
elif orig_type is not py_object_type and not orig_type.same_as(other_type):
error(arg.base_type.pos, "Signature does not agree with previous declaration")
error(type_node.pos, "Previous declaration here")
......@@ -2934,9 +2939,6 @@ class DefNode(FuncDefNode):
arg.name = name_declarator.name
arg.type = type
if type.is_fused:
self.has_fused_arguments = True
self.align_argument_type(env, arg)
if name_declarator and name_declarator.cname:
error(self.pos, "Python function argument cannot have C name specification")
......@@ -2967,6 +2969,9 @@ class DefNode(FuncDefNode):
error(arg.pos, "Only Python type arguments can have 'not None'")
if arg.or_none:
error(arg.pos, "Only Python type arguments can have 'or None'")
if arg.type.is_fused:
self.has_fused_arguments = True
env.fused_to_specific = f2s
if has_np_pythran(env):
......
ctypedef fused NotInPy:
int
float
cdef class TestCls:
cpdef cpfunc(self, NotInPy arg)
# mode: run
# tag: fused, pure3.0
#cython: annotation_typing=True
import cython
InPy = cython.fused_type(cython.int, cython.float)
class TestCls:
# although annotations as strings isn't recommended and generates a warning
# it does allow the test to run on more (pure) Python versions
def func1(self, arg: 'NotInPy'):
"""
>>> TestCls().func1(1.0)
'float'
>>> TestCls().func1(2)
'int'
"""
return cython.typeof(arg)
if cython.compiled:
@cython.locals(arg = NotInPy) # NameError in pure Python
def func2(self, arg):
"""
>>> TestCls().func2(1.0)
'float'
>>> TestCls().func2(2)
'int'
"""
return cython.typeof(arg)
def cpfunc(self, arg):
"""
>>> TestCls().cpfunc(1.0)
'float'
>>> TestCls().cpfunc(2)
'int'
"""
return cython.typeof(arg)
def func1_inpy(self, arg: InPy):
"""
>>> TestCls().func1_inpy(1.0)
'float'
>>> TestCls().func1_inpy(2)
'int'
"""
return cython.typeof(arg)
@cython.locals(arg = InPy)
def func2_inpy(self, arg):
"""
>>> TestCls().func2_inpy(1.0)
'float'
>>> TestCls().func2_inpy(2)
'int'
"""
return cython.typeof(arg)
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