Commit 50de9e73 authored by Stefan Behnel's avatar Stefan Behnel

make list.pop() optimisation integer type safe

parent decd0bf5
...@@ -2503,7 +2503,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2503,7 +2503,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyObject_PopIndex_func_type = PyrexTypes.CFuncType( PyObject_PopIndex_func_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [ PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None), PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("index", PyrexTypes.c_long_type, None), PyrexTypes.CFuncTypeArg("index", PyrexTypes.c_py_ssize_t_type, None),
]) ])
def _handle_simple_method_list_pop(self, node, function, args, is_unbound_method): def _handle_simple_method_list_pop(self, node, function, args, is_unbound_method):
...@@ -2516,10 +2516,10 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2516,10 +2516,10 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
""" """
if not args: if not args:
return node return node
args = args[:] obj = args[0]
if is_list: if is_list:
type_name = 'List' type_name = 'List'
args[0] = args[0].as_none_safe_node( obj = obj.as_none_safe_node(
"'NoneType' object has no attribute '%s'", "'NoneType' object has no attribute '%s'",
error="PyExc_AttributeError", error="PyExc_AttributeError",
format_args=['pop']) format_args=['pop'])
...@@ -2529,24 +2529,33 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2529,24 +2529,33 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_Py%s_Pop" % type_name, node.pos, "__Pyx_Py%s_Pop" % type_name,
self.PyObject_Pop_func_type, self.PyObject_Pop_func_type,
args=args, args=[obj],
may_return_none=True, may_return_none=True,
is_temp=node.is_temp, is_temp=node.is_temp,
utility_code=load_c_utility('pop'), utility_code=load_c_utility('pop'),
) )
elif len(args) == 2: elif len(args) == 2:
index = unwrap_coerced_node(args[1]) index = unwrap_coerced_node(args[1])
orig_index_type = index.type
if not index.type.is_int:
if is_list or isinstance(index, ExprNodes.IntNode): if is_list or isinstance(index, ExprNodes.IntNode):
index = index.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env()) index = index.coerce_to(PyrexTypes.c_py_ssize_t_type, self.current_env())
if index.type.is_int: else:
widest = PyrexTypes.widest_numeric_type( return node
index.type, PyrexTypes.c_py_ssize_t_type) elif not PyrexTypes.numeric_type_fits(index.type, PyrexTypes.c_py_ssize_t_type):
if widest == PyrexTypes.c_py_ssize_t_type: return node
args[1] = index # real type might still be larger at runtime
if not orig_index_type.is_int:
orig_index_type = index.type
if not orig_index_type.create_to_py_utility_code(self.current_env()):
return node
convert_func = orig_index_type.to_py_function
conversion_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [PyrexTypes.CFuncTypeArg("intval", orig_index_type, None)])
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_Py%s_PopIndex" % type_name, node.pos, "__Pyx_Py%s_PopIndex" % type_name,
self.PyObject_PopIndex_func_type, self.PyObject_PopIndex_func_type,
args=args, args=[obj, index, ExprNodes.RawCNameExprNode(index.pos, conversion_type, convert_func)],
may_return_none=True, may_return_none=True,
is_temp=node.is_temp, is_temp=node.is_temp,
utility_code=load_c_utility("pop_index"), utility_code=load_c_utility("pop_index"),
......
...@@ -3672,6 +3672,7 @@ def merge_template_deductions(a, b): ...@@ -3672,6 +3672,7 @@ def merge_template_deductions(a, b):
all[param] = value all[param] = value
return all return all
def widest_numeric_type(type1, type2): def widest_numeric_type(type1, type2):
# Given two numeric types, return the narrowest type # Given two numeric types, return the narrowest type
# encompassing both of them. # encompassing both of them.
...@@ -3698,6 +3699,11 @@ def widest_numeric_type(type1, type2): ...@@ -3698,6 +3699,11 @@ def widest_numeric_type(type1, type2):
widest_type = type2 widest_type = type2
return widest_type return widest_type
def numeric_type_fits(small_type, large_type):
return widest_numeric_type(small_type, large_type) == large_type
def independent_spanning_type(type1, type2): def independent_spanning_type(type1, type2):
# Return a type assignable independently from both type1 and # Return a type assignable independently from both type1 and
# type2, but do not require any interoperability between the two. # type2, but do not require any interoperability between the two.
......
...@@ -110,25 +110,29 @@ static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) { ...@@ -110,25 +110,29 @@ static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) {
/////////////// pop_index.proto /////////////// /////////////// pop_index.proto ///////////////
#define __Pyx_PyObject_PopIndex(L, ix) (PyList_CheckExact(L) ? \ #define __Pyx_PyObject_PopIndex(L, ix, to_py_func) ( \
__Pyx_PyList_PopIndex(L, ix) : __Pyx__PyObject_PopIndex(L, ix)) (PyList_CheckExact(L) && likely(PY_SSIZE_T_MIN <= ix && ix <= PY_SSIZE_T_MAX)) ? \
__Pyx__PyList_PopIndex(L, ix) : __Pyx__PyObject_PopIndex(L, to_py_func(ix)))
static PyObject* __Pyx_PyList_PopIndex(PyObject* L, Py_ssize_t ix); /*proto*/ #define __Pyx_PyList_PopIndex(L, ix, to_py_func) ( \
static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, Py_ssize_t ix); /*proto*/ likely(PY_SSIZE_T_MIN <= ix && ix <= PY_SSIZE_T_MAX) ? \
__Pyx__PyList_PopIndex(L, ix) : __Pyx__PyObject_PopIndex(L, to_py_func(ix)))
static PyObject* __Pyx__PyList_PopIndex(PyObject* L, Py_ssize_t ix); /*proto*/
static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix); /*proto*/
/////////////// pop_index /////////////// /////////////// pop_index ///////////////
//@requires: ObjectHandling.c::PyObjectCallMethod //@requires: ObjectHandling.c::PyObjectCallMethod
static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, Py_ssize_t ix) { static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix) {
PyObject *r, *py_ix; PyObject *r;
py_ix = PyInt_FromSsize_t(ix); if (unlikely(!py_ix)) return NULL;
if (!py_ix) return NULL;
r = __Pyx_PyObject_CallMethod1(L, PYIDENT("pop"), py_ix); r = __Pyx_PyObject_CallMethod1(L, PYIDENT("pop"), py_ix);
Py_DECREF(py_ix); Py_DECREF(py_ix);
return r; return r;
} }
static PyObject* __Pyx_PyList_PopIndex(PyObject* L, Py_ssize_t ix) { static PyObject* __Pyx__PyList_PopIndex(PyObject* L, Py_ssize_t ix) {
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
Py_ssize_t size = PyList_GET_SIZE(L); Py_ssize_t size = PyList_GET_SIZE(L);
if (likely(size > (((PyListObject*)L)->allocated >> 1))) { if (likely(size > (((PyListObject*)L)->allocated >> 1))) {
...@@ -145,7 +149,7 @@ static PyObject* __Pyx_PyList_PopIndex(PyObject* L, Py_ssize_t ix) { ...@@ -145,7 +149,7 @@ static PyObject* __Pyx_PyList_PopIndex(PyObject* L, Py_ssize_t ix) {
} }
} }
#endif #endif
return __Pyx__PyObject_PopIndex(L, ix); return __Pyx__PyObject_PopIndex(L, PyInt_FromSsize_t(ix));
} }
......
cimport cython cimport cython
from libc.stdint cimport uint64_t
class A: class A:
def pop(self, *args): def pop(self, *args):
print args print args
...@@ -199,3 +201,17 @@ def crazy_pop(L): ...@@ -199,3 +201,17 @@ def crazy_pop(L):
(1, 2, 3) (1, 2, 3)
""" """
return L.pop(1, 2, 3) return L.pop(1, 2, 3)
def object_pop_large_int():
"""
>>> object_pop_large_int()
{}
"""
cdef object foo = {}
cdef uint64_t bar = 201213467776703617ULL
foo[bar] = None
assert (<object>bar) in foo
foo.pop(bar)
return foo
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