bpo-39320: Handle unpacking of **values in compiler (GH-18141)

* Add DICT_UPDATE and DICT_MERGE bytecodes. Use them for ** unpacking.

* Remove BUILD_MAP_UNPACK and BUILD_MAP_UNPACK_WITH_CALL, as they are now unused.

* Update magic number for ** unpacking opcodes.

* Update dis.rst to incorporate new bytecodes.

* Add blurb entry.
This commit is contained in:
Mark Shannon 2020-01-27 09:57:45 +00:00 committed by GitHub
parent 72b1004657
commit 8a4cd700a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 3680 additions and 3672 deletions

View file

@ -1007,9 +1007,6 @@ stack_effect(int opcode, int oparg, int jump)
case BUILD_SET:
case BUILD_STRING:
return 1-oparg;
case BUILD_MAP_UNPACK:
case BUILD_MAP_UNPACK_WITH_CALL:
return 1 - oparg;
case BUILD_MAP:
return 1 - 2*oparg;
case BUILD_CONST_KEY_MAP:
@ -1125,6 +1122,8 @@ stack_effect(int opcode, int oparg, int jump)
return 0;
case LIST_EXTEND:
case SET_UPDATE:
case DICT_MERGE:
case DICT_UPDATE:
return -1;
default:
return PY_INVALID_STACK_EFFECT;
@ -3868,37 +3867,58 @@ static int
compiler_dict(struct compiler *c, expr_ty e)
{
Py_ssize_t i, n, elements;
int containers;
int have_dict;
int is_unpacking = 0;
n = asdl_seq_LEN(e->v.Dict.values);
containers = 0;
have_dict = 0;
elements = 0;
for (i = 0; i < n; i++) {
is_unpacking = (expr_ty)asdl_seq_GET(e->v.Dict.keys, i) == NULL;
if (elements == 0xFFFF || (elements && is_unpacking)) {
if (!compiler_subdict(c, e, i - elements, i))
return 0;
containers++;
elements = 0;
}
if (is_unpacking) {
if (elements) {
if (!compiler_subdict(c, e, i - elements, i)) {
return 0;
}
if (have_dict) {
ADDOP_I(c, DICT_UPDATE, 1);
}
have_dict = 1;
elements = 0;
}
if (have_dict == 0) {
ADDOP_I(c, BUILD_MAP, 0);
have_dict = 1;
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Dict.values, i));
containers++;
ADDOP_I(c, DICT_UPDATE, 1);
}
else {
elements++;
if (elements == 0xFFFF) {
if (!compiler_subdict(c, e, i - elements, i)) {
return 0;
}
if (have_dict) {
ADDOP_I(c, DICT_UPDATE, 1);
}
have_dict = 1;
elements = 0;
}
else {
elements++;
}
}
}
if (elements || containers == 0) {
if (!compiler_subdict(c, e, n - elements, n))
if (elements) {
if (!compiler_subdict(c, e, n - elements, n)) {
return 0;
containers++;
}
if (have_dict) {
ADDOP_I(c, DICT_UPDATE, 1);
}
have_dict = 1;
}
/* If there is more than one dict, they need to be merged into a new
* dict. If there is one dict and it's an unpacking, then it needs
* to be copied into a new dict." */
if (containers > 1 || is_unpacking) {
ADDOP_I(c, BUILD_MAP_UNPACK, containers);
if (!have_dict) {
ADDOP_I(c, BUILD_MAP, 0);
}
return 1;
}
@ -4263,8 +4283,8 @@ ex_call:
}
/* Then keyword arguments */
if (nkwelts) {
/* the number of dictionaries on the stack */
Py_ssize_t nsubkwargs = 0;
/* Has a new dict been pushed */
int have_dict = 0;
nseen = 0; /* the number of keyword arguments on the stack following */
for (i = 0; i < nkwelts; i++) {
@ -4272,13 +4292,18 @@ ex_call:
if (kw->arg == NULL) {
/* A keyword argument unpacking. */
if (nseen) {
if (!compiler_subkwargs(c, keywords, i - nseen, i))
if (!compiler_subkwargs(c, keywords, i - nseen, i)) {
return 0;
nsubkwargs++;
}
have_dict = 1;
nseen = 0;
}
if (!have_dict) {
ADDOP_I(c, BUILD_MAP, 0);
have_dict = 1;
}
VISIT(c, expr, kw->value);
nsubkwargs++;
ADDOP_I(c, DICT_MERGE, 1);
}
else {
nseen++;
@ -4286,14 +4311,15 @@ ex_call:
}
if (nseen) {
/* Pack up any trailing keyword arguments. */
if (!compiler_subkwargs(c, keywords, nkwelts - nseen, nkwelts))
if (!compiler_subkwargs(c, keywords, nkwelts - nseen, nkwelts)) {
return 0;
nsubkwargs++;
}
if (nsubkwargs > 1) {
/* Pack it all up */
ADDOP_I(c, BUILD_MAP_UNPACK_WITH_CALL, nsubkwargs);
}
if (have_dict) {
ADDOP_I(c, DICT_MERGE, 1);
}
have_dict = 1;
}
assert(have_dict);
}
ADDOP_I(c, CALL_FUNCTION_EX, nkwelts > 0);
return 1;