Merge pull request #14331 from lorentey/rdar/35995647-4.1

[4.1][Foundation] Coalesce duplicate String keys in bridged NSDictionary and NSSet
diff --git a/stdlib/public/SDK/Foundation/NSDictionary.swift b/stdlib/public/SDK/Foundation/NSDictionary.swift
index a979698..57eb4cd 100644
--- a/stdlib/public/SDK/Foundation/NSDictionary.swift
+++ b/stdlib/public/SDK/Foundation/NSDictionary.swift
@@ -80,6 +80,23 @@
       return
     }
 
+    if Key.self == String.self {
+      // String and NSString have different concepts of equality, so
+      // string-keyed NSDictionaries may generate key collisions when bridged
+      // over to Swift. See rdar://problem/35995647
+      var dict = Dictionary(minimumCapacity: d.count)
+      d.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in
+        let key = Swift._forceBridgeFromObjectiveC(
+          anyKey as AnyObject, Key.self)
+        let value = Swift._forceBridgeFromObjectiveC(
+          anyValue as AnyObject, Value.self)
+        // FIXME: Log a warning if `dict` already had a value for `key`
+        dict[key] = value
+      })
+      result = dict
+      return
+    }
+
     // `Dictionary<Key, Value>` where either `Key` or `Value` is a value type
     // may not be backed by an NSDictionary.
     var builder = _DictionaryBuilder<Key, Value>(count: d.count)
@@ -115,26 +132,9 @@
     // dictionary; map it to an empty dictionary.
     if _slowPath(d == nil) { return Dictionary() }
 
-    if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf(
-        d! as AnyObject) {
-      return native
-    }
-
-    if _isBridgedVerbatimToObjectiveC(Key.self) &&
-       _isBridgedVerbatimToObjectiveC(Value.self) {
-      return [Key : Value](
-        _cocoaDictionary: unsafeBitCast(d! as AnyObject, to: _NSDictionary.self))
-    }
-
-    // `Dictionary<Key, Value>` where either `Key` or `Value` is a value type
-    // may not be backed by an NSDictionary.
-    var builder = _DictionaryBuilder<Key, Value>(count: d!.count)
-    d!.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in
-      builder.add(
-          key: Swift._forceBridgeFromObjectiveC(anyKey as AnyObject, Key.self),
-          value: Swift._forceBridgeFromObjectiveC(anyValue as AnyObject, Value.self))
-    })
-    return builder.take()
+    var result: Dictionary? = nil
+    _forceBridgeFromObjectiveC(d!, result: &result)
+    return result!
   }
 }
 
diff --git a/stdlib/public/SDK/Foundation/NSSet.swift b/stdlib/public/SDK/Foundation/NSSet.swift
index ddcdcd2..bb15004 100644
--- a/stdlib/public/SDK/Foundation/NSSet.swift
+++ b/stdlib/public/SDK/Foundation/NSSet.swift
@@ -73,6 +73,21 @@
       return
     }
 
+    if Element.self == String.self {
+      // String and NSString have different concepts of equality, so
+      // string-keyed NSSets may generate key collisions when bridged over to
+      // Swift. See rdar://problem/35995647
+      var set = Set(minimumCapacity: s.count)
+      s.enumerateObjects({ (anyMember: Any, _) in
+        let member = Swift._forceBridgeFromObjectiveC(
+          anyMember as AnyObject, Element.self)
+        // FIXME: Log a warning if `member` is already in the set.
+        set.insert(member)
+      })
+      result = set
+      return
+    }
+
     // `Set<Element>` where `Element` is a value type may not be backed by
     // an NSSet.
     var builder = _SetBuilder<Element>(count: s.count)
@@ -101,25 +116,9 @@
     // set; map it to an empty set.
     if _slowPath(s == nil) { return Set() }
 
-    if let native =
-      Set<Element>._bridgeFromObjectiveCAdoptingNativeStorageOf(s! as AnyObject) {
-
-      return native
-    }
-
-    if _isBridgedVerbatimToObjectiveC(Element.self) {
-      return Set<Element>(_cocoaSet: unsafeBitCast(s! as AnyObject,
-                                                   to: _NSSet.self))
-    }
-
-    // `Set<Element>` where `Element` is a value type may not be backed by
-    // an NSSet.
-    var builder = _SetBuilder<Element>(count: s!.count)
-    s!.enumerateObjects({ (anyMember: Any, _) in
-      builder.add(member: Swift._forceBridgeFromObjectiveC(
-        anyMember as AnyObject, Element.self))
-    })
-    return builder.take()
+    var result: Set? = nil
+    Set<Element>._forceBridgeFromObjectiveC(s!, result: &result)
+    return result!
   }
 }
 
diff --git a/validation-test/stdlib/Dictionary.swift b/validation-test/stdlib/Dictionary.swift
index 89cc7d6..5d2f57e 100644
--- a/validation-test/stdlib/Dictionary.swift
+++ b/validation-test/stdlib/Dictionary.swift
@@ -3154,6 +3154,26 @@
   }
 }
 
+DictionaryTestSuite.test("BridgedFromObjC.Nonverbatim.StringEqualityMismatch") {
+  // NSString's isEqual(_:) implementation is stricter than Swift's String, so
+  // Dictionary values bridged over from Objective-C may have duplicate keys.
+  // rdar://problem/35995647
+  let cafe1 = "Cafe\u{301}" as NSString
+  let cafe2 = "Café" as NSString
+
+  let nsd = NSMutableDictionary()
+  nsd.setObject(42, forKey: cafe1)
+  nsd.setObject(23, forKey: cafe2)
+  expectEqual(2, nsd.count)
+  expectTrue((42 as NSNumber).isEqual(nsd.object(forKey: cafe1)))
+  expectTrue((23 as NSNumber).isEqual(nsd.object(forKey: cafe2)))
+
+  let d = convertNSDictionaryToDictionary(nsd) as [String: Int]
+  expectEqual(1, d.count)
+  expectEqual(d["Cafe\u{301}"], d["Café"])
+  let v = d["Café"]
+  expectTrue(v == 42 || v == 23)
+}
 
 //===---
 // Dictionary -> NSDictionary bridging tests.
diff --git a/validation-test/stdlib/Set.swift b/validation-test/stdlib/Set.swift
index 2b507e8..fa1eb78 100644
--- a/validation-test/stdlib/Set.swift
+++ b/validation-test/stdlib/Set.swift
@@ -2152,6 +2152,28 @@
   }
 }
 
+SetTestSuite.test("BridgedFromObjC.Nonverbatim.StringEqualityMismatch") {
+  // NSString's isEqual(_:) implementation is stricter than Swift's String, so
+  // Set values bridged over from Objective-C may have duplicate keys.
+  // rdar://problem/35995647
+  let cafe1 = "Cafe\u{301}" as NSString
+  let cafe2 = "Café" as NSString
+
+  let nsset = NSMutableSet()
+  nsset.add(cafe1)
+  expectTrue(nsset.contains(cafe1))
+  expectFalse(nsset.contains(cafe2))
+  nsset.add(cafe2)
+  expectEqual(2, nsset.count)
+  expectTrue(nsset.contains(cafe1))
+  expectTrue(nsset.contains(cafe2))
+  
+  let s: Set<String> = convertNSSetToSet(nsset)
+  expectEqual(1, s.count)
+  expectTrue(s.contains("Cafe\u{301}"))
+  expectTrue(s.contains("Café"))
+  expectTrue(Array(s) == ["Café"])
+}
 
 //===---
 // Dictionary -> NSDictionary bridging tests.