Merge pull request #12752 from hamishknight/dictionary-subscript-addressor

[stdlib] Use addressor for Dictionary's subscript(_:default:)
diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt
index 1fc5e5a..d837a3d 100644
--- a/benchmark/CMakeLists.txt
+++ b/benchmark/CMakeLists.txt
@@ -57,6 +57,7 @@
     single-source/DictionaryGroup
     single-source/DictionaryLiteral
     single-source/DictionaryRemove
+    single-source/DictionarySubscriptDefault
     single-source/DictionarySwap
     single-source/DropFirst
     single-source/DropLast
diff --git a/benchmark/single-source/DictionarySubscriptDefault.swift b/benchmark/single-source/DictionarySubscriptDefault.swift
new file mode 100644
index 0000000..2c90d01
--- /dev/null
+++ b/benchmark/single-source/DictionarySubscriptDefault.swift
@@ -0,0 +1,124 @@
+//===--- DictionarySubscriptDefault.swift ---------------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+
+import TestsUtils
+
+public let DictionarySubscriptDefault = [
+  BenchmarkInfo(name: "DictionarySubscriptDefaultMutation",
+                runFunction: run_DictionarySubscriptDefaultMutation,
+                tags: [.validation, .api, .Dictionary]),
+  BenchmarkInfo(name: "DictionarySubscriptDefaultMutationArray",
+                runFunction: run_DictionarySubscriptDefaultMutationArray,
+                tags: [.validation, .api, .Dictionary]),
+  BenchmarkInfo(name: "DictionarySubscriptDefaultMutationOfObjects",
+                runFunction: run_DictionarySubscriptDefaultMutationOfObjects,
+                tags: [.validation, .api, .Dictionary]),
+  BenchmarkInfo(name: "DictionarySubscriptDefaultMutationArrayOfObjects",
+                runFunction:
+                  run_DictionarySubscriptDefaultMutationArrayOfObjects,
+                tags: [.validation, .api, .Dictionary]),
+]
+
+let count = 10_000
+let result = count / 100
+
+@inline(never)
+public func run_DictionarySubscriptDefaultMutation(_ N: Int) {
+  for _ in 1...N {
+
+    var dict = [Int: Int]()
+
+    for i in 0..<count {
+      dict[i % 100, default: 0] += 1
+    }
+
+    CheckResults(dict.count == 100)
+    CheckResults(dict[0]! == result)
+  }
+}
+
+@inline(never)
+public func run_DictionarySubscriptDefaultMutationArray(_ N: Int) {
+  for _ in 1...N {
+
+    var dict = [Int: [Int]]()
+
+    for i in 0..<count {
+      dict[i % 100, default: []].append(i)
+    }
+
+    CheckResults(dict.count == 100)
+    CheckResults(dict[0]!.count == result)
+  }
+}
+
+// Hack to workaround the fact that if we attempt to increment the Box's value
+// from the subscript, the compiler will just call the subscript's getter (and
+// therefore not insert the instance) as it's dealing with a reference type.
+// By using a mutating method in a protocol extension, the compiler is forced to
+// treat this an actual mutation, so cannot call the getter.
+protocol P {
+  associatedtype T
+  var value: T { get set }
+}
+
+extension P {
+  mutating func mutateValue(_ mutations: (inout T) -> Void) {
+    mutations(&value)
+  }
+}
+
+class Box<T : Hashable> : Hashable, P {
+  var value: T
+
+  init(_ v: T) {
+    value = v
+  }
+
+  var hashValue: Int {
+    return value.hashValue
+  }
+
+  static func ==(lhs: Box, rhs: Box) -> Bool {
+    return lhs.value == rhs.value
+  }
+}
+
+@inline(never)
+public func run_DictionarySubscriptDefaultMutationOfObjects(_ N: Int) {
+  for _ in 1...N {
+
+    var dict = [Box<Int>: Box<Int>]()
+
+    for i in 0..<count {
+      dict[Box(i % 100), default: Box(0)].mutateValue { $0 += 1 }
+    }
+
+    CheckResults(dict.count == 100)
+    CheckResults(dict[Box(0)]!.value == result)
+  }
+}
+
+@inline(never)
+public func run_DictionarySubscriptDefaultMutationArrayOfObjects(_ N: Int) {
+  for _ in 1...N {
+
+    var dict = [Box<Int>: [Box<Int>]]()
+
+    for i in 0..<count {
+      dict[Box(i % 100), default: []].append(Box(i))
+    }
+
+    CheckResults(dict.count == 100)
+    CheckResults(dict[Box(0)]!.count == result)
+  }
+}
diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift
index 25ff8b2..13a6708 100644
--- a/benchmark/utils/main.swift
+++ b/benchmark/utils/main.swift
@@ -44,6 +44,7 @@
 import DictionaryGroup
 import DictionaryLiteral
 import DictionaryRemove
+import DictionarySubscriptDefault
 import DictionarySwap
 import DropFirst
 import DropLast
@@ -164,6 +165,7 @@
 registerBenchmark(DictionaryGroup)
 registerBenchmark(DictionaryLiteral)
 registerBenchmark(DictionaryRemove)
+registerBenchmark(DictionarySubscriptDefault)
 registerBenchmark(DictionarySwap)
 registerBenchmark(DropFirst)
 registerBenchmark(DropLast)
diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb
index b68de19..3eec29b 100644
--- a/stdlib/public/core/HashedCollections.swift.gyb
+++ b/stdlib/public/core/HashedCollections.swift.gyb
@@ -2153,9 +2153,11 @@
     get {
       return _variantBuffer.maybeGet(key) ?? defaultValue()
     }
-    set(newValue) {
-      // FIXME(performance): this loads and discards the old value.
-      _variantBuffer.updateValue(newValue, forKey: key)
+    mutableAddressWithNativeOwner {
+      let (_, address) = _variantBuffer
+        .pointerToValue(forKey: key, insertingDefault: defaultValue)
+      return (address, Builtin.castToNativeObject(
+        _variantBuffer.asNative._storage))
     }
   }
 
@@ -4865,6 +4867,17 @@
     }
   }
 
+  @_inlineable // FIXME(sil-serialize-all)
+  @_versioned // FIXME(sil-serialize-all)
+  internal mutating func ensureNativeBuffer() {
+#if _runtime(_ObjC)
+    if _fastPath(guaranteedNative) { return }
+    if case .cocoa(let cocoaBuffer) = self {
+      migrateDataToNativeBuffer(cocoaBuffer)
+    }
+#endif
+  }
+
 #if _runtime(_ObjC)
   @_inlineable // FIXME(sil-serialize-all)
   @_versioned // FIXME(sil-serialize-all)
@@ -5314,6 +5327,42 @@
 #endif
     }
   }
+
+  @_inlineable // FIXME(sil-serialize-all)
+  @_versioned // FIXME(sil-serialize-all)
+  internal mutating func nativePointerToValue(
+    forKey key: Key, insertingDefault defaultValue: () -> Value
+  ) -> (inserted: Bool, pointer: UnsafeMutablePointer<Value>) {
+
+    var (i, found) = asNative._find(key, startBucket: asNative._bucket(key))
+    if found {
+      let pointer = nativePointerToValue(at: ._native(i))
+      return (inserted: false, pointer: pointer)
+    }
+
+    let minCapacity = NativeBuffer.minimumCapacity(
+      minimumCount: asNative.count + 1,
+      maxLoadFactorInverse: _hashContainerDefaultMaxLoadFactorInverse)
+
+    let (_, capacityChanged) = ensureUniqueNativeBuffer(minCapacity)
+
+    if capacityChanged {
+      i = asNative._find(key, startBucket: asNative._bucket(key)).pos
+    }
+
+    asNative.initializeKey(key, value: defaultValue(), at: i.offset)
+    asNative.count += 1
+    return (inserted: true, pointer: asNative.values + i.offset)
+  }
+
+  @_inlineable // FIXME(sil-serialize-all)
+  @_versioned // FIXME(sil-serialize-all)
+  internal mutating func pointerToValue(
+    forKey key: Key, insertingDefault defaultValue: () -> Value
+  ) -> (inserted: Bool, pointer: UnsafeMutablePointer<Value>) {
+    ensureNativeBuffer()
+    return nativePointerToValue(forKey: key, insertingDefault: defaultValue)
+  }
 %end
 
   @_inlineable // FIXME(sil-serialize-all)
@@ -5358,20 +5407,8 @@
   internal mutating func insert(
     _ value: Value, forKey key: Key
   ) -> (inserted: Bool, memberAfterInsert: Value) {
-
-    if _fastPath(guaranteedNative) {
-      return nativeInsert(value, forKey: key)
-    }
-
-    switch self {
-    case .native:
-      return nativeInsert(value, forKey: key)
-#if _runtime(_ObjC)
-    case .cocoa(let cocoaBuffer):
-      migrateDataToNativeBuffer(cocoaBuffer)
-      return nativeInsert(value, forKey: key)
-#endif
-    }
+    ensureNativeBuffer()
+    return nativeInsert(value, forKey: key)
   }
 
 %if Self == 'Dictionary':
@@ -5475,20 +5512,8 @@
     _ keysAndValues: S,
     uniquingKeysWith combine: (Value, Value) throws -> Value
   ) rethrows where S.Element == (Key, Value) {
-    if _fastPath(guaranteedNative) {
-      try nativeMerge(keysAndValues, uniquingKeysWith: combine)
-      return
-    }
-
-    switch self {
-    case .native:
-      try nativeMerge(keysAndValues, uniquingKeysWith: combine)
-#if _runtime(_ObjC)
-    case .cocoa(let cocoaStorage):
-      migrateDataToNativeBuffer(cocoaStorage)
-      try nativeMerge(keysAndValues, uniquingKeysWith: combine)
-#endif
-    }
+    ensureNativeBuffer()
+    try nativeMerge(keysAndValues, uniquingKeysWith: combine)
   }
 
   @_inlineable // FIXME(sil-serialize-all)
diff --git a/test/stdlib/Inputs/DictionaryKeyValueTypes.swift b/test/stdlib/Inputs/DictionaryKeyValueTypes.swift
index e951b82..9fd935a 100644
--- a/test/stdlib/Inputs/DictionaryKeyValueTypes.swift
+++ b/test/stdlib/Inputs/DictionaryKeyValueTypes.swift
@@ -25,6 +25,31 @@
   return lhs.sorted().elementsEqual(rhs.sorted())
 }
 
+// A COW wrapper type that holds an Int.
+struct TestValueCOWTy {
+  
+  class Base {
+    var value: Int
+    init(_ value: Int) { self.value = value }
+  }
+
+  private var base: Base
+  init(_ value: Int = 0) { self.base = Base(value) }
+
+  var value: Int {
+    get { return base.value }
+    set {
+      if !isKnownUniquelyReferenced(&base) {
+        base = Base(newValue)
+      } else {
+        base.value = newValue
+      }
+    }
+  }
+
+  var baseAddress: Int { return unsafeBitCast(base, to: Int.self) }
+}
+
 var _keyCount = _stdlib_AtomicInt(0)
 var _keySerial = _stdlib_AtomicInt(0)
 
diff --git a/validation-test/stdlib/Dictionary.swift b/validation-test/stdlib/Dictionary.swift
index 8da6fe4..32d9b54 100644
--- a/validation-test/stdlib/Dictionary.swift
+++ b/validation-test/stdlib/Dictionary.swift
@@ -127,6 +127,14 @@
   return d
 }
 
+func getCOWFastDictionaryWithCOWValues() -> Dictionary<Int, TestValueCOWTy> {
+  var d = Dictionary<Int, TestValueCOWTy>(minimumCapacity: 10)
+  d[10] = TestValueCOWTy(1010)
+  d[20] = TestValueCOWTy(1020)
+  d[30] = TestValueCOWTy(1030)
+  return d
+}
+
 func getCOWSlowDictionary() -> Dictionary<TestKeyTy, TestValueTy> {
   var d = Dictionary<TestKeyTy, TestValueTy>(minimumCapacity: 10)
   d[TestKeyTy(10)] = TestValueTy(1010)
@@ -804,6 +812,32 @@
   }
 }
 
+DictionaryTestSuite.test("COW.Fast.DefaultedSubscriptDoesNotCopyValue") {
+  do {
+    var d = getCOWFastDictionaryWithCOWValues()
+    let identityValue30 = d[30]!.baseAddress
+
+    // Increment the value without having to reallocate the underlying Base
+    // instance, as uniquely referenced.
+    d[30, default: TestValueCOWTy()].value += 1
+    assert(identityValue30 == d[30]!.baseAddress)
+    assert(d[30]!.value == 1031)
+
+    let value40 = TestValueCOWTy()
+    let identityValue40 = value40.baseAddress
+
+    // Increment the value, reallocating the underlying Base, as not uniquely
+    // referenced.
+    d[40, default: value40].value += 1
+    assert(identityValue40 != d[40]!.baseAddress)
+    assert(d[40]!.value == 1)
+
+    // Keep variables alive.
+    _fixLifetime(d)
+    _fixLifetime(value40)
+  }
+}
+
 DictionaryTestSuite.test("COW.Fast.IndexForKeyDoesNotReallocate") {
   var d = getCOWFastDictionary()
   var identity1 = d._rawIdentifier()