Skip to content

Commit c215289

Browse files
markshannonshihai1991
authored andcommitted
bpo-39320: Handle unpacking of *values in compiler (pythonGH-17984)
* Add three new bytecodes: LIST_TO_TUPLE, LIST_EXTEND, SET_UPDATE. Use them to implement star unpacking expressions. * Remove four bytecodes BUILD_LIST_UNPACK, BUILD_TUPLE_UNPACK, BUILD_SET_UNPACK and BUILD_TUPLE_UNPACK_WITH_CALL opcodes as they are now unused. * Update magic number and dis.rst for new bytecodes.
1 parent 9b8cd78 commit c215289

File tree

10 files changed

+2336
-2347
lines changed

10 files changed

+2336
-2347
lines changed

Doc/library/dis.rst

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -859,40 +859,25 @@ All of the following opcodes use their arguments.
859859
.. versionadded:: 3.6
860860

861861

862-
.. opcode:: BUILD_TUPLE_UNPACK (count)
862+
.. opcode:: LIST_TO_TUPLE
863863

864-
Pops *count* iterables from the stack, joins them in a single tuple,
865-
and pushes the result. Implements iterable unpacking in tuple
866-
displays ``(*x, *y, *z)``.
864+
Pops a list from the stack and pushes a tuple containing the same values.
867865

868-
.. versionadded:: 3.5
869-
870-
871-
.. opcode:: BUILD_TUPLE_UNPACK_WITH_CALL (count)
872-
873-
This is similar to :opcode:`BUILD_TUPLE_UNPACK`,
874-
but is used for ``f(*x, *y, *z)`` call syntax. The stack item at position
875-
``count + 1`` should be the corresponding callable ``f``.
876-
877-
.. versionadded:: 3.6
866+
.. versionadded:: 3.9
878867

879868

880-
.. opcode:: BUILD_LIST_UNPACK (count)
869+
.. opcode:: LIST_EXTEND (i)
881870

882-
This is similar to :opcode:`BUILD_TUPLE_UNPACK`, but pushes a list
883-
instead of tuple. Implements iterable unpacking in list
884-
displays ``[*x, *y, *z]``.
871+
Calls ``list.extend(TOS1[-i], TOS)``. Used to build lists.
885872

886-
.. versionadded:: 3.5
873+
.. versionadded:: 3.9
887874

888875

889-
.. opcode:: BUILD_SET_UNPACK (count)
876+
.. opcode:: SET_UPDATE
890877

891-
This is similar to :opcode:`BUILD_TUPLE_UNPACK`, but pushes a set
892-
instead of tuple. Implements iterable unpacking in set
893-
displays ``{*x, *y, *z}``.
878+
Calls ``set.update(TOS1[-i], TOS)``. Used to build sets.
894879

895-
.. versionadded:: 3.5
880+
.. versionadded:: 3.9
896881

897882

898883
.. opcode:: BUILD_MAP_UNPACK (count)
@@ -1124,10 +1109,6 @@ All of the following opcodes use their arguments.
11241109
Calls a callable object with variable set of positional and keyword
11251110
arguments. If the lowest bit of *flags* is set, the top of the stack
11261111
contains a mapping object containing additional keyword arguments.
1127-
Below that is an iterable object containing positional arguments and
1128-
a callable object to call. :opcode:`BUILD_MAP_UNPACK_WITH_CALL` and
1129-
:opcode:`BUILD_TUPLE_UNPACK_WITH_CALL` can be used for merging multiple
1130-
mapping objects and iterables containing arguments.
11311112
Before the callable is called, the mapping object and iterable object
11321113
are each "unpacked" and their contents passed in as keyword and
11331114
positional arguments respectively.

Include/opcode.h

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ def _write_atomic(path, data, mode=0o666):
275275
# Python 3.9a0 3421 (simplified bytecode for with blocks #32949)
276276
# Python 3.9a0 3422 (remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY, POP_FINALLY bytecodes #33387)
277277
# Python 3.9a2 3423 (add IS_OP, CONTAINS_OP and JUMP_IF_NOT_EXC_MATCH bytecodes #39156)
278+
# Python 3.9a2 3424 (simplify bytecodes for *value unpacking)
279+
278280
#
279281
# MAGIC must change whenever the bytecode emitted by the compiler may no
280282
# longer be understood by older implementations of the eval loop (usually
@@ -283,7 +285,7 @@ def _write_atomic(path, data, mode=0o666):
283285
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
284286
# in PC/launcher.c must also be updated.
285287

286-
MAGIC_NUMBER = (3423).to_bytes(2, 'little') + b'\r\n'
288+
MAGIC_NUMBER = (3424).to_bytes(2, 'little') + b'\r\n'
287289
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
288290

289291
_PYCACHE = '__pycache__'

Lib/opcode.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def jabs_op(name, op):
117117
def_op('INPLACE_XOR', 78)
118118
def_op('INPLACE_OR', 79)
119119

120+
def_op('LIST_TO_TUPLE', 82)
120121
def_op('RETURN_VALUE', 83)
121122
def_op('IMPORT_STAR', 84)
122123
def_op('SETUP_ANNOTATIONS', 85)
@@ -199,20 +200,19 @@ def jabs_op(name, op):
199200
def_op('EXTENDED_ARG', 144)
200201
EXTENDED_ARG = 144
201202

202-
def_op('BUILD_LIST_UNPACK', 149)
203203
def_op('BUILD_MAP_UNPACK', 150)
204204
def_op('BUILD_MAP_UNPACK_WITH_CALL', 151)
205-
def_op('BUILD_TUPLE_UNPACK', 152)
206-
def_op('BUILD_SET_UNPACK', 153)
207205

208206
jrel_op('SETUP_ASYNC_WITH', 154)
209207

210208
def_op('FORMAT_VALUE', 155)
211209
def_op('BUILD_CONST_KEY_MAP', 156)
212210
def_op('BUILD_STRING', 157)
213-
def_op('BUILD_TUPLE_UNPACK_WITH_CALL', 158)
214211

215212
name_op('LOAD_METHOD', 160)
216213
def_op('CALL_METHOD', 161)
217214

215+
def_op('LIST_EXTEND', 162)
216+
def_op('SET_UPDATE', 163)
217+
218218
del def_op, name_op, jrel_op, jabs_op

Lib/test/test_extcall.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,12 @@
252252
>>> h(1, *h)
253253
Traceback (most recent call last):
254254
...
255-
TypeError: test.test_extcall.h() argument after * must be an iterable, not function
255+
TypeError: Value after * must be an iterable, not function
256256
257257
>>> h(*[1], *h)
258258
Traceback (most recent call last):
259259
...
260-
TypeError: test.test_extcall.h() argument after * must be an iterable, not function
260+
TypeError: Value after * must be an iterable, not function
261261
262262
>>> dir(*h)
263263
Traceback (most recent call last):
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Replace four complex bytecodes for building sequences with three simpler ones.
2+
3+
4+
The following four bytecodes have been removed:
5+
6+
* BUILD_LIST_UNPACK
7+
* BUILD_TUPLE_UNPACK
8+
* BUILD_SET_UNPACK
9+
* BUILD_TUPLE_UNPACK_WITH_CALL
10+
11+
The following three bytecodes have been added:
12+
13+
* LIST_TO_TUPLE
14+
* LIST_EXTEND
15+
* SET_UPDATE

Python/ceval.c

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,46 +2621,46 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
26212621
DISPATCH();
26222622
}
26232623

2624-
case TARGET(BUILD_TUPLE_UNPACK_WITH_CALL):
2625-
case TARGET(BUILD_TUPLE_UNPACK):
2626-
case TARGET(BUILD_LIST_UNPACK): {
2627-
int convert_to_tuple = opcode != BUILD_LIST_UNPACK;
2628-
Py_ssize_t i;
2629-
PyObject *sum = PyList_New(0);
2630-
PyObject *return_value;
2631-
2632-
if (sum == NULL)
2624+
case TARGET(LIST_TO_TUPLE): {
2625+
PyObject *list = POP();
2626+
PyObject *tuple = PyList_AsTuple(list);
2627+
Py_DECREF(list);
2628+
if (tuple == NULL) {
26332629
goto error;
2630+
}
2631+
PUSH(tuple);
2632+
DISPATCH();
2633+
}
26342634

2635-
for (i = oparg; i > 0; i--) {
2636-
PyObject *none_val;
2637-
2638-
none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));
2639-
if (none_val == NULL) {
2640-
if (opcode == BUILD_TUPLE_UNPACK_WITH_CALL &&
2641-
_PyErr_ExceptionMatches(tstate, PyExc_TypeError))
2642-
{
2643-
check_args_iterable(tstate, PEEK(1 + oparg), PEEK(i));
2644-
}
2645-
Py_DECREF(sum);
2646-
goto error;
2635+
case TARGET(LIST_EXTEND): {
2636+
PyObject *iterable = POP();
2637+
PyObject *list = PEEK(oparg);
2638+
PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable);
2639+
if (none_val == NULL) {
2640+
if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) &&
2641+
(iterable->ob_type->tp_iter == NULL && !PySequence_Check(iterable)))
2642+
{
2643+
PyErr_Clear();
2644+
_PyErr_Format(tstate, PyExc_TypeError,
2645+
"Value after * must be an iterable, not %.200s",
2646+
Py_TYPE(iterable)->tp_name);
26472647
}
2648-
Py_DECREF(none_val);
2648+
Py_DECREF(iterable);
2649+
goto error;
26492650
}
2651+
Py_DECREF(none_val);
2652+
Py_DECREF(iterable);
2653+
DISPATCH();
2654+
}
26502655

2651-
if (convert_to_tuple) {
2652-
return_value = PyList_AsTuple(sum);
2653-
Py_DECREF(sum);
2654-
if (return_value == NULL)
2655-
goto error;
2656-
}
2657-
else {
2658-
return_value = sum;
2656+
case TARGET(SET_UPDATE): {
2657+
PyObject *iterable = POP();
2658+
PyObject *set = PEEK(oparg);
2659+
int err = _PySet_Update(set, iterable);
2660+
Py_DECREF(iterable);
2661+
if (err < 0) {
2662+
goto error;
26592663
}
2660-
2661-
while (oparg--)
2662-
Py_DECREF(POP());
2663-
PUSH(return_value);
26642664
DISPATCH();
26652665
}
26662666

@@ -2685,25 +2685,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
26852685
DISPATCH();
26862686
}
26872687

2688-
case TARGET(BUILD_SET_UNPACK): {
2689-
Py_ssize_t i;
2690-
PyObject *sum = PySet_New(NULL);
2691-
if (sum == NULL)
2692-
goto error;
2693-
2694-
for (i = oparg; i > 0; i--) {
2695-
if (_PySet_Update(sum, PEEK(i)) < 0) {
2696-
Py_DECREF(sum);
2697-
goto error;
2698-
}
2699-
}
2700-
2701-
while (oparg--)
2702-
Py_DECREF(POP());
2703-
PUSH(sum);
2704-
DISPATCH();
2705-
}
2706-
27072688
case TARGET(BUILD_MAP): {
27082689
Py_ssize_t i;
27092690
PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg);

0 commit comments

Comments
 (0)