Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Gwenaël Samain
cython
Commits
47ce63c9
Commit
47ce63c9
authored
Apr 27, 2011
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support for fused types in cdef functions
parent
b386862c
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
616 additions
and
60 deletions
+616
-60
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+17
-4
Cython/Compiler/ModuleNode.py
Cython/Compiler/ModuleNode.py
+48
-30
Cython/Compiler/Naming.py
Cython/Compiler/Naming.py
+1
-0
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+164
-4
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+51
-6
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+45
-7
Cython/Compiler/PyrexTypes.py
Cython/Compiler/PyrexTypes.py
+132
-9
Cython/Compiler/Symtab.py
Cython/Compiler/Symtab.py
+7
-0
Cython/Shadow.py
Cython/Shadow.py
+26
-0
tests/errors/fused_types.pyx
tests/errors/fused_types.pyx
+22
-0
tests/run/fused_types.pyx
tests/run/fused_types.pyx
+103
-0
No files found.
Cython/Compiler/ExprNodes.py
View file @
47ce63c9
...
...
@@ -2949,6 +2949,7 @@ class SimpleCallNode(CallNode):
else
:
for
arg
in
self
.
args
:
arg
.
analyse_types
(
env
)
if
self
.
self
and
func_type
.
args
:
# Coerce 'self' to the type expected by the method.
self_arg
=
func_type
.
args
[
0
]
...
...
@@ -2965,10 +2966,13 @@ class SimpleCallNode(CallNode):
def
function_type
(
self
):
# Return the type of the function being called, coercing a function
# pointer to a function if necessary.
# pointer to a function if necessary. If the function has fused
# arguments, return the specific type.
func_type
=
self
.
function
.
type
if
func_type
.
is_ptr
:
func_type
=
func_type
.
base_type
return
func_type
def
is_simple
(
self
):
...
...
@@ -2982,6 +2986,7 @@ class SimpleCallNode(CallNode):
if
self
.
function
.
type
is
error_type
:
self
.
type
=
error_type
return
if
self
.
function
.
type
.
is_cpp_class
:
overloaded_entry
=
self
.
function
.
type
.
scope
.
lookup
(
"operator()"
)
if
overloaded_entry
is
None
:
...
...
@@ -2992,8 +2997,16 @@ class SimpleCallNode(CallNode):
overloaded_entry
=
self
.
function
.
entry
else
:
overloaded_entry
=
None
if
overloaded_entry
:
entry
=
PyrexTypes
.
best_match
(
self
.
args
,
overloaded_entry
.
all_alternatives
(),
self
.
pos
)
if
overloaded_entry
.
fused_cfunction
:
specific_cdef_funcs
=
overloaded_entry
.
fused_cfunction
.
nodes
alternatives
=
[
n
.
entry
for
n
in
specific_cdef_funcs
]
else
:
alternatives
=
overloaded_entry
.
all_alternatives
()
entry
=
PyrexTypes
.
best_match
(
self
.
args
,
alternatives
,
self
.
pos
,
env
)
if
not
entry
:
self
.
type
=
PyrexTypes
.
error_type
self
.
result_code
=
"<error>"
...
...
@@ -3130,8 +3143,8 @@ class SimpleCallNode(CallNode):
for
actual_arg
in
self
.
args
[
len
(
formal_args
):]:
arg_list_code
.
append
(
actual_arg
.
result
())
result
=
"%s(%s)"
%
(
self
.
function
.
result
(),
', '
.
join
(
arg_list_code
))
result
=
"%s(%s)"
%
(
self
.
function
.
result
(),
', '
.
join
(
arg_list_code
))
return
result
def
generate_result_code
(
self
,
code
):
...
...
Cython/Compiler/ModuleNode.py
View file @
47ce63c9
...
...
@@ -156,13 +156,22 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
f
.
close
()
def
generate_public_declaration
(
self
,
entry
,
h_code
,
i_code
):
if
entry
.
fused_cfunction
:
for
cfunction
in
entry
.
fused_cfunction
.
nodes
:
self
.
_generate_public_declaration
(
cfunction
.
entry
,
cfunction
.
entry
.
cname
,
h_code
,
i_code
)
else
:
self
.
_generate_public_declaration
(
entry
,
entry
.
cname
,
h_code
,
i_code
)
def
_generate_public_declaration
(
self
,
entry
,
cname
,
h_code
,
i_code
):
h_code
.
putln
(
"%s %s;"
%
(
Naming
.
extern_c_macro
,
entry
.
type
.
declaration_code
(
entry
.
cname
,
dll_linkage
=
"DL_IMPORT"
)))
cname
,
dll_linkage
=
"DL_IMPORT"
)))
if
i_code
:
i_code
.
putln
(
"cdef extern %s"
%
entry
.
type
.
declaration_code
(
entry
.
cname
,
pyrex
=
1
))
entry
.
type
.
declaration_code
(
cname
,
pyrex
=
1
))
def
api_name
(
self
,
env
):
return
env
.
qualified_name
.
replace
(
"."
,
"__"
)
...
...
@@ -987,6 +996,15 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def
generate_cfunction_predeclarations
(
self
,
env
,
code
,
definition
):
for
entry
in
env
.
cfunc_entries
:
if
entry
.
fused_cfunction
:
for
node
in
entry
.
fused_cfunction
.
nodes
:
self
.
_generate_cfunction_predeclaration
(
code
,
definition
,
node
.
entry
)
else
:
self
.
_generate_cfunction_predeclaration
(
code
,
definition
,
entry
)
def
_generate_cfunction_predeclaration
(
self
,
code
,
definition
,
entry
):
if
entry
.
inline_func_in_pxd
or
(
not
entry
.
in_cinclude
and
(
definition
or
entry
.
defined_in_pxd
or
entry
.
visibility
==
'extern'
)):
if
entry
.
visibility
==
'public'
:
...
...
Cython/Compiler/Naming.py
View file @
47ce63c9
...
...
@@ -93,6 +93,7 @@ enc_scope_cname = pyrex_prefix + "enc_scope"
frame_cname
=
pyrex_prefix
+
"frame"
frame_code_cname
=
pyrex_prefix
+
"frame_code"
binding_cfunc
=
pyrex_prefix
+
"binding_PyCFunctionType"
fused_func_prefix
=
pyrex_prefix
+
'fuse_'
genexpr_id_ref
=
'genexpr'
...
...
Cython/Compiler/Nodes.py
View file @
47ce63c9
...
...
@@ -681,6 +681,7 @@ class CArgDeclNode(Node):
could_be_name
=
True
else
:
could_be_name
=
False
self
.
base_type
.
is_arg
=
True
base_type
=
self
.
base_type
.
analyse
(
env
,
could_be_name
=
could_be_name
)
if
hasattr
(
self
.
base_type
,
'arg_name'
)
and
self
.
base_type
.
arg_name
:
self
.
declarator
.
name
=
self
.
base_type
.
arg_name
...
...
@@ -777,9 +778,10 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
if
scope
:
if
scope
.
is_c_class_scope
:
scope
=
scope
.
global_scope
()
entry
=
scope
.
lookup
(
self
.
name
)
if
entry
and
entry
.
is_type
:
type
=
entry
.
type
type
=
scope
.
lookup_type
(
self
.
name
)
if
type
is
not
None
:
pass
elif
could_be_name
:
if
self
.
is_self_arg
and
env
.
is_c_class_scope
:
type
=
env
.
parent_type
...
...
@@ -921,6 +923,26 @@ class CComplexBaseTypeNode(CBaseTypeNode):
return
type
class
FusedTypeNode
(
CBaseTypeNode
):
"""
Represents a fused type in a ctypedef statement:
ctypedef cython.fused_type(int, long, long long) integral
types [CSimpleBaseTypeNode] is the list of types to be fused
"""
child_attrs
=
[]
def
analyse
(
self
,
env
):
self
.
types
=
[
type
.
analyse_as_type
(
env
)
for
type
in
self
.
types
]
if
len
(
self
.
types
)
==
1
:
return
self
.
types
[
0
]
return
PyrexTypes
.
FusedType
(
self
.
types
)
class
CVarDefNode
(
StatNode
):
# C variable definition or forward/extern function declaration.
#
...
...
@@ -1162,15 +1184,29 @@ class CTypeDefNode(StatNode):
child_attrs
=
[
"base_type"
,
"declarator"
]
def
analyse_declarations
(
self
,
env
):
"""
If we are a fused type, do a normal type declaration, as we want
declared variables to have a FusedType type, not a CTypeDefType.
"""
base
=
self
.
base_type
.
analyse
(
env
)
name_declarator
,
type
=
self
.
declarator
.
analyse
(
base
,
env
)
name
=
name_declarator
.
name
cname
=
name_declarator
.
cname
entry
=
env
.
declare_typedef
(
name
,
type
,
self
.
pos
,
cname
=
cname
,
visibility
=
self
.
visibility
,
api
=
self
.
api
)
if
self
.
in_pxd
and
not
env
.
in_cinclude
:
entry
.
defined_in_pxd
=
1
if
base
.
is_fused
:
# Omit the typedef declaration that self.declarator would produce
entry
.
in_cinclude
=
True
if
self
.
visibility
==
'public'
or
self
.
api
:
error
(
self
.
pos
,
"Fused types cannot be public or api"
)
base
.
name
=
name
def
analyse_expressions
(
self
,
env
):
pass
def
generate_execution_code
(
self
,
code
):
...
...
@@ -1187,6 +1223,11 @@ class FuncDefNode(StatNode, BlockNode):
# needs_outer_scope boolean Whether or not this function requires outer scope
# directive_locals { string : NameNode } locals defined by cython.locals(...)
# has_fused_arguments boolean
# Whether this cdef function has fused parameters. This is needed
# by AnalyseDeclarationsTransform, so it can replace CFuncDefNodes
# with fused argument types with a FusedCFuncDefNode
py_func
=
None
assmt
=
None
needs_closure
=
False
...
...
@@ -1194,6 +1235,7 @@ class FuncDefNode(StatNode, BlockNode):
is_generator
=
False
is_generator_body
=
False
modifiers
=
[]
has_fused_arguments
=
False
def
analyse_default_values
(
self
,
env
):
genv
=
env
.
global_scope
()
...
...
@@ -1652,6 +1694,9 @@ class CFuncDefNode(FuncDefNode):
# visibility 'private' or 'public' or 'extern'
# base_type CBaseTypeNode
# declarator CDeclaratorNode
# cfunc_declarator the CFuncDeclarator of this function
# (this is also available through declarator or a
# base thereof)
# body StatListNode
# api boolean
# decorators [DecoratorNode] list of decorators
...
...
@@ -1667,6 +1712,7 @@ class CFuncDefNode(FuncDefNode):
inline_in_pxd
=
False
decorators
=
None
directive_locals
=
None
cname_postfix
=
None
def
unqualified_name
(
self
):
return
self
.
entry
.
name
...
...
@@ -1695,21 +1741,31 @@ class CFuncDefNode(FuncDefNode):
declarator
=
self
.
declarator
while
not
hasattr
(
declarator
,
'args'
):
declarator
=
declarator
.
base
self
.
cfunc_declarator
=
declarator
self
.
args
=
declarator
.
args
for
formal_arg
,
type_arg
in
zip
(
self
.
args
,
type
.
args
):
self
.
align_argument_type
(
env
,
type_arg
)
formal_arg
.
type
=
type_arg
.
type
formal_arg
.
name
=
type_arg
.
name
formal_arg
.
cname
=
type_arg
.
cname
if
type_arg
.
type
.
is_fused
:
self
.
has_fused_arguments
=
True
if
type_arg
.
type
.
is_buffer
and
'inline'
in
self
.
modifiers
:
warning
(
formal_arg
.
pos
,
"Buffer unpacking not optimized away."
,
1
)
name
=
name_declarator
.
name
cname
=
name_declarator
.
cname
self
.
entry
=
env
.
declare_cfunction
(
name
,
type
,
self
.
pos
,
cname
=
cname
,
visibility
=
self
.
visibility
,
defining
=
self
.
body
is
not
None
,
api
=
self
.
api
,
modifiers
=
self
.
modifiers
)
self
.
entry
.
inline_func_in_pxd
=
self
.
inline_in_pxd
self
.
return_type
=
type
.
return_type
if
self
.
return_type
.
is_array
and
visibility
!=
'extern'
:
...
...
@@ -1900,6 +1956,96 @@ class CFuncDefNode(FuncDefNode):
code
.
putln
(
'}'
)
class
FusedCFuncDefNode
(
StatListNode
):
"""
This node replaces a function with fused arguments. It deep-copies the
function for every permutation of fused types, and allocates a new local
scope for it. It keeps track of the original function in self.node, and
the entry of the original function in the symbol table is given the
'fused_cfunction' attribute which points back to us.
Then when a function lookup occurs (to e.g. call it), the call can be
dispatched to the right function.
node FuncDefNode the original function
nodes [FuncDefNode] list of copies of node with different specific types
"""
child_attrs
=
[
'nodes'
]
def
__init__
(
self
,
node
,
env
):
super
(
FusedCFuncDefNode
,
self
).
__init__
(
node
.
pos
)
self
.
nodes
=
self
.
stats
=
[]
self
.
node
=
node
self
.
copy_cdefs
(
node
.
type
.
get_fused_types
(),
env
)
# Perform some sanity checks. If anything fails, it's a bug
for
n
in
self
.
nodes
:
assert
not
n
.
type
.
is_fused
assert
not
n
.
local_scope
.
return_type
.
is_fused
if
node
.
return_type
.
is_fused
:
assert
not
n
.
return_type
.
is_fused
assert
node
.
type
.
is_fused
node
.
entry
.
fused_cfunction
=
self
def
copy_cdefs
(
self
,
fused_types
,
env
):
"""
Gives a list of fused types and the parent environment, make copies
of the original cdef function.
"""
permutations
=
self
.
get_all_specific_permutations
(
fused_types
)
for
cname
,
fused_to_specific
in
permutations
:
copied_node
=
copy
.
deepcopy
(
self
.
node
)
# Make the types in our CFuncType specific
newtype
=
copied_node
.
type
.
specialize
(
fused_to_specific
)
copied_node
.
type
=
newtype
copied_node
.
entry
.
type
=
newtype
copied_node
.
return_type
=
newtype
.
return_type
copied_node
.
create_local_scope
(
env
)
copied_node
.
local_scope
.
fused_to_specific
=
fused_to_specific
# This is copied from the original function, set it to false to
# stop recursivon
copied_node
.
has_fused_arguments
=
False
self
.
nodes
.
append
(
copied_node
)
# Make the argument types in the CFuncDeclarator specific
for
arg
in
copied_node
.
cfunc_declarator
.
args
:
arg
.
type
=
arg
.
type
.
specialize
(
fused_to_specific
)
cname
=
'%s%s%s'
%
(
Naming
.
fused_func_prefix
,
cname
,
self
.
node
.
entry
.
func_cname
)
copied_node
.
entry
.
func_cname
=
copied_node
.
entry
.
cname
=
cname
def
get_all_specific_permutations
(
self
,
fused_types
):
"""
Permute all the types. For every specific instance of a fused type, we
want all other specific instances of all other fused types.
It returns an iterable of two-tuples of the cname that should prefix
the cname of the function, and a dict mapping any fused types to their
respective specific types.
"""
fused_type
=
fused_types
[
0
]
for
specific_type
in
fused_type
.
types
:
cname
=
str
(
specific_type
)
result_fused_to_specific
=
{
fused_type
:
specific_type
}
if
len
(
fused_types
)
>
1
:
it
=
self
.
get_all_specific_permutations
(
fused_types
[
1
:])
for
next_cname
,
fused_to_specific
in
it
:
d
=
dict
(
fused_to_specific
,
**
result_fused_to_specific
)
yield
'%s_%s'
%
(
cname
,
next_cname
),
d
else
:
yield
cname
,
result_fused_to_specific
class
PyArgDeclNode
(
Node
):
# Argument which must be a Python object (used
# for * and ** arguments).
...
...
@@ -3653,6 +3799,20 @@ class SingleAssignmentNode(AssignmentNode):
if
len
(
args
)
>
2
or
kwds
is
not
None
:
error
(
self
.
rhs
.
pos
,
"Can only declare one type at a time."
)
return
# See if we're dealing with this:
# dtype = cython.typedef(cython.fused_type(...))
nested_func_name
=
args
[
0
].
function
.
as_cython_attribute
()
if
nested_func_name
==
u'fused_type'
:
nested_args
,
nested_kwds
=
args
[
0
].
explicit_args_kwds
()
if
nested_kwds
is
not
None
:
error
(
self
.
rhs
.
pos
,
"fused_type does not take keyword arguments"
)
args
[
0
]
=
FusedTypeNode
(
self
.
rhs
.
pos
,
types
=
nested_args
)
args
[
0
].
name
=
self
.
lhs
.
name
type
=
args
[
0
].
analyse_as_type
(
env
)
if
type
is
None
:
error
(
args
[
0
].
pos
,
"Unknown type"
)
...
...
Cython/Compiler/ParseTreeTransforms.py
View file @
47ce63c9
...
...
@@ -610,8 +610,9 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
'operator.comma'
:
ExprNodes
.
c_binop_constructor
(
','
),
}
special_methods
=
cython
.
set
([
'declare'
,
'union'
,
'struct'
,
'typedef'
,
'sizeof'
,
'cast'
,
'pointer'
,
'compiled'
,
'NULL'
])
special_methods
=
cython
.
set
([
'declare'
,
'union'
,
'struct'
,
'typedef'
,
'sizeof'
,
'cast'
,
'pointer'
,
'compiled'
,
'NULL'
,
'fused_type'
])
special_methods
.
update
(
unop_method_nodes
.
keys
())
def
__init__
(
self
,
context
,
compilation_directive_defaults
):
...
...
@@ -896,6 +897,36 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
return
self
.
visit_with_directives
(
node
.
body
,
directive_dict
)
return
self
.
visit_Node
(
node
)
def
visit_CTypeDefNode
(
self
,
node
):
"Don't skip ctypedefs"
self
.
visitchildren
(
node
)
return
node
def
visit_FusedTypeNode
(
self
,
node
):
"""
See if a function call expression in a ctypedef is actually
cython.fused_type()
"""
def
err
():
error
(
node
.
pos
,
"Can only fuse types with cython.fused_type()"
)
if
len
(
node
.
funcname
)
==
1
:
fused_type
,
=
node
.
funcname
else
:
cython_module
,
fused_type
=
node
.
funcname
wrong_module
=
cython_module
not
in
self
.
cython_module_names
if
wrong_module
or
fused_type
!=
u'fused_type'
:
err
()
return
node
if
not
self
.
directive_names
.
get
(
fused_type
):
err
()
return
node
class
WithTransform
(
CythonTransform
,
SkipDeclarations
):
# EXCINFO is manually set to a variable that contains
...
...
@@ -1115,6 +1146,14 @@ if VALUE is not None:
return
node
def
visit_FuncDefNode
(
self
,
node
):
"""
Analyse a function and its body, as that hasn't happend yet. Also
analyse the directive_locals set by @cython.locals(). Then, if we are
a function with fused arguments, replace the function (after it has
declared itself in the symbol table!) with a FusedCFuncDefNode, and
analyse its children (which are in turn normal functions). If we're a
normal function, just analyse the body of the function.
"""
self
.
seen_vars_stack
.
append
(
cython
.
set
())
lenv
=
node
.
local_scope
node
.
body
.
analyse_control_flow
(
lenv
)
# this will be totally refactored
...
...
@@ -1126,10 +1165,16 @@ if VALUE is not None:
lenv
.
declare_var
(
var
,
type
,
type_node
.
pos
)
else
:
error
(
type_node
.
pos
,
"Not a type"
)
if
node
.
has_fused_arguments
:
node
=
Nodes
.
FusedCFuncDefNode
(
node
,
self
.
env_stack
[
-
1
])
self
.
visitchildren
(
node
)
else
:
node
.
body
.
analyse_declarations
(
lenv
)
self
.
env_stack
.
append
(
lenv
)
self
.
visitchildren
(
node
)
self
.
env_stack
.
pop
()
self
.
seen_vars_stack
.
pop
()
return
node
...
...
Cython/Compiler/Parsing.py
View file @
47ce63c9
...
...
@@ -2572,6 +2572,24 @@ def p_c_func_or_var_declaration(s, pos, ctx):
overridable
=
ctx
.
overridable
)
return
result
def
p_typelist
(
s
):
"""
parse a list of basic c types as part of a function call, like
cython.fused_type(int, long, double)
"""
types
=
[]
pos
=
s
.
position
()
while
s
.
sy
==
'IDENT'
:
types
.
append
(
p_c_base_type
(
s
))
if
s
.
sy
!=
','
:
if
s
.
sy
!=
')'
:
s
.
expect
(
','
)
break
s
.
next
()
return
Nodes
.
FusedTypeNode
(
pos
,
types
=
types
)
def
p_ctypedef_statement
(
s
,
ctx
):
# s.sy == 'ctypedef'
pos
=
s
.
position
()
...
...
@@ -2588,10 +2606,30 @@ def p_ctypedef_statement(s, ctx):
return
p_c_enum_definition
(
s
,
pos
,
ctx
)
else
:
return
p_c_struct_or_union_definition
(
s
,
pos
,
ctx
)
elif
looking_at_expr
(
s
):
# ctypedef cython.fused_types(int, long) integral
if
s
.
sy
==
'IDENT'
:
funcname
=
[
s
.
systring
]
s
.
next
()
if
s
.
systring
==
u'.'
:
s
.
next
()
funcname
.
append
(
s
.
systring
)
s
.
expect
(
'IDENT'
)
s
.
expect
(
'('
)
base_type
=
p_typelist
(
s
)
s
.
expect
(
')'
)
# Check if funcname equals cython.fused_types in
# InterpretCompilerDirectives
base_type
.
funcname
=
funcname
else
:
s
.
error
(
"Syntax error in ctypedef statement"
)
else
:
base_type
=
p_c_base_type
(
s
,
nonempty
=
1
)
if
base_type
.
name
is
None
:
s
.
error
(
"Syntax error in ctypedef statement"
)
declarator
=
p_c_declarator
(
s
,
ctx
,
is_type
=
1
,
nonempty
=
1
)
s
.
expect_newline
(
"Syntax error in ctypedef statement"
)
return
Nodes
.
CTypeDefNode
(
...
...
Cython/Compiler/PyrexTypes.py
View file @
47ce63c9
...
...
@@ -2,6 +2,8 @@
# Pyrex - Types
#
import
cython
from
Code
import
UtilityCode
import
StringEncoding
import
Naming
...
...
@@ -12,6 +14,9 @@ class BaseType(object):
#
# Base class for all Pyrex types including pseudo-types.
# List of attribute names of any subtypes
subtypes
=
[]
def
can_coerce_to_pyobject
(
self
,
env
):
return
False
...
...
@@ -27,6 +32,42 @@ class BaseType(object):
else
:
return
base_code
def
__deepcopy__
(
self
,
memo
):
"""
Types never need to be copied, if we do copy, Unfortunate Things
Will Happen!
"""
return
self
def
get_fused_types
(
self
,
result
=
None
,
seen
=
None
):
if
self
.
subtypes
:
def
add_fused_types
(
types
):
for
type
in
types
or
():
if
type
not
in
seen
:
seen
.
add
(
type
)
result
.
append
(
type
)
if
result
is
None
:
result
=
[]
seen
=
cython
.
set
()
for
attr
in
self
.
subtypes
:
list_or_subtype
=
getattr
(
self
,
attr
)
if
isinstance
(
list_or_subtype
,
BaseType
):
list_or_subtype
.
get_fused_types
(
result
,
seen
)
else
:
for
subtype
in
list_or_subtype
:
subtype
.
get_fused_types
(
result
,
seen
)
return
result
return
None
is_fused
=
property
(
get_fused_types
,
doc
=
"Whether this type or any of its "
"subtypes is a fused type"
)
class
PyrexType
(
BaseType
):
#
# Base class for all Pyrex types.
...
...
@@ -196,6 +237,7 @@ class CTypedefType(BaseType):
to_py_utility_code
=
None
from_py_utility_code
=
None
subtypes
=
[
'typedef_base_type'
]
def
__init__
(
self
,
name
,
base_type
,
cname
,
is_external
=
0
):
assert
not
base_type
.
is_complex
...
...
@@ -314,6 +356,9 @@ class BufferType(BaseType):
is_buffer
=
1
writable
=
True
subtypes
=
[
'dtype'
]
def
__init__
(
self
,
base
,
dtype
,
ndim
,
mode
,
negative_indices
,
cast
):
self
.
base
=
base
self
.
dtype
=
dtype
...
...
@@ -618,6 +663,45 @@ class CType(PyrexType):
return
0
class
FusedType
(
CType
):
"""
Represents a Fused Type. All it needs to do is keep track of the types
it aggregates, as it will be replaced with its specific version wherever
needed.
See http://wiki.cython.org/enhancements/fusedtypes
types [CSimpleBaseTypeNode] is the list of types to be fused
name str the name of the ctypedef
"""
is_fused
=
1
def
__init__
(
self
,
types
):
self
.
types
=
types
def
declaration_code
(
self
,
entity_code
,
for_display
=
0
,
dll_linkage
=
None
,
pyrex
=
0
):
if
pyrex
or
for_display
:
return
self
.
name
raise
Exception
(
"This may never happen, please report a bug"
)
def
__repr__
(
self
):
return
'FusedType(name=%r)'
%
self
.
name
def
specialize
(
self
,
values
):
return
values
[
self
]
def
get_fused_types
(
self
,
result
=
None
,
seen
=
None
):
if
result
is
None
:
return
[
self
]
if
self
not
in
seen
:
result
.
append
(
self
)
seen
.
add
(
self
)
class
CVoidType
(
CType
):
#
# C "void" type
...
...
@@ -1532,6 +1616,8 @@ class CArrayType(CType):
is_array
=
1
subtypes
=
[
'base_type'
]
def
__init__
(
self
,
base_type
,
size
):
self
.
base_type
=
base_type
self
.
size
=
size
...
...
@@ -1578,6 +1664,8 @@ class CPtrType(CType):
is_ptr
=
1
default_value
=
"0"
subtypes
=
[
'base_type'
]
def
__init__
(
self
,
base_type
):
self
.
base_type
=
base_type
...
...
@@ -1676,6 +1764,8 @@ class CFuncType(CType):
is_cfunction
=
1
original_sig
=
None
subtypes
=
[
'return_type'
,
'args'
]
def
__init__
(
self
,
return_type
,
args
,
has_varargs
=
0
,
exception_value
=
None
,
exception_check
=
0
,
calling_convention
=
""
,
nogil
=
0
,
with_gil
=
0
,
is_overridable
=
0
,
optional_arg_count
=
0
,
...
...
@@ -1915,7 +2005,7 @@ class CFuncType(CType):
return
self
.
op_arg_struct
.
base_type
.
scope
.
lookup
(
arg_name
).
cname
class
CFuncTypeArg
(
object
):
class
CFuncTypeArg
(
BaseType
):
# name string
# cname string
# type PyrexType
...
...
@@ -1926,6 +2016,8 @@ class CFuncTypeArg(object):
or_none
=
False
accept_none
=
True
subtypes
=
[
'type'
]
def
__init__
(
self
,
name
,
type
,
pos
,
cname
=
None
):
self
.
name
=
name
if
cname
is
not
None
:
...
...
@@ -2478,7 +2570,7 @@ def is_promotion(src_type, dst_type):
return
src_type
.
is_float
and
src_type
.
rank
<=
dst_type
.
rank
return
False
def
best_match
(
args
,
functions
,
pos
=
None
):
def
best_match
(
args
,
functions
,
pos
=
None
,
env
=
None
):
"""
Given a list args of arguments and a list of functions, choose one
to call which seems to be the "best" fit for this list of arguments.
...
...
@@ -2546,12 +2638,33 @@ def best_match(args, functions, pos=None):
possibilities
=
[]
bad_types
=
[]
needed_coercions
=
{}
for
func
,
func_type
in
candidates
:
score
=
[
0
,
0
,
0
]
for
i
in
range
(
min
(
len
(
args
),
len
(
func_type
.
args
))):
src_type
=
args
[
i
].
type
dst_type
=
func_type
.
args
[
i
].
type
if
dst_type
.
assignable_from
(
src_type
):
assignable
=
dst_type
.
assignable_from
(
src_type
)
# Now take care of normal string literals. So when you call a cdef
# function that takes a char *, the coercion will mean that the
# type will simply become bytes. We need to do this coercion
# manually for overloaded and fused functions
if
not
assignable
and
src_type
.
is_pyobject
:
if
(
src_type
.
is_builtin_type
and
src_type
.
name
==
'str'
and
dst_type
.
resolve
()
is
c_char_ptr_type
):
c_src_type
=
c_char_ptr_type
else
:
c_src_type
=
src_type
.
default_coerced_ctype
()
if
c_src_type
:
assignable
=
dst_type
.
assignable_from
(
c_src_type
)
if
assignable
:
src_type
=
c_src_type
needed_coercions
[
func
]
=
i
,
dst_type
if
assignable
:
if
src_type
==
dst_type
or
dst_type
.
same_as
(
src_type
):
pass
# score 0
elif
is_promotion
(
src_type
,
dst_type
):
...
...
@@ -2567,18 +2680,28 @@ def best_match(args, functions, pos=None):
break
else
:
possibilities
.
append
((
score
,
func
))
# so we can sort it
if
possibilities
:
possibilities
.
sort
()
if
len
(
possibilities
)
>
1
and
possibilities
[
0
][
0
]
==
possibilities
[
1
][
0
]:
if
pos
is
not
None
:
error
(
pos
,
"ambiguous overloaded method"
)
return
None
return
possibilities
[
0
][
1
]
function
=
possibilities
[
0
][
1
]
if
function
in
needed_coercions
and
env
:
arg_i
,
coerce_to_type
=
needed_coercions
[
function
]
args
[
arg_i
]
=
args
[
arg_i
].
coerce_to
(
coerce_to_type
,
env
)
return
function
if
pos
is
not
None
:
if
len
(
bad_types
)
==
1
:
error
(
pos
,
bad_types
[
0
][
1
])
else
:
error
(
pos
,
"no suitable method found"
)
return
None
def
widest_numeric_type
(
type1
,
type2
):
...
...
Cython/Compiler/Symtab.py
View file @
47ce63c9
...
...
@@ -176,6 +176,7 @@ class Entry(object):
buffer_aux = None
prev_entry = None
might_overflow = 0
fused_cfunction = None
def __init__(self, name, cname, type, pos = None, init = None):
self.name = name
...
...
@@ -241,6 +242,7 @@ class Scope(object):
scope_prefix = ""
in_cinclude = 0
nogil = 0
fused_to_specific = None
def __init__(self, name, outer_scope, parent_scope):
# The outer_scope is the next scope in the lookup chain.
...
...
@@ -279,6 +281,9 @@ class Scope(object):
self.return_type = None
self.id_counters = {}
def __deepcopy__(self, memo):
return self
def start_branching(self, pos):
self.control_flow = self.control_flow.start_branch(pos)
...
...
@@ -677,6 +682,8 @@ class Scope(object):
def lookup_type(self, name):
entry = self.lookup(name)
if entry and entry.is_type:
if entry.type.is_fused and self.fused_to_specific:
return entry.type.specialize(self.fused_to_specific)
return entry.type
def lookup_operator(self, operator, operands):
...
...
Cython/Shadow.py
View file @
47ce63c9
...
...
@@ -225,6 +225,30 @@ class typedef(CythonType):
value
=
cast
(
self
.
_basetype
,
*
arg
)
return
value
class
_FusedType
(
CythonType
):
def
__call__
(
self
,
type
,
value
):
return
value
def
fused_type
(
*
args
):
if
not
args
:
raise
TypeError
(
"Expected at least one type as argument"
)
rank
=
-
1
for
type
in
args
:
if
type
not
in
(
py_int
,
py_long
,
py_float
,
py_complex
):
break
if
type_ordering
.
index
(
type
)
>
rank
:
result_type
=
type
else
:
return
result_type
# Not a simple numeric type, return a fused type instance. The result
# isn't really meant to be used, as we can't keep track of the context in
# pure-mode. Casting won't do anything in this case.
return
_FusedType
()
py_int
=
int
...
...
@@ -277,3 +301,5 @@ for t in int_types + float_types + complex_types + other_types:
void
=
typedef
(
None
)
NULL
=
p_void
(
0
)
type_ordering
=
[
py_int
,
py_long
,
py_float
,
py_complex
]
\ No newline at end of file
tests/errors/fused_types.pyx
0 → 100644
View file @
47ce63c9
# mode: error
cimport
cython
from
cython
import
fused_type
# This is all invalid
ctypedef
foo
(
int
)
dtype1
ctypedef
foo
.
bar
(
float
)
dtype2
ctypedef
fused_type
(
foo
)
dtype3
dtype4
=
cython
.
typedef
(
cython
.
fused_type
(
int
,
long
,
kw
=
None
))
# This is all valid
ctypedef
fused_type
(
int
,
long
,
float
)
dtype5
ctypedef
cython
.
fused_type
(
int
,
long
)
dtype6
_ERRORS
=
u"""
fused_types.pyx:7:13: Can only fuse types with cython.fused_type()
fused_types.pyx:8:17: Can only fuse types with cython.fused_type()
fused_types.pyx:9:20: 'foo' is not a type identifier
fused_types.pyx:10:23: fused_type does not take keyword arguments
"""
tests/run/fused_types.pyx
0 → 100644
View file @
47ce63c9
# mode: run
cimport
cython
from
cpython
cimport
Py_INCREF
from
Cython
import
Shadow
as
pure_cython
ctypedef
char
*
string_t
ctypedef
cython
.
fused_type
(
int
,
long
,
float
,
double
,
string_t
)
fused_type1
ctypedef
cython
.
fused_type
(
string_t
)
fused_type2
def
test_pure
():
"""
>>> test_pure()
(10+0j)
"""
mytype
=
pure_cython
.
typedef
(
pure_cython
.
fused_type
(
int
,
long
,
complex
))
print
mytype
(
10
)
cdef
cdef
_func_with_fused_args
(
fused_type1
x
,
fused_type1
y
,
fused_type2
z
):
print
x
,
y
,
z
return
x
+
y
def
test_cdef_func_with_fused_args
():
"""
>>> test_cdef_func_with_fused_args()
spam ham eggs
spamham
10 20 butter
30
4.2 8.6 bunny
12.8
"""
print
cdef
_func_with_fused_args
(
'spam'
,
'ham'
,
'eggs'
)
print
cdef
_func_with_fused_args
(
10
,
20
,
'butter'
)
print
cdef
_func_with_fused_args
(
4.2
,
8.6
,
'bunny'
)
cdef
fused_type1
fused_with_pointer
(
fused_type1
*
array
):
for
i
in
range
(
5
):
print
array
[
i
]
obj
=
array
[
0
]
+
array
[
1
]
+
array
[
2
]
+
array
[
3
]
+
array
[
4
]
# if cython.typeof(fused_type1) is string_t:
Py_INCREF
(
obj
)
return
obj
def
test_fused_with_pointer
():
"""
>>> test_fused_with_pointer()
0
1
2
3
4
10
<BLANKLINE>
0
1
2
3
4
10
<BLANKLINE>
0.0
1.0
2.0
3.0
4.0
10.0
<BLANKLINE>
humpty
dumpty
fall
splatch
breakfast
humptydumptyfallsplatchbreakfast
"""
cdef
int
int_array
[
5
]
cdef
long
long_array
[
5
]
cdef
float
float_array
[
5
]
cdef
string_t
string_array
[
5
]
cdef
char
*
s1
=
"humpty"
,
*
s2
=
"dumpty"
,
*
s3
=
"fall"
,
*
s4
=
"splatch"
,
*
s5
=
"breakfast"
strings
=
[
"humpty"
,
"dumpty"
,
"fall"
,
"splatch"
,
"breakfast"
]
for
i
in
range
(
5
):
int_array
[
i
]
=
i
long_array
[
i
]
=
i
float_array
[
i
]
=
i
s
=
strings
[
i
]
string_array
[
i
]
=
s
print
fused_with_pointer
(
int_array
)
print
print
fused_with_pointer
(
long_array
)
print
print
fused_with_pointer
(
float_array
)
print
print
fused_with_pointer
(
string_array
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment