Skip to content
Closed
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
7 changes: 7 additions & 0 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,13 @@ All of the following opcodes use their arguments.
Works as :opcode:`BUILD_TUPLE`, but creates a list.


.. opcode:: BUILD_LIST_PREALLOC (var_num)

Creates a list with a pre-allocated size and pushes the resulting list onto the stack.
The size is the length-hint of the local ``co_varnames[var_num]``, if the length cannot
be established, it will default a length of 0.


.. opcode:: BUILD_SET (count)

Works as :opcode:`BUILD_TUPLE`, but creates a set.
Expand Down
2 changes: 1 addition & 1 deletion Include/listobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ PyAPI_FUNC(int) PyList_Reverse(PyObject *);
PyAPI_FUNC(PyObject *) PyList_AsTuple(PyObject *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *);

PyAPI_FUNC(PyObject *) _PyList_NewPrealloc(Py_ssize_t size);
PyAPI_FUNC(int) PyList_ClearFreeList(void);
PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out);
#endif
Expand Down
1 change: 1 addition & 0 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ def jabs_op(name, op):
def_op('BUILD_CONST_KEY_MAP', 156)
def_op('BUILD_STRING', 157)
def_op('BUILD_TUPLE_UNPACK_WITH_CALL', 158)
def_op('BUILD_LIST_PREALLOC', 159)

name_op('LOAD_METHOD', 160)
def_op('CALL_METHOD', 161)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def foo(x):

dis_nested_2 = """%s
Disassembly of <code object <listcomp> at 0x..., file "%s", line %d>:
%3d 0 BUILD_LIST 0
%3d 0 BUILD_LIST_PREALLOC 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 12 (to 18)
6 STORE_FAST 1 (z)
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_listcomps.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@
>>> test_func()
[2, 2, 2, 2, 2]

Verify that an overflow error is raised for listcomps with very-large iterators

>>> [y for y in range(2**256)] # doctest: +IGNORE_EXCEPTION_DETAIL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really weird to me that this module is tested entirely with doctests (though I know that's not part of this PR).

Traceback (most recent call last):
...
OverflowError: ...

"""


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Change the compiled bytecode for list comprehensions where there is no if statement. Allocates the size of the list to the length of the iterator in order to avoid having to continuously resize the list.
Also fixes a bug for a list comprehension containing an iterator with a potential size > PY_SSIZE_MAX, e.g. range(2**256).
6 changes: 6 additions & 0 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ list_new_prealloc(Py_ssize_t size)
return (PyObject *) op;
}

PyObject *
_PyList_NewPrealloc(Py_ssize_t size)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding a new function _PyList_NewPrealloc with identical functionality as list_new_prealloc, why not just rename list_new_prealloc to _PyList_NewPrealloc?

{
return list_new_prealloc(size);
}

Py_ssize_t
PyList_Size(PyObject *op)
{
Expand Down
15 changes: 15 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
}

case TARGET(LOAD_FAST): {
PREDICTED(LOAD_FAST);
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
format_exc_check_arg(PyExc_UnboundLocalError,
Expand Down Expand Up @@ -2484,6 +2485,20 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
DISPATCH();
}

case TARGET(BUILD_LIST_PREALLOC): {
PyObject *list, *target = GETLOCAL(oparg);
Py_INCREF(target);
Py_ssize_t size = PyObject_LengthHint(target, 0);
Py_DECREF(target);
if (size < 0)
goto error;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO this should raise a ValueError or something other than OverflowError with an error message for an average Python user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have raised that separately in #12720 as it's out-of-scope for this change and impact other things


list = _PyList_NewPrealloc(size);
PUSH(list);
PREDICT(LOAD_FAST);
DISPATCH();
}

case TARGET(BUILD_LIST): {
PyObject *list = PyList_New(oparg);
if (list == NULL)
Expand Down
7 changes: 6 additions & 1 deletion Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,8 @@ stack_effect(int opcode, int oparg, int jump)
case BUILD_SET:
case BUILD_STRING:
return 1-oparg;
case BUILD_LIST_PREALLOC:
return 1;
case BUILD_LIST_UNPACK:
case BUILD_TUPLE_UNPACK:
case BUILD_TUPLE_UNPACK_WITH_CALL:
Expand Down Expand Up @@ -4368,7 +4370,10 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
int op;
switch (type) {
case COMP_LISTCOMP:
op = BUILD_LIST;
if (asdl_seq_LEN(outermost->ifs) > 0)
op = BUILD_LIST;
else
op = BUILD_LIST_PREALLOC;
break;
case COMP_SETCOMP:
op = BUILD_SET;
Expand Down
2 changes: 1 addition & 1 deletion Python/opcode_targets.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.