diff options
author | da-woods <dw-git@d-woods.co.uk> | 2020-03-03 13:01:18 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-03 14:01:18 +0100 |
commit | a8cb127df0cb970b00a8f58797740ef571dc8817 (patch) | |
tree | 3e91b2ec4a5cd382552b4617f4ec779c185f2ec8 | |
parent | f6bf6aa9c7d2414b54e7289639ae5f43b15ede05 (diff) | |
download | cython-a8cb127df0cb970b00a8f58797740ef571dc8817.tar.gz |
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
-rw-r--r-- | Cython/Compiler/ExprNodes.py | 6 | ||||
-rw-r--r-- | Cython/Compiler/Nodes.py | 13 | ||||
-rw-r--r-- | tests/run/pure_fused.pxd | 6 | ||||
-rw-r--r-- | tests/run/pure_fused.py | 60 |
4 files changed, 76 insertions, 9 deletions
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 48e6feadc..1915369df 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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 diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 9a63bc550..9b5dc2591 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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): diff --git a/tests/run/pure_fused.pxd b/tests/run/pure_fused.pxd new file mode 100644 index 000000000..8517227d0 --- /dev/null +++ b/tests/run/pure_fused.pxd @@ -0,0 +1,6 @@ +ctypedef fused NotInPy: + int + float + +cdef class TestCls: + cpdef cpfunc(self, NotInPy arg) diff --git a/tests/run/pure_fused.py b/tests/run/pure_fused.py new file mode 100644 index 000000000..b6d211e0f --- /dev/null +++ b/tests/run/pure_fused.py @@ -0,0 +1,60 @@ +# 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) + |