Commit 406674e1 authored by Stefan Behnel's avatar Stefan Behnel

generic way to wrap an ExprNode in a NoneCheckNode

parent d1b5807f
...@@ -637,9 +637,21 @@ class ExprNode(Node): ...@@ -637,9 +637,21 @@ class ExprNode(Node):
# reference, or temporary. # reference, or temporary.
return self.result_in_temp() return self.result_in_temp()
def may_be_none(self):
return self.type.is_pyobject
def as_cython_attribute(self): def as_cython_attribute(self):
return None 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): class AtomicExprNode(ExprNode):
# Abstract base class for expression nodes which have # Abstract base class for expression nodes which have
# no sub-expressions. # no sub-expressions.
...@@ -661,6 +673,9 @@ class PyConstNode(AtomicExprNode): ...@@ -661,6 +673,9 @@ class PyConstNode(AtomicExprNode):
def is_simple(self): def is_simple(self):
return 1 return 1
def may_be_none(self):
return False
def analyse_types(self, env): def analyse_types(self, env):
pass pass
...@@ -683,6 +698,10 @@ class NoneNode(PyConstNode): ...@@ -683,6 +698,10 @@ class NoneNode(PyConstNode):
def compile_time_value(self, denv): def compile_time_value(self, denv):
return None return None
def may_be_none(self):
return True
class EllipsisNode(PyConstNode): class EllipsisNode(PyConstNode):
# '...' in a subscript list. # '...' in a subscript list.
...@@ -705,6 +724,9 @@ class ConstNode(AtomicExprNode): ...@@ -705,6 +724,9 @@ class ConstNode(AtomicExprNode):
def is_simple(self): def is_simple(self):
return 1 return 1
def may_be_none(self):
return False
def analyse_types(self, env): def analyse_types(self, env):
pass # Types are held in class variables pass # Types are held in class variables
...@@ -1012,6 +1034,9 @@ class LongNode(AtomicExprNode): ...@@ -1012,6 +1034,9 @@ class LongNode(AtomicExprNode):
def analyse_types(self, env): def analyse_types(self, env):
self.is_temp = 1 self.is_temp = 1
def may_be_none(self):
return False
gil_message = "Constructing Python long int" gil_message = "Constructing Python long int"
def generate_result_code(self, code): def generate_result_code(self, code):
...@@ -1039,6 +1064,9 @@ class ImagNode(AtomicExprNode): ...@@ -1039,6 +1064,9 @@ class ImagNode(AtomicExprNode):
def analyse_types(self, env): def analyse_types(self, env):
self.type.create_declaration_utility_code(env) self.type.create_declaration_utility_code(env)
def may_be_none(self):
return False
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
if self.type is dst_type: if self.type is dst_type:
return self return self
...@@ -1099,6 +1127,9 @@ class NewExprNode(AtomicExprNode): ...@@ -1099,6 +1127,9 @@ class NewExprNode(AtomicExprNode):
if self.type is None: if self.type is None:
self.infer_type(env) self.infer_type(env)
def may_be_none(self):
return False
def generate_result_code(self, code): def generate_result_code(self, code):
pass pass
...@@ -2958,6 +2989,9 @@ class AsTupleNode(ExprNode): ...@@ -2958,6 +2989,9 @@ class AsTupleNode(ExprNode):
self.type = tuple_type self.type = tuple_type
self.is_temp = 1 self.is_temp = 1
def may_be_none(self):
return False
nogil_check = Node.gil_error nogil_check = Node.gil_error
gil_message = "Constructing Python tuple" gil_message = "Constructing Python tuple"
...@@ -3434,6 +3468,9 @@ class SequenceNode(ExprNode): ...@@ -3434,6 +3468,9 @@ class SequenceNode(ExprNode):
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
def may_be_none(self):
return False
def analyse_target_types(self, env): def analyse_target_types(self, env):
self.iterator = PyTempNode(self.pos, env) self.iterator = PyTempNode(self.pos, env)
self.unpacked_items = [] self.unpacked_items = []
...@@ -3817,6 +3854,9 @@ class ComprehensionNode(ExprNode): ...@@ -3817,6 +3854,9 @@ class ComprehensionNode(ExprNode):
self.type = self.target.type self.type = self.target.type
self.loop.analyse_expressions(env) self.loop.analyse_expressions(env)
def may_be_none(self):
return False
def calculate_result_code(self): def calculate_result_code(self):
return self.target.result() return self.target.result()
...@@ -3897,6 +3937,9 @@ class SetNode(ExprNode): ...@@ -3897,6 +3937,9 @@ class SetNode(ExprNode):
self.type = set_type self.type = set_type
self.is_temp = 1 self.is_temp = 1
def may_be_none(self):
return False
def calculate_constant_result(self): def calculate_constant_result(self):
self.constant_result = set([ self.constant_result = set([
arg.constant_result for arg in self.args]) arg.constant_result for arg in self.args])
...@@ -3965,6 +4008,9 @@ class DictNode(ExprNode): ...@@ -3965,6 +4008,9 @@ class DictNode(ExprNode):
self.obj_conversion_errors = held_errors() self.obj_conversion_errors = held_errors()
release_errors(ignore=True) release_errors(ignore=True)
def may_be_none(self):
return False
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
if dst_type.is_pyobject: if dst_type.is_pyobject:
self.release_errors() self.release_errors()
...@@ -4094,6 +4140,9 @@ class ClassNode(ExprNode): ...@@ -4094,6 +4140,9 @@ class ClassNode(ExprNode):
self.is_temp = 1 self.is_temp = 1
env.use_utility_code(create_class_utility_code); env.use_utility_code(create_class_utility_code);
def may_be_none(self):
return False
gil_message = "Constructing Python class" gil_message = "Constructing Python class"
def generate_result_code(self, code): def generate_result_code(self, code):
...@@ -4129,6 +4178,9 @@ class UnboundMethodNode(ExprNode): ...@@ -4129,6 +4178,9 @@ class UnboundMethodNode(ExprNode):
def analyse_types(self, env): def analyse_types(self, env):
self.function.analyse_types(env) self.function.analyse_types(env)
def may_be_none(self):
return False
gil_message = "Constructing an unbound method" gil_message = "Constructing an unbound method"
def generate_result_code(self, code): def generate_result_code(self, code):
...@@ -4155,6 +4207,9 @@ class PyCFunctionNode(AtomicExprNode): ...@@ -4155,6 +4207,9 @@ class PyCFunctionNode(AtomicExprNode):
def analyse_types(self, env): def analyse_types(self, env):
pass pass
def may_be_none(self):
return False
gil_message = "Constructing Python function" gil_message = "Constructing Python function"
def generate_result_code(self, code): def generate_result_code(self, code):
...@@ -4676,6 +4731,9 @@ class TypeofNode(ExprNode): ...@@ -4676,6 +4731,9 @@ class TypeofNode(ExprNode):
self.literal.analyse_types(env) self.literal.analyse_types(env)
self.literal = self.literal.coerce_to_pyobject(env) self.literal = self.literal.coerce_to_pyobject(env)
def may_be_none(self):
return False
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
self.literal.generate_evaluation_code(code) self.literal.generate_evaluation_code(code)
...@@ -5756,8 +5814,8 @@ class PrimaryCmpNode(ExprNode, CmpNode): ...@@ -5756,8 +5814,8 @@ class PrimaryCmpNode(ExprNode, CmpNode):
self.operand2 = self.operand2.coerce_to(bytes_type, env) self.operand2 = self.operand2.coerce_to(bytes_type, env)
env.use_utility_code(char_in_bytes_utility_code) env.use_utility_code(char_in_bytes_utility_code)
if not isinstance(self.operand2, (UnicodeNode, BytesNode)): if not isinstance(self.operand2, (UnicodeNode, BytesNode)):
self.operand2 = NoneCheckNode( self.operand2 = self.operand2.as_none_safe_node(
self.operand2, "PyExc_TypeError", "PyExc_TypeError",
"argument of type 'NoneType' is not iterable") "argument of type 'NoneType' is not iterable")
else: else:
common_type = py_object_type common_type = py_object_type
...@@ -6087,6 +6145,9 @@ class NoneCheckNode(CoercionNode): ...@@ -6087,6 +6145,9 @@ class NoneCheckNode(CoercionNode):
def analyse_types(self, env): def analyse_types(self, env):
pass pass
def may_be_none(self):
return False
def result_in_temp(self): def result_in_temp(self):
return self.arg.result_in_temp() return self.arg.result_in_temp()
...@@ -6129,6 +6190,10 @@ class CoerceToPyTypeNode(CoercionNode): ...@@ -6129,6 +6190,10 @@ class CoerceToPyTypeNode(CoercionNode):
gil_message = "Converting to Python object" gil_message = "Converting to Python object"
def may_be_none(self):
# FIXME: is this always safe?
return False
def coerce_to_boolean(self, env): def coerce_to_boolean(self, env):
return self.arg.coerce_to_boolean(env).coerce_to_temp(env) return self.arg.coerce_to_boolean(env).coerce_to_temp(env)
...@@ -6351,6 +6416,9 @@ class ModuleRefNode(ExprNode): ...@@ -6351,6 +6416,9 @@ class ModuleRefNode(ExprNode):
def analyse_types(self, env): def analyse_types(self, env):
pass pass
def may_be_none(self):
return False
def calculate_result_code(self): def calculate_result_code(self):
return Naming.module_cname return Naming.module_cname
......
...@@ -173,8 +173,7 @@ class IterationTransform(Visitor.VisitorTransform): ...@@ -173,8 +173,7 @@ class IterationTransform(Visitor.VisitorTransform):
return node return node
unpack_temp_node = UtilNodes.LetRefNode( unpack_temp_node = UtilNodes.LetRefNode(
ExprNodes.NoneCheckNode( slice_node.as_none_safe_node("PyExc_TypeError", "'NoneType' is not iterable"))
slice_node, "PyExc_TypeError", "'NoneType' is not iterable"))
slice_base_node = ExprNodes.PythonCapiCallNode( slice_base_node = ExprNodes.PythonCapiCallNode(
slice_node.pos, unpack_func, unpack_func_type, slice_node.pos, unpack_func, unpack_func_type,
...@@ -1313,8 +1312,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1313,8 +1312,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
return node return node
arg = pos_args[0] arg = pos_args[0]
if arg.type is Builtin.dict_type: if arg.type is Builtin.dict_type:
arg = ExprNodes.NoneCheckNode( arg = arg.as_none_safe_node("PyExc_TypeError", "'NoneType' is not iterable")
arg, "PyExc_TypeError", "'NoneType' is not iterable")
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "PyDict_Copy", self.PyDict_Copy_func_type, node.pos, "PyDict_Copy", self.PyDict_Copy_func_type,
args = [arg], args = [arg],
...@@ -1337,9 +1335,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1337,9 +1335,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
return node return node
if not isinstance(list_arg, (ExprNodes.ComprehensionNode, if not isinstance(list_arg, (ExprNodes.ComprehensionNode,
ExprNodes.ListNode)): ExprNodes.ListNode)):
pos_args[0] = ExprNodes.NoneCheckNode( pos_args[0] = list_arg.as_none_safe_node(
list_arg, "PyExc_TypeError", "PyExc_TypeError", "'NoneType' object is not iterable")
"'NoneType' object is not iterable")
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "PyList_AsTuple", self.PyList_AsTuple_func_type, node.pos, "PyList_AsTuple", self.PyList_AsTuple_func_type,
...@@ -1499,9 +1496,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1499,9 +1496,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
if cfunc_name is None: if cfunc_name is None:
return node return node
if not arg.is_literal: if not arg.is_literal:
arg = ExprNodes.NoneCheckNode( arg = arg.as_none_safe_node(
arg, "PyExc_TypeError", "PyExc_TypeError", "object of type 'NoneType' has no len()")
"object of type 'NoneType' has no len()")
new_node = ExprNodes.PythonCapiCallNode( new_node = ExprNodes.PythonCapiCallNode(
node.pos, cfunc_name, self.PyObject_Size_func_type, node.pos, cfunc_name, self.PyObject_Size_func_type,
args = [arg], args = [arg],
...@@ -1564,8 +1560,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1564,8 +1560,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
if not type_arg.type_entry: if not type_arg.type_entry:
# arbitrary variable, needs a None check for safety # arbitrary variable, needs a None check for safety
type_arg = ExprNodes.NoneCheckNode( type_arg = type_arg.as_none_safe_node(
type_arg, "PyExc_TypeError", "PyExc_TypeError",
"object.__new__(X): X is not a type object (NoneType)") "object.__new__(X): X is not a type object (NoneType)")
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
...@@ -2126,13 +2122,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -2126,13 +2122,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
if args and not args[0].is_literal: if args and not args[0].is_literal:
self_arg = args[0] self_arg = args[0]
if is_unbound_method: if is_unbound_method:
self_arg = ExprNodes.NoneCheckNode( self_arg = self_arg.as_none_safe_node(
self_arg, "PyExc_TypeError", "PyExc_TypeError",
"descriptor '%s' requires a '%s' object but received a 'NoneType'" % ( "descriptor '%s' requires a '%s' object but received a 'NoneType'" % (
attr_name, node.function.obj.name)) attr_name, node.function.obj.name))
else: else:
self_arg = ExprNodes.NoneCheckNode( self_arg = self_arg.as_none_safe_node(
self_arg, "PyExc_AttributeError", "PyExc_AttributeError",
"'NoneType' object has no attribute '%s'" % attr_name) "'NoneType' object has no attribute '%s'" % attr_name)
args[0] = self_arg args[0] = self_arg
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment