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
Xavier Thompson
cython
Commits
1c3f95c9
Commit
1c3f95c9
authored
Aug 22, 2020
by
Xavier Thompson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add a new exception-signaling mode using enriched return values
parent
9844c461
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
192 additions
and
14 deletions
+192
-14
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+9
-5
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+16
-9
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+3
-0
Cython/Compiler/PyrexTypes.py
Cython/Compiler/PyrexTypes.py
+125
-0
Cython/Compiler/TypeInference.py
Cython/Compiler/TypeInference.py
+2
-0
Cython/Utility/CppSupport.cpp
Cython/Utility/CppSupport.cpp
+37
-0
No files found.
Cython/Compiler/ExprNodes.py
View file @
1c3f95c9
...
@@ -5933,7 +5933,7 @@ class SimpleCallNode(CallNode):
...
@@ -5933,7 +5933,7 @@ class SimpleCallNode(CallNode):
else
:
else
:
self
.
args
=
[
arg
.
analyse_types
(
env
)
for
arg
in
self
.
args
]
self
.
args
=
[
arg
.
analyse_types
(
env
)
for
arg
in
self
.
args
]
self
.
analyse_c_function_call
(
env
)
self
.
analyse_c_function_call
(
env
)
if
func_type
.
exception_check
==
'+'
or
self
.
type
.
is_cyp_class
:
if
func_type
.
exception_check
in
(
'+'
,
'~'
)
or
self
.
type
.
is_cyp_class
:
self
.
is_temp
=
True
self
.
is_temp
=
True
return
self
return
self
...
@@ -6228,7 +6228,7 @@ class SimpleCallNode(CallNode):
...
@@ -6228,7 +6228,7 @@ class SimpleCallNode(CallNode):
def
is_c_result_required
(
self
):
def
is_c_result_required
(
self
):
func_type
=
self
.
function_type
()
func_type
=
self
.
function_type
()
if
not
func_type
.
exception_value
or
func_type
.
exception_check
==
'+'
:
if
not
func_type
.
exception_value
or
func_type
.
exception_check
in
(
'+'
,
'~'
)
:
return
False
# skip allocation of unused result temp
return
False
# skip allocation of unused result temp
return
True
return
True
...
@@ -6330,10 +6330,14 @@ class SimpleCallNode(CallNode):
...
@@ -6330,10 +6330,14 @@ class SimpleCallNode(CallNode):
if
exc_val
is
not
None
:
if
exc_val
is
not
None
:
exc_checks
.
append
(
"%s == %s"
%
(
self
.
result
(),
func_type
.
return_type
.
cast_code
(
exc_val
)))
exc_checks
.
append
(
"%s == %s"
%
(
self
.
result
(),
func_type
.
return_type
.
cast_code
(
exc_val
)))
if
exc_check
:
if
exc_check
:
if
self
.
nogil
:
if
exc_check
==
'~'
:
exc_checks
.
append
(
"__Pyx_ErrOccurredWithGIL()"
)
assert
self
.
is_temp
exc_checks
.
append
(
self
.
type
.
error_condition
(
self
.
result
()))
else
:
else
:
exc_checks
.
append
(
"PyErr_Occurred()"
)
if
self
.
nogil
:
exc_checks
.
append
(
"__Pyx_ErrOccurredWithGIL()"
)
else
:
exc_checks
.
append
(
"PyErr_Occurred()"
)
if
self
.
is_temp
or
exc_checks
:
if
self
.
is_temp
or
exc_checks
:
rhs
=
self
.
c_call_code
()
rhs
=
self
.
c_call_code
()
if
self
.
result
():
if
self
.
result
():
...
...
Cython/Compiler/Nodes.py
View file @
1c3f95c9
...
@@ -701,12 +701,16 @@ class CFuncDeclaratorNode(CDeclaratorNode):
...
@@ -701,12 +701,16 @@ class CFuncDeclaratorNode(CDeclaratorNode):
env
.
add_include_file
(
'new'
)
# for std::bad_alloc
env
.
add_include_file
(
'new'
)
# for std::bad_alloc
env
.
add_include_file
(
'stdexcept'
)
env
.
add_include_file
(
'stdexcept'
)
env
.
add_include_file
(
'typeinfo'
)
# for std::bad_cast
env
.
add_include_file
(
'typeinfo'
)
# for std::bad_cast
elif
self
.
exception_check
==
'~'
:
self
.
cpp_check
(
env
)
env
.
use_utility_code
(
UtilityCode
.
load_cached
(
"CheckedResult"
,
"CppSupport.cpp"
))
return_type
=
PyrexTypes
.
CheckedResultType
(
self
.
pos
,
return_type
)
if
(
return_type
.
is_pyobject
if
(
return_type
.
is_pyobject
and
(
self
.
exception_value
or
self
.
exception_check
)
and
(
self
.
exception_value
or
self
.
exception_check
)
and
self
.
exception_check
!=
'+'
):
and
self
.
exception_check
!=
'+'
):
error
(
self
.
pos
,
"Exception clause not allowed for function returning Python object"
)
error
(
self
.
pos
,
"Exception clause not allowed for function returning Python object"
)
else
:
else
:
if
self
.
exception_value
is
None
and
self
.
exception_check
and
self
.
exception_check
!=
'+'
:
if
self
.
exception_value
is
None
and
self
.
exception_check
and
self
.
exception_check
not
in
(
'+'
,
'~'
)
:
# Use an explicit exception return value to speed up exception checks.
# Use an explicit exception return value to speed up exception checks.
# Even if it is not declared, we can use the default exception value of the return type,
# Even if it is not declared, we can use the default exception value of the return type,
# unless the function is some kind of external function that we do not control.
# unless the function is some kind of external function that we do not control.
...
@@ -2244,14 +2248,17 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -2244,14 +2248,17 @@ class FuncDefNode(StatNode, BlockNode):
self
.
entry
.
qualified_name
,
0
)
self
.
entry
.
qualified_name
,
0
)
assure_gil
(
'error'
)
assure_gil
(
'error'
)
code
.
put_unraisable
(
self
.
entry
.
qualified_name
)
code
.
put_unraisable
(
self
.
entry
.
qualified_name
)
default_retval
=
return_type
.
default_value
if
return_type
.
is_checked_result
:
if
err_val
is
None
and
default_retval
:
code
.
putln
(
return_type
.
set_error
(
Naming
.
retval_cname
))
err_val
=
default_retval
else
:
if
err_val
is
not
None
:
default_retval
=
return_type
.
default_value
if
err_val
!=
Naming
.
retval_cname
:
if
err_val
is
None
and
default_retval
:
code
.
putln
(
"%s = %s;"
%
(
Naming
.
retval_cname
,
err_val
))
err_val
=
default_retval
elif
not
return_type
.
is_void
:
if
err_val
is
not
None
:
code
.
putln
(
"__Pyx_pretend_to_initialize(&%s);"
%
Naming
.
retval_cname
)
if
err_val
!=
Naming
.
retval_cname
:
code
.
putln
(
"%s = %s;"
%
(
Naming
.
retval_cname
,
err_val
))
elif
not
return_type
.
is_void
:
code
.
putln
(
"__Pyx_pretend_to_initialize(&%s);"
%
Naming
.
retval_cname
)
if
is_getbuffer_slot
:
if
is_getbuffer_slot
:
assure_gil
(
'error'
)
assure_gil
(
'error'
)
...
...
Cython/Compiler/Parsing.py
View file @
1c3f95c9
...
@@ -2986,6 +2986,9 @@ def p_exception_value_clause(s):
...
@@ -2986,6 +2986,9 @@ def p_exception_value_clause(s):
elif
s
.
sy
==
'*'
:
elif
s
.
sy
==
'*'
:
exc_val
=
ExprNodes
.
CharNode
(
s
.
position
(),
value
=
u'*'
)
exc_val
=
ExprNodes
.
CharNode
(
s
.
position
(),
value
=
u'*'
)
s
.
next
()
s
.
next
()
elif
s
.
sy
==
'~'
:
exc_check
=
'~'
s
.
next
()
else
:
else
:
if
s
.
sy
==
'?'
:
if
s
.
sy
==
'?'
:
exc_check
=
1
exc_check
=
1
...
...
Cython/Compiler/PyrexTypes.py
View file @
1c3f95c9
...
@@ -264,6 +264,7 @@ class PyrexType(BaseType):
...
@@ -264,6 +264,7 @@ class PyrexType(BaseType):
is_memoryviewslice
=
0
is_memoryviewslice
=
0
is_pythran_expr
=
0
is_pythran_expr
=
0
is_numpy_buffer
=
0
is_numpy_buffer
=
0
is_checked_result
=
0
has_attributes
=
0
has_attributes
=
0
needs_refcounting
=
0
needs_refcounting
=
0
default_value
=
""
default_value
=
""
...
@@ -595,6 +596,130 @@ class CTypedefType(BaseType):
...
@@ -595,6 +596,130 @@ class CTypedefType(BaseType):
return
self
.
typedef_base_type
.
can_coerce_from_pyobject
(
env
)
return
self
.
typedef_base_type
.
can_coerce_from_pyobject
(
env
)
class
CheckedResultType
(
BaseType
):
# Pseudo-type to wrap the return value of c functions in a "CheckedResult"
is_checked_result
=
1
is_void
=
0
to_py_utility_code
=
None
from_py_utility_code
=
None
subtypes
=
[
'checked_base_type'
]
def
__init__
(
self
,
pos
,
base_type
):
if
base_type
.
is_reference
:
error
(
pos
,
"Cannot use 'except ~' exception signaling with functions that return by reference"
)
elif
base_type
.
is_cpp_class
:
base_type
.
check_nullary_constructor
(
pos
)
self
.
checked_base_type
=
base_type
def
invalid_value
(
self
):
return
self
.
checked_base_type
.
invalid_value
()
def
resolve
(
self
):
return
self
.
checked_base_type
.
resolve
()
def
declaration_code
(
self
,
entity_code
,
for_display
=
0
,
dll_linkage
=
None
,
pyrex
=
0
):
checked_code
=
"CheckedResult<%s>"
%
self
.
checked_base_type
.
declaration_code
(
''
)
if
pyrex
or
for_display
:
base_code
=
checked_code
else
:
base_code
=
public_decl
(
checked_code
,
dll_linkage
)
return
self
.
base_declaration_code
(
base_code
,
entity_code
)
def
as_argument_type
(
self
):
return
self
.
checked_base_type
.
as_argument_type
()
def
cast_code
(
self
,
expr_code
):
return
self
.
checked_base_type
.
cast_code
(
expr_code
)
def
specialize
(
self
,
values
):
return
self
.
checked_base_type
.
specialize
(
values
)
def
__repr__
(
self
):
return
"<CheckedResult[%s]>"
%
repr
(
self
.
checked_base_type
)
def
__str__
(
self
):
return
str
(
self
.
checked_base_type
)
def
create_to_py_utility_code
(
self
,
env
):
return
self
.
checked_base_type
.
create_to_py_utility_code
(
env
)
def
create_from_py_utility_code
(
self
,
env
):
return
self
.
checked_base_type
.
create_from_py_utility_code
(
env
)
def
to_py_call_code
(
self
,
source_code
,
result_code
,
result_type
,
to_py_function
=
None
):
return
self
.
checked_base_type
.
to_py_call_code
(
source_code
,
result_code
,
result_type
,
to_py_function
)
def
from_py_call_code
(
self
,
source_code
,
result_code
,
error_pos
,
code
,
from_py_function
=
None
,
error_condition
=
None
):
return
self
.
checked_base_type
.
from_py_call_code
(
source_code
,
result_code
,
error_pos
,
code
,
from_py_function
or
self
.
from_py_function
,
error_condition
or
self
.
error_condition
(
result_code
)
)
def
overflow_check_binop
(
self
,
binop
,
env
,
const_rhs
=
False
):
return
self
.
checked_base_type
.
overflow_check_binop
(
binop
,
env
,
const_rhs
)
def
error_condition
(
self
,
result_code
):
return
"%s.is_error()"
%
result_code
def
set_error
(
self
,
result_code
):
return
"%s.set_error();"
%
result_code
def
__getattr__
(
self
,
name
):
return
getattr
(
self
.
checked_base_type
,
name
)
def
py_type_name
(
self
):
return
self
.
checked_base_type
.
py_type_name
()
def
can_coerce_to_pyobject
(
self
,
env
):
return
self
.
checked_base_type
.
can_coerce_to_pyobject
(
env
)
def
can_coerce_from_pyobject
(
self
,
env
):
return
self
.
checked_base_type
.
can_coerce_from_pyobject
(
env
)
def
generate_incref
(
self
,
code
,
cname
,
**
kwds
):
self
.
checked_base_type
.
generate_incref
(
code
,
"%s.result"
%
cname
,
**
kwds
)
def
generate_xincref
(
self
,
code
,
cname
,
**
kwds
):
self
.
checked_base_type
.
generate_xincref
(
code
,
"%s.result"
%
cname
,
**
kwds
)
def
generate_decref
(
self
,
code
,
cname
,
**
kwds
):
self
.
checked_base_type
.
generate_decref
(
code
,
"%s.result"
%
cname
,
**
kwds
)
def
generate_decref_clear
(
self
,
code
,
cname
,
**
kwds
):
self
.
checked_base_type
.
generate_decref_clear
(
code
,
"%s.result"
%
cname
,
**
kwds
)
def
generate_xdecref
(
self
,
code
,
cname
,
**
kwds
):
self
.
checked_base_type
.
generate_xdecref
(
code
,
"%s.result"
%
cname
,
**
kwds
)
def
generate_xdecref_clear
(
self
,
code
,
cname
,
**
kwds
):
self
.
checked_base_type
.
generate_xdecref_clear
(
code
,
"%s.result"
%
cname
,
**
kwds
)
def
generate_gotref
(
self
,
code
,
cname
):
self
.
checked_base_type
.
generate_gotref
(
code
,
"%s.result"
%
cname
)
def
generate_xgotref
(
self
,
code
,
cname
):
self
.
checked_base_type
.
generate_xgotref
(
code
,
"%s.result"
%
cname
)
def
generate_giveref
(
self
,
code
,
cname
):
self
.
checked_base_type
.
generate_giveref
(
code
,
"%s.result"
%
cname
)
def
generate_xgiveref
(
self
,
code
,
cname
):
self
.
checked_base_type
.
generate_xgiveref
(
code
,
"%s.result"
%
cname
)
def
generate_decref_set
(
self
,
code
,
cname
,
rhs_cname
):
self
.
checked_base_type
.
generate_decref_set
(
code
,
"%s.result"
%
cname
,
rhs_cname
)
def
generate_xdecref_set
(
self
,
code
,
cname
,
rhs_cname
):
self
.
checked_base_type
.
generate_xdecref_set
(
code
,
"%s.result"
%
cname
,
rhs_cname
)
class
MemoryViewSliceType
(
PyrexType
):
class
MemoryViewSliceType
(
PyrexType
):
is_memoryviewslice
=
1
is_memoryviewslice
=
1
...
...
Cython/Compiler/TypeInference.py
View file @
1c3f95c9
...
@@ -532,6 +532,8 @@ def find_spanning_type(type1, type2):
...
@@ -532,6 +532,8 @@ def find_spanning_type(type1, type2):
return
result_type
return
result_type
def
simply_type
(
result_type
,
pos
):
def
simply_type
(
result_type
,
pos
):
if
result_type
.
is_checked_result
:
result_type
=
result_type
.
checked_base_type
if
result_type
.
is_reference
:
if
result_type
.
is_reference
:
result_type
=
result_type
.
ref_base_type
result_type
=
result_type
.
ref_base_type
if
result_type
.
is_cv_qualified
:
if
result_type
.
is_cv_qualified
:
...
...
Cython/Utility/CppSupport.cpp
View file @
1c3f95c9
...
@@ -67,3 +67,40 @@ auto __Pyx_pythran_to_python(T &&value) -> decltype(to_python(
...
@@ -67,3 +67,40 @@ auto __Pyx_pythran_to_python(T &&value) -> decltype(to_python(
#else
#else
#define __PYX_STD_MOVE_IF_SUPPORTED(x) x
#define __PYX_STD_MOVE_IF_SUPPORTED(x) x
#endif
#endif
/////////////// CheckedResult.proto ///////////////
#include <type_traits>
template
<
typename
T
,
bool
=
std
::
is_void
<
T
>
::
value
>
struct
CheckedResult
{};
template
<
typename
T
>
class
CheckedResult
<
T
,
false
>
{
enum
Status
{
Ok
,
Err
};
private:
enum
Status
status
;
public:
T
result
;
CheckedResult
(
const
T
&
value
)
:
status
(
Ok
),
result
(
value
)
{}
CheckedResult
()
:
status
(
Err
)
{}
operator
T
()
{
return
result
;
}
void
set_error
()
{
status
=
Err
;
}
bool
is_error
()
{
return
status
==
Err
;
}
};
template
<
typename
T
>
class
CheckedResult
<
T
,
true
>
{
enum
Status
{
Ok
,
Err
};
public:
CheckedResult
()
:
status
(
Ok
)
{}
void
set_error
()
{
status
=
Err
;
}
bool
is_error
()
{
return
status
==
Err
;
}
private:
enum
Status
status
;
};
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