PEP 448: additional unpacking generalizations (closes #2292)

Patch by Neil Girdhar.
diff --git a/Python/ceval.c b/Python/ceval.c
index 2f3d3ad..5ee9077 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -12,8 +12,10 @@
 #include "Python.h"
 
 #include "code.h"
+#include "dictobject.h"
 #include "frameobject.h"
 #include "opcode.h"
+#include "setobject.h"
 #include "structmember.h"
 
 #include <ctype.h>
@@ -2379,6 +2381,43 @@
             DISPATCH();
         }
 
+        TARGET_WITH_IMPL(BUILD_TUPLE_UNPACK, _build_list_unpack)
+        TARGET(BUILD_LIST_UNPACK)
+        _build_list_unpack: {
+            int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK;
+            int i;
+            PyObject *sum = PyList_New(0);
+            PyObject *return_value;
+            if (sum == NULL)
+                goto error;
+
+            for (i = oparg; i > 0; i--) {
+                PyObject *none_val;
+
+                none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));
+                if (none_val == NULL) {
+                    Py_DECREF(sum);
+                    goto error;
+                }
+                Py_DECREF(none_val);
+            }
+
+            if (convert_to_tuple) {
+                return_value = PyList_AsTuple(sum);
+                Py_DECREF(sum);
+                if (return_value == NULL)
+                    goto error;
+            }
+            else {
+                return_value = sum;
+            }
+
+            while (oparg--)
+                Py_DECREF(POP());
+            PUSH(return_value);
+            DISPATCH();
+        }
+
         TARGET(BUILD_SET) {
             PyObject *set = PySet_New(NULL);
             int err = 0;
@@ -2398,14 +2437,127 @@
             DISPATCH();
         }
 
+        TARGET(BUILD_SET_UNPACK) {
+            int i;
+            PyObject *sum = PySet_New(NULL);
+            if (sum == NULL)
+                goto error;
+
+            for (i = oparg; i > 0; i--) {
+                if (_PySet_Update(sum, PEEK(i)) < 0) {
+                    Py_DECREF(sum);
+                    goto error;
+                }
+            }
+
+            while (oparg--)
+                Py_DECREF(POP());
+            PUSH(sum);
+            DISPATCH();
+        }
+
         TARGET(BUILD_MAP) {
             PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg);
             if (map == NULL)
                 goto error;
+            while (--oparg >= 0) {
+                int err;
+                PyObject *key = TOP();
+                PyObject *value = SECOND();
+                STACKADJ(-2);
+                err = PyDict_SetItem(map, key, value);
+                Py_DECREF(value);
+                Py_DECREF(key);
+                if (err != 0) {
+                    Py_DECREF(map);
+                    goto error;
+                }
+            }
             PUSH(map);
             DISPATCH();
         }
 
+        TARGET_WITH_IMPL(BUILD_MAP_UNPACK_WITH_CALL, _build_map_unpack)
+        TARGET(BUILD_MAP_UNPACK)
+        _build_map_unpack: {
+            int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL;
+            int num_maps;
+            int function_location;
+            int i;
+            PyObject *sum = PyDict_New();
+            if (sum == NULL)
+                goto error;
+            if (with_call) {
+                num_maps = oparg & 0xff;
+                function_location = (oparg>>8) & 0xff;
+            }
+            else {
+                num_maps = oparg;
+            }
+
+            for (i = num_maps; i > 0; i--) {
+                PyObject *arg = PEEK(i);
+                if (with_call) {
+                    PyObject *intersection = _PyDictView_Intersect(sum, arg);
+
+                    if (intersection == NULL) {
+                        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+                            PyObject *func = (
+                                    PEEK(function_location + num_maps));
+                            PyErr_Format(PyExc_TypeError,
+                                    "%.200s%.200s argument after ** "
+                                    "must be a mapping, not %.200s",
+                                    PyEval_GetFuncName(func),
+                                    PyEval_GetFuncDesc(func),
+                                    arg->ob_type->tp_name);
+                        }
+                        Py_DECREF(sum);
+                        goto error;
+                    }
+
+                    if (PySet_GET_SIZE(intersection)) {
+                        Py_ssize_t idx = 0;
+                        PyObject *key;
+                        PyObject *func = PEEK(function_location + num_maps);
+                        Py_hash_t hash;
+                        _PySet_NextEntry(intersection, &idx, &key, &hash);
+                        if (!PyUnicode_Check(key)) {
+                            PyErr_Format(PyExc_TypeError,
+                                    "%.200s%.200s keywords must be strings",
+                                    PyEval_GetFuncName(func),
+                                    PyEval_GetFuncDesc(func));
+                        } else {
+                            PyErr_Format(PyExc_TypeError,
+                                    "%.200s%.200s got multiple "
+                                    "values for keyword argument '%U'",
+                                    PyEval_GetFuncName(func),
+                                    PyEval_GetFuncDesc(func),
+                                    key);
+                        }
+                        Py_DECREF(intersection);
+                        Py_DECREF(sum);
+                        goto error;
+                    }
+                    Py_DECREF(intersection);
+                }
+
+                if (PyDict_Update(sum, arg) < 0) {
+                    if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+                        PyErr_Format(PyExc_TypeError,
+                                "'%.200s' object is not a mapping",
+                                arg->ob_type->tp_name);
+                    }
+                    Py_DECREF(sum);
+                    goto error;
+                }
+            }
+
+            while (num_maps--)
+                Py_DECREF(POP());
+            PUSH(sum);
+            DISPATCH();
+        }
+
         TARGET(STORE_MAP) {
             PyObject *key = TOP();
             PyObject *value = SECOND();
@@ -3050,6 +3202,7 @@
             goto dispatch_opcode;
         }
 
+
 #if USE_COMPUTED_GOTOS
         _unknown_opcode:
 #endif
@@ -4557,6 +4710,12 @@
             kwdict = d;
         }
     }
+    if (nk > 0) {
+        kwdict = update_keyword_args(kwdict, nk, pp_stack, func);
+        if (kwdict == NULL)
+            goto ext_call_fail;
+    }
+
     if (flags & CALL_FLAG_VAR) {
         stararg = EXT_POP(*pp_stack);
         if (!PyTuple_Check(stararg)) {
@@ -4578,11 +4737,6 @@
         }
         nstar = PyTuple_GET_SIZE(stararg);
     }
-    if (nk > 0) {
-        kwdict = update_keyword_args(kwdict, nk, pp_stack, func);
-        if (kwdict == NULL)
-            goto ext_call_fail;
-    }
     callargs = update_star_args(na, nstar, stararg, pp_stack);
     if (callargs == NULL)
         goto ext_call_fail;