From e7e3dfd6ce1374efbb16a90ac5cb7a3599ea54de Mon Sep 17 00:00:00 2001 From: 1ndahous3 <9205230+1ndahous3@users.noreply.github.com> Date: Sun, 18 Oct 2026 17:00:18 +0300 Subject: [PATCH] Allow reflected fallback for in-place set operators (#9182) Assisted-by: Codex:gpt-6 --- crates/vm/src/builtins/set.rs | 16 ++++++++++++---- extra_tests/snippets/builtin_set.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/crates/vm/src/builtins/set.rs b/crates/vm/src/builtins/set.rs index 9c2255d00..f126ad318 100644 --- a/crates/vm/src/builtins/set.rs +++ b/crates/vm/src/builtins/set.rs @@ -1151,7 +1151,9 @@ impl AsNumber for PySet { } }), inplace_subtract: Some(|a, b, vm| { - if let Some(a) = a.downcast_ref::() { + if let Some(a) = a.downcast_ref::() + && PyRef::::check(b, vm) + { PySet::__isub__( a.to_owned(), PyRef::::try_from_object(vm, b.to_owned())?, @@ -1163,7 +1165,9 @@ impl AsNumber for PySet { } }), inplace_and: Some(|a, b, vm| { - if let Some(a) = a.downcast_ref::() { + if let Some(a) = a.downcast_ref::() + && PyRef::::check(b, vm) + { PySet::__iand__( a.to_owned(), PyRef::::try_from_object(vm, b.to_owned())?, @@ -1175,7 +1179,9 @@ impl AsNumber for PySet { } }), inplace_xor: Some(|a, b, vm| { - if let Some(a) = a.downcast_ref::() { + if let Some(a) = a.downcast_ref::() + && PyRef::::check(b, vm) + { PySet::__ixor__( a.to_owned(), PyRef::::try_from_object(vm, b.to_owned())?, @@ -1187,7 +1193,9 @@ impl AsNumber for PySet { } }), inplace_or: Some(|a, b, vm| { - if let Some(a) = a.downcast_ref::() { + if let Some(a) = a.downcast_ref::() + && PyRef::::check(b, vm) + { PySet::__ior__( a.to_owned(), PyRef::::try_from_object(vm, b.to_owned())?, diff --git a/extra_tests/snippets/builtin_set.py b/extra_tests/snippets/builtin_set.py index 8e96a0959..19fab874b 100644 --- a/extra_tests/snippets/builtin_set.py +++ b/extra_tests/snippets/builtin_set.py @@ -1,3 +1,5 @@ +import operator + from testutils import assert_raises assert set([1, 2]) == set([1, 2]) @@ -706,3 +708,17 @@ for use_operator in (False, True): intersection_target.intersection_update({1}) assert not intersection_target assert intersection_events == [True, True] + + +# Unsupported right operands must reach their reflected set operation. +keys = {2: None, 3: None}.keys() +for method, operation, expected in ( + (set.__ior__, operator.ior, {1, 2, 3}), + (set.__iand__, operator.iand, {2}), + (set.__isub__, operator.isub, {1}), + (set.__ixor__, operator.ixor, {1, 3}), +): + source = {1, 2} + assert method(source, keys) is NotImplemented + assert operation(source, keys) == expected + assert source == {1, 2}