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):
else
:
self
.
args
=
[
arg
.
analyse_types
(
env
)
for
arg
in
self
.
args
]
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
return
self
...
...
@@ -6228,7 +6228,7 @@ class SimpleCallNode(CallNode):
def
is_c_result_required
(
self
):
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
True
...
...
@@ -6330,10 +6330,14 @@ class SimpleCallNode(CallNode):
if
exc_val
is
not
None
:
exc_checks
.
append
(
"%s == %s"
%
(
self
.
result
(),
func_type
.
return_type
.
cast_code
(
exc_val
)))
if
exc_check
:
if
self
.
nogil
:
exc_checks
.
append
(
"__Pyx_ErrOccurredWithGIL()"
)
if
exc_check
==
'~'
:
assert
self
.
is_temp
exc_checks
.
append
(
self
.
type
.
error_condition
(
self
.
result
()))
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
:
rhs
=
self
.
c_call_code
()
if
self
.
result
():
...
...
Cython/Compiler/Nodes.py
View file @
1c3f95c9
...
...
@@ -701,12 +701,16 @@ class CFuncDeclaratorNode(CDeclaratorNode):
env
.
add_include_file
(
'new'
)
# for std::bad_alloc
env
.
add_include_file
(
'stdexcept'
)
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
and
(
self
.
exception_value
or
self
.
exception_check
)
and
self
.
exception_check
!=
'+'
):
error
(
self
.
pos
,
"Exception clause not allowed for function returning Python object"
)
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.
# 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.
...
...
@@ -2244,14 +2248,17 @@ class FuncDefNode(StatNode, BlockNode):
self
.
entry
.
qualified_name
,
0
)
assure_gil
(
'error'
)
code
.
put_unraisable
(
self
.
entry
.
qualified_name
)
default_retval
=
return_type
.
default_value
if
err_val
is
None
and
default_retval
:
err_val
=
default_retval
if
err_val
is
not
None
:
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
return_type
.
is_checked_result
:
code
.
putln
(
return_type
.
set_error
(
Naming
.
retval_cname
))
else
:
default_retval
=
return_type
.
default_value
if
err_val
is
None
and
default_retval
:
err_val
=
default_retval
if
err_val
is
not
None
:
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
:
assure_gil
(
'error'
)
...
...
Cython/Compiler/Parsing.py
View file @
1c3f95c9
...
...
@@ -2986,6 +2986,9 @@ def p_exception_value_clause(s):
elif
s
.
sy
==
'*'
:
exc_val
=
ExprNodes
.
CharNode
(
s
.
position
(),
value
=
u'*'
)
s
.
next
()
elif
s
.
sy
==
'~'
:
exc_check
=
'~'
s
.
next
()
else
:
if
s
.
sy
==
'?'
:
exc_check
=
1
...
...
Cython/Compiler/PyrexTypes.py
View file @
1c3f95c9
...
...
@@ -264,6 +264,7 @@ class PyrexType(BaseType):
is_memoryviewslice
=
0
is_pythran_expr
=
0
is_numpy_buffer
=
0
is_checked_result
=
0
has_attributes
=
0
needs_refcounting
=
0
default_value
=
""
...
...
@@ -595,6 +596,130 @@ class CTypedefType(BaseType):
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
):
is_memoryviewslice
=
1
...
...
Cython/Compiler/TypeInference.py
View file @
1c3f95c9
...
...
@@ -532,6 +532,8 @@ def find_spanning_type(type1, type2):
return
result_type
def
simply_type
(
result_type
,
pos
):
if
result_type
.
is_checked_result
:
result_type
=
result_type
.
checked_base_type
if
result_type
.
is_reference
:
result_type
=
result_type
.
ref_base_type
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(
#else
#define __PYX_STD_MOVE_IF_SUPPORTED(x) x
#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