You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
set/frozenset re-hash elements when constructed from a set, frozenset, or dict #8489
set/frozenset recompute __hash__ for every element when the source is a set, frozenset, or dict. CPython reuses the hash already stored in the source's entries (set_update_internal in Objects/setobject.c, branching on PyAnySet_Check / PyDict_CheckExact), so no extra __hash__ calls happen.
Scope
The set/frozenset target only. The dict target (dict.fromkeys) is #8490. test_do_not_rehash_dict_keys asserts on both directions, so its @unittest.expectedFailure # TODO: RUSTPYTHON marker can only be dropped once both are done.
Lib/test/test_set.py:333 — TestJointOps.test_do_not_rehash_dict_keys, inherited by TestSet, TestSetSubclass, TestFrozenSet and TestFrozenSetSubclass (4 of the 10 expected failures in test_set.py).
Analysis
The hash is already stored — DictEntry keeps it next to the key (crates/vm/src/dict_inner.rs:227), and PySetInner::update_internal (crates/vm/src/builtins/set.rs:379) already special-cases AnySet and PyDict. But both branches drop it and go through self.add() → content.insert(), which re-hashes:
fnmerge_set(&self,any_set:AnySet,vm:&VirtualMachine) -> PyResult<()>{for item in any_set.as_inner().elements(){self.add(item, vm)?;// recomputes the hash}Ok(())}
update_internal is also only reached from set.update() (:719). These paths never dispatch on the source type at all:
PySetInner::from_iter (:187), used by the set() / frozenset() constructors
union (:252), intersection (:261), difference (:272), symmetric_difference (:280)
Activity
Reuse stored hashes when building a set from a set/frozenset/dict
Reuse stored hashes when building a set from a set/frozenset/dict (#8491
Reuse stored hashes when building a set from a set/frozenset/dict (#8491