From 406674e1903ad2e15704c7a6a3e224120ff3366f Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 21 Apr 2010 15:36:27 +0200 Subject: [PATCH] generic way to wrap an ExprNode in a NoneCheckNode --- Cython/Compiler/ExprNodes.py | 84 ++++++++++++++++++++++++++++++++---- Cython/Compiler/Optimize.py | 30 ++++++------- 2 files changed, 89 insertions(+), 25 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index ef978585..4777fe48 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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 diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index 5add59cb..9cff706b 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -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( -- 2.26.2