generic way to wrap an ExprNode in a NoneCheckNode
authorStefan Behnel <scoder@users.berlios.de>
Wed, 21 Apr 2010 13:36:27 +0000 (15:36 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Wed, 21 Apr 2010 13:36:27 +0000 (15:36 +0200)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Optimize.py

index ef978585f5e71ac86519038e36315003dd294fd9..4777fe48a30cbdae609c796f985c4aabb978c749 100755 (executable)
@@ -636,10 +636,22 @@ class ExprNode(Node):
         #  a constant, local var, C global var, struct member
         #  reference, or temporary.
         return self.result_in_temp()
-        
+
+    def may_be_none(self):
+        return self.type.is_pyobject
+
     def as_cython_attribute(self):
         return None
 
+    def as_none_safe_node(self, error, message):
+        # Wraps the node in a NoneCheckNode if it is not known to be
+        # not-None (e.g. because it is a Python literal).
+        if self.may_be_none():
+            return NoneCheckNode(self, error, message)
+        else:
+            return self
+
+
 class AtomicExprNode(ExprNode):
     #  Abstract base class for expression nodes which have
     #  no sub-expressions.
@@ -660,7 +672,10 @@ class PyConstNode(AtomicExprNode):
     
     def is_simple(self):
         return 1
-    
+
+    def may_be_none(self):
+        return False
+
     def analyse_types(self, env):
         pass
     
@@ -682,7 +697,11 @@ class NoneNode(PyConstNode):
 
     def compile_time_value(self, denv):
         return None
-    
+
+    def may_be_none(self):
+        return True
+
+
 class EllipsisNode(PyConstNode):
     #  '...' in a subscript list.
     
@@ -704,7 +723,10 @@ class ConstNode(AtomicExprNode):
 
     def is_simple(self):
         return 1
-    
+
+    def may_be_none(self):
+        return False
+
     def analyse_types(self, env):
         pass # Types are held in class variables
     
@@ -1012,6 +1034,9 @@ class LongNode(AtomicExprNode):
     def analyse_types(self, env):
         self.is_temp = 1
 
+    def may_be_none(self):
+        return False
+
     gil_message = "Constructing Python long int"
 
     def generate_result_code(self, code):
@@ -1039,6 +1064,9 @@ class ImagNode(AtomicExprNode):
     def analyse_types(self, env):
         self.type.create_declaration_utility_code(env)
 
+    def may_be_none(self):
+        return False
+
     def coerce_to(self, dst_type, env):
         if self.type is dst_type:
             return self
@@ -1098,7 +1126,10 @@ class NewExprNode(AtomicExprNode):
     def analyse_types(self, env):
         if self.type is None:
             self.infer_type(env)
-   
+
+    def may_be_none(self):
+        return False
+
     def generate_result_code(self, code):
         pass
    
@@ -2958,6 +2989,9 @@ class AsTupleNode(ExprNode):
         self.type = tuple_type
         self.is_temp = 1
 
+    def may_be_none(self):
+        return False
+
     nogil_check = Node.gil_error
     gil_message = "Constructing Python tuple"
 
@@ -3434,6 +3468,9 @@ class SequenceNode(ExprNode):
         self.type = py_object_type
         self.is_temp = 1
 
+    def may_be_none(self):
+        return False
+
     def analyse_target_types(self, env):
         self.iterator = PyTempNode(self.pos, env)
         self.unpacked_items = []
@@ -3817,6 +3854,9 @@ class ComprehensionNode(ExprNode):
         self.type = self.target.type
         self.loop.analyse_expressions(env)
 
+    def may_be_none(self):
+        return False
+
     def calculate_result_code(self):
         return self.target.result()
     
@@ -3897,6 +3937,9 @@ class SetNode(ExprNode):
         self.type = set_type
         self.is_temp = 1
 
+    def may_be_none(self):
+        return False
+
     def calculate_constant_result(self):
         self.constant_result = set([
                 arg.constant_result for arg in self.args])
@@ -3964,6 +4007,9 @@ class DictNode(ExprNode):
             item.analyse_types(env)
         self.obj_conversion_errors = held_errors()
         release_errors(ignore=True)
+
+    def may_be_none(self):
+        return False
         
     def coerce_to(self, dst_type, env):
         if dst_type.is_pyobject:
@@ -4094,6 +4140,9 @@ class ClassNode(ExprNode):
         self.is_temp = 1
         env.use_utility_code(create_class_utility_code);
 
+    def may_be_none(self):
+        return False
+
     gil_message = "Constructing Python class"
 
     def generate_result_code(self, code):
@@ -4129,6 +4178,9 @@ class UnboundMethodNode(ExprNode):
     def analyse_types(self, env):
         self.function.analyse_types(env)
 
+    def may_be_none(self):
+        return False
+
     gil_message = "Constructing an unbound method"
 
     def generate_result_code(self, code):
@@ -4154,6 +4206,9 @@ class PyCFunctionNode(AtomicExprNode):
     
     def analyse_types(self, env):
         pass
+
+    def may_be_none(self):
+        return False
     
     gil_message = "Constructing Python function"
 
@@ -4675,7 +4730,10 @@ class TypeofNode(ExprNode):
             self.pos, value=StringEncoding.EncodedString(str(self.operand.type)))
         self.literal.analyse_types(env)
         self.literal = self.literal.coerce_to_pyobject(env)
-    
+
+    def may_be_none(self):
+        return False
+
     def generate_evaluation_code(self, code):
         self.literal.generate_evaluation_code(code)
     
@@ -5756,8 +5814,8 @@ class PrimaryCmpNode(ExprNode, CmpNode):
                         self.operand2 = self.operand2.coerce_to(bytes_type, env)
                     env.use_utility_code(char_in_bytes_utility_code)
                 if not isinstance(self.operand2, (UnicodeNode, BytesNode)):
-                    self.operand2 = NoneCheckNode(
-                        self.operand2, "PyExc_TypeError",
+                    self.operand2 = self.operand2.as_none_safe_node(
+                        "PyExc_TypeError",
                         "argument of type 'NoneType' is not iterable")
             else:
                 common_type = py_object_type
@@ -6087,6 +6145,9 @@ class NoneCheckNode(CoercionNode):
     def analyse_types(self, env):
         pass
 
+    def may_be_none(self):
+        return False
+
     def result_in_temp(self):
         return self.arg.result_in_temp()
 
@@ -6129,6 +6190,10 @@ class CoerceToPyTypeNode(CoercionNode):
 
     gil_message = "Converting to Python object"
 
+    def may_be_none(self):
+        # FIXME: is this always safe?
+        return False
+
     def coerce_to_boolean(self, env):
         return self.arg.coerce_to_boolean(env).coerce_to_temp(env)
     
@@ -6351,6 +6416,9 @@ class ModuleRefNode(ExprNode):
     def analyse_types(self, env):
         pass
 
+    def may_be_none(self):
+        return False
+
     def calculate_result_code(self):
         return Naming.module_cname
 
index 5add59cb59ea9601e02beb3284e9684f999fe352..9cff706b2cff1a16ed21a6c4d47461517c77887a 100644 (file)
@@ -173,8 +173,7 @@ class IterationTransform(Visitor.VisitorTransform):
             return node
 
         unpack_temp_node = UtilNodes.LetRefNode(
-            ExprNodes.NoneCheckNode(
-                slice_node, "PyExc_TypeError", "'NoneType' is not iterable"))
+            slice_node.as_none_safe_node("PyExc_TypeError", "'NoneType' is not iterable"))
 
         slice_base_node = ExprNodes.PythonCapiCallNode(
             slice_node.pos, unpack_func, unpack_func_type,
@@ -1313,8 +1312,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return node
         arg = pos_args[0]
         if arg.type is Builtin.dict_type:
-            arg = ExprNodes.NoneCheckNode(
-                arg, "PyExc_TypeError", "'NoneType' is not iterable")
+            arg = arg.as_none_safe_node("PyExc_TypeError", "'NoneType' is not iterable")
             return ExprNodes.PythonCapiCallNode(
                 node.pos, "PyDict_Copy", self.PyDict_Copy_func_type,
                 args = [arg],
@@ -1337,9 +1335,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return node
         if not isinstance(list_arg, (ExprNodes.ComprehensionNode,
                                      ExprNodes.ListNode)):
-            pos_args[0] = ExprNodes.NoneCheckNode(
-                list_arg, "PyExc_TypeError",
-                "'NoneType' object is not iterable")
+            pos_args[0] = list_arg.as_none_safe_node(
+                "PyExc_TypeError", "'NoneType' object is not iterable")
 
         return ExprNodes.PythonCapiCallNode(
             node.pos, "PyList_AsTuple", self.PyList_AsTuple_func_type,
@@ -1499,9 +1496,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             if cfunc_name is None:
                 return node
             if not arg.is_literal:
-                arg = ExprNodes.NoneCheckNode(
-                    arg, "PyExc_TypeError",
-                    "object of type 'NoneType' has no len()")
+                arg = arg.as_none_safe_node(
+                    "PyExc_TypeError", "object of type 'NoneType' has no len()")
             new_node = ExprNodes.PythonCapiCallNode(
                 node.pos, cfunc_name, self.PyObject_Size_func_type,
                 args = [arg],
@@ -1564,8 +1560,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
 
         if not type_arg.type_entry:
             # arbitrary variable, needs a None check for safety
-            type_arg = ExprNodes.NoneCheckNode(
-                type_arg, "PyExc_TypeError",
+            type_arg = type_arg.as_none_safe_node(
+                "PyExc_TypeError",
                 "object.__new__(X): X is not a type object (NoneType)")
 
         return ExprNodes.PythonCapiCallNode(
@@ -2126,13 +2122,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
         if args and not args[0].is_literal:
             self_arg = args[0]
             if is_unbound_method:
-                self_arg = ExprNodes.NoneCheckNode(
-                    self_arg, "PyExc_TypeError",
+                self_arg = self_arg.as_none_safe_node(
+                    "PyExc_TypeError",
                     "descriptor '%s' requires a '%s' object but received a 'NoneType'" % (
-                    attr_name, node.function.obj.name))
+                        attr_name, node.function.obj.name))
             else:
-                self_arg = ExprNodes.NoneCheckNode(
-                    self_arg, "PyExc_AttributeError",
+                self_arg = self_arg.as_none_safe_node(
+                    "PyExc_AttributeError",
                     "'NoneType' object has no attribute '%s'" % attr_name)
             args[0] = self_arg
         return ExprNodes.PythonCapiCallNode(