From 8ae2dc75f6d6720b252a462e0cf91ddc1524db36 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 26 Aug 2025 12:15:45 +0900 Subject: [PATCH] MATCH_SELF --- compiler/codegen/src/compile.rs | 14 +++++++------- vm/src/builtins/bool.rs | 2 +- vm/src/builtins/bytearray.rs | 2 +- vm/src/builtins/bytes.rs | 2 +- vm/src/builtins/dict.rs | 2 +- vm/src/builtins/float.rs | 2 +- vm/src/builtins/int.rs | 2 +- vm/src/builtins/list.rs | 2 +- vm/src/builtins/set.rs | 4 ++-- vm/src/builtins/str.rs | 2 +- vm/src/builtins/tuple.rs | 2 +- vm/src/frame.rs | 30 ++++++++++-------------------- vm/src/types/slot.rs | 4 ++++ 13 files changed, 32 insertions(+), 38 deletions(-) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 5815f37ec..fe72b5238 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -36,7 +36,7 @@ use rustpython_compiler_core::{ Mode, OneIndexed, SourceFile, SourceLocation, bytecode::{ self, Arg as OpArgMarker, BinaryOperator, CodeObject, ComparisonOperator, ConstantData, - Instruction, OpArg, OpArgType, TestOperator, UnpackExArgs, + Instruction, OpArg, OpArgType, UnpackExArgs, }, }; use rustpython_wtf8::Wtf8Buf; @@ -3484,7 +3484,7 @@ impl Compiler { // Process each sub-pattern. for subpattern in patterns.iter().chain(kwd_patterns.iter()) { - // Decrement the on_top counter BEFORE processing each sub-pattern + // Decrement the on_top counter before processing each sub-pattern pc.on_top -= 1; // Check if this is a true wildcard (underscore pattern without name binding) @@ -3557,6 +3557,7 @@ impl Compiler { // Check if the mapping has at least 'size' keys emit!(self, Instruction::GetLen); self.emit_load_const(ConstantData::Integer { value: size.into() }); + // Stack: [subject, len, size] emit!( self, Instruction::CompareOperation { @@ -3564,6 +3565,7 @@ impl Compiler { } ); self.jump_to_fail_pop(pc, JumpOp::PopJumpIfFalse)?; + // Stack: [subject] } // Check for overflow (INT_MAX < size - 1) @@ -3606,9 +3608,8 @@ impl Compiler { self.compile_expression(key)?; } - // Stack: [subject, key1, key2, ..., keyn] } - // Stack: [subject] if size==0, or [subject, key1, ..., keyn] if size>0 + // Stack: [subject, key1, key2, ..., key_n] // Build tuple of keys (empty tuple if size==0) emit!(self, Instruction::BuildTuple { size }); @@ -3628,7 +3629,7 @@ impl Compiler { emit!( self, Instruction::TestOperation { - op: TestOperator::IsNot + op: bytecode::TestOperator::IsNot } ); // Stack: [subject, keys_tuple, values_tuple, bool] @@ -3924,8 +3925,7 @@ impl Compiler { Singleton::False => ConstantData::Boolean { value: false }, Singleton::True => ConstantData::Boolean { value: true }, }); - // Compare using the "Is" operator (identity check, not equality). - // This is important: False should not match 0, even though False == 0 + // Compare using the "Is" operator. emit!( self, Instruction::TestOperation { diff --git a/vm/src/builtins/bool.rs b/vm/src/builtins/bool.rs index a7569e2f8..a20e821d2 100644 --- a/vm/src/builtins/bool.rs +++ b/vm/src/builtins/bool.rs @@ -109,7 +109,7 @@ impl Constructor for PyBool { } } -#[pyclass(with(Constructor, AsNumber, Representable))] +#[pyclass(with(Constructor, AsNumber, Representable), flags(_MATCH_SELF))] impl PyBool { #[pymethod] fn __format__(obj: PyObjectRef, spec: PyStrRef, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 2d3bd1ebf..32eaa2b3e 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -170,7 +170,7 @@ impl PyByteArray { } #[pyclass( - flags(BASETYPE), + flags(BASETYPE, _MATCH_SELF), with( Py, PyRef, diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 6d77a3734..a06588cc8 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -132,7 +132,7 @@ impl PyRef { } #[pyclass( - flags(BASETYPE), + flags(BASETYPE, _MATCH_SELF), with( Py, PyRef, diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 2f1c91323..f9911ce03 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -176,7 +176,7 @@ impl PyDict { AsMapping, Representable ), - flags(BASETYPE, MAPPING) + flags(BASETYPE, MAPPING, _MATCH_SELF) )] impl PyDict { #[pyclassmethod] diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index b213be3a6..ad18db36a 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -201,7 +201,7 @@ fn float_from_string(val: PyObjectRef, vm: &VirtualMachine) -> PyResult { } #[pyclass( - flags(BASETYPE), + flags(BASETYPE, _MATCH_SELF), with(Comparable, Hashable, Constructor, AsNumber, Representable) )] impl PyFloat { diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index 9b38de499..70fd9bad9 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -317,7 +317,7 @@ impl PyInt { } #[pyclass( - flags(BASETYPE), + flags(BASETYPE, _MATCH_SELF), with(PyRef, Comparable, Hashable, Constructor, AsNumber, Representable) )] impl PyInt { diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 14c8341e4..13f29de40 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -109,7 +109,7 @@ pub type PyListRef = PyRef; AsSequence, Representable ), - flags(BASETYPE, SEQUENCE) + flags(BASETYPE, SEQUENCE, _MATCH_SELF) )] impl PyList { #[pymethod] diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index b68a51cb3..53ebc931a 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -529,7 +529,7 @@ fn reduce_set( AsNumber, Representable ), - flags(BASETYPE) + flags(BASETYPE, _MATCH_SELF) )] impl PySet { #[pymethod] @@ -946,7 +946,7 @@ impl Constructor for PyFrozenSet { } #[pyclass( - flags(BASETYPE), + flags(BASETYPE, _MATCH_SELF), with( Constructor, AsSequence, diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 641bf1e42..ddf04e6de 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -521,7 +521,7 @@ impl Py { } #[pyclass( - flags(BASETYPE), + flags(BASETYPE, _MATCH_SELF), with( PyRef, AsMapping, diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index abd7bf71d..f6a6dde79 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -244,7 +244,7 @@ impl PyTuple> { } #[pyclass( - flags(BASETYPE, SEQUENCE), + flags(BASETYPE, SEQUENCE, _MATCH_SELF), with( AsMapping, AsSequence, diff --git a/vm/src/frame.rs b/vm/src/frame.rs index d664804eb..7f43625d5 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -702,19 +702,17 @@ impl ExecutingFrame<'_> { } bytecode::Instruction::Swap { index } => { let len = self.state.stack.len(); - if len == 0 { - return Err(vm.new_runtime_error("stack underflow in SWAP".to_string())); - } + debug_assert!(len > 0, "stack underflow in SWAP"); let i = len - 1; // TOS index let index_val = index.get(arg) as usize; // CPython: SWAP(n) swaps TOS with PEEK(n) where PEEK(n) = stack_pointer[-n] // This means swap TOS with the element at index (len - n) - if index_val > len { - return Err(vm.new_runtime_error(format!( - "SWAP index {} exceeds stack size {}", - index_val, len - ))); - } + debug_assert!( + index_val <= len, + "SWAP index {} exceeds stack size {}", + index_val, + len + ); let j = len - index_val; self.state.stack.swap(i, j); Ok(None) @@ -1433,17 +1431,9 @@ impl ExecutingFrame<'_> { // No __match_args__, check if this is a type with MATCH_SELF behavior // For built-in types like bool, int, str, list, tuple, dict, etc. // they match the subject itself as the single positional argument - let is_match_self_type = cls.is(vm.ctx.types.bool_type) - || cls.is(vm.ctx.types.int_type) - || cls.is(vm.ctx.types.float_type) - || cls.is(vm.ctx.types.str_type) - || cls.is(vm.ctx.types.bytes_type) - || cls.is(vm.ctx.types.bytearray_type) - || cls.is(vm.ctx.types.list_type) - || cls.is(vm.ctx.types.tuple_type) - || cls.is(vm.ctx.types.dict_type) - || cls.is(vm.ctx.types.set_type) - || cls.is(vm.ctx.types.frozenset_type); + let is_match_self_type = cls + .downcast::() + .is_ok_and(|t| t.slots.flags.contains(PyTypeFlags::_MATCH_SELF)); if is_match_self_type { if nargs_val == 1 { diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 182f902bc..44e46fdb5 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -129,6 +129,10 @@ bitflags! { const HEAPTYPE = 1 << 9; const BASETYPE = 1 << 10; const METHOD_DESCRIPTOR = 1 << 17; + // For built-in types that match the subject itself in pattern matching + // (bool, int, float, str, bytes, bytearray, list, tuple, dict, set, frozenset) + // This is not a stable API + const _MATCH_SELF = 1 << 22; const HAS_DICT = 1 << 40; #[cfg(debug_assertions)]