bpo-46219, 46221: simplify except* implementation following exc_info changes. Move helpers to exceptions.c. Do not assume that exception groups are truthy. (GH-30289)

This commit is contained in:
Irit Katriel 2022-01-02 23:22:42 +00:00 committed by GitHub
parent 8e75c6b49b
commit 65e7c1f90e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 179 additions and 149 deletions

View file

@ -1092,7 +1092,6 @@ fail:
static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause);
static PyObject *do_reraise_star(PyObject *excs, PyObject *orig);
static int exception_group_match(
PyObject* exc_value, PyObject *match_type,
PyObject **match, PyObject **rest);
@ -2777,7 +2776,7 @@ check_eval_breaker:
assert(PyList_Check(excs));
PyObject *orig = POP();
PyObject *val = do_reraise_star(excs, orig);
PyObject *val = _PyExc_PrepReraiseStar(orig, excs);
Py_DECREF(excs);
Py_DECREF(orig);
@ -2785,8 +2784,6 @@ check_eval_breaker:
goto error;
}
PyObject *lasti_unused = Py_NewRef(_PyLong_GetZero());
PUSH(lasti_unused);
PUSH(val);
DISPATCH();
}
@ -6313,134 +6310,6 @@ exception_group_match(PyObject* exc_value, PyObject *match_type,
return 0;
}
/* Logic for the final raise/reraise of a try-except* contruct
(too complicated for inlining).
*/
static bool
is_same_exception_metadata(PyObject *exc1, PyObject *exc2)
{
assert(PyExceptionInstance_Check(exc1));
assert(PyExceptionInstance_Check(exc2));
PyObject *tb1 = PyException_GetTraceback(exc1);
PyObject *ctx1 = PyException_GetContext(exc1);
PyObject *cause1 = PyException_GetCause(exc1);
PyObject *tb2 = PyException_GetTraceback(exc2);
PyObject *ctx2 = PyException_GetContext(exc2);
PyObject *cause2 = PyException_GetCause(exc2);
bool result = (Py_Is(tb1, tb2) &&
Py_Is(ctx1, ctx2) &&
Py_Is(cause1, cause2));
Py_XDECREF(tb1);
Py_XDECREF(ctx1);
Py_XDECREF(cause1);
Py_XDECREF(tb2);
Py_XDECREF(ctx2);
Py_XDECREF(cause2);
return result;
}
/*
excs: a list of exceptions to raise/reraise
orig: the original except that was caught
Calculates an exception group to raise. It contains
all exceptions in excs, where those that were reraised
have same nesting structure as in orig, and those that
were raised (if any) are added as siblings in a new EG.
Returns NULL and sets an exception on failure.
*/
static PyObject *
do_reraise_star(PyObject *excs, PyObject *orig)
{
assert(PyList_Check(excs));
assert(PyExceptionInstance_Check(orig));
Py_ssize_t numexcs = PyList_GET_SIZE(excs);
if (numexcs == 0) {
return Py_NewRef(Py_None);
}
if (!_PyBaseExceptionGroup_Check(orig)) {
/* a naked exception was caught and wrapped. Only one except* clause
* could have executed,so there is at most one exception to raise.
*/
assert(numexcs == 1 || (numexcs == 2 && PyList_GET_ITEM(excs, 1) == Py_None));
PyObject *e = PyList_GET_ITEM(excs, 0);
assert(e != NULL);
return Py_NewRef(e);
}
PyObject *raised_list = PyList_New(0);
if (raised_list == NULL) {
return NULL;
}
PyObject* reraised_list = PyList_New(0);
if (reraised_list == NULL) {
Py_DECREF(raised_list);
return NULL;
}
/* Now we are holding refs to raised_list and reraised_list */
PyObject *result = NULL;
/* Split excs into raised and reraised by comparing metadata with orig */
for (Py_ssize_t i = 0; i < numexcs; i++) {
PyObject *e = PyList_GET_ITEM(excs, i);
assert(e != NULL);
if (Py_IsNone(e)) {
continue;
}
bool is_reraise = is_same_exception_metadata(e, orig);
PyObject *append_list = is_reraise ? reraised_list : raised_list;
if (PyList_Append(append_list, e) < 0) {
goto done;
}
}
PyObject *reraised_eg = _PyExc_ExceptionGroupProjection(orig, reraised_list);
if (reraised_eg == NULL) {
goto done;
}
if (!Py_IsNone(reraised_eg)) {
assert(is_same_exception_metadata(reraised_eg, orig));
}
Py_ssize_t num_raised = PyList_GET_SIZE(raised_list);
if (num_raised == 0) {
result = reraised_eg;
}
else if (num_raised > 0) {
int res = 0;
if (!Py_IsNone(reraised_eg)) {
res = PyList_Append(raised_list, reraised_eg);
}
Py_DECREF(reraised_eg);
if (res < 0) {
goto done;
}
result = _PyExc_CreateExceptionGroup("", raised_list);
if (result == NULL) {
goto done;
}
}
done:
Py_XDECREF(raised_list);
Py_XDECREF(reraised_list);
return result;
}
/* Iterate v argcnt times and store the results on the stack (via decreasing
sp). Return 1 for success, 0 if error.

View file

@ -1134,7 +1134,7 @@ stack_effect(int opcode, int oparg, int jump)
return jump ? 1 : 0;
case PREP_RERAISE_STAR:
return 0;
return -1;
case RERAISE:
return -1;
case PUSH_EXC_INFO:
@ -3488,12 +3488,16 @@ compiler_try_except(struct compiler *c, stmt_ty s)
[orig, res, rest] Ln+1: LIST_APPEND 1 ) add unhandled exc to res (could be None)
[orig, res] PREP_RERAISE_STAR
[i, exc] POP_JUMP_IF_TRUE RER
[i, exc] POP
[i] POP
[exc] DUP_TOP
[exc, exc] LOAD_CONST None
[exc, exc, None] COMPARE_IS
[exc, is_none] POP_JUMP_IF_FALSE RER
[exc] POP_TOP
[] JUMP_FORWARD L0
[i, exc] RER: POP_EXCEPT_AND_RERAISE
[exc] RER: ROT_TWO
[exc, prev_exc_info] POP_EXCEPT
[exc] RERAISE 0
[] L0: <next statement>
*/
@ -3657,18 +3661,21 @@ compiler_try_star_except(struct compiler *c, stmt_ty s)
compiler_use_next_block(c, reraise_star);
ADDOP(c, PREP_RERAISE_STAR);
ADDOP(c, DUP_TOP);
ADDOP_JUMP(c, POP_JUMP_IF_TRUE, reraise);
ADDOP_LOAD_CONST(c, Py_None);
ADDOP_COMPARE(c, Is);
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, reraise);
NEXT_BLOCK(c);
/* Nothing to reraise - pop it */
ADDOP(c, POP_TOP);
/* Nothing to reraise */
ADDOP(c, POP_TOP);
ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
ADDOP_JUMP(c, JUMP_FORWARD, end);
compiler_use_next_block(c, reraise);
ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT_AND_RERAISE);
ADDOP(c, ROT_TWO);
ADDOP(c, POP_EXCEPT);
ADDOP_I(c, RERAISE, 0);
compiler_use_next_block(c, cleanup);
ADDOP(c, POP_EXCEPT_AND_RERAISE);
compiler_use_next_block(c, orelse);