From: Robert Bradshaw Date: Thu, 3 Apr 2008 02:55:50 +0000 (-0700) Subject: C++ error handling X-Git-Tag: 0.9.6.14~29^2~5 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=072b05eb6e8dbe177f8dcfdda7d913636e9d6164;p=cython.git C++ error handling --- diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f10853c5..e5273e34 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -889,7 +889,7 @@ class NameNode(AtomicExprNode): def check_const(self): entry = self.entry - if not (entry.is_const or entry.is_cfunction or entry.is_builtin): + if entry is not None and not (entry.is_const or entry.is_cfunction or entry.is_builtin): self.not_const() def check_const_addr(self): @@ -1604,7 +1604,11 @@ class SimpleCallNode(ExprNode): self.is_temp = 1 if self.type.is_pyobject: self.result_ctype = py_object_type - + # C++ exception handler + if func_type.exception_check == '+': + if func_type.exception_value is None: + env.use_utility_code(cpp_exception_utility_code) + def calculate_result_code(self): return self.c_call_code() @@ -1678,12 +1682,18 @@ class SimpleCallNode(ExprNode): rhs = typecast(py_object_type, self.type, rhs) else: lhs = "" - # added for generating C++ try/catch block if func_type.exception_check == '+': + if func_type.exception_value is None: + raise_py_exception = "__Pyx_CppExn2PyErr()" + elif func_type.exception_value.type.is_pyobject: + raise_py_exception = 'PyErr_SetString(%s, "")' % func_type.exception_value.entry.cname + else: + raise_py_exception = '%s(); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError , "Error converting c++ exception.")' % func_type.exception_value.entry.cname code.putln( - "try {%s%s;} catch(...) {CppExn2PyErr(); %s}" % ( + "try {%s%s;} catch(...) {%s; %s}" % ( lhs, rhs, + raise_py_exception, code.error_goto(self.pos))) return code.putln( @@ -4036,3 +4046,28 @@ bad: """] #------------------------------------------------------------------------------------ + +cpp_exception_utility_code = [ +""" +static int __Pyx_CppExn2PyErr(); /*proto*/ +""",""" +void __Pyx_CppExn2PyErr() { + try { + if (PyErr_Occurred()) + ; // let the latest Python exn pass through and ignore the current one + else + throw; + } catch (const std::out_of_range& exn) { + // catch out_of_range explicitly so the proper Python exn may be raised + PyErr_SetString(PyExc_IndexError, exn.what()); + } catch (const std::exception& exn) { + PyErr_SetString(PyExc_RuntimeError, exn.what()); + } + catch (...) + { + PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); + } +} +"""] + +#------------------------------------------------------------------------------------ diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 0ff2983b..564ff97a 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -483,10 +483,19 @@ class CFuncDeclaratorNode(CDeclaratorNode): else: if self.exception_value: self.exception_value.analyse_const_expression(env) - exc_val = self.exception_value.result_code - if not return_type.assignable_from(self.exception_value.type): - error(self.exception_value.pos, - "Exception value incompatible with function return type") + if self.exception_check == '+': + exc_val_type = self.exception_value.type + if not exc_val_type.is_error and \ + not exc_val_type.is_pyobject and \ + not (exc_val_type.is_cfunction and not exc_val_type.return_type.is_pyobject and len(exc_val_type.args)==0): + error(self.exception_value.pos, + "Exception value must be a Python exception or cdef function with no arguments.") + exc_val = self.exception_value + else: + exc_val = self.exception_value.result_code + if not return_type.assignable_from(self.exception_value.type): + error(self.exception_value.pos, + "Exception value incompatible with function return type") exc_check = self.exception_check if return_type.is_pyobject and self.nogil: error(self.pos, diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index da25abd9..0b403f9b 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -1648,6 +1648,10 @@ def p_exception_value_clause(s): elif s.sy == '+': exc_check = '+' s.next() + if s.sy == 'IDENT': + name = s.systring + s.next() + exc_val = p_name(s, name) else: if s.sy == '?': exc_check = 1