mirror of
https://github.com/AdaCore/cpython.git
synced 2026-02-12 12:57:15 -08:00
bpo-42128: Structural Pattern Matching (PEP 634) (GH-22917)
Co-authored-by: Guido van Rossum <guido@python.org> Co-authored-by: Talin <viridia@gmail.com> Co-authored-by: Pablo Galindo <pablogsal@gmail.com>
This commit is contained in:
469
Python/Python-ast.c
generated
469
Python/Python-ast.c
generated
File diff suppressed because it is too large
Load Diff
29
Python/ast.c
29
Python/ast.c
@@ -309,6 +309,14 @@ validate_expr(expr_ty exp, expr_context_ty ctx)
|
||||
return validate_exprs(exp->v.Tuple.elts, ctx, 0);
|
||||
case NamedExpr_kind:
|
||||
return validate_expr(exp->v.NamedExpr.value, Load);
|
||||
case MatchAs_kind:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"MatchAs is only valid in match_case patterns");
|
||||
return 0;
|
||||
case MatchOr_kind:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"MatchOr is only valid in match_case patterns");
|
||||
return 0;
|
||||
/* This last case doesn't have any checking. */
|
||||
case Name_kind:
|
||||
return 1;
|
||||
@@ -317,6 +325,13 @@ validate_expr(expr_ty exp, expr_context_ty ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
validate_pattern(expr_ty p)
|
||||
{
|
||||
// Coming soon (thanks Batuhan)!
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
_validate_nonempty_seq(asdl_seq *seq, const char *what, const char *owner)
|
||||
{
|
||||
@@ -415,6 +430,20 @@ validate_stmt(stmt_ty stmt)
|
||||
return 0;
|
||||
}
|
||||
return validate_body(stmt->v.AsyncWith.body, "AsyncWith");
|
||||
case Match_kind:
|
||||
if (!validate_expr(stmt->v.Match.subject, Load)
|
||||
|| !validate_nonempty_seq(stmt->v.Match.cases, "cases", "Match")) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < asdl_seq_LEN(stmt->v.Match.cases); i++) {
|
||||
match_case_ty m = asdl_seq_GET(stmt->v.Match.cases, i);
|
||||
if (!validate_pattern(m->pattern)
|
||||
|| (m->guard && !validate_expr(m->guard, Load))
|
||||
|| !validate_body(m->body, "match_case")) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
case Raise_kind:
|
||||
if (stmt->v.Raise.exc) {
|
||||
return validate_expr(stmt->v.Raise.exc, Load) &&
|
||||
|
||||
130
Python/ast_opt.c
130
Python/ast_opt.c
@@ -408,6 +408,9 @@ static int astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOp
|
||||
static int astfold_keyword(keyword_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
|
||||
static int astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
|
||||
static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
|
||||
static int astfold_match_case(match_case_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
|
||||
static int astfold_pattern(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
|
||||
|
||||
#define CALL(FUNC, TYPE, ARG) \
|
||||
if (!FUNC((ARG), ctx_, state)) \
|
||||
return 0;
|
||||
@@ -590,6 +593,10 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
case Constant_kind:
|
||||
// Already a constant, nothing further to do
|
||||
break;
|
||||
case MatchAs_kind:
|
||||
case MatchOr_kind:
|
||||
// These can't occur outside of patterns.
|
||||
Py_UNREACHABLE();
|
||||
// No default case, so the compiler will emit a warning if new expression
|
||||
// kinds are added without being handled here
|
||||
}
|
||||
@@ -709,6 +716,10 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
case Expr_kind:
|
||||
CALL(astfold_expr, expr_ty, node_->v.Expr.value);
|
||||
break;
|
||||
case Match_kind:
|
||||
CALL(astfold_expr, expr_ty, node_->v.Match.subject);
|
||||
CALL_SEQ(astfold_match_case, match_case, node_->v.Match.cases);
|
||||
break;
|
||||
// The following statements don't contain any subexpressions to be folded
|
||||
case Import_kind:
|
||||
case ImportFrom_kind:
|
||||
@@ -746,6 +757,125 @@ astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
astfold_pattern_negative(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
{
|
||||
assert(node_->kind == UnaryOp_kind);
|
||||
assert(node_->v.UnaryOp.op == USub);
|
||||
assert(node_->v.UnaryOp.operand->kind == Constant_kind);
|
||||
PyObject *value = node_->v.UnaryOp.operand->v.Constant.value;
|
||||
assert(PyComplex_CheckExact(value) ||
|
||||
PyFloat_CheckExact(value) ||
|
||||
PyLong_CheckExact(value));
|
||||
PyObject *negated = PyNumber_Negative(value);
|
||||
if (negated == NULL) {
|
||||
return 0;
|
||||
}
|
||||
assert(PyComplex_CheckExact(negated) ||
|
||||
PyFloat_CheckExact(negated) ||
|
||||
PyLong_CheckExact(negated));
|
||||
return make_const(node_, negated, ctx_);
|
||||
}
|
||||
|
||||
static int
|
||||
astfold_pattern_complex(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
{
|
||||
expr_ty left = node_->v.BinOp.left;
|
||||
expr_ty right = node_->v.BinOp.right;
|
||||
if (left->kind == UnaryOp_kind) {
|
||||
CALL(astfold_pattern_negative, expr_ty, left);
|
||||
}
|
||||
assert(left->kind = Constant_kind);
|
||||
assert(right->kind = Constant_kind);
|
||||
// LHS must be real, RHS must be imaginary:
|
||||
if (!(PyFloat_CheckExact(left->v.Constant.value) ||
|
||||
PyLong_CheckExact(left->v.Constant.value)) ||
|
||||
!PyComplex_CheckExact(right->v.Constant.value))
|
||||
{
|
||||
// Not actually valid, but it's the compiler's job to complain:
|
||||
return 1;
|
||||
}
|
||||
PyObject *new;
|
||||
if (node_->v.BinOp.op == Add) {
|
||||
new = PyNumber_Add(left->v.Constant.value, right->v.Constant.value);
|
||||
}
|
||||
else {
|
||||
assert(node_->v.BinOp.op == Sub);
|
||||
new = PyNumber_Subtract(left->v.Constant.value, right->v.Constant.value);
|
||||
}
|
||||
if (new == NULL) {
|
||||
return 0;
|
||||
}
|
||||
assert(PyComplex_CheckExact(new));
|
||||
return make_const(node_, new, ctx_);
|
||||
}
|
||||
|
||||
static int
|
||||
astfold_pattern_keyword(keyword_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
{
|
||||
CALL(astfold_pattern, expr_ty, node_->value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
astfold_pattern(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
{
|
||||
// Don't blindly optimize the pattern as an expr; it plays by its own rules!
|
||||
// Currently, this is only used to form complex/negative numeric constants.
|
||||
switch (node_->kind) {
|
||||
case Attribute_kind:
|
||||
break;
|
||||
case BinOp_kind:
|
||||
CALL(astfold_pattern_complex, expr_ty, node_);
|
||||
break;
|
||||
case Call_kind:
|
||||
CALL_SEQ(astfold_pattern, expr, node_->v.Call.args);
|
||||
CALL_SEQ(astfold_pattern_keyword, keyword, node_->v.Call.keywords);
|
||||
break;
|
||||
case Constant_kind:
|
||||
break;
|
||||
case Dict_kind:
|
||||
CALL_SEQ(astfold_pattern, expr, node_->v.Dict.keys);
|
||||
CALL_SEQ(astfold_pattern, expr, node_->v.Dict.values);
|
||||
break;
|
||||
// Not actually valid, but it's the compiler's job to complain:
|
||||
case JoinedStr_kind:
|
||||
break;
|
||||
case List_kind:
|
||||
CALL_SEQ(astfold_pattern, expr, node_->v.List.elts);
|
||||
break;
|
||||
case MatchAs_kind:
|
||||
CALL(astfold_pattern, expr_ty, node_->v.MatchAs.pattern);
|
||||
break;
|
||||
case MatchOr_kind:
|
||||
CALL_SEQ(astfold_pattern, expr, node_->v.MatchOr.patterns);
|
||||
break;
|
||||
case Name_kind:
|
||||
break;
|
||||
case Starred_kind:
|
||||
CALL(astfold_pattern, expr_ty, node_->v.Starred.value);
|
||||
break;
|
||||
case Tuple_kind:
|
||||
CALL_SEQ(astfold_pattern, expr, node_->v.Tuple.elts);
|
||||
break;
|
||||
case UnaryOp_kind:
|
||||
CALL(astfold_pattern_negative, expr_ty, node_);
|
||||
break;
|
||||
default:
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
astfold_match_case(match_case_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
{
|
||||
CALL(astfold_pattern, expr_ty, node_->pattern);
|
||||
CALL_OPT(astfold_expr, expr_ty, node_->guard);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->body);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#undef CALL
|
||||
#undef CALL_OPT
|
||||
#undef CALL_SEQ
|
||||
|
||||
382
Python/ceval.c
382
Python/ceval.c
@@ -880,6 +880,230 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// PEP 634: Structural Pattern Matching
|
||||
|
||||
|
||||
// Return a tuple of values corresponding to keys, with error checks for
|
||||
// duplicate/missing keys.
|
||||
static PyObject*
|
||||
match_keys(PyThreadState *tstate, PyObject *map, PyObject *keys)
|
||||
{
|
||||
assert(PyTuple_CheckExact(keys));
|
||||
Py_ssize_t nkeys = PyTuple_GET_SIZE(keys);
|
||||
if (!nkeys) {
|
||||
// No keys means no items.
|
||||
return PyTuple_New(0);
|
||||
}
|
||||
PyObject *seen = NULL;
|
||||
PyObject *dummy = NULL;
|
||||
PyObject *values = NULL;
|
||||
// We use the two argument form of map.get(key, default) for two reasons:
|
||||
// - Atomically check for a key and get its value without error handling.
|
||||
// - Don't cause key creation or resizing in dict subclasses like
|
||||
// collections.defaultdict that define __missing__ (or similar).
|
||||
_Py_IDENTIFIER(get);
|
||||
PyObject *get = _PyObject_GetAttrId(map, &PyId_get);
|
||||
if (get == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
seen = PySet_New(NULL);
|
||||
if (seen == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
// dummy = object()
|
||||
dummy = _PyObject_CallNoArg((PyObject *)&PyBaseObject_Type);
|
||||
if (dummy == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
values = PyList_New(0);
|
||||
if (values == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
for (Py_ssize_t i = 0; i < nkeys; i++) {
|
||||
PyObject *key = PyTuple_GET_ITEM(keys, i);
|
||||
if (PySet_Contains(seen, key) || PySet_Add(seen, key)) {
|
||||
if (!_PyErr_Occurred(tstate)) {
|
||||
// Seen it before!
|
||||
_PyErr_Format(tstate, PyExc_ValueError,
|
||||
"mapping pattern checks duplicate key (%R)", key);
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
PyObject *value = PyObject_CallFunctionObjArgs(get, key, dummy, NULL);
|
||||
if (value == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
if (value == dummy) {
|
||||
// key not in map!
|
||||
Py_DECREF(value);
|
||||
Py_DECREF(values);
|
||||
// Return None:
|
||||
Py_INCREF(Py_None);
|
||||
values = Py_None;
|
||||
goto done;
|
||||
}
|
||||
PyList_Append(values, value);
|
||||
Py_DECREF(value);
|
||||
}
|
||||
Py_SETREF(values, PyList_AsTuple(values));
|
||||
// Success:
|
||||
done:
|
||||
Py_DECREF(get);
|
||||
Py_DECREF(seen);
|
||||
Py_DECREF(dummy);
|
||||
return values;
|
||||
fail:
|
||||
Py_XDECREF(get);
|
||||
Py_XDECREF(seen);
|
||||
Py_XDECREF(dummy);
|
||||
Py_XDECREF(values);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Extract a named attribute from the subject, with additional bookkeeping to
|
||||
// raise TypeErrors for repeated lookups. On failure, return NULL (with no
|
||||
// error set). Use _PyErr_Occurred(tstate) to disambiguate.
|
||||
static PyObject*
|
||||
match_class_attr(PyThreadState *tstate, PyObject *subject, PyObject *type,
|
||||
PyObject *name, PyObject *seen)
|
||||
{
|
||||
assert(PyUnicode_CheckExact(name));
|
||||
assert(PySet_CheckExact(seen));
|
||||
if (PySet_Contains(seen, name) || PySet_Add(seen, name)) {
|
||||
if (!_PyErr_Occurred(tstate)) {
|
||||
// Seen it before!
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"%s() got multiple sub-patterns for attribute %R",
|
||||
((PyTypeObject*)type)->tp_name, name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
PyObject *attr = PyObject_GetAttr(subject, name);
|
||||
if (attr == NULL && _PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
|
||||
_PyErr_Clear(tstate);
|
||||
}
|
||||
return attr;
|
||||
}
|
||||
|
||||
// On success (match), return a tuple of extracted attributes. On failure (no
|
||||
// match), return NULL. Use _PyErr_Occurred(tstate) to disambiguate.
|
||||
static PyObject*
|
||||
match_class(PyThreadState *tstate, PyObject *subject, PyObject *type,
|
||||
Py_ssize_t nargs, PyObject *kwargs)
|
||||
{
|
||||
if (!PyType_Check(type)) {
|
||||
const char *e = "called match pattern must be a type";
|
||||
_PyErr_Format(tstate, PyExc_TypeError, e);
|
||||
return NULL;
|
||||
}
|
||||
assert(PyTuple_CheckExact(kwargs));
|
||||
// First, an isinstance check:
|
||||
if (PyObject_IsInstance(subject, type) <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
// So far so good:
|
||||
PyObject *seen = PySet_New(NULL);
|
||||
if (seen == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *attrs = PyList_New(0);
|
||||
if (attrs == NULL) {
|
||||
Py_DECREF(seen);
|
||||
return NULL;
|
||||
}
|
||||
// NOTE: From this point on, goto fail on failure:
|
||||
PyObject *match_args = NULL;
|
||||
// First, the positional subpatterns:
|
||||
if (nargs) {
|
||||
int match_self = 0;
|
||||
match_args = PyObject_GetAttrString(type, "__match_args__");
|
||||
if (match_args) {
|
||||
if (PyList_CheckExact(match_args)) {
|
||||
Py_SETREF(match_args, PyList_AsTuple(match_args));
|
||||
}
|
||||
if (match_args == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
if (!PyTuple_CheckExact(match_args)) {
|
||||
const char *e = "%s.__match_args__ must be a list or tuple "
|
||||
"(got %s)";
|
||||
_PyErr_Format(tstate, PyExc_TypeError, e,
|
||||
((PyTypeObject *)type)->tp_name,
|
||||
Py_TYPE(match_args)->tp_name);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
|
||||
_PyErr_Clear(tstate);
|
||||
// _Py_TPFLAGS_MATCH_SELF is only acknowledged if the type does not
|
||||
// define __match_args__. This is natural behavior for subclasses:
|
||||
// it's as if __match_args__ is some "magic" value that is lost as
|
||||
// soon as they redefine it.
|
||||
match_args = PyTuple_New(0);
|
||||
match_self = PyType_HasFeature((PyTypeObject*)type,
|
||||
_Py_TPFLAGS_MATCH_SELF);
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
assert(PyTuple_CheckExact(match_args));
|
||||
Py_ssize_t allowed = match_self ? 1 : PyTuple_GET_SIZE(match_args);
|
||||
if (allowed < nargs) {
|
||||
const char *plural = (allowed == 1) ? "" : "s";
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"%s() accepts %d positional sub-pattern%s (%d given)",
|
||||
((PyTypeObject*)type)->tp_name,
|
||||
allowed, plural, nargs);
|
||||
goto fail;
|
||||
}
|
||||
if (match_self) {
|
||||
// Easy. Copy the subject itself, and move on to kwargs.
|
||||
PyList_Append(attrs, subject);
|
||||
}
|
||||
else {
|
||||
for (Py_ssize_t i = 0; i < nargs; i++) {
|
||||
PyObject *name = PyTuple_GET_ITEM(match_args, i);
|
||||
if (!PyUnicode_CheckExact(name)) {
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"__match_args__ elements must be strings "
|
||||
"(got %s)", Py_TYPE(name)->tp_name);
|
||||
goto fail;
|
||||
}
|
||||
PyObject *attr = match_class_attr(tstate, subject, type, name,
|
||||
seen);
|
||||
if (attr == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
PyList_Append(attrs, attr);
|
||||
Py_DECREF(attr);
|
||||
}
|
||||
}
|
||||
Py_CLEAR(match_args);
|
||||
}
|
||||
// Finally, the keyword subpatterns:
|
||||
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwargs); i++) {
|
||||
PyObject *name = PyTuple_GET_ITEM(kwargs, i);
|
||||
PyObject *attr = match_class_attr(tstate, subject, type, name, seen);
|
||||
if (attr == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
PyList_Append(attrs, attr);
|
||||
Py_DECREF(attr);
|
||||
}
|
||||
Py_SETREF(attrs, PyList_AsTuple(attrs));
|
||||
Py_DECREF(seen);
|
||||
return attrs;
|
||||
fail:
|
||||
// We really don't care whether an error was raised or not... that's our
|
||||
// caller's problem. All we know is that the match failed.
|
||||
Py_XDECREF(match_args);
|
||||
Py_DECREF(seen);
|
||||
Py_DECREF(attrs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause);
|
||||
static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **);
|
||||
|
||||
@@ -3618,6 +3842,164 @@ main_loop:
|
||||
#endif
|
||||
}
|
||||
|
||||
case TARGET(GET_LEN): {
|
||||
// PUSH(len(TOS))
|
||||
Py_ssize_t len_i = PyObject_Length(TOP());
|
||||
if (len_i < 0) {
|
||||
goto error;
|
||||
}
|
||||
PyObject *len_o = PyLong_FromSsize_t(len_i);
|
||||
if (len_o == NULL) {
|
||||
goto error;
|
||||
}
|
||||
PUSH(len_o);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(MATCH_CLASS): {
|
||||
// Pop TOS. On success, set TOS to True and TOS1 to a tuple of
|
||||
// attributes. On failure, set TOS to False.
|
||||
PyObject *names = POP();
|
||||
PyObject *type = TOP();
|
||||
PyObject *subject = SECOND();
|
||||
assert(PyTuple_CheckExact(names));
|
||||
PyObject *attrs = match_class(tstate, subject, type, oparg, names);
|
||||
Py_DECREF(names);
|
||||
if (attrs) {
|
||||
// Success!
|
||||
assert(PyTuple_CheckExact(attrs));
|
||||
Py_DECREF(subject);
|
||||
SET_SECOND(attrs);
|
||||
}
|
||||
else if (_PyErr_Occurred(tstate)) {
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(type);
|
||||
SET_TOP(PyBool_FromLong(!!attrs));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(MATCH_MAPPING): {
|
||||
// PUSH(isinstance(TOS, _collections_abc.Mapping))
|
||||
PyObject *subject = TOP();
|
||||
// Fast path for dicts:
|
||||
if (PyDict_Check(subject)) {
|
||||
Py_INCREF(Py_True);
|
||||
PUSH(Py_True);
|
||||
DISPATCH();
|
||||
}
|
||||
// Lazily import _collections_abc.Mapping, and keep it handy on the
|
||||
// PyInterpreterState struct (it gets cleaned up at exit):
|
||||
PyInterpreterState *interp = PyInterpreterState_Get();
|
||||
if (interp->map_abc == NULL) {
|
||||
PyObject *abc = PyImport_ImportModule("_collections_abc");
|
||||
if (abc == NULL) {
|
||||
goto error;
|
||||
}
|
||||
interp->map_abc = PyObject_GetAttrString(abc, "Mapping");
|
||||
if (interp->map_abc == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
int match = PyObject_IsInstance(subject, interp->map_abc);
|
||||
if (match < 0) {
|
||||
goto error;
|
||||
}
|
||||
PUSH(PyBool_FromLong(match));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(MATCH_SEQUENCE): {
|
||||
// PUSH(not isinstance(TOS, (bytearray, bytes, str))
|
||||
// and isinstance(TOS, _collections_abc.Sequence))
|
||||
PyObject *subject = TOP();
|
||||
// Fast path for lists and tuples:
|
||||
if (PyType_FastSubclass(Py_TYPE(subject),
|
||||
Py_TPFLAGS_LIST_SUBCLASS |
|
||||
Py_TPFLAGS_TUPLE_SUBCLASS))
|
||||
{
|
||||
Py_INCREF(Py_True);
|
||||
PUSH(Py_True);
|
||||
DISPATCH();
|
||||
}
|
||||
// Bail on some possible Sequences that we intentionally exclude:
|
||||
if (PyType_FastSubclass(Py_TYPE(subject),
|
||||
Py_TPFLAGS_BYTES_SUBCLASS |
|
||||
Py_TPFLAGS_UNICODE_SUBCLASS) ||
|
||||
PyByteArray_Check(subject))
|
||||
{
|
||||
Py_INCREF(Py_False);
|
||||
PUSH(Py_False);
|
||||
DISPATCH();
|
||||
}
|
||||
// Lazily import _collections_abc.Sequence, and keep it handy on the
|
||||
// PyInterpreterState struct (it gets cleaned up at exit):
|
||||
PyInterpreterState *interp = PyInterpreterState_Get();
|
||||
if (interp->seq_abc == NULL) {
|
||||
PyObject *abc = PyImport_ImportModule("_collections_abc");
|
||||
if (abc == NULL) {
|
||||
goto error;
|
||||
}
|
||||
interp->seq_abc = PyObject_GetAttrString(abc, "Sequence");
|
||||
if (interp->seq_abc == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
int match = PyObject_IsInstance(subject, interp->seq_abc);
|
||||
if (match < 0) {
|
||||
goto error;
|
||||
}
|
||||
PUSH(PyBool_FromLong(match));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(MATCH_KEYS): {
|
||||
// On successful match for all keys, PUSH(values) and PUSH(True).
|
||||
// Otherwise, PUSH(None) and PUSH(False).
|
||||
PyObject *keys = TOP();
|
||||
PyObject *subject = SECOND();
|
||||
PyObject *values_or_none = match_keys(tstate, subject, keys);
|
||||
if (values_or_none == NULL) {
|
||||
goto error;
|
||||
}
|
||||
PUSH(values_or_none);
|
||||
if (values_or_none == Py_None) {
|
||||
Py_INCREF(Py_False);
|
||||
PUSH(Py_False);
|
||||
DISPATCH();
|
||||
}
|
||||
assert(PyTuple_CheckExact(values_or_none));
|
||||
Py_INCREF(Py_True);
|
||||
PUSH(Py_True);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(COPY_DICT_WITHOUT_KEYS): {
|
||||
// rest = dict(TOS1)
|
||||
// for key in TOS:
|
||||
// del rest[key]
|
||||
// SET_TOP(rest)
|
||||
PyObject *keys = TOP();
|
||||
PyObject *subject = SECOND();
|
||||
PyObject *rest = PyDict_New();
|
||||
if (rest == NULL || PyDict_Update(rest, subject)) {
|
||||
Py_XDECREF(rest);
|
||||
goto error;
|
||||
}
|
||||
// This may seem a bit inefficient, but keys is rarely big enough to
|
||||
// actually impact runtime.
|
||||
assert(PyTuple_CheckExact(keys));
|
||||
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(keys); i++) {
|
||||
if (PyDict_DelItem(rest, PyTuple_GET_ITEM(keys, i))) {
|
||||
Py_DECREF(rest);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
Py_DECREF(keys);
|
||||
SET_TOP(rest);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(GET_ITER): {
|
||||
/* before: [obj]; after [getiter(obj)] */
|
||||
PyObject *iterable = TOP();
|
||||
|
||||
739
Python/compile.c
739
Python/compile.c
File diff suppressed because it is too large
Load Diff
218
Python/importlib_external.h
generated
218
Python/importlib_external.h
generated
File diff suppressed because it is too large
Load Diff
12
Python/opcode_targets.h
generated
12
Python/opcode_targets.h
generated
@@ -29,11 +29,11 @@ static void *opcode_targets[256] = {
|
||||
&&TARGET_BINARY_TRUE_DIVIDE,
|
||||
&&TARGET_INPLACE_FLOOR_DIVIDE,
|
||||
&&TARGET_INPLACE_TRUE_DIVIDE,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_GET_LEN,
|
||||
&&TARGET_MATCH_MAPPING,
|
||||
&&TARGET_MATCH_SEQUENCE,
|
||||
&&TARGET_MATCH_KEYS,
|
||||
&&TARGET_COPY_DICT_WITHOUT_KEYS,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
@@ -151,7 +151,7 @@ static void *opcode_targets[256] = {
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_MATCH_CLASS,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_SETUP_ASYNC_WITH,
|
||||
&&TARGET_FORMAT_VALUE,
|
||||
|
||||
@@ -308,6 +308,8 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
|
||||
Py_CLEAR(interp->importlib);
|
||||
Py_CLEAR(interp->import_func);
|
||||
Py_CLEAR(interp->dict);
|
||||
Py_CLEAR(interp->map_abc);
|
||||
Py_CLEAR(interp->seq_abc);
|
||||
#ifdef HAVE_FORK
|
||||
Py_CLEAR(interp->before_forkers);
|
||||
Py_CLEAR(interp->after_forkers_parent);
|
||||
|
||||
@@ -207,6 +207,7 @@ static int symtable_visit_argannotations(struct symtable *st, asdl_arg_seq *args
|
||||
static int symtable_implicit_arg(struct symtable *st, int pos);
|
||||
static int symtable_visit_annotations(struct symtable *st, arguments_ty, expr_ty);
|
||||
static int symtable_visit_withitem(struct symtable *st, withitem_ty item);
|
||||
static int symtable_visit_match_case(struct symtable *st, match_case_ty m);
|
||||
|
||||
|
||||
static identifier top = NULL, lambda = NULL, genexpr = NULL,
|
||||
@@ -239,6 +240,7 @@ symtable_new(void)
|
||||
goto fail;
|
||||
st->st_cur = NULL;
|
||||
st->st_private = NULL;
|
||||
st->in_pattern = 0;
|
||||
return st;
|
||||
fail:
|
||||
PySymtable_Free(st);
|
||||
@@ -1294,6 +1296,10 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
|
||||
if (s->v.If.orelse)
|
||||
VISIT_SEQ(st, stmt, s->v.If.orelse);
|
||||
break;
|
||||
case Match_kind:
|
||||
VISIT(st, expr, s->v.Match.subject);
|
||||
VISIT_SEQ(st, match_case, s->v.Match.cases);
|
||||
break;
|
||||
case Raise_kind:
|
||||
if (s->v.Raise.exc) {
|
||||
VISIT(st, expr, s->v.Raise.exc);
|
||||
@@ -1648,6 +1654,13 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
|
||||
VISIT(st, expr, e->v.Slice.step)
|
||||
break;
|
||||
case Name_kind:
|
||||
// Don't make "_" a local when used in a pattern:
|
||||
if (st->in_pattern &&
|
||||
e->v.Name.ctx == Store &&
|
||||
_PyUnicode_EqualToASCIIString(e->v.Name.id, "_"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!symtable_add_def(st, e->v.Name.id,
|
||||
e->v.Name.ctx == Load ? USE : DEF_LOCAL))
|
||||
VISIT_QUIT(st, 0);
|
||||
@@ -1667,6 +1680,13 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
|
||||
case Tuple_kind:
|
||||
VISIT_SEQ(st, expr, e->v.Tuple.elts);
|
||||
break;
|
||||
case MatchAs_kind:
|
||||
VISIT(st, expr, e->v.MatchAs.pattern);
|
||||
symtable_add_def(st, e->v.MatchAs.name, DEF_LOCAL);
|
||||
break;
|
||||
case MatchOr_kind:
|
||||
VISIT_SEQ(st, expr, e->v.MatchOr.patterns);
|
||||
break;
|
||||
}
|
||||
VISIT_QUIT(st, 1);
|
||||
}
|
||||
@@ -1785,6 +1805,20 @@ symtable_visit_withitem(struct symtable *st, withitem_ty item)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
symtable_visit_match_case(struct symtable *st, match_case_ty m)
|
||||
{
|
||||
assert(!st->in_pattern);
|
||||
st->in_pattern = 1;
|
||||
VISIT(st, expr, m->pattern);
|
||||
assert(st->in_pattern);
|
||||
st->in_pattern = 0;
|
||||
if (m->guard) {
|
||||
VISIT(st, expr, m->guard);
|
||||
}
|
||||
VISIT_SEQ(st, stmt, m->body);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
symtable_visit_alias(struct symtable *st, alias_ty a)
|
||||
|
||||
Reference in New Issue
Block a user