From 860ea6e3c72ea0811006ed1f5df14079ad91e83a Mon Sep 17 00:00:00 2001 From: 1ndahous3 <9205230+1ndahous3@users.noreply.github.com> Date: Sun, 18 Oct 2026 01:51:10 +0300 Subject: [PATCH] Allow reflected fallback for in-place set operators 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 9c2255d0084..f126ad318ab 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 8e96a095962..19fab874b19 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 @@ def __del__(self): 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}