class CallNode(ExprNode):
+ # allow overriding the default 'may_be_none' behaviour
+ may_return_none = None
+
+ def may_be_none(self):
+ if self.may_return_none is not None:
+ return self.may_return_none
+ return ExprNode.may_be_none(self)
+
def analyse_as_type_constructor(self, env):
type = self.function.analyse_as_type(env)
if type and type.is_struct_or_union:
else:
self.type = Builtin.builtin_types[function.entry.name]
self.result_ctype = py_object_type
+ self.may_return_none = False
elif function.is_name and function.type_entry:
# We are calling an extension type constructor. As
# long as we do not support __new__(), the result type
# is clear
self.type = function.type_entry.type
self.result_ctype = py_object_type
+ self.may_return_none = False
else:
self.type = py_object_type
self.is_temp = 1
class PythonCapiCallNode(SimpleCallNode):
# Python C-API Function call (only created in transforms)
+ # By default, we assume that the call never returns None, as this
+ # is true for most C-API functions in CPython. If this does not
+ # apply to a call, set the following to True (or None to inherit
+ # the default behaviour).
+ may_return_none = False
+
def __init__(self, pos, function_name, func_type,
utility_code = None, py_name=None, **kwargs):
self.type = func_type.return_type
# as we do not support __new__(), the result type is clear
self.type = function.type_entry.type
self.result_ctype = py_object_type
+ self.may_return_none = False
else:
self.type = py_object_type
self.is_temp = 1
type2 = self.operand2.infer_type(env)
return PyrexTypes.independent_spanning_type(type1, type2)
+ def may_be_none(self):
+ if self.operator == 'or':
+ return self.operand2.may_be_none()
+ else:
+ return self.operand1.may_be_none() or self.operand2.may_be_none()
+
def calculate_constant_result(self):
if self.operator == 'and':
self.constant_result = \
def __init__(self, arg, new_type):
CoercionNode.__init__(self, arg)
self.type = new_type
+
+ def may_be_none(self):
+ return self.arg.may_be_none()
def calculate_result_code(self):
return self.arg.result_as(self.type)
def analyse_types(self, env):
pass
+
+ def may_be_none(self):
+ if self.notnone:
+ return False
+ return self.arg.may_be_none()
def result_in_temp(self):
return self.arg.result_in_temp()
return ExprNodes.PythonCapiCallNode(
node.pos, "PyObject_GetAttr", self.PyObject_GetAttr2_func_type,
args = pos_args,
+ may_return_none = True,
is_temp = node.is_temp)
elif len(pos_args) == 3:
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_GetAttr3", self.PyObject_GetAttr3_func_type,
args = pos_args,
+ may_return_none = True,
is_temp = node.is_temp,
utility_code = Builtin.getattr3_utility_code)
else:
return ExprNodes.PythonCapiCallNode(
node.pos, "PyObject_GetIter", self.PyObject_GetIter_func_type,
args = pos_args,
+ may_return_none = True,
is_temp = node.is_temp)
elif len(pos_args) == 2:
return ExprNodes.PythonCapiCallNode(
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_PyObject_Append", self.PyObject_Append_func_type,
args = args,
+ may_return_none = True,
is_temp = node.is_temp,
utility_code = append_utility_code
)
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_PyObject_Pop", self.PyObject_Pop_func_type,
args = args,
+ may_return_none = True,
is_temp = node.is_temp,
utility_code = pop_utility_code
)
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_PyObject_PopIndex", self.PyObject_PopIndex_func_type,
args = args,
+ may_return_none = True,
is_temp = node.is_temp,
utility_code = pop_index_utility_code
)
return self._substitute_method_call(
node, "__Pyx_PyDict_GetItemDefault", self.Pyx_PyDict_GetItem_func_type,
'get', is_unbound_method, args,
+ may_return_none = True,
utility_code = dict_getitem_default_utility_code)
def _substitute_method_call(self, node, name, func_type,
attr_name, is_unbound_method, args=(),
- utility_code=None):
+ utility_code=None,
+ may_return_none=ExprNodes.PythonCapiCallNode.may_return_none):
args = list(args)
if args and not args[0].is_literal:
self_arg = args[0]
node.pos, name, func_type,
args = args,
is_temp = node.is_temp,
- utility_code = utility_code
+ utility_code = utility_code,
+ may_return_none = may_return_none,
)
def _inject_int_default_argument(self, node, args, arg_index, type, default_value):
just before the C code generation phase.
The optimizations currently implemented in this class are:
- - Eliminate None assignment and refcounting for first assignment.
+ - eliminate None assignment and refcounting for first assignment.
- isinstance -> typecheck for cdef types
+ - eliminate checks for None and/or types that became redundant after tree changes
"""
def visit_SingleAssignmentNode(self, node):
"""Avoid redundant initialisation of local variables before their
PyTypeObjectPtr = PyrexTypes.CPtrType(utility_scope.lookup('PyTypeObject').type)
node.args[1] = ExprNodes.CastNode(node.args[1], PyTypeObjectPtr)
return node
+
+ def visit_PyTypeTestNode(self, node):
+ """Remove tests for alternatively allowed None values from
+ type tests when we know that the argument cannot be None
+ anyway.
+ """
+ self.visitchildren(node)
+ if not node.notnone:
+ if not node.arg.may_be_none():
+ node.notnone = True
+ return node