Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions mypyc/lib-rt/list_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,19 +235,51 @@ void CPyList_SetItemUnsafe(PyObject *list, Py_ssize_t index, PyObject *value) {
PyList_SET_ITEM(list, index, value);
}

PyObject *CPyList_PopLast(PyObject *obj)
#ifdef Py_GIL_DISABLED
// The original optimized list.pop implementation doesn't work on free-threaded
// builds, so provide an alternative that is a bit slower but works.
//
// Note that this implementation isn't intended to be atomic.
static inline PyObject *list_pop_index(PyObject *list, Py_ssize_t index) {
PyObject *item = PyList_GetItemRef(list, index);
if (item == NULL) {
return NULL;
}
if (PySequence_DelItem(list, index) < 0) {
Py_DECREF(item);
return NULL;
}
return item;
}
#endif

PyObject *CPyList_PopLast(PyObject *list)
{
#ifdef Py_GIL_DISABLED
// The other implementation causes segfaults on a free-threaded Python 3.14b4 build.
Py_ssize_t index = PyList_GET_SIZE(list) - 1;
return list_pop_index(list, index);
#else
// I tried a specalized version of pop_impl for just removing the
// last element and it wasn't any faster in microbenchmarks than
// the generic one so I ditched it.
return list_pop_impl((PyListObject *)obj, -1);
return list_pop_impl((PyListObject *)list, -1);
#endif
}

PyObject *CPyList_Pop(PyObject *obj, CPyTagged index)
{
if (CPyTagged_CheckShort(index)) {
Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
#ifdef Py_GIL_DISABLED
// We must use a slower implementation on free-threaded builds.
if (n < 0) {
n += PyList_GET_SIZE(obj);
}
return list_pop_index(obj, n);
#else
return list_pop_impl((PyListObject *)obj, n);
#endif
} else {
PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
return NULL;
Expand Down
2 changes: 1 addition & 1 deletion mypyc/primitives/list_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
)

# list.pop(index)
list_pop = method_op(
method_op(
name="pop",
arg_types=[list_rprimitive, int_rprimitive],
return_type=object_rprimitive,
Expand Down
29 changes: 23 additions & 6 deletions mypyc/test-data/run-lists.test
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ print(primes(13))
\[0, 0, 1, 1]
\[0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1]

[case testListBuild]
[case testListPrimitives]
from testutil import assertRaises

def test_list_build() -> None:
# Currently LIST_BUILDING_EXPANSION_THRESHOLD equals to 10
# long list built by list_build_op
Expand All @@ -169,9 +171,6 @@ def test_list_build() -> None:
l3.append('a')
assert l3 == ['a']

[case testListPrims]
from typing import List

def test_append() -> None:
l = [1, 2]
l.append(10)
Expand All @@ -189,10 +188,28 @@ def test_pop_last() -> None:

def test_pop_index() -> None:
l = [1, 2, 10, 3]
l.pop(2)
assert l.pop(2) == 10
assert l == [1, 2, 3]
l.pop(-2)
assert l.pop(-2) == 2
assert l == [1, 3]
assert l.pop(-2) == 1
assert l.pop(0) == 3
assert l == []
l = [int() + 1000, int() + 1001, int() + 1002]
assert l.pop(0) == 1000
assert l.pop(-1) == 1002
assert l == [1001]

def test_pop_index_errors() -> None:
l = [int() + 1000]
with assertRaises(IndexError):
l.pop(1)
with assertRaises(IndexError):
l.pop(-2)
with assertRaises(OverflowError):
l.pop(1 << 100)
with assertRaises(OverflowError):
l.pop(-(1 << 100))

def test_count() -> None:
l = [1, 3]
Expand Down