container/intsets: fix bug in UnionWith 'changed' result

This change corrects the computation of the 'changed' result
of the x.UnionWith(y) method in the case where a block of y is
a subset of the corresponding block of x. It also adds a test.

I audited for similar problems and found none.

Fixes golang/go#50352

Change-Id: I5224211a3cab06ce8986cdaa7b75c3b0531b3a9e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/374455
Trust: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Alan Donovan 2021-12-27 11:04:36 -05:00 коммит произвёл Ian Lance Taylor
Родитель 2c49d4f1ee
Коммит ee1ca4ffc4
2 изменённых файлов: 38 добавлений и 2 удалений

Просмотреть файл

@ -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
}
}

Просмотреть файл

@ -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