summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>2021-12-14 16:48:15 (GMT)
committerGitHub <noreply@github.com>2021-12-14 16:48:15 (GMT)
commitd60457a6673cf0263213c2f2be02c633ec2e2038 (patch)
tree04461db9079cf30a98c5a4070098f795275aa910 /Python
parent850aefc2c651110a784cd5478af9774b1f6287a3 (diff)
downloadcpython-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.c229
-rw-r--r--Python/ast.c25
-rw-r--r--Python/ast_opt.c6
-rw-r--r--Python/ceval.c355
-rw-r--r--Python/compile.c339
-rw-r--r--Python/opcode_targets.h6
-rw-r--r--Python/symtable.c6
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)