Pyrex merge: nogil declaration checking
authorStefan Behnel <scoder@users.berlios.de>
Thu, 5 Jun 2008 10:20:27 +0000 (12:20 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Thu, 5 Jun 2008 10:20:27 +0000 (12:20 +0200)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
Cython/Compiler/PyrexTypes.py
Cython/Compiler/Symtab.py
runtests.py
tests/compile/nogil.h [new file with mode: 0644]
tests/compile/nogil.pyx
tests/errors/nogil.pyx [new file with mode: 0644]
tests/errors/nogilfunctype.pyx [new file with mode: 0644]
tests/run/withnogil.pyx

index 5446b29e8544707a8b4e240b4286f700560167be..13166a88f1cf03349fd3fa98ec9cd2d973152667 100644 (file)
@@ -297,7 +297,11 @@ class ExprNode(Node):
     
     def analyse_target_types(self, env):
         self.analyse_types(env)
-    
+
+    def gil_assignment_check(self, env):
+        if env.nogil and self.type.is_pyobject:
+            error(self.pos, "Assignment of Python object not allowed without gil")
+
     def check_const(self):
         self.not_const()
     
@@ -309,7 +313,11 @@ class ExprNode(Node):
     
     def addr_not_const(self):
         error(self.pos, "Address is not constant")
-    
+
+    def gil_check(self, env):
+        if env.nogil and self.type.is_pyobject:
+            self.gil_error()
+
     # ----------------- Result Allocation -----------------
     
     def result_in_temp(self):
@@ -755,11 +763,16 @@ class LongNode(AtomicExprNode):
     
     def compile_time_value(self, denv):
         return long(self.value)
+
+    gil_message = "Constructing Python long int"
     
     def analyse_types(self, env):
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Constructing Python long int"
+
     def generate_evaluation_code(self, code):
         code.putln(
             '%s = PyLong_FromString("%s", 0, 0); %s' % (
@@ -778,8 +791,11 @@ class ImagNode(AtomicExprNode):
     
     def analyse_types(self, env):
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Constructing complex number"
+
     def generate_evaluation_code(self, code):
         code.putln(
             "%s = PyComplex_FromDoubles(0.0, %s); %s" % (
@@ -880,7 +896,10 @@ class NameNode(AtomicExprNode):
             else:
                 self.is_temp = 1
             env.use_utility_code(get_name_interned_utility_code)
-    
+            self.gil_check(env)
+
+    gil_message = "Accessing Python global or builtin"
+
     def analyse_entry(self, env):
         #print "NameNode.analyse_entry:", self.name ###
         self.check_identifier_kind()
@@ -1057,8 +1076,11 @@ class BackquoteNode(ExprNode):
         self.arg.analyse_types(env)
         self.arg = self.arg.coerce_to_pyobject(env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Backquote expression"
+
     def generate_result_code(self, code):
         code.putln(
             "%s = PyObject_Repr(%s); %s" % (
@@ -1083,9 +1105,12 @@ class ImportNode(ExprNode):
         if self.name_list:
             self.name_list.analyse_types(env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
         env.use_utility_code(import_utility_code)
-    
+
+    gil_message = "Python import"
+
     def generate_result_code(self, code):
         if self.name_list:
             name_list_code = self.name_list.py_result()
@@ -1111,11 +1136,14 @@ class IteratorNode(ExprNode):
         self.sequence.analyse_types(env)
         self.sequence = self.sequence.coerce_to_pyobject(env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
         
         self.counter = TempNode(self.pos, PyrexTypes.c_py_ssize_t_type, env)
         self.counter.allocate_temp(env)
-        
+
+    gil_message = "Iterating over Python object"
+
     def release_temp(self, env):
         env.release_temp(self.result_code)
         self.counter.release_temp(env)
@@ -1262,6 +1290,7 @@ class IndexNode(ExprNode):
             else:
                 self.index = self.index.coerce_to_pyobject(env)
             self.type = py_object_type
+            self.gil_check(env)
             self.is_temp = 1
         else:
             if self.base.type.is_ptr or self.base.type.is_array:
@@ -1278,7 +1307,9 @@ class IndexNode(ExprNode):
                 error(self.pos,
                     "Invalid index type '%s'" %
                         self.index.type)
-    
+
+    gil_message = "Indexing Python object"
+
     def check_const_addr(self):
         self.base.check_const_addr()
         self.index.check_const()
@@ -1405,8 +1436,11 @@ class SliceIndexNode(ExprNode):
         if self.stop:
             self.stop = self.stop.coerce_to(c_int, env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Slicing Python object"
+
     def generate_result_code(self, code):
         code.putln(
             "%s = PySequence_GetSlice(%s, %s, %s); %s" % (
@@ -1479,8 +1513,11 @@ class SliceNode(ExprNode):
         self.stop = self.stop.coerce_to_pyobject(env)
         self.step = self.step.coerce_to_pyobject(env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Constructing Python slice object"
+
     def generate_result_code(self, code):
         code.putln(
             "%s = PySlice_New(%s, %s, %s); %s" % (
@@ -1490,7 +1527,15 @@ class SliceNode(ExprNode):
                 self.step.py_result(),
                 code.error_goto_if_null(self.result_code, self.pos)))
 
-class SimpleCallNode(ExprNode):
+
+class CallNode(ExprNode):
+    def gil_check(self, env):
+        # Make sure we're not in a nogil environment
+        if env.nogil:
+            error(self.pos, "Calling gil-requiring function without gil")
+
+
+class SimpleCallNode(CallNode):
     #  Function call without keyword, * or ** args.
     #
     #  function       ExprNode
@@ -1539,6 +1584,7 @@ class SimpleCallNode(ExprNode):
             self.arg_tuple.analyse_types(env)
             self.args = None
             self.type = py_object_type
+            self.gil_check(env)
             self.is_temp = 1
         else:
             for arg in self.args:
@@ -1611,6 +1657,9 @@ class SimpleCallNode(ExprNode):
         if func_type.exception_check == '+':
             if func_type.exception_value is None:
                 env.use_utility_code(cpp_exception_utility_code)
+        # Check gil
+        if not func_type.nogil:
+            self.gil_check(env)
 
     def calculate_result_code(self):
         return self.c_call_code()
@@ -1705,7 +1754,7 @@ class SimpleCallNode(ExprNode):
                         rhs,
                         code.error_goto_if(" && ".join(exc_checks), self.pos)))    
 
-class GeneralCallNode(ExprNode):
+class GeneralCallNode(CallNode):
     #  General Python function call, including keyword,
     #  * and ** arguments.
     #
@@ -1741,6 +1790,7 @@ class GeneralCallNode(ExprNode):
             self.starstar_arg = \
                 self.starstar_arg.coerce_to_pyobject(env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
         
     def generate_result_code(self, code):
@@ -1791,8 +1841,11 @@ class AsTupleNode(ExprNode):
         self.arg.analyse_types(env)
         self.arg = self.arg.coerce_to_pyobject(env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Constructing Python tuple"
+
     def generate_result_code(self, code):
         code.putln(
             "%s = PySequence_Tuple(%s); %s" % (
@@ -1997,12 +2050,15 @@ class AttributeNode(ExprNode):
             self.type = py_object_type
             self.is_py_attr = 1
             self.interned_attr_cname = env.intern_identifier(self.attribute)
+            self.gil_check(env)
         else:
             if not obj_type.is_error:
                 error(self.pos, 
                     "Object of type '%s' has no attribute '%s'" %
                     (obj_type, self.attribute))
-        
+
+    gil_message = "Accessing Python attribute"
+
     def is_simple(self):
         if self.obj:
             return self.result_in_temp() or self.obj.is_simple()
@@ -2119,8 +2175,9 @@ class SequenceNode(ExprNode):
             arg.analyse_types(env)
             self.args[i] = arg.coerce_to_pyobject(env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
     def analyse_target_types(self, env):
         self.iterator = PyTempNode(self.pos, env)
         self.unpacked_items = []
@@ -2217,7 +2274,9 @@ class SequenceNode(ExprNode):
 
 class TupleNode(SequenceNode):
     #  Tuple constructor.
-    
+
+    gil_message = "Constructing Python tuple"
+
     def analyse_types(self, env):
         if len(self.args) == 0:
             self.is_temp = 0
@@ -2268,7 +2327,9 @@ class TupleNode(SequenceNode):
 
 class ListNode(SequenceNode):
     #  List constructor.
-    
+
+    gil_message = "Constructing Python list"
+
     def analyse_types(self, env):
         SequenceNode.analyse_types(self, env)
         self.type = list_type
@@ -2365,8 +2426,11 @@ class DictNode(ExprNode):
         for item in self.key_value_pairs:
             item.analyse_types(env)
         self.type = dict_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Constructing Python dict"
+
     def allocate_temps(self, env, result = None):
         #  Custom method used here because key-value
         #  pairs are evaluated and used one at a time.
@@ -2441,9 +2505,12 @@ class ClassNode(ExprNode):
             self.doc = self.doc.coerce_to_pyobject(env)
         self.module_name = env.global_scope().qualified_name
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
         env.use_utility_code(create_class_utility_code);
 
+    gil_message = "Constructing Python class"
+
     def generate_result_code(self, code):
         if self.doc:
             code.put_error_if_neg(self.pos, 
@@ -2473,8 +2540,11 @@ class UnboundMethodNode(ExprNode):
     def analyse_types(self, env):
         self.function.analyse_types(env)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Constructing an unbound method"
+
     def generate_result_code(self, code):
         code.putln(
             "%s = PyMethod_New(%s, 0, %s); %s" % (
@@ -2493,8 +2563,11 @@ class PyCFunctionNode(AtomicExprNode):
     
     def analyse_types(self, env):
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
-    
+
+    gil_message = "Constructing Python function"
+
     def generate_result_code(self, code):
         code.putln(
             "%s = PyCFunction_New(&%s, 0); %s" % (
@@ -2546,6 +2619,7 @@ class UnopNode(ExprNode):
         if self.is_py_operation():
             self.coerce_operand_to_pyobject(env)
             self.type = py_object_type
+            self.gil_check(env)
             self.is_temp = 1
         else:
             self.analyse_c_operation(env)
@@ -2895,6 +2969,7 @@ class BinopNode(ExprNode):
         if self.is_py_operation():
             self.coerce_operands_to_pyobjects(env)
             self.type = py_object_type
+            self.gil_check(env)
             self.is_temp = 1
             if Options.incref_local_binop and self.operand1.type.is_pyobject:
                 self.operand1 = self.operand1.coerce_to_temp(env)
@@ -3126,6 +3201,7 @@ class BoolBinopNode(ExprNode):
             self.operand2 = self.operand2.coerce_to_pyobject(env)
             self.temp_bool = TempNode(self.pos, PyrexTypes.c_bint_type, env)
             self.type = py_object_type
+            self.gil_check(env)
         else:
             self.operand1 = self.operand1.coerce_to_boolean(env)
             self.operand2 = self.operand2.coerce_to_boolean(env)
@@ -3134,11 +3210,10 @@ class BoolBinopNode(ExprNode):
         # both operands be temp nodes.
         self.operand1 = self.operand1.coerce_to_temp(env) #CTT
         self.operand2 = self.operand2.coerce_to_temp(env)
-        # coerce_to_simple does not seem to be sufficient
-        #self.operand1 = self.operand1.coerce_to_simple(env)
-        #self.operand2 = self.operand2.coerce_to_simple(env)
         self.is_temp = 1
-    
+
+    gil_message = "Truth-testing Python object"
+
     def allocate_temps(self, env, result_code = None):
         #  We don't need both operands at the same time, and
         #  one of the operands will also be our result. So we
@@ -3435,7 +3510,6 @@ class PrimaryCmpNode(ExprNode, CmpNode):
         if self.has_int_operands():
             self.coerce_chars_to_ints(env)
         if self.cascade:
-            #self.operand2 = self.operand2.coerce_to_temp(env) #CTT
             self.operand2 = self.operand2.coerce_to_simple(env)
             self.cascade.coerce_cascaded_operands_to_temp(env)
         self.check_operand_types(env)
@@ -3685,8 +3759,11 @@ class PyTypeTestNode(CoercionNode):
         assert dst_type.is_extension_type or dst_type.is_builtin_type, "PyTypeTest on non extension type"
         CoercionNode.__init__(self, arg)
         self.type = dst_type
+        self.gil_check(env)
         self.result_ctype = arg.ctype()
         env.use_utility_code(type_test_utility_code)
+
+    gil_message = "Python type test"
     
     def analyse_types(self, env):
         pass
@@ -3721,11 +3798,14 @@ class CoerceToPyTypeNode(CoercionNode):
     def __init__(self, arg, env):
         CoercionNode.__init__(self, arg)
         self.type = py_object_type
+        self.gil_check(env)
         self.is_temp = 1
         if not arg.type.to_py_function:
             error(arg.pos,
                 "Cannot convert '%s' to Python object" % arg.type)
-    
+
+    gil_message = "Converting to Python object"
+
     def generate_result_code(self, code):
         function = self.arg.type.to_py_function
         code.putln('%s = %s(%s); %s' % (
@@ -3770,7 +3850,11 @@ class CoerceToBooleanNode(CoercionNode):
         CoercionNode.__init__(self, arg)
         self.type = PyrexTypes.c_bint_type
         if arg.type.is_pyobject:
+            if env.nogil:
+                self.gil_error()
             self.is_temp = 1
+
+    gil_message = "Truth-testing Python object"
     
     def check_const(self):
         if self.is_temp:
@@ -3799,8 +3883,11 @@ class CoerceToTempNode(CoercionNode):
         self.type = self.arg.type
         self.is_temp = 1
         if self.type.is_pyobject:
+            self.gil_check(env)
             self.result_ctype = py_object_type
-    
+
+    gil_message = "Creating temporary Python reference"
+
     def generate_result_code(self, code):
         #self.arg.generate_evaluation_code(code) # Already done
         # by generic generate_subexpr_evaluation_code!
index e8ac2f84606d9540c3e5c2e037c0b9f3a5ff639e..58831036d7e107e4dd7cfba5c64d017f79670e3a 100644 (file)
@@ -106,7 +106,16 @@ class Node(object):
     def __init__(self, pos, **kw):
         self.pos = pos
         self.__dict__.update(kw)
-    
+
+    gil_message = "Operation"
+
+    def gil_check(self, env):
+        if env.nogil:
+            self.gil_error()
+
+    def gil_error(self):
+        error(self.pos, "%s not allowed without gil" % self.gil_message)
+
     def get_child_accessors(self):
         """Returns an iterator over the children of the Node. Each member in the
         iterated list is an object with get(), set(value), and name() methods,
@@ -488,9 +497,9 @@ class CFuncDeclaratorNode(CDeclaratorNode):
             # Catch attempted C-style func(void) decl
             if type.is_void:
                 error(arg_node.pos, "Use spam() rather than spam(void) to declare a function with no arguments.")
-            if type.is_pyobject and self.nogil:
-                error(self.pos,
-                    "Function with Python argument cannot be declared nogil")
+#            if type.is_pyobject and self.nogil:
+#                error(self.pos,
+#                    "Function with Python argument cannot be declared nogil")
             func_type_args.append(
                 PyrexTypes.CFuncTypeArg(name, type, arg_node.pos))
             if arg_node.default:
@@ -537,9 +546,6 @@ class CFuncDeclaratorNode(CDeclaratorNode):
                         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,
-                "Function with Python return type cannot be declared nogil")
         if return_type.is_array:
             error(self.pos,
                 "Function cannot return an array")
@@ -867,6 +873,9 @@ class FuncDefNode(StatNode, BlockNode):
         genv = env.global_scope()
         lenv = LocalScope(name = self.entry.name, outer_scope = genv)
         lenv.return_type = self.return_type
+        type = self.entry.type
+        if type.is_cfunction:
+            lenv.nogil = type.nogil and not type.with_gil
         code.init_labels()
         self.declare_arguments(lenv)
         transforms.run('before_analyse_function', self, env=env, lenv=lenv, genv=genv)
@@ -1113,8 +1122,12 @@ class CFuncDefNode(FuncDefNode):
             self.declare_argument(env, arg)
             
     def need_gil_acquisition(self, lenv):
+        type = self.type
         with_gil = self.type.with_gil
-        if self.type.nogil and not with_gil:
+        if type.nogil and not with_gil:
+            if type.return_type.is_pyobject:
+                error(self.pos,
+                      "Function with Python return type cannot be declared nogil")
             for entry in lenv.var_entries + lenv.temp_entries:
                 if entry.type.is_pyobject:
                     error(self.pos, "Function declared nogil has Python locals or temporaries")
@@ -2216,6 +2229,7 @@ class SingleAssignmentNode(AssignmentNode):
     def analyse_types(self, env, use_temp = 0):
         self.rhs.analyse_types(env)
         self.lhs.analyse_target_types(env)
+        self.lhs.gil_assignment_check(env)
         self.rhs = self.rhs.coerce_to(self.lhs.type, env)
         if use_temp:
             self.rhs = self.rhs.coerce_to_temp(env)
@@ -2280,6 +2294,7 @@ class CascadedAssignmentNode(AssignmentNode):
         self.coerced_rhs_list = []
         for lhs in self.lhs_list:
             lhs.analyse_target_types(env)
+            lhs.gil_assignment_check(env)
             rhs = CloneNode(self.rhs)
             rhs = rhs.coerce_to(lhs.type, env)
             self.coerced_rhs_list.append(rhs)
@@ -2522,15 +2537,9 @@ class PrintStatNode(StatNode):
         self.arg_tuple = self.arg_tuple.coerce_to_pyobject(env)
         self.arg_tuple.release_temp(env)
         env.use_utility_code(printing_utility_code)
-        return
-        for i in range(len(self.args)):
-            arg = self.args[i]
-            arg.analyse_types(env)
-            arg = arg.coerce_to_pyobject(env)
-            arg.allocate_temps(env)
-            arg.release_temp(env)
-            self.args[i] = arg
-            #env.recycle_pending_temps() # TEMPORARY
+        self.gil_check(env)
+
+    gil_message = "Python print statement"
 
     def generate_execution_code(self, code):
         self.arg_tuple.generate_evaluation_code(code)
@@ -2559,10 +2568,14 @@ class DelStatNode(StatNode):
     def analyse_expressions(self, env):
         for arg in self.args:
             arg.analyse_target_expression(env, None)
-            if not arg.type.is_pyobject:
+            if arg.type.is_pyobject:
+                self.gil_check(env)
+            else:
                 error(arg.pos, "Deletion of non-Python object")
             #arg.release_target_temp(env)
-    
+
+    gil_message = "Deleting Python object"
+
     def generate_execution_code(self, code):
         for arg in self.args:
             if arg.type.is_pyobject:
@@ -2649,7 +2662,11 @@ class ReturnStatNode(StatNode):
                 and not return_type.is_pyobject
                 and not return_type.is_returncode):
                     error(self.pos, "Return value required")
-    
+        if return_type.is_pyobject:
+            self.gil_check(env)
+
+    gil_message = "Returning Python object"
+
     def generate_execution_code(self, code):
         code.mark_pos(self.pos)
         if not self.return_type:
@@ -2711,11 +2728,11 @@ class RaiseStatNode(StatNode):
             self.exc_value.release_temp(env)
         if self.exc_tb:
             self.exc_tb.release_temp(env)
-#              if not (self.exc_type or self.exc_value or self.exc_tb):
-#                      env.use_utility_code(reraise_utility_code)
-#              else:
         env.use_utility_code(raise_utility_code)
-    
+        self.gil_check(env)
+
+    gil_message = "Raising exception"
+
     def generate_execution_code(self, code):
         if self.exc_type:
             self.exc_type.generate_evaluation_code(code)
@@ -2764,8 +2781,11 @@ class ReraiseStatNode(StatNode):
     child_attrs = []
 
     def analyse_expressions(self, env):
+        self.gil_check(env)
         env.use_utility_code(raise_utility_code)
 
+    gil_message = "Raising exception"
+
     def generate_execution_code(self, code):
         vars = code.exc_vars
         if vars:
@@ -2792,7 +2812,10 @@ class AssertStatNode(StatNode):
         self.cond.release_temp(env)
         if self.value:
             self.value.release_temp(env)
+        self.gil_check(env)
         #env.recycle_pending_temps() # TEMPORARY
+
+    gil_message = "Raising exception"
     
     def generate_execution_code(self, code):
         code.putln("#ifndef PYREX_WITHOUT_ASSERTIONS")
@@ -2888,7 +2911,6 @@ class IfClauseNode(Node):
         self.condition = \
             self.condition.analyse_temp_boolean_expression(env)
         self.condition.release_temp(env)
-        #env.recycle_pending_temps() # TEMPORARY
         self.body.analyse_expressions(env)
     
     def generate_execution_code(self, code, end_label):
@@ -3250,6 +3272,7 @@ class TryExceptStatNode(StatNode):
             except_clause.analyse_declarations(env)
         if self.else_clause:
             self.else_clause.analyse_declarations(env)
+        self.gil_check(env)
     
     def analyse_expressions(self, env):
         self.body.analyse_expressions(env)
@@ -3258,7 +3281,10 @@ class TryExceptStatNode(StatNode):
             except_clause.analyse_expressions(env)
         if self.else_clause:
             self.else_clause.analyse_expressions(env)
-    
+        self.gil_check(env)
+
+    gil_message = "Try-except statement"
+
     def generate_execution_code(self, code):
         old_error_label = code.new_error_label()
         our_error_label = code.error_label
@@ -3418,16 +3444,11 @@ class TryFinallyStatNode(StatNode):
     def analyse_expressions(self, env):
         self.body.analyse_expressions(env)
         self.cleanup_list = env.free_temp_entries[:]
-        #self.exc_vars = (
-        #      env.allocate_temp(PyrexTypes.py_object_type),
-        #      env.allocate_temp(PyrexTypes.py_object_type),
-        #      env.allocate_temp(PyrexTypes.py_object_type))
-        #self.lineno_var = \
-        #      env.allocate_temp(PyrexTypes.c_int_type)
         self.finally_clause.analyse_expressions(env)
-        #for var in self.exc_vars:
-        #      env.release_temp(var)
-    
+        self.gil_check(env)
+
+    gil_message = "Try-finally statement"
+
     def generate_execution_code(self, code):
         old_error_label = code.error_label
         old_labels = code.all_new_labels()
@@ -3572,6 +3593,15 @@ class GILStatNode(TryFinallyStatNode):
             body = body,
             finally_clause = GILExitNode(pos, state = state))
 
+    def analyse_expressions(self, env):
+        was_nogil = env.nogil
+        env.nogil = 1
+        TryFinallyStatNode.analyse_expressions(self, env)
+        env.nogil = was_nogil
+
+    def gil_check(self, env):
+        pass
+
     def generate_execution_code(self, code):
         code.putln("/*with %s:*/ {" % self.state)
         if self.state == 'gil':
@@ -3582,19 +3612,6 @@ class GILStatNode(TryFinallyStatNode):
         TryFinallyStatNode.generate_execution_code(self, code)
         code.putln("}")
 
-#class GILEntryNode(StatNode):
-#      #  state   string   'gil' or 'nogil'
-#
-#      def analyse_expressions(self, env):
-#              pass
-#
-#      def generate_execution_code(self, code):
-#              if self.state == 'gil':
-#                      code.putln("PyGILState_STATE _save = PyGILState_Ensure();")
-#              else:
-#                      code.putln("PyThreadState *_save;")
-#                      code.putln("Py_UNBLOCK_THREADS")
-
 
 class GILExitNode(StatNode):
     #  Used as the 'finally' block in a GILStatNode
index cb71d828fc8dec508220010e0c261023e5450d3d..a9183fc7d721296ec99c5238c8ce8f70fbb32943 100644 (file)
@@ -695,9 +695,10 @@ class CFuncType(CType):
             return 0
         if not self.same_calling_convention_as(other_type):
             return 0
+        if self.nogil != other_type.nogil:
+            return 0
         return 1
-        
-    
+
     def compatible_signature_with(self, other_type, as_cmethod = 0):
         return self.compatible_signature_with_resolved_type(other_type.resolve(), as_cmethod)
     
@@ -789,22 +790,24 @@ class CFuncType(CType):
         arg_decl_code = ", ".join(arg_decl_list)
         if not arg_decl_code and not pyrex:
             arg_decl_code = "void"
-        exc_clause = ""
+        trailer = ""
         if (pyrex or for_display) and not self.return_type.is_pyobject:
             if self.exception_value and self.exception_check:
-                exc_clause = " except? %s" % self.exception_value
+                trailer = " except? %s" % self.exception_value
             elif self.exception_value:
-                exc_clause = " except %s" % self.exception_value
+                trailer = " except %s" % self.exception_value
             elif self.exception_check == '+':
-                exc_clause = " except +"
+                trailer = " except +"
             else:
-                " except *"
+                " except *" # ignored
+            if self.nogil:
+                trailer += " nogil"
         cc = self.calling_convention_prefix()
         if (not entity_code and cc) or entity_code.startswith("*"):
             entity_code = "(%s%s)" % (cc, entity_code)
             cc = ""
         return self.return_type.declaration_code(
-            "%s%s(%s)%s" % (cc, entity_code, arg_decl_code, exc_clause),
+            "%s%s(%s)%s" % (cc, entity_code, arg_decl_code, trailer),
             for_display, dll_linkage, pyrex)
        
     def function_header_code(self, func_name, arg_code):
index 3f8a8f4beb254db309fe56ada81b318e4f2f8295..11d0a73d4139571faafc153203d53f75b63a315f 100644 (file)
@@ -150,12 +150,14 @@ class Scope:
     # pystring_entries  [Entry]            String const entries newly used as
     #                                        Python strings in this scope
     # control_flow     ControlFlow  Used for keeping track of environment state
+    # nogil             boolean            In a nogil section
 
     is_py_class_scope = 0
     is_c_class_scope = 0
     is_module_scope = 0
     scope_prefix = ""
     in_cinclude = 0
+    nogil = 0
     
     def __init__(self, name, outer_scope, parent_scope):
         # The outer_scope is the next scope in the lookup chain.
@@ -1324,6 +1326,7 @@ class CClassScope(ClassScope):
 #                    entry.type = type
                 else:
                     error(pos, "Signature not compatible with previous declaration")
+                    error(entry.pos, "Previous declaration is here")
         else:
             if self.defined:
                 error(pos,
@@ -1363,8 +1366,8 @@ class CClassScope(ClassScope):
                 entry.is_variable = 1
                 self.inherited_var_entries.append(entry)
         for base_entry in base_scope.cfunc_entries:
-            entry = self.add_cfunction(base_entry.name, base_entry.type, None,
-                adapt(base_entry.cname), base_entry.visibility)
+            entry = self.add_cfunction(base_entry.name, base_entry.type,
+                    base_entry.pos, adapt(base_entry.cname), base_entry.visibility)
             entry.is_inherited = 1
             
     def allocate_temp(self, type):
index 1d86bd0e0b7afa96b12d1559a481b0325826ee68..ba98c8c902e2b1ca756e3cc40498161182b52f82 100644 (file)
@@ -17,7 +17,7 @@ CFLAGS = os.getenv('CFLAGS', '').split()
 
 
 class ErrorWriter(object):
-    match_error = re.compile('(warning:)?(?:.*:)?([-0-9]+):([-0-9]+):(.*)').match
+    match_error = re.compile('(warning:)?(?:.*:)?\s*([-0-9]+)\s*:\s*([-0-9]+)\s*:\s*(.*)').match
     def __init__(self):
         self.output = []
         self.write = self.output.append
@@ -31,8 +31,9 @@ class ErrorWriter(object):
                 is_warning, line, column, message = match.groups()
                 if (is_warning and collect_warnings) or \
                         (not is_warning and collect_errors):
-                    result.append( "%d:%d:%s" % (int(line), int(column), message.strip()) )
-        return result
+                    result.append( (int(line), int(column), message.strip()) )
+        result.sort()
+        return [ "%d:%d:%s" % values for values in result ]
 
     def geterrors(self):
         return self._collect(True, False)
diff --git a/tests/compile/nogil.h b/tests/compile/nogil.h
new file mode 100644 (file)
index 0000000..fb2e4d4
--- /dev/null
@@ -0,0 +1,2 @@
+void e1(void);
+void *e2(void);
index 8f747ad0df0d3ef4516f2c569a36c8d349a93273..ba6f856a6f72fe472543e545d9f478d813cb8a15 100644 (file)
@@ -1,5 +1,18 @@
-cdef extern void g(int x) nogil
+cdef extern object g(object x) nogil
+cdef extern void g2(object x) nogil
+
+cdef extern from "nogil.h":
+        void e1() nogil
+        int *e2() nogil
 
 cdef void f(int x) nogil:
-    cdef int y
-    y = 42
+        cdef int y
+        y = 42
+
+cdef void h(object x) nogil:
+        cdef void *p
+        g2(x)
+        g2(<object>p)
+        p = <void *>x
+        e1()
+        e2()
diff --git a/tests/errors/nogil.pyx b/tests/errors/nogil.pyx
new file mode 100644 (file)
index 0000000..17e2e43
--- /dev/null
@@ -0,0 +1,136 @@
+cdef object f(object x) nogil:
+    pass
+
+cdef void g(int x) nogil:
+    cdef object z
+    z = None
+
+cdef void h(int x) nogil:
+    p()
+
+cdef object p() nogil:
+    pass
+
+cdef void r() nogil:
+    q()
+    
+cdef object m():
+    cdef object x, y, obj
+    cdef int i, j, k
+    global fred
+    q()
+    with nogil:
+        r()
+        q()
+        i = 42
+        obj = None
+        17L
+        7j
+        help
+        `"Hello"`
+        import fred
+        from fred import obj
+        for x in obj:
+            pass
+        obj[i]
+        obj[i:j]
+        obj[i:j:k]
+        obj.fred
+        (x, y)
+        [x, y]
+        {x: y}
+        obj and x
+        t(obj)
+#        f(42) # Cython handles this internally
+        x + obj
+        -obj
+        x = y = obj
+        x, y = y, x
+        obj[i] = x
+        obj.fred = x
+        print obj
+        del fred
+        return obj
+        raise obj
+        if obj:
+            pass
+        while obj:
+            pass
+        for x <= obj <= y:
+            pass
+        try:
+            pass
+        except:
+            pass
+        try:
+            pass
+        finally:
+            pass
+
+cdef void q():
+    pass
+
+cdef class C:
+    pass
+
+cdef void t(C c) nogil:
+    pass
+
+
+_ERRORS = u"""
+ 1: 5: Function with Python return type cannot be declared nogil
+ 6: 6: Assignment of Python object not allowed without gil
+ 4: 5: Function declared nogil has Python locals or temporaries
+ 8: 5: Function declared nogil has Python locals or temporaries
+11: 5: Function with Python return type cannot be declared nogil
+15: 5: Calling gil-requiring function without gil
+24: 9: Calling gil-requiring function without gil
+26:12: Assignment of Python object not allowed without gil
+27: 8: Constructing Python long int not allowed without gil
+28: 8: Constructing complex number not allowed without gil
+29:12: Accessing Python global or builtin not allowed without gil
+30: 8: Backquote expression not allowed without gil
+31:15: Python import not allowed without gil
+31:15: Assignment of Python object not allowed without gil
+32:13: Python import not allowed without gil
+32:25: Constructing Python list not allowed without gil
+33:17: Iterating over Python object not allowed without gil
+35:11: Indexing Python object not allowed without gil
+36:11: Slicing Python object not allowed without gil
+37:11: Constructing Python slice object not allowed without gil
+37:11: Indexing Python object not allowed without gil
+37:13: Converting to Python object not allowed without gil
+37:15: Converting to Python object not allowed without gil
+37:17: Converting to Python object not allowed without gil
+38:11: Accessing Python attribute not allowed without gil
+39: 9: Constructing Python tuple not allowed without gil
+40: 8: Constructing Python list not allowed without gil
+41: 8: Constructing Python dict not allowed without gil
+42:12: Creating temporary Python reference not allowed without gil
+42:12: Truth-testing Python object not allowed without gil
+42:17: Creating temporary Python reference not allowed without gil
+43:13: Python type test not allowed without gil
+#44: 4: Converting to Python object not allowed without gil
+45:10: Operation not allowed without gil
+46: 8: Operation not allowed without gil
+47:10: Assignment of Python object not allowed without gil
+47:14: Assignment of Python object not allowed without gil
+48: 9: Assignment of Python object not allowed without gil
+48:13: Assignment of Python object not allowed without gil
+48:16: Creating temporary Python reference not allowed without gil
+48:19: Creating temporary Python reference not allowed without gil
+49:11: Indexing Python object not allowed without gil
+49:11: Assignment of Python object not allowed without gil
+50:11: Accessing Python attribute not allowed without gil
+50:11: Assignment of Python object not allowed without gil
+51: 8: Constructing Python tuple not allowed without gil
+51: 8: Python print statement not allowed without gil
+52: 8: Deleting Python object not allowed without gil
+53: 8: Returning Python object not allowed without gil
+54: 8: Raising exception not allowed without gil
+55:14: Truth-testing Python object not allowed without gil
+57:17: Truth-testing Python object not allowed without gil
+59: 8: Converting to Python object not allowed without gil
+61: 8: Try-except statement not allowed without gil
+65: 8: Try-finally statement not allowed without gil
+"""
diff --git a/tests/errors/nogilfunctype.pyx b/tests/errors/nogilfunctype.pyx
new file mode 100644 (file)
index 0000000..617ed82
--- /dev/null
@@ -0,0 +1,9 @@
+cdef extern from *:
+    cdef void f() nogil
+    cdef void (*fp)()
+
+fp = f
+
+_ERRORS = u"""
+5:6: Cannot assign type 'void (void) nogil' to 'void (*)(void)'
+"""
index 02a42b742a77dcd88a00e6aea747dadd422ac9e3..8d1f8d366ca07e7337aa23c063ea1442b44c2816 100644 (file)
@@ -18,5 +18,5 @@ def g():
         h()
     return 1
 
-cdef int h() except -1:
+cdef int h() nogil except -1:
     pass