Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 25 additions & 19 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1899,28 +1899,34 @@ iterations of the loop.

The operand determines which intrinsic function is called:

+----------------------------------------+-----------------------------------+
| Operand | Description |
+========================================+===================================+
| ``INTRINSIC_2_INVALID`` | Not valid |
+----------------------------------------+-----------------------------------+
| ``INTRINSIC_PREP_RERAISE_STAR`` | Calculates the |
| | :exc:`ExceptionGroup` to raise |
| | from a ``try-except*``. |
+----------------------------------------+-----------------------------------+
| ``INTRINSIC_TYPEVAR_WITH_BOUND`` | Creates a :class:`typing.TypeVar` |
| | with a bound. |
+----------------------------------------+-----------------------------------+
| ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a |
| | :class:`typing.TypeVar` with |
| | constraints. |
+----------------------------------------+-----------------------------------+
| ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__`` |
| | attribute of a function. |
+----------------------------------------+-----------------------------------+
+----------------------------------------+-----------------------------------------+
| Operand | Description |
+========================================+=========================================+
| ``INTRINSIC_2_INVALID`` | Not valid |
+----------------------------------------+-----------------------------------------+
| ``INTRINSIC_PREP_RERAISE_STAR`` | Calculates the |
| | :exc:`ExceptionGroup` to raise |
| | from a ``try-except*``. |
+----------------------------------------+-----------------------------------------+
| ``INTRINSIC_TYPEVAR_WITH_BOUND`` | Creates a :class:`typing.TypeVar` |
| | with a bound. |
+----------------------------------------+-----------------------------------------+
| ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a |
| | :class:`typing.TypeVar` with |
| | constraints. |
+----------------------------------------+-----------------------------------------+
| ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__`` |
| | attribute of a function. |
+----------------------------------------+-----------------------------------------+
| ``INTRINSIC_MATCH_CLASS_ISINSTANCE`` | Do :func:`isinstance` checks for |
| | :ref:`Class patterns <class-patterns>`. |
+----------------------------------------+-----------------------------------------+

.. versionadded:: 3.12

.. versionchanged:: 3.16
Added ``INTRINSIC_MATCH_CLASS_ISINSTANCE``.


.. opcode:: LOAD_SPECIAL

Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
#define INTRINSIC_TYPEVAR_WITH_CONSTRAINTS 3
#define INTRINSIC_SET_FUNCTION_TYPE_PARAMS 4
#define INTRINSIC_SET_TYPEPARAM_DEFAULT 5
#define INTRINSIC_MATCH_CLASS_ISINSTANCE 6

#define MAX_INTRINSIC_2 5
#define MAX_INTRINSIC_2 6

typedef PyObject *(*intrinsic_func1)(PyThreadState* tstate, PyObject *value);
typedef PyObject *(*intrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2);
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -5721,12 +5721,13 @@ def testfunc(n):
def test_match_class(self):
def testfunc(n):
class A:
__match_args__ = ("val",)
val = 1
x = A()
ret = 0
for _ in range(n):
match x:
case A():
case A(1):
ret += x.val
return ret

Expand All @@ -5735,7 +5736,7 @@ class A:
uops = get_opnames(ex)

self.assertIn("_MATCH_CLASS", uops)
self.assertEqual(count_ops(ex, "_POP_TOP_NOP"), 4)
self.assertEqual(count_ops(ex, "_POP_TOP_NOP"), 5)

def test_dict_update(self):
def testfunc(n):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add fast path for :keyword:`match` class patterns without any sub-patterns
to improve performance by ~15%.
7 changes: 7 additions & 0 deletions Python/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -6179,6 +6179,13 @@ codegen_pattern_class(compiler *c, pattern_ty p, pattern_context *pc)
if (nattrs) {
RETURN_IF_ERROR(validate_kwd_attrs(c, kwd_attrs, kwd_patterns));
}
if (nargs + nattrs == 0) {
// Fast path if there are no sub-patterns
VISIT(c, expr, p->v.MatchClass.cls);
ADDOP_I(c, LOC(p), CALL_INTRINSIC_2, INTRINSIC_MATCH_CLASS_ISINSTANCE);
RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE));
return SUCCESS;
}
VISIT(c, expr, p->v.MatchClass.cls);
PyObject *attr_names = PyTuple_New(nattrs);
if (attr_names == NULL) {
Expand Down
20 changes: 20 additions & 0 deletions Python/intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,25 @@ make_typevar_with_constraints(PyThreadState* Py_UNUSED(ignored), PyObject *name,
return _Py_make_typevar(name, NULL, evaluate_constraints);
}

static PyObject *
match_class_isinstance(PyThreadState* tstate, PyObject *subject, PyObject *type)
{
/* Fast path for class patterns with no sub-patterns, e.g. `case C():`
Equivalent to the isinstance check performed by _PyEval_MatchClass,
including the same TypeError when the pattern does not refer to a
class. */
if (!PyType_Check(type)) {
_PyErr_SetString(tstate, PyExc_TypeError,
"class pattern must refer to a class");
return NULL;
}
int res = PyObject_IsInstance(subject, type);
if (res < 0) {
return NULL;
}
return res ? Py_True : Py_False;
}

const intrinsic_func2_info
_PyIntrinsics_BinaryFunctions[] = {
INTRINSIC_FUNC_ENTRY(INTRINSIC_2_INVALID, no_intrinsic2)
Expand All @@ -278,6 +297,7 @@ _PyIntrinsics_BinaryFunctions[] = {
INTRINSIC_FUNC_ENTRY(INTRINSIC_TYPEVAR_WITH_CONSTRAINTS, make_typevar_with_constraints)
INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_FUNCTION_TYPE_PARAMS, _Py_set_function_type_params)
INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_TYPEPARAM_DEFAULT, _Py_set_typeparam_default)
INTRINSIC_FUNC_ENTRY(INTRINSIC_MATCH_CLASS_ISINSTANCE, match_class_isinstance)
};

#undef INTRINSIC_FUNC_ENTRY
Expand Down
Loading