bpo-43892: Make match patterns explicit in the AST (GH-25585)

Co-authored-by: Brandt Bucher <brandtbucher@gmail.com>
diff --git a/Python/ast_opt.c b/Python/ast_opt.c
index 6eb514e..254dd64 100644
--- a/Python/ast_opt.c
+++ b/Python/ast_opt.c
@@ -411,7 +411,7 @@ static int astfold_arg(arg_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);
+static int astfold_pattern(pattern_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
 
 #define CALL(FUNC, TYPE, ARG) \
     if (!FUNC((ARG), ctx_, state)) \
@@ -602,10 +602,6 @@ 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
     }
@@ -797,112 +793,48 @@ astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
 }
 
 static int
-astfold_pattern_negative(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
+astfold_pattern(pattern_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) {
+    // Currently, this is really only used to form complex/negative numeric
+    // constants in MatchValue and MatchMapping nodes
+    // We still recurse into all subexpressions and subpatterns anyway
+    if (++state->recursion_depth > state->recursion_limit) {
+        PyErr_SetString(PyExc_RecursionError,
+                        "maximum recursion depth exceeded during compilation");
         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:
+        case MatchValue_kind:
+            CALL(astfold_expr, expr_ty, node_->v.MatchValue.value);
             break;
-        case BinOp_kind:
-            CALL(astfold_pattern_complex, expr_ty, node_);
+        case MatchSingleton_kind:
             break;
-        case Call_kind:
-            CALL_SEQ(astfold_pattern, expr, node_->v.Call.args);
-            CALL_SEQ(astfold_pattern_keyword, keyword, node_->v.Call.keywords);
+        case MatchSequence_kind:
+            CALL_SEQ(astfold_pattern, pattern, node_->v.MatchSequence.patterns);
             break;
-        case Constant_kind:
+        case MatchMapping_kind:
+            CALL_SEQ(astfold_expr, expr, node_->v.MatchMapping.keys);
+            CALL_SEQ(astfold_pattern, pattern, node_->v.MatchMapping.patterns);
             break;
-        case Dict_kind:
-            CALL_SEQ(astfold_pattern, expr, node_->v.Dict.keys);
-            CALL_SEQ(astfold_pattern, expr, node_->v.Dict.values);
+        case MatchClass_kind:
+            CALL(astfold_expr, expr_ty, node_->v.MatchClass.cls);
+            CALL_SEQ(astfold_pattern, pattern, node_->v.MatchClass.patterns);
+            CALL_SEQ(astfold_pattern, pattern, node_->v.MatchClass.kwd_patterns);
             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);
+        case MatchStar_kind:
             break;
         case MatchAs_kind:
-            CALL(astfold_pattern, expr_ty, node_->v.MatchAs.pattern);
+            if (node_->v.MatchAs.pattern) {
+                CALL(astfold_pattern, expr_ty, node_->v.MatchAs.pattern);
+            }
             break;
         case MatchOr_kind:
-            CALL_SEQ(astfold_pattern, expr, node_->v.MatchOr.patterns);
+            CALL_SEQ(astfold_pattern, pattern, 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();
+    // No default case, so the compiler will emit a warning if new pattern
+    // kinds are added without being handled here
     }
+    state->recursion_depth--;
     return 1;
 }