Support signed numeric constants in match pattern ASTs (#9279)

Recognize numeric Expr::Constant values when validating signed literal
patterns, alongside parser-produced NumberLiteral nodes. Python AST
conversion uses the former representation. Accept unary plus as well as
minus and fold signed real/imaginary and complex patterns consistently.

Boolean and non-numeric constants remain excluded. Preserve the existing
parser-produced literal path and do not change source grammar.

Assisted-by: OpenAI Codex:model-version-unavailable
This commit is contained in:
youknowdot authored and GitHub committed 2026-10-22 11:21:10 +09:00
1 parent aba1f5cf69
commit 43368209c5
2 files changed
+24 -9

No files matched your search

+13 -5
View File
@@ -12288,12 +12288,12 @@ impl<'warnings> Compiler<'warnings> {
&mut self,
expr: &ast::Expr,
) -> CompileResult<Option<ConstantData>> {
// CPython 3.14 ast_preprocess.c::fold_const_match_patterns()
// CPython ast_preprocess.c::fold_const_match_patterns()
// folds only the constant forms needed by match patterns before
// codegen_pattern_value()/codegen_pattern_mapping_key() visit them.
Ok(match expr {
ast::Expr::UnaryOp(ast::ExprUnaryOp {
op: ast::UnaryOp::USub,
op: op @ (ast::UnaryOp::UAdd | ast::UnaryOp::USub),
operand,
..
}) => {
@@ -12301,7 +12301,11 @@ impl<'warnings> Compiler<'warnings> {
else {
return Ok(None);
};
Self::try_negate_match_pattern_constant(constant)
if *op == ast::UnaryOp::USub {
Self::try_negate_match_pattern_constant(constant)
} else {
Some(constant)
}
}
ast::Expr::BinOp(ast::ExprBinOp {
left, op, right, ..
@@ -12326,7 +12330,7 @@ impl<'warnings> Compiler<'warnings> {
return Ok(Some(constant));
}
let ast::Expr::UnaryOp(ast::ExprUnaryOp {
op: ast::UnaryOp::USub,
op: op @ (ast::UnaryOp::UAdd | ast::UnaryOp::USub),
operand,
..
}) = expr
@@ -12336,7 +12340,11 @@ impl<'warnings> Compiler<'warnings> {
let Some(constant) = self.try_compile_match_pattern_real_constant(operand)? else {
return Ok(None);
};
Ok(Self::try_negate_match_pattern_constant(constant))
Ok(if *op == ast::UnaryOp::USub {
Self::try_negate_match_pattern_constant(constant)
} else {
Some(constant)
})
}
fn try_compile_match_pattern_real_constant(
+11 -4
View File
@@ -191,6 +191,13 @@ fn validate_interpolated_elements<'a>(
}
fn ensure_literal_number(expr: &ast::Expr, allow_real: bool, allow_imaginary: bool) -> bool {
if let Some(constant) = ast_constant_value(expr) {
return match constant {
ConstantData::Integer { .. } | ConstantData::Float { .. } => allow_real,
ConstantData::Complex { .. } => allow_imaginary,
_ => false,
};
}
let ast::Expr::NumberLiteral(number) = expr else {
return false;
};
@@ -200,11 +207,11 @@ fn ensure_literal_number(expr: &ast::Expr, allow_real: bool, allow_imaginary: bo
}
}
fn ensure_literal_negative(expr: &ast::Expr, allow_real: bool, allow_imaginary: bool) -> bool {
fn ensure_literal_signed(expr: &ast::Expr, allow_real: bool, allow_imaginary: bool) -> bool {
let ast::Expr::UnaryOp(unary) = expr else {
return false;
};
if unary.op != ast::UnaryOp::USub {
if !matches!(unary.op, ast::UnaryOp::UAdd | ast::UnaryOp::USub) {
return false;
}
ensure_literal_number(&unary.operand, allow_real, allow_imaginary)
@@ -218,7 +225,7 @@ fn ensure_literal_complex(expr: &ast::Expr) -> bool {
return false;
}
let real_left = ensure_literal_number(&bin.left, true, false)
|| ensure_literal_negative(&bin.left, true, false);
|| ensure_literal_signed(&bin.left, true, false);
real_left && ensure_literal_number(&bin.right, false, true)
}
@@ -244,7 +251,7 @@ fn validate_pattern_match_value(vm: &VirtualMachine, expr: &ast::Expr) -> PyResu
Ok(())
}
ast::Expr::Attribute(_) => Ok(()),
ast::Expr::UnaryOp(_) if ensure_literal_negative(expr, true, true) => Ok(()),
ast::Expr::UnaryOp(_) if ensure_literal_signed(expr, true, true) => Ok(()),
ast::Expr::BinOp(_) if ensure_literal_complex(expr) => Ok(()),
ast::Expr::FString(_) | ast::Expr::TString(_) => Ok(()),
ast::Expr::BooleanLiteral(_)