Why does Set#intersection behave differently when passed a Sequence instead of a Set?

Viewed 36

I noticed an odd bug in an app and narrowed it down to a surprising cause: if you pass a sequence to Set#intersection, you get different behavior than if you pass a Set, even though both are supported methods of the Set data structure.

You can verify at any Swift REPL:

  1> Set([3, 6, 0, 1, 5, 2, 4]).intersection([0, 1, 1, 2, 3, 4, 5])
$R0: Set<Int> = 7 values {
  [0] = 5
  [1] = 0
  [2] = 3
  [3] = 1
  [4] = 2
  [5] = 6
  [6] = 4
}
  2> Set([3, 6, 0, 1, 5, 2, 4]).intersection(Set([0, 1, 1, 2, 3, 4, 5]))
$R1: Set<Int> = 6 values {
  [0] = 1
  [1] = 5
  [2] = 0
  [3] = 4
  [4] = 2
  [5] = 3
}

The issue here is that the first result has an extra value, the integer 6, even though there is no 6 in the sequence passed to intersection. The second result is correct - the first is wrong. And the only difference between the two calls is that the second call has its sequence converted to a Set.

Am I losing my mind, or is this unexpected behavior?

1 Answers

I think this is the bug SR-16061. The report shows a similar unexpected behaviour to yours, where using the Set overload of intersection produces the correct output, but the Sequence overload does not.

let s = Set("ae")

print("s: \(s)") // ["a", "e"]

let i = s.intersection("aa")
let j = s.intersection(Set("aa"))

print("i: \(i)") // ["a", "e"]
print("j: \(j)") // ["a"]

I tried to look a little into what's happened. The previous commit that modified the intersection methods in NativeSet.swift was back in November 2021. This agrees with the release dates of the Swift version in which this bug can be reproduced - just between Swift 5.5 and 5.6. It is said that this change is supposed to speed up Set.intersection by using a bit set.

Related