Skip to content
Merged
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
3,572 changes: 3,572 additions & 0 deletions Lib/test/test_patma.py

Large diffs are not rendered by default.

272 changes: 159 additions & 113 deletions compiler/codegen/src/compile.rs

Large diffs are not rendered by default.

43 changes: 35 additions & 8 deletions extra_tests/snippets/syntax_match.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,46 @@ def test_or(i):

match data:
case {"a": x, "b": y}:
assert x == 1, x
assert y == 2, y
case _:
assert False

# test mapping with rest
match data:
case {"a": x, **rest}:
assert x == 1
assert rest == {"b": 2}
case _:
assert False

# test empty rest
data2 = {"a": 1}
match data2:
case {"a": x, **rest}:
assert x == 1
assert rest == {}
case _:
assert False

# test rest with multiple keys
data3 = {"a": 1, "b": 2, "c": 3, "d": 4}
match data3:
case {"a": x, "b": y, **rest}:
assert x == 1
assert y == 2
assert rest == {"c": 3, "d": 4}
case _:
assert False

# test mapping with rest (TODO: implement **rest pattern)
# match data:
# case {"a": x, **rest}:
# assert x == 1
# assert rest == {"b": 2}
# case _:
# assert False
match data3:
case {"a": x, "b": y, "c": z, **rest}:
assert x == 1
assert y == 2
assert z == 3
assert rest == {"d": 4}
case _:
assert False

# test mapping pattern with wildcard fallback (reproduces wheelinfo.py issue)
test_dict = {"sha256": "abc123"}
Expand Down Expand Up @@ -118,7 +146,6 @@ def test_mapping_comprehensive():
cap_x = cap_y = None
assert cap_x == 1, f"Expected x=1, got {cap_x}"
assert cap_y == 2, f"Expected y=2, got {cap_y}"
print("Comprehensive mapping tests passed!")


test_mapping_comprehensive()
2 changes: 1 addition & 1 deletion vm/src/builtins/bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/bytearray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl PyByteArray {
}

#[pyclass(
flags(BASETYPE),
flags(BASETYPE, _MATCH_SELF),
with(
Py,
PyRef,
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl PyRef<PyBytes> {
}

#[pyclass(
flags(BASETYPE),
flags(BASETYPE, _MATCH_SELF),
with(
Py,
PyRef,
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl PyDict {
AsMapping,
Representable
),
flags(BASETYPE, MAPPING)
flags(BASETYPE, MAPPING, _MATCH_SELF)
)]
impl PyDict {
#[pyclassmethod]
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ fn float_from_string(val: PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
}

#[pyclass(
flags(BASETYPE),
flags(BASETYPE, _MATCH_SELF),
with(Comparable, Hashable, Constructor, AsNumber, Representable)
)]
impl PyFloat {
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ impl PyInt {
}

#[pyclass(
flags(BASETYPE),
flags(BASETYPE, _MATCH_SELF),
with(PyRef, Comparable, Hashable, Constructor, AsNumber, Representable)
)]
impl PyInt {
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub type PyListRef = PyRef<PyList>;
AsSequence,
Representable
),
flags(BASETYPE, SEQUENCE)
flags(BASETYPE, SEQUENCE, _MATCH_SELF)
)]
impl PyList {
#[pymethod]
Expand Down
4 changes: 2 additions & 2 deletions vm/src/builtins/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ fn reduce_set(
AsNumber,
Representable
),
flags(BASETYPE)
flags(BASETYPE, _MATCH_SELF)
)]
impl PySet {
#[pymethod]
Expand Down Expand Up @@ -946,7 +946,7 @@ impl Constructor for PyFrozenSet {
}

#[pyclass(
flags(BASETYPE),
flags(BASETYPE, _MATCH_SELF),
with(
Constructor,
AsSequence,
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ impl Py<PyStr> {
}

#[pyclass(
flags(BASETYPE),
flags(BASETYPE, _MATCH_SELF),
with(
PyRef,
AsMapping,
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ impl<T> PyTuple<PyRef<T>> {
}

#[pyclass(
flags(BASETYPE, SEQUENCE),
flags(BASETYPE, SEQUENCE, _MATCH_SELF),
with(
AsMapping,
AsSequence,
Expand Down
27 changes: 20 additions & 7 deletions vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,25 @@ impl PyType {
linearise_mro(mros)
}

/// Inherit SEQUENCE and MAPPING flags from base class (CPython: inherit_patma_flags)
fn inherit_patma_flags(slots: &mut PyTypeSlots, base: &PyRef<Self>) {
/// Inherit SEQUENCE and MAPPING flags from base classes
/// Check all bases in order and inherit the first SEQUENCE or MAPPING flag found
fn inherit_patma_flags(slots: &mut PyTypeSlots, bases: &[PyRef<Self>]) {
const COLLECTION_FLAGS: PyTypeFlags = PyTypeFlags::from_bits_truncate(
PyTypeFlags::SEQUENCE.bits() | PyTypeFlags::MAPPING.bits(),
);
if !slots.flags.intersects(COLLECTION_FLAGS) {
slots.flags |= base.slots.flags & COLLECTION_FLAGS;

// If flags are already set, don't override
if slots.flags.intersects(COLLECTION_FLAGS) {
return;
}

// Check each base in order and inherit the first collection flag found
for base in bases {
let base_flags = base.slots.flags & COLLECTION_FLAGS;
if !base_flags.is_empty() {
slots.flags |= base_flags;
return;
}
}
}

Expand Down Expand Up @@ -300,8 +312,8 @@ impl PyType {
slots.flags |= PyTypeFlags::HAS_DICT
}

// Inherit SEQUENCE and MAPPING flags from base class
Self::inherit_patma_flags(&mut slots, &base);
// Inherit SEQUENCE and MAPPING flags from base classes
Self::inherit_patma_flags(&mut slots, &bases);

// Check for __abc_tpflags__ from ABCMeta (for collections.abc.Sequence, Mapping, etc.)
Self::check_abc_tpflags(&mut slots, &attrs, &bases, ctx);
Expand Down Expand Up @@ -359,7 +371,8 @@ impl PyType {
}

// Inherit SEQUENCE and MAPPING flags from base class
Self::inherit_patma_flags(&mut slots, &base);
// For static types, we only have a single base
Self::inherit_patma_flags(&mut slots, std::slice::from_ref(&base));

if slots.basicsize == 0 {
slots.basicsize = base.slots.basicsize;
Expand Down
Loading
Loading