diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1742,6 +1742,19 @@ s.update(range(100)) list(si) + def test_merge_and_mutate(self): + class X: + def __hash__(self): + return hash(0) + def __eq__(self, o): + other.clear() + return False + + other = set() + other = {X() for i in range(10)} + s = {0} + s.update(other) + # Application tests (based on David Eppstein's graph recipes ==================================== def powerset(U): diff --git a/Objects/setobject.c b/Objects/setobject.c --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -127,7 +127,7 @@ static int set_table_resize(PySetObject *, Py_ssize_t); static int -set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash) +_set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash) { setentry *table = so->table; setentry *freeslot; @@ -162,7 +162,7 @@ if (cmp < 0) /* unlikely */ return -1; if (table != so->table || entry->key != startkey) /* unlikely */ - return set_add_entry(so, key, hash); + return _set_add_entry(so, key, hash); if (cmp > 0) /* likely */ goto found_active; mask = so->mask; /* help avoid a register spill */ @@ -190,7 +190,7 @@ if (cmp < 0) return -1; if (table != so->table || entry->key != startkey) - return set_add_entry(so, key, hash); + return _set_add_entry(so, key, hash); if (cmp > 0) goto found_active; mask = so->mask; @@ -211,14 +211,12 @@ found_unused_or_dummy: if (freeslot == NULL) goto found_unused; - Py_INCREF(key); so->used++; freeslot->key = key; freeslot->hash = hash; return 0; found_unused: - Py_INCREF(key); so->fill++; so->used++; entry->key = key; @@ -231,6 +229,16 @@ return 0; } +static int +set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash) +{ + Py_INCREF(key); + if (!_set_add_entry(so, key, hash)) + return 0; + Py_DECREF(key); + return -1; +} + /* Internal routine used by set_table_resize() to insert an item which is known to be absent from the set. This routine also assumes that @@ -668,7 +676,8 @@ } /* We can't assure there are no duplicates, so do normal insertions */ - for (i = 0; i <= other->mask; i++, other_entry++) { + for (i = 0; i <= other->mask; i++) { + other_entry = &other->table[i]; key = other_entry->key; if (key != NULL && key != dummy) { if (set_add_entry(so, key, other_entry->hash))