[Python-checkins] gh-106529: Fix subtle Tier 2 edge case with list iterator (#106756)

gvanrossum webhook-mailer at python.org
Fri Jul 14 20:22:09 EDT 2023


https://github.com/python/cpython/commit/0db85eeba762e72f9f3c027e432cdebc627aac6c
commit: 0db85eeba762e72f9f3c027e432cdebc627aac6c
branch: main
author: Guido van Rossum <guido at python.org>
committer: gvanrossum <gvanrossum at gmail.com>
date: 2023-07-14T17:22:06-07:00
summary:

gh-106529: Fix subtle Tier 2 edge case with list iterator (#106756)

The Tier 2 opcode _IS_ITER_EXHAUSTED_LIST (and _TUPLE)
didn't set it->it_seq to NULL, causing a subtle bug
that resulted in test_exhausted_iterator in list_tests.py
to fail when running all tests with -Xuops.

The bug was introduced in gh-106696.

Added this as an explicit test.

Also fixed the dependencies for ceval.o -- it depends on executor_cases.c.h.

files:
M Lib/test/test_capi/test_misc.py
M Makefile.pre.in
M Python/bytecodes.c
M Python/executor_cases.c.h

diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index 43c04463236a2..6df918997b2b1 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -2649,6 +2649,19 @@ def testfunc(a):
         # Verification that the jump goes past END_FOR
         # is done by manual inspection of the output
 
+    def test_list_edge_case(self):
+        def testfunc(it):
+            for x in it:
+                pass
+
+        opt = _testinternalcapi.get_uop_optimizer()
+        with temporary_optimizer(opt):
+            a = [1, 2, 3]
+            it = iter(a)
+            testfunc(it)
+            a.append(4)
+            with self.assertRaises(StopIteration):
+                next(it)
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Makefile.pre.in b/Makefile.pre.in
index ddf524ac17d72..553b2aa480c18 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1564,6 +1564,7 @@ Python/ceval.o: \
 		$(srcdir)/Python/ceval_macros.h \
 		$(srcdir)/Python/condvar.h \
 		$(srcdir)/Python/generated_cases.c.h \
+		$(srcdir)/Python/executor_cases.c.h \
 		$(srcdir)/Include/internal/pycore_opcode_metadata.h \
 		$(srcdir)/Python/opcode_targets.h
 
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 15b48ae9d8267..3432b02771346 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2448,7 +2448,12 @@ dummy_func(
             _PyListIterObject *it = (_PyListIterObject *)iter;
             assert(Py_TYPE(iter) == &PyListIter_Type);
             PyListObject *seq = it->it_seq;
-            if (seq == NULL || it->it_index >= PyList_GET_SIZE(seq)) {
+            if (seq == NULL) {
+                exhausted = Py_True;
+            }
+            else if (it->it_index >= PyList_GET_SIZE(seq)) {
+                Py_DECREF(seq);
+                it->it_seq = NULL;
                 exhausted = Py_True;
             }
             else {
@@ -2499,7 +2504,12 @@ dummy_func(
             _PyTupleIterObject *it = (_PyTupleIterObject *)iter;
             assert(Py_TYPE(iter) == &PyTupleIter_Type);
             PyTupleObject *seq = it->it_seq;
-            if (seq == NULL || it->it_index >= PyTuple_GET_SIZE(seq)) {
+            if (seq == NULL) {
+                exhausted = Py_True;
+            }
+            else if (it->it_index >= PyTuple_GET_SIZE(seq)) {
+                Py_DECREF(seq);
+                it->it_seq = NULL;
                 exhausted = Py_True;
             }
             else {
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 626baece81460..ae21ffad94d80 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -1750,7 +1750,12 @@
             _PyListIterObject *it = (_PyListIterObject *)iter;
             assert(Py_TYPE(iter) == &PyListIter_Type);
             PyListObject *seq = it->it_seq;
-            if (seq == NULL || it->it_index >= PyList_GET_SIZE(seq)) {
+            if (seq == NULL) {
+                exhausted = Py_True;
+            }
+            else if (it->it_index >= PyList_GET_SIZE(seq)) {
+                Py_DECREF(seq);
+                it->it_seq = NULL;
                 exhausted = Py_True;
             }
             else {
@@ -1787,7 +1792,12 @@
             _PyTupleIterObject *it = (_PyTupleIterObject *)iter;
             assert(Py_TYPE(iter) == &PyTupleIter_Type);
             PyTupleObject *seq = it->it_seq;
-            if (seq == NULL || it->it_index >= PyTuple_GET_SIZE(seq)) {
+            if (seq == NULL) {
+                exhausted = Py_True;
+            }
+            else if (it->it_index >= PyTuple_GET_SIZE(seq)) {
+                Py_DECREF(seq);
+                it->it_seq = NULL;
                 exhausted = Py_True;
             }
             else {



More information about the Python-checkins mailing list