diff --git a/container/intsets/sparse.go b/container/intsets/sparse.go index e9f19e62f7..2f1a0eaf31 100644 --- a/container/intsets/sparse.go +++ b/container/intsets/sparse.go @@ -666,8 +666,9 @@ func (s *Sparse) UnionWith(x *Sparse) bool { for xb != &none { if sb != &none && sb.offset == xb.offset { for i := range xb.bits { - if sb.bits[i] != xb.bits[i] { - sb.bits[i] |= xb.bits[i] + union := sb.bits[i] | xb.bits[i] + if sb.bits[i] != union { + sb.bits[i] = union changed = true } } diff --git a/container/intsets/sparse_test.go b/container/intsets/sparse_test.go index 7481a06b89..cd8ec6e084 100644 --- a/container/intsets/sparse_test.go +++ b/container/intsets/sparse_test.go @@ -460,6 +460,41 @@ func TestSetOperations(t *testing.T) { } } +// TestUnionWithChanged checks the 'changed' result of UnionWith. +func TestUnionWithChanged(t *testing.T) { + setOf := func(elems ...int) *intsets.Sparse { + s := new(intsets.Sparse) + for _, elem := range elems { + s.Insert(elem) + } + return s + } + + checkUnionWith := func(x, y *intsets.Sparse) { + xstr := x.String() + prelen := x.Len() + changed := x.UnionWith(y) + if (x.Len() > prelen) != changed { + t.Errorf("%s.UnionWith(%s) => %s, changed=%t", xstr, y, x, changed) + } + } + + // The case marked "!" is a regression test for Issue 50352, + // which spuriously returned true when y ⊂ x. + + // same block + checkUnionWith(setOf(1, 2), setOf(1, 2)) + checkUnionWith(setOf(1, 2, 3), setOf(1, 2)) // ! + checkUnionWith(setOf(1, 2), setOf(1, 2, 3)) + checkUnionWith(setOf(1, 2), setOf()) + + // different blocks + checkUnionWith(setOf(1, 1000000), setOf(1, 1000000)) + checkUnionWith(setOf(1, 2, 1000000), setOf(1, 2)) + checkUnionWith(setOf(1, 2), setOf(1, 2, 1000000)) + checkUnionWith(setOf(1, 1000000), setOf()) +} + func TestIntersectionWith(t *testing.T) { // Edge cases: the pairs (1,1), (1000,2000), (8000,4000) // exercise the <, >, == cases in IntersectionWith that the