mirror of
https://github.com/python/cpython.git
synced 2025-08-30 05:35:08 +00:00
bpo-39320: Handle unpacking of *values in compiler (GH-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.
This commit is contained in:
parent
f9e07e116c
commit
13bc13960c
10 changed files with 2349 additions and 2360 deletions
211
Python/compile.c
211
Python/compile.c
|
@ -1007,10 +1007,6 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
case BUILD_SET:
|
||||
case BUILD_STRING:
|
||||
return 1-oparg;
|
||||
case BUILD_LIST_UNPACK:
|
||||
case BUILD_TUPLE_UNPACK:
|
||||
case BUILD_TUPLE_UNPACK_WITH_CALL:
|
||||
case BUILD_SET_UNPACK:
|
||||
case BUILD_MAP_UNPACK:
|
||||
case BUILD_MAP_UNPACK_WITH_CALL:
|
||||
return 1 - oparg;
|
||||
|
@ -1125,6 +1121,11 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
return 1;
|
||||
case LOAD_ASSERTION_ERROR:
|
||||
return 1;
|
||||
case LIST_TO_TUPLE:
|
||||
return 0;
|
||||
case LIST_EXTEND:
|
||||
case SET_UPDATE:
|
||||
return -1;
|
||||
default:
|
||||
return PY_INVALID_STACK_EFFECT;
|
||||
}
|
||||
|
@ -1705,7 +1706,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
|
|||
compiler_pop_fblock(c, POP_VALUE, NULL);
|
||||
}
|
||||
return 1;
|
||||
|
||||
|
||||
case FINALLY_END:
|
||||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_FOUR);
|
||||
|
@ -3675,11 +3676,11 @@ compiler_boolop(struct compiler *c, expr_ty e)
|
|||
}
|
||||
|
||||
static int
|
||||
starunpack_helper(struct compiler *c, asdl_seq *elts,
|
||||
int single_op, int inner_op, int outer_op)
|
||||
starunpack_helper(struct compiler *c, asdl_seq *elts, int pushed,
|
||||
int build, int add, int extend, int tuple)
|
||||
{
|
||||
Py_ssize_t n = asdl_seq_LEN(elts);
|
||||
Py_ssize_t i, nsubitems = 0, nseen = 0;
|
||||
Py_ssize_t i, seen_star = 0;
|
||||
if (n > 2 && are_all_items_const(elts, 0, n)) {
|
||||
PyObject *folded = PyTuple_New(n);
|
||||
if (folded == NULL) {
|
||||
|
@ -3691,41 +3692,63 @@ starunpack_helper(struct compiler *c, asdl_seq *elts,
|
|||
Py_INCREF(val);
|
||||
PyTuple_SET_ITEM(folded, i, val);
|
||||
}
|
||||
if (outer_op == BUILD_SET_UNPACK) {
|
||||
Py_SETREF(folded, PyFrozenSet_New(folded));
|
||||
if (folded == NULL) {
|
||||
return 0;
|
||||
if (tuple) {
|
||||
ADDOP_LOAD_CONST_NEW(c, folded);
|
||||
} else {
|
||||
if (add == SET_ADD) {
|
||||
Py_SETREF(folded, PyFrozenSet_New(folded));
|
||||
if (folded == NULL) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ADDOP_I(c, build, pushed);
|
||||
ADDOP_LOAD_CONST_NEW(c, folded);
|
||||
ADDOP_I(c, extend, 1);
|
||||
}
|
||||
ADDOP_LOAD_CONST_NEW(c, folded);
|
||||
ADDOP_I(c, outer_op, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
expr_ty elt = asdl_seq_GET(elts, i);
|
||||
if (elt->kind == Starred_kind) {
|
||||
if (nseen) {
|
||||
ADDOP_I(c, inner_op, nseen);
|
||||
nseen = 0;
|
||||
nsubitems++;
|
||||
seen_star = 1;
|
||||
}
|
||||
}
|
||||
if (seen_star) {
|
||||
seen_star = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
expr_ty elt = asdl_seq_GET(elts, i);
|
||||
if (elt->kind == Starred_kind) {
|
||||
if (seen_star == 0) {
|
||||
ADDOP_I(c, build, i+pushed);
|
||||
seen_star = 1;
|
||||
}
|
||||
VISIT(c, expr, elt->v.Starred.value);
|
||||
ADDOP_I(c, extend, 1);
|
||||
}
|
||||
else {
|
||||
VISIT(c, expr, elt);
|
||||
if (seen_star) {
|
||||
ADDOP_I(c, add, 1);
|
||||
}
|
||||
}
|
||||
VISIT(c, expr, elt->v.Starred.value);
|
||||
nsubitems++;
|
||||
}
|
||||
else {
|
||||
assert(seen_star);
|
||||
if (tuple) {
|
||||
ADDOP(c, LIST_TO_TUPLE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < n; i++) {
|
||||
expr_ty elt = asdl_seq_GET(elts, i);
|
||||
VISIT(c, expr, elt);
|
||||
nseen++;
|
||||
}
|
||||
if (tuple) {
|
||||
ADDOP_I(c, BUILD_TUPLE, n+pushed);
|
||||
} else {
|
||||
ADDOP_I(c, build, n+pushed);
|
||||
}
|
||||
}
|
||||
if (nsubitems) {
|
||||
if (nseen) {
|
||||
ADDOP_I(c, inner_op, nseen);
|
||||
nsubitems++;
|
||||
}
|
||||
ADDOP_I(c, outer_op, nsubitems);
|
||||
}
|
||||
else
|
||||
ADDOP_I(c, single_op, nseen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -3767,8 +3790,8 @@ compiler_list(struct compiler *c, expr_ty e)
|
|||
return assignment_helper(c, elts);
|
||||
}
|
||||
else if (e->v.List.ctx == Load) {
|
||||
return starunpack_helper(c, elts,
|
||||
BUILD_LIST, BUILD_TUPLE, BUILD_LIST_UNPACK);
|
||||
return starunpack_helper(c, elts, 0, BUILD_LIST,
|
||||
LIST_APPEND, LIST_EXTEND, 0);
|
||||
}
|
||||
else
|
||||
VISIT_SEQ(c, expr, elts);
|
||||
|
@ -3783,8 +3806,8 @@ compiler_tuple(struct compiler *c, expr_ty e)
|
|||
return assignment_helper(c, elts);
|
||||
}
|
||||
else if (e->v.Tuple.ctx == Load) {
|
||||
return starunpack_helper(c, elts,
|
||||
BUILD_TUPLE, BUILD_TUPLE, BUILD_TUPLE_UNPACK);
|
||||
return starunpack_helper(c, elts, 0, BUILD_LIST,
|
||||
LIST_APPEND, LIST_EXTEND, 1);
|
||||
}
|
||||
else
|
||||
VISIT_SEQ(c, expr, elts);
|
||||
|
@ -3794,8 +3817,8 @@ compiler_tuple(struct compiler *c, expr_ty e)
|
|||
static int
|
||||
compiler_set(struct compiler *c, expr_ty e)
|
||||
{
|
||||
return starunpack_helper(c, e->v.Set.elts, BUILD_SET,
|
||||
BUILD_SET, BUILD_SET_UNPACK);
|
||||
return starunpack_helper(c, e->v.Set.elts, 0, BUILD_SET,
|
||||
SET_ADD, SET_UPDATE, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -4184,57 +4207,65 @@ compiler_call_helper(struct compiler *c,
|
|||
asdl_seq *keywords)
|
||||
{
|
||||
Py_ssize_t i, nseen, nelts, nkwelts;
|
||||
int mustdictunpack = 0;
|
||||
|
||||
/* the number of tuples and dictionaries on the stack */
|
||||
Py_ssize_t nsubargs = 0, nsubkwargs = 0;
|
||||
|
||||
nelts = asdl_seq_LEN(args);
|
||||
nkwelts = asdl_seq_LEN(keywords);
|
||||
|
||||
for (i = 0; i < nkwelts; i++) {
|
||||
keyword_ty kw = asdl_seq_GET(keywords, i);
|
||||
if (kw->arg == NULL) {
|
||||
mustdictunpack = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nseen = n; /* the number of positional arguments on the stack */
|
||||
for (i = 0; i < nelts; i++) {
|
||||
expr_ty elt = asdl_seq_GET(args, i);
|
||||
if (elt->kind == Starred_kind) {
|
||||
/* A star-arg. If we've seen positional arguments,
|
||||
pack the positional arguments into a tuple. */
|
||||
if (nseen) {
|
||||
ADDOP_I(c, BUILD_TUPLE, nseen);
|
||||
nseen = 0;
|
||||
nsubargs++;
|
||||
}
|
||||
VISIT(c, expr, elt->v.Starred.value);
|
||||
nsubargs++;
|
||||
goto ex_call;
|
||||
}
|
||||
else {
|
||||
VISIT(c, expr, elt);
|
||||
nseen++;
|
||||
}
|
||||
for (i = 0; i < nkwelts; i++) {
|
||||
keyword_ty kw = asdl_seq_GET(keywords, i);
|
||||
if (kw->arg == NULL) {
|
||||
goto ex_call;
|
||||
}
|
||||
}
|
||||
|
||||
/* Same dance again for keyword arguments */
|
||||
if (nsubargs || mustdictunpack) {
|
||||
if (nseen) {
|
||||
/* Pack up any trailing positional arguments. */
|
||||
ADDOP_I(c, BUILD_TUPLE, nseen);
|
||||
nsubargs++;
|
||||
/* No * or ** args, so can use faster calling sequence */
|
||||
for (i = 0; i < nelts; i++) {
|
||||
expr_ty elt = asdl_seq_GET(args, i);
|
||||
assert(elt->kind != Starred_kind);
|
||||
VISIT(c, expr, elt);
|
||||
}
|
||||
if (nkwelts) {
|
||||
PyObject *names;
|
||||
VISIT_SEQ(c, keyword, keywords);
|
||||
names = PyTuple_New(nkwelts);
|
||||
if (names == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (nsubargs > 1) {
|
||||
/* If we ended up with more than one stararg, we need
|
||||
to concatenate them into a single sequence. */
|
||||
ADDOP_I(c, BUILD_TUPLE_UNPACK_WITH_CALL, nsubargs);
|
||||
}
|
||||
else if (nsubargs == 0) {
|
||||
ADDOP_I(c, BUILD_TUPLE, 0);
|
||||
for (i = 0; i < nkwelts; i++) {
|
||||
keyword_ty kw = asdl_seq_GET(keywords, i);
|
||||
Py_INCREF(kw->arg);
|
||||
PyTuple_SET_ITEM(names, i, kw->arg);
|
||||
}
|
||||
ADDOP_LOAD_CONST_NEW(c, names);
|
||||
ADDOP_I(c, CALL_FUNCTION_KW, n + nelts + nkwelts);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
ADDOP_I(c, CALL_FUNCTION, n + nelts);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ex_call:
|
||||
|
||||
/* Do positional arguments. */
|
||||
if (n ==0 && nelts == 1 && ((expr_ty)asdl_seq_GET(args, 0))->kind == Starred_kind) {
|
||||
VISIT(c, expr, ((expr_ty)asdl_seq_GET(args, 0))->v.Starred.value);
|
||||
}
|
||||
else if (starunpack_helper(c, args, n, BUILD_LIST,
|
||||
LIST_APPEND, LIST_EXTEND, 1) == 0) {
|
||||
return 0;
|
||||
}
|
||||
/* Then keyword arguments */
|
||||
if (nkwelts) {
|
||||
/* the number of dictionaries on the stack */
|
||||
Py_ssize_t nsubkwargs = 0;
|
||||
|
||||
nseen = 0; /* the number of keyword arguments on the stack following */
|
||||
for (i = 0; i < nkwelts; i++) {
|
||||
keyword_ty kw = asdl_seq_GET(keywords, i);
|
||||
|
@ -4263,29 +4294,9 @@ compiler_call_helper(struct compiler *c,
|
|||
/* Pack it all up */
|
||||
ADDOP_I(c, BUILD_MAP_UNPACK_WITH_CALL, nsubkwargs);
|
||||
}
|
||||
ADDOP_I(c, CALL_FUNCTION_EX, nsubkwargs > 0);
|
||||
return 1;
|
||||
}
|
||||
else if (nkwelts) {
|
||||
PyObject *names;
|
||||
VISIT_SEQ(c, keyword, keywords);
|
||||
names = PyTuple_New(nkwelts);
|
||||
if (names == NULL) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < nkwelts; i++) {
|
||||
keyword_ty kw = asdl_seq_GET(keywords, i);
|
||||
Py_INCREF(kw->arg);
|
||||
PyTuple_SET_ITEM(names, i, kw->arg);
|
||||
}
|
||||
ADDOP_LOAD_CONST_NEW(c, names);
|
||||
ADDOP_I(c, CALL_FUNCTION_KW, n + nelts + nkwelts);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
ADDOP_I(c, CALL_FUNCTION, n + nelts);
|
||||
return 1;
|
||||
}
|
||||
ADDOP_I(c, CALL_FUNCTION_EX, nkwelts > 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -4860,9 +4871,9 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
|
|||
|
||||
ADDOP(c, POP_BLOCK);
|
||||
compiler_pop_fblock(c, WITH, block);
|
||||
|
||||
|
||||
/* End of body; start the cleanup. */
|
||||
|
||||
|
||||
/* For successful outcome:
|
||||
* call __exit__(None, None, None)
|
||||
*/
|
||||
|
@ -5984,7 +5995,7 @@ makecode(struct compiler *c, struct assembler *a)
|
|||
goto error;
|
||||
}
|
||||
co = PyCode_NewWithPosOnlyArgs(posonlyargcount+posorkeywordargcount,
|
||||
posonlyargcount, kwonlyargcount, nlocals_int,
|
||||
posonlyargcount, kwonlyargcount, nlocals_int,
|
||||
maxdepth, flags, bytecode, consts, names,
|
||||
varnames, freevars, cellvars, c->c_filename,
|
||||
c->u->u_name, c->u->u_firstlineno, a->a_lnotab);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue