C++ error handling
authorRobert Bradshaw <robertwb@math.washington.edu>
Thu, 3 Apr 2008 02:55:50 +0000 (19:55 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Thu, 3 Apr 2008 02:55:50 +0000 (19:55 -0700)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
Cython/Compiler/Parsing.py

index f10853c5c8a22ac726693006ebfe8d201a84bad5..e5273e3426230f9a099a540998a7a28fd7fdcb4c 100644 (file)
@@ -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 = ""
-                # <fsw> 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");
+  }
+}
+"""]
+
+#------------------------------------------------------------------------------------
index 0ff2983b48c24b3c260b4bcee2def8ea740f9f44..564ff97afa20d763c38e6ca2ddbc13a0865408b1 100644 (file)
@@ -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,
index da25abd9270dbcea1bf3c5c7c832e19446d248c6..0b403f9b4f7ca840ef2c7f26b01b36f2e8877d90 100644 (file)
@@ -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