diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2021-12-14 16:48:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-14 16:48:15 (GMT) |
commit | d60457a6673cf0263213c2f2be02c633ec2e2038 (patch) | |
tree | 04461db9079cf30a98c5a4070098f795275aa910 /Python | |
parent | 850aefc2c651110a784cd5478af9774b1f6287a3 (diff) | |
download | cpython-d60457a6673cf0263213c2f2be02c633ec2e2038.zip cpython-d60457a6673cf0263213c2f2be02c633ec2e2038.tar.gz cpython-d60457a6673cf0263213c2f2be02c633ec2e2038.tar.bz2 |
bpo-45292: [PEP-654] add except* (GH-29581)
Diffstat (limited to 'Python')
-rw-r--r-- | Python/Python-ast.c | 229 | ||||
-rw-r--r-- | Python/ast.c | 25 | ||||
-rw-r--r-- | Python/ast_opt.c | 6 | ||||
-rw-r--r-- | Python/ceval.c | 355 | ||||
-rw-r--r-- | Python/compile.c | 339 | ||||
-rw-r--r-- | Python/opcode_targets.h | 6 | ||||
-rw-r--r-- | Python/symtable.c | 6 |
7 files changed, 954 insertions, 12 deletions
diff --git a/Python/Python-ast.c b/Python/Python-ast.c index ce6e6a9..1670184 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -146,6 +146,7 @@ void _PyAST_Fini(PyInterpreterState *interp) Py_CLEAR(state->Sub_singleton); Py_CLEAR(state->Sub_type); Py_CLEAR(state->Subscript_type); + Py_CLEAR(state->TryStar_type); Py_CLEAR(state->Try_type); Py_CLEAR(state->Tuple_type); Py_CLEAR(state->TypeIgnore_type); @@ -486,6 +487,12 @@ static const char * const Try_fields[]={ "orelse", "finalbody", }; +static const char * const TryStar_fields[]={ + "body", + "handlers", + "orelse", + "finalbody", +}; static const char * const Assert_fields[]={ "test", "msg", @@ -1139,6 +1146,7 @@ init_types(struct ast_state *state) " | Match(expr subject, match_case* cases)\n" " | Raise(expr? exc, expr? cause)\n" " | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n" + " | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n" " | Assert(expr test, expr? msg)\n" " | Import(alias* names)\n" " | ImportFrom(identifier? module, alias* names, int? level)\n" @@ -1254,6 +1262,10 @@ init_types(struct ast_state *state) state->Try_type = make_type(state, "Try", state->stmt_type, Try_fields, 4, "Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)"); if (!state->Try_type) return 0; + state->TryStar_type = make_type(state, "TryStar", state->stmt_type, + TryStar_fields, 4, + "TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)"); + if (!state->TryStar_type) return 0; state->Assert_type = make_type(state, "Assert", state->stmt_type, Assert_fields, 2, "Assert(expr test, expr? msg)"); @@ -2380,6 +2392,28 @@ _PyAST_Try(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers, } stmt_ty +_PyAST_TryStar(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers, + asdl_stmt_seq * orelse, asdl_stmt_seq * finalbody, int lineno, + int col_offset, int end_lineno, int end_col_offset, PyArena + *arena) +{ + stmt_ty p; + p = (stmt_ty)_PyArena_Malloc(arena, sizeof(*p)); + if (!p) + return NULL; + p->kind = TryStar_kind; + p->v.TryStar.body = body; + p->v.TryStar.handlers = handlers; + p->v.TryStar.orelse = orelse; + p->v.TryStar.finalbody = finalbody; + p->lineno = lineno; + p->col_offset = col_offset; + p->end_lineno = end_lineno; + p->end_col_offset = end_col_offset; + return p; +} + +stmt_ty _PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena) { @@ -4049,6 +4083,34 @@ ast2obj_stmt(struct ast_state *state, void* _o) goto failed; Py_DECREF(value); break; + case TryStar_kind: + tp = (PyTypeObject *)state->TryStar_type; + result = PyType_GenericNew(tp, NULL, NULL); + if (!result) goto failed; + value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.body, ast2obj_stmt); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->body, value) == -1) + goto failed; + Py_DECREF(value); + value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.handlers, + ast2obj_excepthandler); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->handlers, value) == -1) + goto failed; + Py_DECREF(value); + value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.orelse, + ast2obj_stmt); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->orelse, value) == -1) + goto failed; + Py_DECREF(value); + value = ast2obj_list(state, (asdl_seq*)o->v.TryStar.finalbody, + ast2obj_stmt); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->finalbody, value) == -1) + goto failed; + Py_DECREF(value); + break; case Assert_kind: tp = (PyTypeObject *)state->Assert_type; result = PyType_GenericNew(tp, NULL, NULL); @@ -7477,6 +7539,170 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (*out == NULL) goto failed; return 0; } + tp = state->TryStar_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (isinstance) { + asdl_stmt_seq* body; + asdl_excepthandler_seq* handlers; + asdl_stmt_seq* orelse; + asdl_stmt_seq* finalbody; + + if (_PyObject_LookupAttr(obj, state->body, &tmp) < 0) { + return 1; + } + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from TryStar"); + return 1; + } + else { + int res; + Py_ssize_t len; + Py_ssize_t i; + if (!PyList_Check(tmp)) { + PyErr_Format(PyExc_TypeError, "TryStar field \"body\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); + goto failed; + } + len = PyList_GET_SIZE(tmp); + body = _Py_asdl_stmt_seq_new(len, arena); + if (body == NULL) goto failed; + for (i = 0; i < len; i++) { + stmt_ty val; + PyObject *tmp2 = PyList_GET_ITEM(tmp, i); + Py_INCREF(tmp2); + if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { + goto failed; + } + res = obj2ast_stmt(state, tmp2, &val, arena); + Py_LeaveRecursiveCall(); + Py_DECREF(tmp2); + if (res != 0) goto failed; + if (len != PyList_GET_SIZE(tmp)) { + PyErr_SetString(PyExc_RuntimeError, "TryStar field \"body\" changed size during iteration"); + goto failed; + } + asdl_seq_SET(body, i, val); + } + Py_CLEAR(tmp); + } + if (_PyObject_LookupAttr(obj, state->handlers, &tmp) < 0) { + return 1; + } + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"handlers\" missing from TryStar"); + return 1; + } + else { + int res; + Py_ssize_t len; + Py_ssize_t i; + if (!PyList_Check(tmp)) { + PyErr_Format(PyExc_TypeError, "TryStar field \"handlers\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); + goto failed; + } + len = PyList_GET_SIZE(tmp); + handlers = _Py_asdl_excepthandler_seq_new(len, arena); + if (handlers == NULL) goto failed; + for (i = 0; i < len; i++) { + excepthandler_ty val; + PyObject *tmp2 = PyList_GET_ITEM(tmp, i); + Py_INCREF(tmp2); + if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { + goto failed; + } + res = obj2ast_excepthandler(state, tmp2, &val, arena); + Py_LeaveRecursiveCall(); + Py_DECREF(tmp2); + if (res != 0) goto failed; + if (len != PyList_GET_SIZE(tmp)) { + PyErr_SetString(PyExc_RuntimeError, "TryStar field \"handlers\" changed size during iteration"); + goto failed; + } + asdl_seq_SET(handlers, i, val); + } + Py_CLEAR(tmp); + } + if (_PyObject_LookupAttr(obj, state->orelse, &tmp) < 0) { + return 1; + } + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from TryStar"); + return 1; + } + else { + int res; + Py_ssize_t len; + Py_ssize_t i; + if (!PyList_Check(tmp)) { + PyErr_Format(PyExc_TypeError, "TryStar field \"orelse\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); + goto failed; + } + len = PyList_GET_SIZE(tmp); + orelse = _Py_asdl_stmt_seq_new(len, arena); + if (orelse == NULL) goto failed; + for (i = 0; i < len; i++) { + stmt_ty val; + PyObject *tmp2 = PyList_GET_ITEM(tmp, i); + Py_INCREF(tmp2); + if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { + goto failed; + } + res = obj2ast_stmt(state, tmp2, &val, arena); + Py_LeaveRecursiveCall(); + Py_DECREF(tmp2); + if (res != 0) goto failed; + if (len != PyList_GET_SIZE(tmp)) { + PyErr_SetString(PyExc_RuntimeError, "TryStar field \"orelse\" changed size during iteration"); + goto failed; + } + asdl_seq_SET(orelse, i, val); + } + Py_CLEAR(tmp); + } + if (_PyObject_LookupAttr(obj, state->finalbody, &tmp) < 0) { + return 1; + } + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"finalbody\" missing from TryStar"); + return 1; + } + else { + int res; + Py_ssize_t len; + Py_ssize_t i; + if (!PyList_Check(tmp)) { + PyErr_Format(PyExc_TypeError, "TryStar field \"finalbody\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); + goto failed; + } + len = PyList_GET_SIZE(tmp); + finalbody = _Py_asdl_stmt_seq_new(len, arena); + if (finalbody == NULL) goto failed; + for (i = 0; i < len; i++) { + stmt_ty val; + PyObject *tmp2 = PyList_GET_ITEM(tmp, i); + Py_INCREF(tmp2); + if (Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { + goto failed; + } + res = obj2ast_stmt(state, tmp2, &val, arena); + Py_LeaveRecursiveCall(); + Py_DECREF(tmp2); + if (res != 0) goto failed; + if (len != PyList_GET_SIZE(tmp)) { + PyErr_SetString(PyExc_RuntimeError, "TryStar field \"finalbody\" changed size during iteration"); + goto failed; + } + asdl_seq_SET(finalbody, i, val); + } + Py_CLEAR(tmp); + } + *out = _PyAST_TryStar(body, handlers, orelse, finalbody, lineno, + col_offset, end_lineno, end_col_offset, arena); + if (*out == NULL) goto failed; + return 0; + } tp = state->Assert_type; isinstance = PyObject_IsInstance(obj, tp); if (isinstance == -1) { @@ -11687,6 +11913,9 @@ astmodule_exec(PyObject *m) if (PyModule_AddObjectRef(m, "Try", state->Try_type) < 0) { return -1; } + if (PyModule_AddObjectRef(m, "TryStar", state->TryStar_type) < 0) { + return -1; + } if (PyModule_AddObjectRef(m, "Assert", state->Assert_type) < 0) { return -1; } diff --git a/Python/ast.c b/Python/ast.c index 0c3121d..607281e 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -817,6 +817,31 @@ validate_stmt(struct validator *state, stmt_ty stmt) (!asdl_seq_LEN(stmt->v.Try.orelse) || validate_stmts(state, stmt->v.Try.orelse)); break; + case TryStar_kind: + if (!validate_body(state, stmt->v.TryStar.body, "TryStar")) + return 0; + if (!asdl_seq_LEN(stmt->v.TryStar.handlers) && + !asdl_seq_LEN(stmt->v.TryStar.finalbody)) { + PyErr_SetString(PyExc_ValueError, "TryStar has neither except handlers nor finalbody"); + return 0; + } + if (!asdl_seq_LEN(stmt->v.TryStar.handlers) && + asdl_seq_LEN(stmt->v.TryStar.orelse)) { + PyErr_SetString(PyExc_ValueError, "TryStar has orelse but no except handlers"); + return 0; + } + for (i = 0; i < asdl_seq_LEN(stmt->v.TryStar.handlers); i++) { + excepthandler_ty handler = asdl_seq_GET(stmt->v.TryStar.handlers, i); + if ((handler->v.ExceptHandler.type && + !validate_expr(state, handler->v.ExceptHandler.type, Load)) || + !validate_body(state, handler->v.ExceptHandler.body, "ExceptHandler")) + return 0; + } + ret = (!asdl_seq_LEN(stmt->v.TryStar.finalbody) || + validate_stmts(state, stmt->v.TryStar.finalbody)) && + (!asdl_seq_LEN(stmt->v.TryStar.orelse) || + validate_stmts(state, stmt->v.TryStar.orelse)); + break; case Assert_kind: ret = validate_expr(state, stmt->v.Assert.test, Load) && (!stmt->v.Assert.msg || validate_expr(state, stmt->v.Assert.msg, Load)); diff --git a/Python/ast_opt.c b/Python/ast_opt.c index 5f219c6..2821e69 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -972,6 +972,12 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) CALL_SEQ(astfold_stmt, stmt, node_->v.Try.orelse); CALL_SEQ(astfold_stmt, stmt, node_->v.Try.finalbody); break; + case TryStar_kind: + CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.body); + CALL_SEQ(astfold_excepthandler, excepthandler, node_->v.TryStar.handlers); + CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.orelse); + CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.finalbody); + break; case Assert_kind: CALL(astfold_expr, expr_ty, node_->v.Assert.test); CALL_OPT(astfold_expr, expr_ty, node_->v.Assert.msg); diff --git a/Python/ceval.c b/Python/ceval.c index 4f5ccf5..fb19f78 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -37,6 +37,7 @@ #include "structmember.h" // struct PyMemberDef, T_OFFSET_EX #include <ctype.h> +#include <stdbool.h> #ifdef Py_DEBUG /* For debugging the interpreter: */ @@ -95,6 +96,7 @@ static void format_exc_check_arg(PyThreadState *, PyObject *, const char *, PyOb static void format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg); static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg); static int check_except_type_valid(PyThreadState *tstate, PyObject* right); +static int check_except_star_type_valid(PyThreadState *tstate, PyObject* right); static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs); static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int); static int get_exception_handler(PyCodeObject *, int, int*, int*, int*); @@ -1090,6 +1092,11 @@ 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_type, PyObject* exc_value, PyObject *match_type, + PyObject **match, PyObject **rest); + static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **); #ifdef Py_DEBUG @@ -2727,6 +2734,7 @@ check_eval_breaker: type = exc_info->exc_type; value = exc_info->exc_value; traceback = exc_info->exc_traceback; + exc_info->exc_type = POP(); exc_info->exc_value = POP(); exc_info->exc_traceback = POP(); @@ -2791,6 +2799,36 @@ check_eval_breaker: goto exception_unwind; } + TARGET(PREP_RERAISE_STAR) { + PyObject *excs = POP(); + assert(PyList_Check(excs)); + PyObject *orig = POP(); + + PyObject *val = do_reraise_star(excs, orig); + Py_DECREF(excs); + Py_DECREF(orig); + + if (val == NULL) { + goto error; + } + + PyObject *lasti_unused = Py_NewRef(_PyLong_GetZero()); + PUSH(lasti_unused); + if (!Py_IsNone(val)) { + PyObject *tb = PyException_GetTraceback(val); + PUSH(tb ? tb : Py_NewRef(Py_None)); + PUSH(val); + PUSH(Py_NewRef(Py_TYPE(val))); + } + else { + // nothing to reraise + PUSH(Py_NewRef(Py_None)); + PUSH(val); + PUSH(Py_NewRef(Py_None)); + } + DISPATCH(); + } + TARGET(END_ASYNC_FOR) { PyObject *exc = POP(); PyObject *val = POP(); @@ -3922,6 +3960,83 @@ check_eval_breaker: DISPATCH(); } + TARGET(JUMP_IF_NOT_EG_MATCH) { + PyObject *match_type = POP(); + PyObject *exc_type = TOP(); + PyObject *exc_value = SECOND(); + if (check_except_star_type_valid(tstate, match_type) < 0) { + Py_DECREF(match_type); + goto error; + } + + PyObject *match = NULL, *rest = NULL; + int res = exception_group_match(exc_type, exc_value, + match_type, &match, &rest); + Py_DECREF(match_type); + if (res < 0) { + goto error; + } + + if (match == NULL || rest == NULL) { + assert(match == NULL); + assert(rest == NULL); + goto error; + } + + if (Py_IsNone(match)) { + Py_DECREF(match); + Py_XDECREF(rest); + /* no match - jump to target */ + JUMPTO(oparg); + } + else { + + /* Total or partial match - update the stack from + * [tb, val, exc] + * to + * [tb, rest, exc, tb, match, exc] + * (rest can be Py_None) + */ + + + PyObject *type = TOP(); + PyObject *val = SECOND(); + PyObject *tb = THIRD(); + + if (!Py_IsNone(rest)) { + /* tb remains the same */ + SET_TOP(Py_NewRef(Py_TYPE(rest))); + SET_SECOND(Py_NewRef(rest)); + SET_THIRD(Py_NewRef(tb)); + } + else { + SET_TOP(Py_NewRef(Py_None)); + SET_SECOND(Py_NewRef(Py_None)); + SET_THIRD(Py_NewRef(Py_None)); + } + /* Push match */ + + PUSH(Py_NewRef(tb)); + PUSH(Py_NewRef(match)); + PUSH(Py_NewRef(Py_TYPE(match))); + + // set exc_info to the current match + PyErr_SetExcInfo( + Py_NewRef(Py_TYPE(match)), + Py_NewRef(match), + Py_NewRef(tb)); + + Py_DECREF(tb); + Py_DECREF(val); + Py_DECREF(type); + + Py_DECREF(match); + Py_DECREF(rest); + } + + DISPATCH(); + } + TARGET(JUMP_IF_NOT_EXC_MATCH) { PyObject *right = POP(); ASSERT_EXC_TYPE_IS_REDUNDANT(TOP(), SECOND()); @@ -3931,17 +4046,12 @@ check_eval_breaker: Py_DECREF(right); goto error; } + int res = PyErr_GivenExceptionMatches(left, right); Py_DECREF(right); - if (res > 0) { - /* Exception matches -- Do nothing */; - } - else if (res == 0) { + if (res == 0) { JUMPTO(oparg); } - else { - goto error; - } DISPATCH(); } @@ -6127,6 +6237,196 @@ raise_error: return 0; } +/* Logic for matching an exception in an except* clause (too + complicated for inlining). +*/ + +static int +exception_group_match(PyObject *exc_type, PyObject* exc_value, + PyObject *match_type, PyObject **match, PyObject **rest) +{ + if (Py_IsNone(exc_type)) { + assert(Py_IsNone(exc_value)); + *match = Py_NewRef(Py_None); + *rest = Py_NewRef(Py_None); + return 0; + } + assert(PyExceptionClass_Check(exc_type)); + assert(PyExceptionInstance_Check(exc_value)); + + if (PyErr_GivenExceptionMatches(exc_type, match_type)) { + /* Full match of exc itself */ + bool is_eg = _PyBaseExceptionGroup_Check(exc_value); + if (is_eg) { + *match = Py_NewRef(exc_value); + } + else { + /* naked exception - wrap it */ + PyObject *excs = PyTuple_Pack(1, exc_value); + if (excs == NULL) { + return -1; + } + PyObject *wrapped = _PyExc_CreateExceptionGroup("", excs); + Py_DECREF(excs); + if (wrapped == NULL) { + return -1; + } + *match = wrapped; + } + *rest = Py_NewRef(Py_None); + return 0; + } + + /* exc_value does not match match_type. + * Check for partial match if it's an exception group. + */ + if (_PyBaseExceptionGroup_Check(exc_value)) { + PyObject *pair = PyObject_CallMethod(exc_value, "split", "(O)", + match_type); + if (pair == NULL) { + return -1; + } + assert(PyTuple_CheckExact(pair)); + assert(PyTuple_GET_SIZE(pair) == 2); + *match = Py_NewRef(PyTuple_GET_ITEM(pair, 0)); + *rest = Py_NewRef(PyTuple_GET_ITEM(pair, 1)); + Py_DECREF(pair); + return 0; + } + /* no match */ + *match = Py_NewRef(Py_None); + *rest = Py_NewRef(Py_None); + 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. @@ -7020,10 +7320,12 @@ import_all_from(PyThreadState *tstate, PyObject *locals, PyObject *v) return err; } - #define CANNOT_CATCH_MSG "catching classes that do not inherit from "\ "BaseException is not allowed" +#define CANNOT_EXCEPT_STAR_EG "catching ExceptionGroup with except* "\ + "is not allowed. Use except instead." + static int check_except_type_valid(PyThreadState *tstate, PyObject* right) { @@ -7050,6 +7352,43 @@ check_except_type_valid(PyThreadState *tstate, PyObject* right) } static int +check_except_star_type_valid(PyThreadState *tstate, PyObject* right) +{ + if (check_except_type_valid(tstate, right) < 0) { + return -1; + } + + /* reject except *ExceptionGroup */ + + int is_subclass = 0; + if (PyTuple_Check(right)) { + Py_ssize_t length = PyTuple_GET_SIZE(right); + for (Py_ssize_t i = 0; i < length; i++) { + PyObject *exc = PyTuple_GET_ITEM(right, i); + is_subclass = PyObject_IsSubclass(exc, PyExc_BaseExceptionGroup); + if (is_subclass < 0) { + return -1; + } + if (is_subclass) { + break; + } + } + } + else { + is_subclass = PyObject_IsSubclass(right, PyExc_BaseExceptionGroup); + if (is_subclass < 0) { + return -1; + } + } + if (is_subclass) { + _PyErr_SetString(tstate, PyExc_TypeError, + CANNOT_EXCEPT_STAR_EG); + return -1; + } + return 0; +} + +static int check_args_iterable(PyThreadState *tstate, PyObject *func, PyObject *args) { if (Py_TYPE(args)->tp_iter == NULL && !PySequence_Check(args)) { diff --git a/Python/compile.c b/Python/compile.c index bbdb011..00e1e01 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -175,7 +175,7 @@ compiler IR. enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END, WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER, - ASYNC_COMPREHENSION_GENERATOR }; + EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR }; struct fblockinfo { enum fblocktype fb_type; @@ -323,6 +323,7 @@ static int compiler_call_helper(struct compiler *c, int n, asdl_expr_seq *args, asdl_keyword_seq *keywords); static int compiler_try_except(struct compiler *, stmt_ty); +static int compiler_try_star_except(struct compiler *, stmt_ty); static int compiler_set_qualname(struct compiler *); static int compiler_sync_comprehension_generator( @@ -1094,6 +1095,8 @@ stack_effect(int opcode, int oparg, int jump) return -1; case JUMP_IF_NOT_EXC_MATCH: return -1; + case JUMP_IF_NOT_EG_MATCH: + return jump > 0 ? -1 : 2; case IMPORT_NAME: return -1; case IMPORT_FROM: @@ -1131,6 +1134,8 @@ stack_effect(int opcode, int oparg, int jump) * if an exception be raised. */ return jump ? -1 + 4 : 0; + case PREP_RERAISE_STAR: + return 2; case RERAISE: return -3; case PUSH_EXC_INFO: @@ -1755,6 +1760,18 @@ find_ann(asdl_stmt_seq *stmts) find_ann(st->v.Try.finalbody) || find_ann(st->v.Try.orelse); break; + case TryStar_kind: + for (j = 0; j < asdl_seq_LEN(st->v.TryStar.handlers); j++) { + excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET( + st->v.TryStar.handlers, j); + if (find_ann(handler->v.ExceptHandler.body)) { + return 1; + } + } + res = find_ann(st->v.TryStar.body) || + find_ann(st->v.TryStar.finalbody) || + find_ann(st->v.TryStar.orelse); + break; default: res = 0; } @@ -1816,6 +1833,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info, switch (info->fb_type) { case WHILE_LOOP: case EXCEPTION_HANDLER: + case EXCEPTION_GROUP_HANDLER: case ASYNC_COMPREHENSION_GENERATOR: return 1; @@ -1919,6 +1937,10 @@ compiler_unwind_fblock_stack(struct compiler *c, int preserve_tos, struct fblock return 1; } struct fblockinfo *top = &c->u->u_fblock[c->u->u_nfblocks-1]; + if (top->fb_type == EXCEPTION_GROUP_HANDLER) { + return compiler_error( + c, "'break', 'continue' and 'return' cannot appear in an except* block"); + } if (loop != NULL && (top->fb_type == WHILE_LOOP || top->fb_type == FOR_LOOP)) { *loop = top; return 1; @@ -3202,6 +3224,62 @@ compiler_try_finally(struct compiler *c, stmt_ty s) return 1; } +static int +compiler_try_star_finally(struct compiler *c, stmt_ty s) +{ + basicblock *body = compiler_new_block(c); + if (body == NULL) { + return 0; + } + basicblock *end = compiler_new_block(c); + if (!end) { + return 0; + } + basicblock *exit = compiler_new_block(c); + if (!exit) { + return 0; + } + basicblock *cleanup = compiler_new_block(c); + if (!cleanup) { + return 0; + } + /* `try` block */ + ADDOP_JUMP(c, SETUP_FINALLY, end); + compiler_use_next_block(c, body); + if (!compiler_push_fblock(c, FINALLY_TRY, body, end, s->v.TryStar.finalbody)) { + return 0; + } + if (s->v.TryStar.handlers && asdl_seq_LEN(s->v.TryStar.handlers)) { + if (!compiler_try_star_except(c, s)) { + return 0; + } + } + else { + VISIT_SEQ(c, stmt, s->v.TryStar.body); + } + ADDOP_NOLINE(c, POP_BLOCK); + compiler_pop_fblock(c, FINALLY_TRY, body); + VISIT_SEQ(c, stmt, s->v.TryStar.finalbody); + ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, exit); + /* `finally` block */ + compiler_use_next_block(c, end); + + UNSET_LOC(c); + ADDOP_JUMP(c, SETUP_CLEANUP, cleanup); + ADDOP(c, PUSH_EXC_INFO); + if (!compiler_push_fblock(c, FINALLY_END, end, NULL, NULL)) { + return 0; + } + VISIT_SEQ(c, stmt, s->v.TryStar.finalbody); + compiler_pop_fblock(c, FINALLY_END, end); + ADDOP_I(c, RERAISE, 0); + compiler_use_next_block(c, cleanup); + ADDOP(c, POP_EXCEPT_AND_RERAISE); + compiler_use_next_block(c, exit); + return 1; +} + + /* Code generated for "try: S except E1 as V1: S1 except E2 as V2: S2 ...": (The contents of the value stack is shown in [], with the top @@ -3360,6 +3438,253 @@ compiler_try_except(struct compiler *c, stmt_ty s) ADDOP(c, POP_EXCEPT_AND_RERAISE); compiler_use_next_block(c, orelse); VISIT_SEQ(c, stmt, s->v.Try.orelse); + ADDOP_JUMP(c, JUMP_FORWARD, end); + compiler_use_next_block(c, end); + return 1; +} + +/* + Code generated for "try: S except* E1 as V1: S1 except* E2 as V2: S2 ...": + (The contents of the value stack is shown in [], with the top + at the right; 'tb' is trace-back info, 'val' the exception instance, + and 'typ' the exception's type.) + + Value stack Label Instruction Argument + [] SETUP_FINALLY L1 + [] <code for S> + [] POP_BLOCK + [] JUMP_FORWARD L0 + + [tb, val, typ] L1: DUP_TOP_TWO ) save a copy of the + [tb, val, typ, orig, typ] POP_TOP ) original raised exception + [tb, val, typ, orig] ROT_FOUR ) + [orig, tb, val, typ] BUILD_LIST ) list for raised/reraised + [orig, tb, val, typ, res] ROT_FOUR ) exceptions ("result") + + [orig, res, tb, val, typ] <evaluate E1> ) + [orig, res, tb, val, typ, E1] JUMP_IF_NOT_EXC_MATCH L2 ) only if E1 + + [orig, res, tb, rest, typ, tb, match, typ] POP + [orig, res, tb, rest, typ, tb, match] <assign to V1> (or POP if no V1) + [orig, res, tb, rest, typ, tb] POP + + [orig, res, tb, rest, typ] SETUP_FINALLY R1 + [orig, res, tb, rest, typ] <code for S1> + [orig, res, tb, rest, typ] JUMP_FORWARD L2 + + [orig, res, tb, rest, typ, i, tb, v, t] R1: POP ) exception raised in except* body + [orig, res, tb, rest, typ, i, tb, v] LIST_APPEND 6 ) add it to res + [orig, res, tb, rest, typ, i, tb] POP + [orig, res, tb, rest, typ, i] POP + + [orig, res, tb, rest, typ] L2: <evaluate E2> + .............................etc....................... + + [orig, res, tb, rest, typ] Ln+1: POP ) add unhandled exception + [orig, res, tb, rest] LIST_APPEND 2 ) to res (could be None) + [orig, res, tb] POP + + [orig, res] PREP_RERAISE_STAR + [i, tb, val, typ] POP_JUMP_IF_TRUE RER + [i, tb, val, typ] POP + [i, tb, val] POP + [i, tb] POP + [i] POP + [] JUMP_FORWARD L0 + + [i, tb, val, typ] RER: POP_EXCEPT_AND_RERAISE + + [] L0: <next statement> +*/ +static int +compiler_try_star_except(struct compiler *c, stmt_ty s) +{ + basicblock *body = compiler_new_block(c); + if (body == NULL) { + return 0; + } + basicblock *except = compiler_new_block(c); + if (except == NULL) { + return 0; + } + basicblock *orelse = compiler_new_block(c); + if (orelse == NULL) { + return 0; + } + basicblock *end = compiler_new_block(c); + if (end == NULL) { + return 0; + } + basicblock *cleanup = compiler_new_block(c); + if (cleanup == NULL) { + return 0; + } + basicblock *reraise_star = compiler_new_block(c); + if (reraise_star == NULL) { + return 0; + } + + ADDOP_JUMP(c, SETUP_FINALLY, except); + compiler_use_next_block(c, body); + if (!compiler_push_fblock(c, TRY_EXCEPT, body, NULL, NULL)) { + return 0; + } + VISIT_SEQ(c, stmt, s->v.TryStar.body); + compiler_pop_fblock(c, TRY_EXCEPT, body); + ADDOP_NOLINE(c, POP_BLOCK); + ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, orelse); + Py_ssize_t n = asdl_seq_LEN(s->v.TryStar.handlers); + compiler_use_next_block(c, except); + + UNSET_LOC(c); + ADDOP_JUMP(c, SETUP_CLEANUP, cleanup); + ADDOP(c, PUSH_EXC_INFO); + /* Runtime will push a block here, so we need to account for that */ + if (!compiler_push_fblock(c, EXCEPTION_GROUP_HANDLER, + NULL, NULL, "except handler")) { + return 0; + } + for (Py_ssize_t i = 0; i < n; i++) { + excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET( + s->v.TryStar.handlers, i); + SET_LOC(c, handler); + except = compiler_new_block(c); + if (except == NULL) { + return 0; + } + if (i == 0) { + /* Push the original EG into the stack */ + /* + [tb, val, exc] DUP_TOP_TWO + [tb, val, exc, val, exc] POP_TOP + [tb, val, exc, val] ROT_FOUR + [val, tb, val, exc] + */ + ADDOP(c, DUP_TOP_TWO); + ADDOP(c, POP_TOP); + ADDOP(c, ROT_FOUR); + + /* create empty list for exceptions raised/reraise in the except* blocks */ + /* + [val, tb, val, exc] BUILD_LIST + [val, tb, val, exc, []] ROT_FOUR + [val, [], tb, val, exc] + */ + ADDOP_I(c, BUILD_LIST, 0); + ADDOP(c, ROT_FOUR); + } + if (handler->v.ExceptHandler.type) { + VISIT(c, expr, handler->v.ExceptHandler.type); + ADDOP_JUMP(c, JUMP_IF_NOT_EG_MATCH, except); + NEXT_BLOCK(c); + } + ADDOP(c, POP_TOP); // exc_type + + basicblock *cleanup_end = compiler_new_block(c); + if (cleanup_end == NULL) { + return 0; + } + basicblock *cleanup_body = compiler_new_block(c); + if (cleanup_body == NULL) { + return 0; + } + + if (handler->v.ExceptHandler.name) { + compiler_nameop(c, handler->v.ExceptHandler.name, Store); + } + else { + ADDOP(c, POP_TOP); // val + } + ADDOP(c, POP_TOP); // tb + + /* + try: + # body + except type as name: + try: + # body + finally: + name = None # in case body contains "del name" + del name + */ + /* second try: */ + ADDOP_JUMP(c, SETUP_CLEANUP, cleanup_end); + compiler_use_next_block(c, cleanup_body); + if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, handler->v.ExceptHandler.name)) + return 0; + + /* second # body */ + VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body); + compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body); + /* name = None; del name; # Mark as artificial */ + UNSET_LOC(c); + ADDOP(c, POP_BLOCK); + if (handler->v.ExceptHandler.name) { + ADDOP_LOAD_CONST(c, Py_None); + compiler_nameop(c, handler->v.ExceptHandler.name, Store); + compiler_nameop(c, handler->v.ExceptHandler.name, Del); + } + ADDOP_JUMP(c, JUMP_FORWARD, except); + + /* except: */ + compiler_use_next_block(c, cleanup_end); + + /* name = None; del name; # Mark as artificial */ + UNSET_LOC(c); + + if (handler->v.ExceptHandler.name) { + ADDOP_LOAD_CONST(c, Py_None); + compiler_nameop(c, handler->v.ExceptHandler.name, Store); + compiler_nameop(c, handler->v.ExceptHandler.name, Del); + } + + /* add exception raised to the res list */ + ADDOP(c, POP_TOP); // type + ADDOP_I(c, LIST_APPEND, 6); // exc + ADDOP(c, POP_TOP); // tb + ADDOP(c, POP_TOP); // lasti + + ADDOP_JUMP(c, JUMP_ABSOLUTE, except); + compiler_use_next_block(c, except); + + if (i == n - 1) { + /* Add exc to the list (if not None it's the unhandled part of the EG) */ + ADDOP(c, POP_TOP); + ADDOP_I(c, LIST_APPEND, 2); + ADDOP(c, POP_TOP); + ADDOP_JUMP(c, JUMP_FORWARD, reraise_star); + } + } + /* Mark as artificial */ + UNSET_LOC(c); + compiler_pop_fblock(c, EXCEPTION_GROUP_HANDLER, NULL); + basicblock *reraise = compiler_new_block(c); + if (!reraise) { + return 0; + } + + compiler_use_next_block(c, reraise_star); + ADDOP(c, PREP_RERAISE_STAR); + ADDOP(c, DUP_TOP); + ADDOP_JUMP(c, POP_JUMP_IF_TRUE, reraise); + NEXT_BLOCK(c); + + /* Nothing to reraise - pop it */ + ADDOP(c, POP_TOP); + ADDOP(c, POP_TOP); + ADDOP(c, POP_TOP); + 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); + compiler_use_next_block(c, cleanup); + ADDOP(c, POP_EXCEPT_AND_RERAISE); + compiler_use_next_block(c, orelse); + VISIT_SEQ(c, stmt, s->v.TryStar.orelse); + ADDOP_JUMP(c, JUMP_FORWARD, end); compiler_use_next_block(c, end); return 1; } @@ -3372,6 +3697,16 @@ compiler_try(struct compiler *c, stmt_ty s) { return compiler_try_except(c, s); } +static int +compiler_try_star(struct compiler *c, stmt_ty s) +{ + if (s->v.TryStar.finalbody && asdl_seq_LEN(s->v.TryStar.finalbody)) { + return compiler_try_star_finally(c, s); + } + else { + return compiler_try_star_except(c, s); + } +} static int compiler_import_as(struct compiler *c, identifier name, identifier asname) @@ -3634,6 +3969,8 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s) break; case Try_kind: return compiler_try(c, s); + case TryStar_kind: + return compiler_try_star(c, s); case Assert_kind: return compiler_assert(c, s); case Import_kind: diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 872a688..aeb9830 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -87,7 +87,7 @@ static void *opcode_targets[256] = { &&TARGET_SETUP_ANNOTATIONS, &&TARGET_YIELD_VALUE, &&TARGET_LOAD_CONST__LOAD_FAST, - &&TARGET_STORE_FAST__STORE_FAST, + &&TARGET_PREP_RERAISE_STAR, &&TARGET_POP_EXCEPT, &&TARGET_STORE_NAME, &&TARGET_DELETE_NAME, @@ -122,11 +122,11 @@ static void *opcode_targets[256] = { &&TARGET_COPY, &&TARGET_JUMP_IF_NOT_EXC_MATCH, &&TARGET_BINARY_OP, - &&_unknown_opcode, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_LOAD_FAST, &&TARGET_STORE_FAST, &&TARGET_DELETE_FAST, - &&_unknown_opcode, + &&TARGET_JUMP_IF_NOT_EG_MATCH, &&_unknown_opcode, &&TARGET_GEN_START, &&TARGET_RAISE_VARARGS, diff --git a/Python/symtable.c b/Python/symtable.c index 7a1f060..01c6ec1 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1345,6 +1345,12 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) VISIT_SEQ(st, excepthandler, s->v.Try.handlers); VISIT_SEQ(st, stmt, s->v.Try.finalbody); break; + case TryStar_kind: + VISIT_SEQ(st, stmt, s->v.TryStar.body); + VISIT_SEQ(st, stmt, s->v.TryStar.orelse); + VISIT_SEQ(st, excepthandler, s->v.TryStar.handlers); + VISIT_SEQ(st, stmt, s->v.TryStar.finalbody); + break; case Assert_kind: VISIT(st, expr, s->v.Assert.test); if (s->v.Assert.msg) |