Merge pull request #11623 from CodaFi/fundef

[stdlib]Custom message for recursive Strideable witness
diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst
index 8f21b7c..c8b41ec 100644
--- a/docs/ABI/Mangling.rst
+++ b/docs/ABI/Mangling.rst
@@ -117,6 +117,10 @@
   global ::= global 'Tm'                 // merged function
   global ::= entity                      // some identifiable thing
   global ::= type type generic-signature? 'T' REABSTRACT-THUNK-TYPE   // reabstraction thunk helper function
+  global ::= entity generic-signature? type type* 'TK' // key path getter
+  global ::= entity generic-signature? type type* 'Tk' // key path setter
+  global ::= type generic-signature 'TH' // key path equality
+  global ::= type generic-signature 'Th' // key path hasher
 
   REABSTRACT-THUNK-TYPE ::= 'R'          // reabstraction thunk helper function
   REABSTRACT-THUNK-TYPE ::= 'r'          // reabstraction thunk
diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h
index 2a64e80..052946a 100644
--- a/include/swift/AST/ASTContext.h
+++ b/include/swift/AST/ASTContext.h
@@ -170,7 +170,6 @@
 };
 
 class SILLayout; // From SIL
-
 /// \brief Describes either a nominal type declaration or an extension
 /// declaration.
 typedef llvm::PointerUnion<NominalTypeDecl *, ExtensionDecl *>
@@ -438,6 +437,9 @@
   FuncDecl *get##Name(LazyResolver *resolver) const;
 #include "swift/AST/KnownDecls.def"
 
+  /// Get the '+' function on two RangeReplaceableCollection.
+  FuncDecl *getPlusFunctionOnRangeReplaceableCollection() const;
+
   /// Check whether the standard library provides all the correct
   /// intrinsic support for Optional<T>.
   ///
diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h
index 4fd228a..feebb2e 100644
--- a/include/swift/AST/ASTMangler.h
+++ b/include/swift/AST/ASTMangler.h
@@ -114,12 +114,18 @@
                                              Type FromType, Type ToType,
                                              ModuleDecl *Module);
   
-  std::string mangleKeyPathGetterThunkHelper(const VarDecl *property,
+  std::string mangleKeyPathGetterThunkHelper(const AbstractStorageDecl *property,
                                              GenericSignature *signature,
-                                             CanType baseType);
-  std::string mangleKeyPathSetterThunkHelper(const VarDecl *property,
+                                             CanType baseType,
+                                             ArrayRef<CanType> subs);
+  std::string mangleKeyPathSetterThunkHelper(const AbstractStorageDecl *property,
                                              GenericSignature *signature,
-                                             CanType baseType);
+                                             CanType baseType,
+                                             ArrayRef<CanType> subs);
+  std::string mangleKeyPathEqualsHelper(ArrayRef<CanType> indices,
+                                        GenericSignature *signature);
+  std::string mangleKeyPathHashHelper(ArrayRef<CanType> indices,
+                                      GenericSignature *signature);
 
   std::string mangleTypeForDebugger(Type decl, const DeclContext *DC,
                                     GenericEnvironment *GE);
diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h
index b484580..2857478 100644
--- a/include/swift/AST/Decl.h
+++ b/include/swift/AST/Decl.h
@@ -367,7 +367,7 @@
     unsigned BodyKind : 3;
 
     /// Number of curried parameter lists.
-    unsigned NumParameterLists : 6;
+    unsigned NumParameterLists : 5;
 
     /// Whether we are overridden later.
     unsigned Overridden : 1;
@@ -380,6 +380,9 @@
 
     /// Whether NeedsNewVTableEntry is valid.
     unsigned HasComputedNeedsNewVTableEntry : 1;
+
+    /// The ResilienceExpansion to use for default arguments.
+    unsigned DefaultArgumentResilienceExpansion : 1;
   };
   enum { NumAbstractFunctionDeclBits = NumValueDeclBits + 13 };
   static_assert(NumAbstractFunctionDeclBits <= 32, "fits in an unsigned");
@@ -4859,6 +4862,8 @@
     AbstractFunctionDeclBits.Throws = Throws;
     AbstractFunctionDeclBits.NeedsNewVTableEntry = false;
     AbstractFunctionDeclBits.HasComputedNeedsNewVTableEntry = false;
+    AbstractFunctionDeclBits.DefaultArgumentResilienceExpansion =
+        unsigned(ResilienceExpansion::Maximal);
 
     // Verify no bitfield truncation.
     assert(AbstractFunctionDeclBits.NumParameterLists == NumParameterLists);
@@ -5076,6 +5081,21 @@
   /// Resolved during type checking
   void setIsOverridden() { AbstractFunctionDeclBits.Overridden = true; }
 
+  /// The ResilienceExpansion for default arguments.
+  ///
+  /// In Swift 4 mode, default argument expressions are serialized, and must
+  /// obey the restrictions imposed upon inlineable function bodies.
+  ResilienceExpansion getDefaultArgumentResilienceExpansion() const {
+    return ResilienceExpansion(
+        AbstractFunctionDeclBits.DefaultArgumentResilienceExpansion);
+  }
+
+  /// Set the ResilienceExpansion for default arguments.
+  void setDefaultArgumentResilienceExpansion(ResilienceExpansion expansion) {
+    AbstractFunctionDeclBits.DefaultArgumentResilienceExpansion =
+        unsigned(expansion);
+  }
+
   /// Set information about the foreign error convention used by this
   /// declaration.
   void setForeignErrorConvention(const ForeignErrorConvention &convention);
diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def
index 63c3da0..b6260cf 100644
--- a/include/swift/AST/DiagnosticsParse.def
+++ b/include/swift/AST/DiagnosticsParse.def
@@ -589,6 +589,15 @@
       "keypath must have at least one component", ())
 ERROR(sil_keypath_no_root,none,
       "keypath must have a root component declared",())
+ERROR(sil_keypath_index_not_hashable,none,
+      "key path index type %0 does not conform to Hashable", (Type))
+ERROR(sil_keypath_index_operand_type_conflict,none,
+      "conflicting types for key path operand %0: %1 vs. %2",
+      (unsigned, Type, Type))
+ERROR(sil_keypath_no_use_of_operand_in_pattern,none,
+      "operand %0 is not referenced by any component in the pattern",
+      (unsigned))
+
 // SIL Basic Blocks
 ERROR(expected_sil_block_name,none,
       "expected basic block name or '}'", ())
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index 130ab65..4d8eb8a 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -470,6 +470,8 @@
       "string interpolation can only appear inside a string literal", ())
 ERROR(unsupported_keypath_tuple_element_reference,none,
       "key path cannot reference tuple elements", ())
+ERROR(expr_keypath_subscript_index_not_hashable, none,
+      "subscript index of type %0 in a key path must be Hashable", (Type))
 
 // Selector expressions.
 ERROR(expr_selector_no_objc_runtime,none,
diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h
index e78e864..c4e6cf0 100644
--- a/include/swift/AST/Expr.h
+++ b/include/swift/AST/Expr.h
@@ -4674,6 +4674,7 @@
     
     llvm::PointerIntPair<Expr *, 3, Kind> SubscriptIndexExprAndKind;
     ArrayRef<Identifier> SubscriptLabels;
+    ArrayRef<ProtocolConformanceRef> SubscriptHashableConformances;
     Type ComponentType;
     SourceLoc Loc;
     
@@ -4681,20 +4682,22 @@
                        DeclNameOrRef decl,
                        Expr *indexExpr,
                        ArrayRef<Identifier> subscriptLabels,
+                       ArrayRef<ProtocolConformanceRef> indexHashables,
                        Kind kind,
                        Type type,
                        SourceLoc loc);
     
   public:
     Component()
-      : Component(nullptr, {}, nullptr, {}, Kind::Invalid, Type(), SourceLoc())
+      : Component(nullptr, {}, nullptr, {}, {}, Kind::Invalid,
+                  Type(), SourceLoc())
     {}
     
     /// Create an unresolved component for a property.
     static Component forUnresolvedProperty(DeclName UnresolvedName,
                                            SourceLoc Loc) {
       return Component(nullptr,
-                       UnresolvedName, nullptr, {},
+                       UnresolvedName, nullptr, {}, {},
                        Kind::UnresolvedProperty,
                        Type(),
                        Loc);
@@ -4720,13 +4723,14 @@
                                          SourceLoc loc) {
       
       return Component(&context,
-                       {}, index, subscriptLabels, Kind::UnresolvedSubscript,
+                       {}, index, subscriptLabels, {},
+                       Kind::UnresolvedSubscript,
                        Type(), loc);
     }
     
     /// Create an unresolved optional force `!` component.
     static Component forUnresolvedOptionalForce(SourceLoc BangLoc) {
-      return Component(nullptr, {}, nullptr, {},
+      return Component(nullptr, {}, nullptr, {}, {},
                        Kind::OptionalForce,
                        Type(),
                        BangLoc);
@@ -4734,7 +4738,7 @@
     
     /// Create an unresolved optional chain `?` component.
     static Component forUnresolvedOptionalChain(SourceLoc QuestionLoc) {
-      return Component(nullptr, {}, nullptr, {},
+      return Component(nullptr, {}, nullptr, {}, {},
                        Kind::OptionalChain,
                        Type(),
                        QuestionLoc);
@@ -4744,7 +4748,7 @@
     static Component forProperty(ConcreteDeclRef property,
                                  Type propertyType,
                                  SourceLoc loc) {
-      return Component(nullptr, property, nullptr, {},
+      return Component(nullptr, property, nullptr, {}, {},
                        Kind::Property,
                        propertyType,
                        loc);
@@ -4752,14 +4756,15 @@
     
     /// Create a component for a subscript.
     static Component forSubscript(ASTContext &ctx,
-                                  ConcreteDeclRef subscript,
-                                  SourceLoc lSquareLoc,
-                                  ArrayRef<Expr *> indexArgs,
-                                  ArrayRef<Identifier> indexArgLabels,
-                                  ArrayRef<SourceLoc> indexArgLabelLocs,
-                                  SourceLoc rSquareLoc,
-                                  Expr *trailingClosure,
-                                  Type elementType);
+                              ConcreteDeclRef subscript,
+                              SourceLoc lSquareLoc,
+                              ArrayRef<Expr *> indexArgs,
+                              ArrayRef<Identifier> indexArgLabels,
+                              ArrayRef<SourceLoc> indexArgLabelLocs,
+                              SourceLoc rSquareLoc,
+                              Expr *trailingClosure,
+                              Type elementType,
+                              ArrayRef<ProtocolConformanceRef> indexHashables);
 
     /// Create a component for a subscript.
     ///
@@ -4767,11 +4772,12 @@
     /// list of index arguments.
     static Component forSubscriptWithPrebuiltIndexExpr(
        ConcreteDeclRef subscript, Expr *index, ArrayRef<Identifier> labels,
-       Type elementType, SourceLoc loc);
+       Type elementType, SourceLoc loc,
+       ArrayRef<ProtocolConformanceRef> indexHashables);
     
     /// Create an optional-forcing `!` component.
     static Component forOptionalForce(Type forcedType, SourceLoc bangLoc) {
-      return Component(nullptr, {}, nullptr, {},
+      return Component(nullptr, {}, nullptr, {}, {},
                        Kind::OptionalForce, forcedType,
                        bangLoc);
     }
@@ -4779,7 +4785,7 @@
     /// Create an optional-chaining `?` component.
     static Component forOptionalChain(Type unwrappedType,
                                       SourceLoc questionLoc) {
-      return Component(nullptr, {}, nullptr, {},
+      return Component(nullptr, {}, nullptr, {}, {},
                        Kind::OptionalChain, unwrappedType,
                        questionLoc);
     }
@@ -4788,7 +4794,7 @@
     /// syntax but may appear when the non-optional result of an optional chain
     /// is implicitly wrapped.
     static Component forOptionalWrap(Type wrappedType) {
-      return Component(nullptr, {}, nullptr, {},
+      return Component(nullptr, {}, nullptr, {}, {},
                        Kind::OptionalWrap, wrappedType,
                        SourceLoc());
     }
@@ -4855,6 +4861,26 @@
         llvm_unreachable("no subscript labels for this kind");
       }
     }
+    
+    ArrayRef<ProtocolConformanceRef>
+    getSubscriptIndexHashableConformances() const {
+      switch (getKind()) {
+      case Kind::Subscript:
+        return SubscriptHashableConformances;
+        
+      case Kind::UnresolvedSubscript:
+      case Kind::Invalid:
+      case Kind::OptionalChain:
+      case Kind::OptionalWrap:
+      case Kind::OptionalForce:
+      case Kind::UnresolvedProperty:
+      case Kind::Property:
+        llvm_unreachable("no hashable conformances for this kind");
+      }
+    }
+    
+    void setSubscriptIndexHashableConformances(
+      ArrayRef<ProtocolConformanceRef> hashables);
 
     DeclName getUnresolvedDeclName() const {
       switch (getKind()) {
diff --git a/include/swift/AST/Initializer.h b/include/swift/AST/Initializer.h
index 754f5a6..bb02e88 100644
--- a/include/swift/AST/Initializer.h
+++ b/include/swift/AST/Initializer.h
@@ -154,26 +154,16 @@
 public:
   explicit DefaultArgumentInitializer(DeclContext *parent, unsigned index)
       : Initializer(InitializerKind::DefaultArgument, parent) {
-    SpareBits = (unsigned(ResilienceExpansion::Maximal) | index << 1);
+    SpareBits = index;
   }
 
-  unsigned getIndex() const { return SpareBits >> 1; }
-
-  ResilienceExpansion getResilienceExpansion() const {
-    return ResilienceExpansion(SpareBits & 1);
-  }
+  unsigned getIndex() const { return SpareBits; }
 
   /// Change the parent of this context.  This is necessary because
   /// the function signature is parsed before the function
   /// declaration/expression itself is built.
   void changeFunction(AbstractFunctionDecl *parent);
 
-  /// Change the resilience expansion of this context, necessary
-  /// for the same reason as above.
-  void changeResilienceExpansion(ResilienceExpansion expansion) {
-    SpareBits = (SpareBits & ~1) | unsigned(expansion);
-  }
-
   static bool classof(const DeclContext *DC) {
     if (auto init = dyn_cast<Initializer>(DC))
       return classof(init);
diff --git a/include/swift/AST/KnownStdlibTypes.def b/include/swift/AST/KnownStdlibTypes.def
index 8120bbb..904d543 100644
--- a/include/swift/AST/KnownStdlibTypes.def
+++ b/include/swift/AST/KnownStdlibTypes.def
@@ -77,5 +77,6 @@
 KNOWN_STDLIB_TYPE_DECL(Decoder, ProtocolDecl, 1)
 KNOWN_STDLIB_TYPE_DECL(KeyedEncodingContainer, NominalTypeDecl, 1)
 KNOWN_STDLIB_TYPE_DECL(KeyedDecodingContainer, NominalTypeDecl, 1)
+KNOWN_STDLIB_TYPE_DECL(RangeReplaceableCollection, ProtocolDecl, 1)
 
 #undef KNOWN_STDLIB_TYPE_DECL
diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h
index 97000a4..2923787 100644
--- a/include/swift/Basic/LangOptions.h
+++ b/include/swift/Basic/LangOptions.h
@@ -236,9 +236,6 @@
     /// Diagnose uses of NSCoding with classes that have unstable mangled names.
     bool EnableNSKeyedArchiverDiagnostics = true;
     
-    /// Enable keypath components that aren't fully implemented.
-    bool EnableExperimentalKeyPathComponents = false;
-
     /// When a conversion from String to Substring fails, emit a fix-it to append
     /// the void subscript '[]'.
     /// FIXME: Remove this flag when void subscripts are implemented.
diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h
index ce20fb6..199537f 100644
--- a/include/swift/Basic/STLExtras.h
+++ b/include/swift/Basic/STLExtras.h
@@ -786,6 +786,24 @@
   return std::transform(C.begin(), C.end(), result, op);
 }
 
+/// Provides default implementations of !=, <=, >, and >= based on == and <.
+template <typename T>
+class RelationalOperationsBase {
+public:
+  friend bool operator>(const T &left, const T &right) {
+    return right < left;
+  }
+  friend bool operator>=(const T &left, const T &right) {
+    return !(left < right);
+  }
+  friend bool operator<=(const T &left, const T &right) {
+    return !(right < left);
+  }
+  friend bool operator!=(const T &left, const T &right) {
+    return !(left == right);
+  }
+};
+
 } // end namespace swift
 
 #endif // SWIFT_BASIC_INTERLEAVE_H
diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def
index d0b26c9..92f7c0f 100644
--- a/include/swift/Demangling/DemangleNodes.def
+++ b/include/swift/Demangling/DemangleNodes.def
@@ -97,6 +97,8 @@
 CONTEXT_NODE(Initializer)
 NODE(KeyPathGetterThunkHelper)
 NODE(KeyPathSetterThunkHelper)
+NODE(KeyPathEqualsThunkHelper)
+NODE(KeyPathHashThunkHelper)
 NODE(LazyProtocolWitnessTableAccessor)
 NODE(LazyProtocolWitnessTableCacheVariable)
 NODE(LocalDeclName)
diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td
index 867e1e9..8a306eb 100644
--- a/include/swift/Option/FrontendOptions.td
+++ b/include/swift/Option/FrontendOptions.td
@@ -283,10 +283,6 @@
   Flag<["-"], "enable-experimental-property-behaviors">,
   HelpText<"Enable experimental property behaviors">;
   
-def enable_experimental_keypath_components :
-  Flag<["-"], "enable-experimental-keypath-components">,
-  HelpText<"Enable unimplemented keypath component kinds">;
-
 def enable_deserialization_recovery :
   Flag<["-"], "enable-deserialization-recovery">,
   HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">;
diff --git a/include/swift/Runtime/HeapObject.h b/include/swift/Runtime/HeapObject.h
index c78c309..c21291d 100644
--- a/include/swift/Runtime/HeapObject.h
+++ b/include/swift/Runtime/HeapObject.h
@@ -889,13 +889,15 @@
 ///
 /// \param ref - never null
 /// \param value - not necessarily a native Swift object; can be null
+/// \return ref
 SWIFT_RUNTIME_EXPORT
-void swift_unknownWeakInit(WeakReference *ref, void *value);
+WeakReference *swift_unknownWeakInit(WeakReference *ref, void *value);
 
 #else
 
-static inline void swift_unknownWeakInit(WeakReference *ref, void *value) {
-  swift_weakInit(ref, static_cast<HeapObject *>(value));
+static inline WeakReference *swift_unknownWeakInit(WeakReference *ref,
+                                                   void *value) {
+  return swift_weakInit(ref, static_cast<HeapObject *>(value));
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -906,13 +908,15 @@
 ///
 /// \param ref - never null
 /// \param value - not necessarily a native Swift object; can be null
+/// \return ref
 SWIFT_RUNTIME_EXPORT
-void swift_unknownWeakAssign(WeakReference *ref, void *value);
+WeakReference *swift_unknownWeakAssign(WeakReference *ref, void *value);
 
 #else
 
-static inline void swift_unknownWeakAssign(WeakReference *ref, void *value) {
-  swift_weakAssign(ref, static_cast<HeapObject *>(value));
+static inline WeakReference *swift_unknownWeakAssign(WeakReference *ref,
+                                                     void *value) {
+  return swift_weakAssign(ref, static_cast<HeapObject *>(value));
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -973,15 +977,16 @@
 
 /// Copy-initialize a weak reference variable from one that might not
 /// refer to a native Swift object.
+/// \return dest
 SWIFT_RUNTIME_EXPORT
-void swift_unknownWeakCopyInit(WeakReference *dest,
-                               WeakReference *src);
+WeakReference *swift_unknownWeakCopyInit(WeakReference *dest,
+                                         WeakReference *src);
 
 #else
 
-static inline void swift_unknownWeakCopyInit(WeakReference *dest,
-                                             WeakReference *src) {
-  swift_weakCopyInit(dest, src);
+static inline WeakReference *swift_unknownWeakCopyInit(WeakReference *dest,
+                                                       WeakReference *src) {
+  return swift_weakCopyInit(dest, src);
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -990,15 +995,16 @@
 
 /// Take-initialize a weak reference variable from one that might not
 /// refer to a native Swift object.
+/// \return dest
 SWIFT_RUNTIME_EXPORT
-void swift_unknownWeakTakeInit(WeakReference *dest,
-                               WeakReference *src);
+WeakReference *swift_unknownWeakTakeInit(WeakReference *dest,
+                                         WeakReference *src);
 
 #else
 
-static inline void swift_unknownWeakTakeInit(WeakReference *dest,
-                                             WeakReference *src) {
-  swift_weakTakeInit(dest, src);
+static inline WeakReference *swift_unknownWeakTakeInit(WeakReference *dest,
+                                                       WeakReference *src) {
+  return swift_weakTakeInit(dest, src);
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -1007,15 +1013,16 @@
 
 /// Copy-assign a weak reference variable from another when either
 /// or both variables might not refer to a native Swift object.
+/// \return dest
 SWIFT_RUNTIME_EXPORT
-void swift_unknownWeakCopyAssign(WeakReference *dest,
-                                 WeakReference *src);
+WeakReference *swift_unknownWeakCopyAssign(WeakReference *dest,
+                                           WeakReference *src);
 
 #else
 
-static inline void swift_unknownWeakCopyAssign(WeakReference *dest,
-                                               WeakReference *src) {
-  swift_weakCopyAssign(dest, src);
+static inline WeakReference *swift_unknownWeakCopyAssign(WeakReference *dest,
+                                                         WeakReference *src) {
+  return swift_weakCopyAssign(dest, src);
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -1024,15 +1031,16 @@
 
 /// Take-assign a weak reference variable from another when either
 /// or both variables might not refer to a native Swift object.
+/// \return dest
 SWIFT_RUNTIME_EXPORT
-void swift_unknownWeakTakeAssign(WeakReference *dest,
-                                 WeakReference *src);
+WeakReference *swift_unknownWeakTakeAssign(WeakReference *dest,
+                                           WeakReference *src);
 
 #else
 
-static inline void swift_unknownWeakTakeAssign(WeakReference *dest,
-                                               WeakReference *src) {
-  swift_weakTakeAssign(dest, src);
+static inline WeakReference *swift_unknownWeakTakeAssign(WeakReference *dest,
+                                                         WeakReference *src) {
+  return swift_weakTakeAssign(dest, src);
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -1045,14 +1053,16 @@
 
 /// Initialize an unowned reference to an object with unknown reference
 /// counting.
+/// \return ref
 SWIFT_RUNTIME_EXPORT
-void swift_unknownUnownedInit(UnownedReference *ref, void *value);
+UnownedReference *swift_unknownUnownedInit(UnownedReference *ref, void *value);
 
 #else
 
-static inline void swift_unknownUnownedInit(UnownedReference *ref,
-                                            void *value) {
+static inline UnownedReference *swift_unknownUnownedInit(UnownedReference *ref,
+                                                         void *value) {
   swift_unownedInit(ref, static_cast<HeapObject*>(value));
+  return ref;
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -1061,14 +1071,17 @@
 
 /// Assign to an unowned reference holding an object with unknown reference
 /// counting.
+/// \return ref
 SWIFT_RUNTIME_EXPORT
-void swift_unknownUnownedAssign(UnownedReference *ref, void *value);
+UnownedReference *swift_unknownUnownedAssign(UnownedReference *ref,
+                                             void *value);
 
 #else
 
-static inline void swift_unknownUnownedAssign(UnownedReference *ref,
-                                              void *value) {
+static inline UnownedReference *
+swift_unknownUnownedAssign(UnownedReference *ref, void *value) {
   swift_unownedAssign(ref, static_cast<HeapObject*>(value));
+  return ref;
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -1121,15 +1134,17 @@
 
 /// Copy-initialize an unowned reference variable from one that might not
 /// refer to a native Swift object.
+/// \return dest
 SWIFT_RUNTIME_EXPORT
-void swift_unknownUnownedCopyInit(UnownedReference *dest,
-                                  UnownedReference *src);
+UnownedReference *swift_unknownUnownedCopyInit(UnownedReference *dest,
+                                               UnownedReference *src);
 
 #else
 
-static inline void swift_unknownUnownedCopyInit(UnownedReference *dest,
-                                                UnownedReference *src) {
+static inline UnownedReference *
+swift_unknownUnownedCopyInit(UnownedReference *dest, UnownedReference *src) {
   swift_unownedCopyInit(dest, src);
+  return dest;
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -1139,14 +1154,15 @@
 /// Take-initialize an unowned reference variable from one that might not
 /// refer to a native Swift object.
 SWIFT_RUNTIME_EXPORT
-void swift_unknownUnownedTakeInit(UnownedReference *dest,
-                                             UnownedReference *src);
+UnownedReference *swift_unknownUnownedTakeInit(UnownedReference *dest,
+                                               UnownedReference *src);
 
 #else
 
-static inline void swift_unknownUnownedTakeInit(UnownedReference *dest,
-                                                UnownedReference *src) {
+static inline UnownedReference *
+swift_unknownUnownedTakeInit(UnownedReference *dest, UnownedReference *src) {
   swift_unownedTakeInit(dest, src);
+  return dest;
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -1155,15 +1171,17 @@
 
 /// Copy-assign an unowned reference variable from another when either
 /// or both variables might not refer to a native Swift object.
+/// \return dest
 SWIFT_RUNTIME_EXPORT
-void swift_unknownUnownedCopyAssign(UnownedReference *dest,
-                                               UnownedReference *src);
+UnownedReference *swift_unknownUnownedCopyAssign(UnownedReference *dest,
+                                                 UnownedReference *src);
 
 #else
 
-static inline void swift_unknownUnownedCopyAssign(UnownedReference *dest,
-                                                  UnownedReference *src) {
+static inline UnownedReference *
+swift_unknownUnownedCopyAssign(UnownedReference *dest, UnownedReference *src) {
   swift_unownedCopyAssign(dest, src);
+  return dest;
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
@@ -1172,15 +1190,17 @@
 
 /// Take-assign an unowned reference variable from another when either
 /// or both variables might not refer to a native Swift object.
+/// \return dest
 SWIFT_RUNTIME_EXPORT
-void swift_unknownUnownedTakeAssign(UnownedReference *dest,
-                                               UnownedReference *src);
+UnownedReference *swift_unknownUnownedTakeAssign(UnownedReference *dest,
+                                                 UnownedReference *src);
 
 #else
 
-static inline void swift_unknownUnownedTakeAssign(UnownedReference *dest,
-                                                  UnownedReference *src) {
+static inline UnownedReference *
+swift_unknownUnownedTakeAssign(UnownedReference *dest, UnownedReference *src) {
   swift_unownedTakeAssign(dest, src);
+  return dest;
 }
 
 #endif /* SWIFT_OBJC_INTEROP */
diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def
index afa1eb9..0f5ff24 100644
--- a/include/swift/Runtime/RuntimeFunctions.def
+++ b/include/swift/Runtime/RuntimeFunctions.def
@@ -520,15 +520,15 @@
 
 // void swift_unknownWeakInit(WeakReference *object, void *value);
 FUNCTION(UnknownWeakInit, swift_unknownWeakInit, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(WeakReferencePtrTy),
          ARGS(WeakReferencePtrTy, UnknownRefCountedPtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
-// void swift_unknownWeakAssign(WeakReference *object, void *value);
+// WeakReference *swift_unknownWeakAssign(WeakReference *object, void *value);
 FUNCTION(UnknownWeakAssign, swift_unknownWeakAssign, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(WeakReferencePtrTy),
          ARGS(WeakReferencePtrTy, UnknownRefCountedPtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
 // void *swift_unknownWeakLoad(WeakReference *object);
 FUNCTION(UnknownWeakLoadStrong, swift_unknownWeakLoadStrong,DefaultCC,
@@ -542,29 +542,29 @@
          ARGS(WeakReferencePtrTy),
          ATTRS(NoUnwind))
 
-// void swift_unknownWeakCopyInit(WeakReference *dest, WeakReference *src);
+// WeakReference *swift_unknownWeakCopyInit(WeakReference *dest, WeakReference *src);
 FUNCTION(UnknownWeakCopyInit, swift_unknownWeakCopyInit, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(WeakReferencePtrTy),
          ARGS(WeakReferencePtrTy, WeakReferencePtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
-// void swift_unknownWeakTakeInit(WeakReference *dest, WeakReference *src);
+// void *swift_unknownWeakTakeInit(WeakReference *dest, WeakReference *src);
 FUNCTION(UnknownWeakTakeInit, swift_unknownWeakTakeInit, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(WeakReferencePtrTy),
          ARGS(WeakReferencePtrTy, WeakReferencePtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
-// void swift_unknownWeakCopyAssign(WeakReference *dest, WeakReference *src);
+// WeakReference *swift_unknownWeakCopyAssign(WeakReference *dest, WeakReference *src);
 FUNCTION(UnknownWeakCopyAssign, swift_unknownWeakCopyAssign, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(WeakReferencePtrTy),
          ARGS(WeakReferencePtrTy, WeakReferencePtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
-// void swift_unknownWeakTakeAssign(WeakReference *dest, WeakReference *src);
+// WeakReference *swift_unknownWeakTakeAssign(WeakReference *dest, WeakReference *src);
 FUNCTION(UnknownWeakTakeAssign, swift_unknownWeakTakeAssign, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(WeakReferencePtrTy),
          ARGS(WeakReferencePtrTy, WeakReferencePtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
 // void swift_unknownUnownedDestroy(UnownedReference *object);
 FUNCTION(UnknownUnownedDestroy, swift_unknownUnownedDestroy, DefaultCC,
@@ -572,17 +572,17 @@
          ARGS(UnownedReferencePtrTy),
          ATTRS(NoUnwind))
 
-// void swift_unknownUnownedInit(UnownedReference *object, void *value);
+// UnownedReference *swift_unknownUnownedInit(UnownedReference *object, void *value);
 FUNCTION(UnknownUnownedInit, swift_unknownUnownedInit, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(UnownedReferencePtrTy),
          ARGS(UnownedReferencePtrTy, UnknownRefCountedPtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
-// void swift_unknownUnownedAssign(UnownedReference *object, void *value);
+// UnownedReference *swift_unknownUnownedAssign(UnownedReference *object, void *value);
 FUNCTION(UnknownUnownedAssign, swift_unknownUnownedAssign, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(UnownedReferencePtrTy),
          ARGS(UnownedReferencePtrTy, UnknownRefCountedPtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
 // void *swift_unknownUnownedLoad(UnownedReference *object);
 FUNCTION(UnknownUnownedLoadStrong, swift_unknownUnownedLoadStrong, DefaultCC,
@@ -596,29 +596,29 @@
          ARGS(UnownedReferencePtrTy),
          ATTRS(NoUnwind))
 
-// void swift_unknownUnownedCopyInit(UnownedReference *dest, UnownedReference *src);
+// UnownedReference *swift_unknownUnownedCopyInit(UnownedReference *dest, UnownedReference *src);
 FUNCTION(UnknownUnownedCopyInit, swift_unknownUnownedCopyInit, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(UnownedReferencePtrTy),
          ARGS(UnownedReferencePtrTy, UnownedReferencePtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
-// void swift_unknownUnownedTakeInit(UnownedReference *dest, UnownedReference *src);
+// UnownedReference *swift_unknownUnownedTakeInit(UnownedReference *dest, UnownedReference *src);
 FUNCTION(UnknownUnownedTakeInit, swift_unknownUnownedTakeInit, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(UnownedReferencePtrTy),
          ARGS(UnownedReferencePtrTy, UnownedReferencePtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
-// void swift_unknownUnownedCopyAssign(UnownedReference *dest, UnownedReference *src);
+// UnownedReference *swift_unknownUnownedCopyAssign(UnownedReference *dest, UnownedReference *src);
 FUNCTION(UnknownUnownedCopyAssign, swift_unknownUnownedCopyAssign, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(UnownedReferencePtrTy),
          ARGS(UnownedReferencePtrTy, UnownedReferencePtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
-// void swift_unknownUnownedTakeAssign(UnownedReference *dest, UnownedReference *src);
+// UnownedReference *swift_unknownUnownedTakeAssign(UnownedReference *dest, UnownedReference *src);
 FUNCTION(UnknownUnownedTakeAssign, swift_unknownUnownedTakeAssign, DefaultCC,
-         RETURNS(VoidTy),
+         RETURNS(UnownedReferencePtrTy),
          ARGS(UnownedReferencePtrTy, UnownedReferencePtrTy),
-         ATTRS(NoUnwind))
+         ATTRS(NoUnwind, FirstParamReturned))
 
 // bool swift_isUniquelyReferencedNonObjC(const void *);
 FUNCTION(IsUniquelyReferencedNonObjC, swift_isUniquelyReferencedNonObjC,
@@ -1287,6 +1287,10 @@
          RETURNS(RefCountedPtrTy),
          ARGS(Int8PtrTy, Int8PtrTy),
          ATTRS(NoUnwind))
+FUNCTION(CopyKeyPathTrivialIndices, swift_copyKeyPathTrivialIndices, DefaultCC,
+         RETURNS(VoidTy),
+         ARGS(Int8PtrTy, Int8PtrTy, SizeTy),
+         ATTRS(NoUnwind))
 
 #if SWIFT_OBJC_INTEROP || !defined(SWIFT_RUNTIME_GENERATE_GLOBAL_SYMBOLS)
 
diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h
index e2bba6f..58c86d9 100644
--- a/include/swift/SIL/SILBuilder.h
+++ b/include/swift/SIL/SILBuilder.h
@@ -535,9 +535,11 @@
   KeyPathInst *createKeyPath(SILLocation Loc,
                              KeyPathPattern *Pattern,
                              SubstitutionList Subs,
+                             ArrayRef<SILValue> Args,
                              SILType Ty) {
     return insert(KeyPathInst::create(getSILDebugLocation(Loc),
-                                      Pattern, Subs, Ty, getFunction()));
+                                      Pattern, Subs, Args,
+                                      Ty, getFunction()));
   }
 
   /// Convenience function for calling emitLoad on the type lowering for
diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h
index 90773f3..6e05444 100644
--- a/include/swift/SIL/SILCloner.h
+++ b/include/swift/SIL/SILCloner.h
@@ -2344,10 +2344,15 @@
 template <typename ImplClass>
 void SILCloner<ImplClass>::visitKeyPathInst(KeyPathInst *Inst) {
   getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
+  SmallVector<SILValue, 4> opValues;
+  for (auto &op : Inst->getAllOperands())
+    opValues.push_back(getOpValue(op.get()));
+  
   doPostProcess(Inst, getBuilder().createKeyPath(
                           getOpLocation(Inst->getLoc()),
                           Inst->getPattern(),
                           getOpSubstitutions(Inst->getSubstitutions()),
+                          opValues,
                           getOpType(Inst->getType())));
 }
 
diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h
index c32b4a6..288bf2b 100644
--- a/include/swift/SIL/SILInstruction.h
+++ b/include/swift/SIL/SILInstruction.h
@@ -1534,11 +1534,13 @@
     OptionalWrap,
   };
   
-  // The pair of a captured index value and its Hashable conformance for a
+  // Description of a captured index value and its Hashable conformance for a
   // subscript keypath.
-  struct IndexPair {
+  struct Index {
     unsigned Operand;
-    ProtocolConformance *Hashable;
+    CanType FormalType;
+    SILType LoweredType;
+    ProtocolConformanceRef Hashable;
   };
   
 private:
@@ -1555,7 +1557,9 @@
                        ComputedPropertyId::KindType>
     SetterAndIdKind;
   ComputedPropertyId::ValueType IdValue;
-  ArrayRef<IndexPair> Indices;
+  ArrayRef<Index> Indices;
+  SILFunction *IndicesEqual;
+  SILFunction *IndicesHash;
   CanType ComponentType;
   
   unsigned kindForPacking(Kind k) {
@@ -1579,13 +1583,21 @@
   KeyPathPatternComponent(ComputedPropertyId id, Kind kind,
                           SILFunction *getter,
                           SILFunction *setter,
-                          ArrayRef<IndexPair> indices,
+                          ArrayRef<Index> indices,
+                          SILFunction *indicesEqual,
+                          SILFunction *indicesHash,
                           CanType ComponentType)
     : ValueAndKind(getter, kindForPacking(kind)),
       SetterAndIdKind(setter, id.Kind),
       IdValue(id.Value),
       Indices(indices),
-      ComponentType(ComponentType) {}
+      IndicesEqual(indicesEqual),
+      IndicesHash(indicesHash),
+      ComponentType(ComponentType) {
+    assert(indices.empty() == !indicesEqual
+           && indices.empty() == !indicesHash
+           && "must have equals/hash functions iff there are indices");
+  }
 
 public:
   KeyPathPatternComponent() : ValueAndKind(nullptr, 0) {}
@@ -1661,7 +1673,7 @@
     llvm_unreachable("unhandled kind");
   }
   
-  ArrayRef<IndexPair> getComputedPropertyIndices() const {
+  ArrayRef<Index> getComputedPropertyIndices() const {
     switch (getKind()) {
     case Kind::StoredProperty:
     case Kind::OptionalChain:
@@ -1674,6 +1686,13 @@
     }
   }
   
+  SILFunction *getComputedPropertyIndexEquals() const {
+    return IndicesEqual;
+  }
+  SILFunction *getComputedPropertyIndexHash() const {
+    return IndicesHash;
+  }
+
   bool isComputedSettablePropertyMutating() const;
   
   static KeyPathPatternComponent forStoredProperty(VarDecl *property,
@@ -1684,20 +1703,26 @@
   static KeyPathPatternComponent
   forComputedGettableProperty(ComputedPropertyId identifier,
                               SILFunction *getter,
-                              ArrayRef<IndexPair> indices,
+                              ArrayRef<Index> indices,
+                              SILFunction *indicesEquals,
+                              SILFunction *indicesHash,
                               CanType ty) {
     return KeyPathPatternComponent(identifier, Kind::GettableProperty,
-                                   getter, nullptr, indices, ty);
+                                   getter, nullptr, indices,
+                                   indicesEquals, indicesHash, ty);
   }
 
   static KeyPathPatternComponent
   forComputedSettableProperty(ComputedPropertyId identifier,
                               SILFunction *getter,
                               SILFunction *setter,
-                              ArrayRef<IndexPair> indices,
+                              ArrayRef<Index> indices,
+                              SILFunction *indicesEquals,
+                              SILFunction *indicesHash,
                               CanType ty) {
     return KeyPathPatternComponent(identifier, Kind::SettableProperty,
-                                   getter, setter, indices, ty);
+                                   getter, setter, indices,
+                                   indicesEquals, indicesHash, ty);
   }
   
   static KeyPathPatternComponent
@@ -1797,32 +1822,40 @@
 /// Instantiates a key path object.
 class KeyPathInst final
   : public SILInstruction,
-    private llvm::TrailingObjects<KeyPathInst, Substitution>
+    private llvm::TrailingObjects<KeyPathInst, Substitution, Operand>
 {
   friend SILBuilder;
   friend TrailingObjects;
   
   KeyPathPattern *Pattern;
-  unsigned NumSubstitutions;
+  unsigned NumSubstitutions, NumOperands;
   
   static KeyPathInst *create(SILDebugLocation Loc,
                              KeyPathPattern *Pattern,
                              SubstitutionList Subs,
+                             ArrayRef<SILValue> Args,
                              SILType Ty,
                              SILFunction &F);
   
   KeyPathInst(SILDebugLocation Loc,
               KeyPathPattern *Pattern,
               SubstitutionList Subs,
+              ArrayRef<SILValue> Args,
               SILType Ty);
   
+  size_t numTrailingObjects(OverloadToken<Substitution>) const {
+    return NumSubstitutions;
+  }
+  size_t numTrailingObjects(OverloadToken<Operand>) const {
+    return NumOperands;
+  }
+  
 public:
   KeyPathPattern *getPattern() const;
   bool hasPattern() const { return (bool)Pattern; }
 
   ArrayRef<Operand> getAllOperands() const {
-    // TODO: Subscript keypaths will have operands.
-    return {};
+    return const_cast<KeyPathInst*>(this)->getAllOperands();
   }
   MutableArrayRef<Operand> getAllOperands();
 
diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h
index e3b548f..12daf09 100644
--- a/include/swift/SIL/SILType.h
+++ b/include/swift/SIL/SILType.h
@@ -258,8 +258,10 @@
   /// True if the type, or the referenced type of an address type, is trivial.
   bool isTrivial(SILModule &M) const;
 
-  /// True if the type, or the referenced type of an address type, is a
-  /// scalar reference-counted type.
+  /// True if the type, or the referenced type of an address type, is known to
+  /// be a scalar reference-counted type. If this is false, then some part of
+  /// the type may be opaque. It may become reference counted later after
+  /// specialization.
   bool isReferenceCounted(SILModule &M) const;
 
   /// Returns true if the referenced type is a function type that never
diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h
index bc7d869..4de46f2 100644
--- a/include/swift/SIL/SILValue.h
+++ b/include/swift/SIL/SILValue.h
@@ -326,15 +326,13 @@
   /// FIXME: this could be space-compressed.
   SILInstruction *Owner;
 
+public:
   Operand(SILInstruction *owner) : Owner(owner) {}
   Operand(SILInstruction *owner, SILValue theValue)
       : TheValue(theValue), Owner(owner) {
     insertIntoCurrent();
   }
-  template<unsigned N> friend class FixedOperandList;
-  template<unsigned N> friend class TailAllocatedOperandList;
 
-public:
   /// Operands are not copyable.
   Operand(const Operand &use) = delete;
   Operand &operator=(const Operand &use) = delete;
diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h
index 4afeb8e..08f2d2e 100644
--- a/include/swift/Serialization/ModuleFormat.h
+++ b/include/swift/Serialization/ModuleFormat.h
@@ -54,7 +54,7 @@
 /// in source control, you should also update the comment to briefly
 /// describe what change you made. The content of this comment isn't important;
 /// it just ensures a conflict if two people change the module format.
-const uint16_t VERSION_MINOR = 364; // Last change: Builtin.assignCopyArray...
+const uint16_t VERSION_MINOR = 366; // Last change: default argument resilience expansion
 
 using DeclID = PointerEmbeddedInt<unsigned, 31>;
 using DeclIDField = BCFixed<31>;
@@ -388,6 +388,13 @@
   /// TODO: Float, string, char, etc.
 };
 
+// These IDs must \em not be renumbered or reordered without incrementing
+// VERSION_MAJOR.
+enum class ResilienceExpansion : uint8_t {
+  Minimal = 0,
+  Maximal,
+};
+
 using EnumElementRawValueKindField = BCFixed<4>;
 
 /// The various types of blocks that can occur within a serialized Swift
@@ -893,6 +900,7 @@
     DeclIDField, // overridden decl
     AccessLevelField, // access level
     BCFixed<1>,   // requires a new vtable slot
+    BCFixed<1>,   // default argument resilience expansion
     BCFixed<1>,   // 'required' but overridden is not (used for recovery)
     BCVBR<5>,     // number of parameter name components
     BCArray<IdentifierIDField> // name components,
@@ -957,6 +965,7 @@
     AddressorKindField, // addressor kind
     AccessLevelField, // access level
     BCFixed<1>,   // requires a new vtable slot
+    BCFixed<1>,   // default argument resilience expansion
     BCArray<IdentifierIDField> // name components,
                                // followed by TypeID dependencies
     // The record is trailed by:
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index b1a9b87..8d003f1 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -129,6 +129,9 @@
   DECL_CLASS *NAME##Decl = nullptr;
 #include "swift/AST/KnownStdlibTypes.def"
 
+  /// The declaration of '+' function for two RangeReplaceableCollection.
+  FuncDecl *PlusFunctionOnRangeReplaceableCollection = nullptr;
+
   /// The declaration of Swift.Optional<T>.Some.
   EnumElementDecl *OptionalSomeDecl = nullptr;
 
@@ -545,6 +548,30 @@
   return nullptr;
 }
 
+FuncDecl *ASTContext::getPlusFunctionOnRangeReplaceableCollection() const {
+  if (Impl.PlusFunctionOnRangeReplaceableCollection) {
+    return Impl.PlusFunctionOnRangeReplaceableCollection;
+  }
+  // Find all of the declarations with this name in the Swift module.
+  SmallVector<ValueDecl *, 1> Results;
+  lookupInSwiftModule("+", Results);
+  for (auto Result : Results) {
+    if (auto *FD = dyn_cast<FuncDecl>(Result)) {
+      if(!FD->getOperatorDecl())
+        continue;
+      for (auto Req: FD->getGenericRequirements()) {
+        if (Req.getKind() == RequirementKind::Conformance &&
+              Req.getSecondType()->getNominalOrBoundGenericNominal() ==
+            getRangeReplaceableCollectionDecl()) {
+          Impl.PlusFunctionOnRangeReplaceableCollection = FD;
+        }
+      }
+    }
+  }
+  return Impl.PlusFunctionOnRangeReplaceableCollection;
+}
+
+
 #define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
   DECL_CLASS *ASTContext::get##NAME##Decl() const { \
     if (!Impl.NAME##Decl) \
diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp
index 8e0db26..0191a17 100644
--- a/lib/AST/ASTMangler.cpp
+++ b/lib/AST/ASTMangler.cpp
@@ -276,30 +276,70 @@
   return finalize();
 }
 
-std::string ASTMangler::mangleKeyPathGetterThunkHelper(const VarDecl *property,
-                                                   GenericSignature *signature,
-                                                   CanType baseType) {
+std::string ASTMangler::mangleKeyPathGetterThunkHelper(
+                                            const AbstractStorageDecl *property,
+                                            GenericSignature *signature,
+                                            CanType baseType,
+                                            ArrayRef<CanType> subs) {
   beginMangling();
   appendEntity(property);
   if (signature)
     appendGenericSignature(signature);
   appendType(baseType);
+  if (isa<SubscriptDecl>(property)) {
+    // Subscripts can be generic, and different key paths could capture the same
+    // subscript at different generic arguments.
+    for (auto &sub : subs) {
+      appendType(sub);
+    }
+  }
   appendOperator("TK");
   return finalize();
 }
 
-std::string ASTMangler::mangleKeyPathSetterThunkHelper(const VarDecl *property,
-                                                   GenericSignature *signature,
-                                                   CanType baseType) {
+std::string ASTMangler::mangleKeyPathSetterThunkHelper(
+                                          const AbstractStorageDecl *property,
+                                          GenericSignature *signature,
+                                          CanType baseType,
+                                          ArrayRef<CanType> subs) {
   beginMangling();
   appendEntity(property);
   if (signature)
     appendGenericSignature(signature);
   appendType(baseType);
+  if (isa<SubscriptDecl>(property)) {
+    // Subscripts can be generic, and different key paths could capture the same
+    // subscript at different generic arguments.
+    for (auto &sub : subs) {
+      appendType(sub);
+    }
+  }
   appendOperator("Tk");
   return finalize();
 }
 
+std::string ASTMangler::mangleKeyPathEqualsHelper(ArrayRef<CanType> indices,
+                                                  GenericSignature *signature) {
+  beginMangling();
+  for (auto &index : indices)
+    appendType(index);
+  if (signature)
+    appendGenericSignature(signature);
+  appendOperator("TH");
+  return finalize();
+}
+
+std::string ASTMangler::mangleKeyPathHashHelper(ArrayRef<CanType> indices,
+                                                GenericSignature *signature) {
+  beginMangling();
+  for (auto &index : indices)
+    appendType(index);
+  if (signature)
+    appendGenericSignature(signature);
+  appendOperator("Th");
+  return finalize();
+}
+
 std::string ASTMangler::mangleGlobalInit(const VarDecl *decl, int counter,
                                          bool isInitFunc) {
   auto topLevelContext = decl->getDeclContext()->getModuleScopeContext();
diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp
index e4055aa..78a2088 100644
--- a/lib/AST/ASTWalker.cpp
+++ b/lib/AST/ASTWalker.cpp
@@ -954,7 +954,8 @@
                 newIndex,
                 component.getSubscriptLabels(),
                 component.getComponentType(),
-                component.getLoc())
+                component.getLoc(),
+                component.getSubscriptIndexHashableConformances())
             : KeyPathExpr::Component
                          ::forUnresolvedSubscriptWithPrebuiltIndexExpr(
                 E->getType()->getASTContext(),
diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp
index f8d4e75..45690b5 100644
--- a/lib/AST/DeclContext.cpp
+++ b/lib/AST/DeclContext.cpp
@@ -519,8 +519,10 @@
   for (const auto *dc = this; dc->isLocalContext(); dc = dc->getParent()) {
     // Default argument initializer contexts have their resilience expansion
     // set when they're type checked.
-    if (auto *DAI = dyn_cast<DefaultArgumentInitializer>(dc))
-      return DAI->getResilienceExpansion();
+    if (auto *DAI = dyn_cast<DefaultArgumentInitializer>(dc)) {
+      return cast<AbstractFunctionDecl>(dc->getParent())
+          ->getDefaultArgumentResilienceExpansion();
+    }
 
     if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc)) {
       // If the function is a nested function, we will serialize its body if
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 819fd19..03ba1bb 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -2131,14 +2131,15 @@
 
 KeyPathExpr::Component
 KeyPathExpr::Component::forSubscript(ASTContext &ctx,
-                                     ConcreteDeclRef subscript,
-                                     SourceLoc lSquareLoc,
-                                     ArrayRef<Expr *> indexArgs,
-                                     ArrayRef<Identifier> indexArgLabels,
-                                     ArrayRef<SourceLoc> indexArgLabelLocs,
-                                     SourceLoc rSquareLoc,
-                                     Expr *trailingClosure,
-                                     Type elementType) {
+                             ConcreteDeclRef subscript,
+                             SourceLoc lSquareLoc,
+                             ArrayRef<Expr *> indexArgs,
+                             ArrayRef<Identifier> indexArgLabels,
+                             ArrayRef<SourceLoc> indexArgLabelLocs,
+                             SourceLoc rSquareLoc,
+                             Expr *trailingClosure,
+                             Type elementType,
+                             ArrayRef<ProtocolConformanceRef> indexHashables) {
   SmallVector<Identifier, 4> indexArgLabelsScratch;
   SmallVector<SourceLoc, 4> indexArgLabelLocsScratch;
   Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels,
@@ -2149,7 +2150,8 @@
   return forSubscriptWithPrebuiltIndexExpr(subscript, index,
                                            indexArgLabels,
                                            elementType,
-                                           lSquareLoc);
+                                           lSquareLoc,
+                                           indexHashables);
 }
 
 KeyPathExpr::Component
@@ -2176,6 +2178,7 @@
                      DeclNameOrRef decl,
                      Expr *indexExpr,
                      ArrayRef<Identifier> subscriptLabels,
+                     ArrayRef<ProtocolConformanceRef> indexHashables,
                      Kind kind,
                      Type type,
                      SourceLoc loc)
@@ -2183,13 +2186,35 @@
       SubscriptLabels(subscriptLabels.empty()
                        ? subscriptLabels
                        : ctxForCopyingLabels->AllocateCopy(subscriptLabels)),
+      SubscriptHashableConformances(indexHashables),
       ComponentType(type), Loc(loc)
   {}
 
 KeyPathExpr::Component
 KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr(
        ConcreteDeclRef subscript, Expr *index, ArrayRef<Identifier> labels,
-       Type elementType, SourceLoc loc) {
+       Type elementType, SourceLoc loc,
+       ArrayRef<ProtocolConformanceRef> indexHashables) {
   return Component(&elementType->getASTContext(),
-                   subscript, index, {}, Kind::Subscript, elementType, loc);
+                   subscript, index, labels, indexHashables,
+                   Kind::Subscript, elementType, loc);
+}
+
+void KeyPathExpr::Component::setSubscriptIndexHashableConformances(
+    ArrayRef<ProtocolConformanceRef> hashables) {
+  switch (getKind()) {
+  case Kind::Subscript:
+    SubscriptHashableConformances = getComponentType()->getASTContext()
+      .AllocateCopy(hashables);
+    return;
+    
+  case Kind::UnresolvedSubscript:
+  case Kind::Invalid:
+  case Kind::OptionalChain:
+  case Kind::OptionalWrap:
+  case Kind::OptionalForce:
+  case Kind::UnresolvedProperty:
+  case Kind::Property:
+    llvm_unreachable("no hashable conformances for this kind");
+  }
 }
diff --git a/lib/ClangImporter/ClangAdapter.cpp b/lib/ClangImporter/ClangAdapter.cpp
index e0155aa..04daaf81 100644
--- a/lib/ClangImporter/ClangAdapter.cpp
+++ b/lib/ClangImporter/ClangAdapter.cpp
@@ -437,7 +437,7 @@
 importer::getSwiftNewtypeAttr(const clang::TypedefNameDecl *decl,
                               ImportNameVersion version) {
   // Newtype was introduced in Swift 3
-  if (version < ImportNameVersion::Swift3 )
+  if (version <= ImportNameVersion::swift2())
     return nullptr;
   return retrieveNewTypeAttr(decl);
 }
@@ -448,7 +448,7 @@
                                                    clang::Sema &clangSema,
                                                    ImportNameVersion version) {
   // Newtype was introduced in Swift 3
-  if (version < ImportNameVersion::Swift3 )
+  if (version <= ImportNameVersion::swift2())
     return nullptr;
 
   auto varDecl = dyn_cast<clang::VarDecl>(decl);
diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp
index c707c36..4d4ab55 100644
--- a/lib/ClangImporter/ClangImporter.cpp
+++ b/lib/ClangImporter/ClangImporter.cpp
@@ -1650,7 +1650,7 @@
       BridgingHeaderExplicitlyRequested(!opts.BridgingHeader.empty()),
       DisableAdapterModules(opts.DisableAdapterModules),
       IsReadingBridgingPCH(false),
-      CurrentVersion(nameVersionFromOptions(ctx.LangOpts)),
+      CurrentVersion(ImportNameVersion::fromOptions(ctx.LangOpts)),
       BridgingHeaderLookupTable(new SwiftLookupTable(nullptr)),
       platformAvailability(ctx.LangOpts),
       nameImporter() {}
@@ -2508,32 +2508,34 @@
 
     bool anyMatching = false;
     TypeDecl *originalDecl = nullptr;
-    owner.forEachDistinctName(clangTypeDecl, [&](ImportedName newName,
-                                                 ImportNameVersion nameVersion){
+    owner.forEachDistinctName(clangTypeDecl,
+                              [&](ImportedName newName,
+                                  ImportNameVersion nameVersion) -> bool {
       if (anyMatching)
-        return;
+        return true;
       if (!newName.getDeclName().isSimpleName(name))
-        return;
+        return true;
 
       auto decl = dyn_cast_or_null<TypeDecl>(
           owner.importDeclReal(clangTypeDecl, nameVersion));
       if (!decl)
-        return;
+        return false;
 
       if (!originalDecl)
         originalDecl = decl;
       else if (originalDecl == decl)
-        return;
+        return true;
 
       auto *importedContext = decl->getDeclContext()->
           getAsNominalTypeOrNominalTypeExtensionContext();
       if (importedContext != baseType)
-        return;
+        return true;
 
       assert(decl->getFullName().matchesRef(name) &&
              "importFullName behaved differently from importDecl");
       results.push_back(decl);
       anyMatching = true;
+      return true;
     });
   }
 
@@ -3150,10 +3152,8 @@
         const clang::NamedDecl *recentClangDecl =
             clangDecl->getMostRecentDecl();
 
-        forEachImportNameVersionFromCurrent(CurrentVersion,
-                                            [&](ImportNameVersion nameVersion) {
-          if (nameVersion == CurrentVersion)
-            return;
+        CurrentVersion.forEachOtherImportNameVersion(
+            [&](ImportNameVersion nameVersion) {
           if (anyMatching)
             return;
 
@@ -3211,12 +3211,12 @@
 
     forEachDistinctName(clangDecl,
                         [&](ImportedName importedName,
-                            ImportNameVersion nameVersion) {
+                            ImportNameVersion nameVersion) -> bool {
       // Import the declaration.
       auto decl =
           cast_or_null<ValueDecl>(importDeclReal(clangDecl, nameVersion));
       if (!decl)
-        return;
+        return false;
 
       // If the name we found matches, report the declaration.
       // FIXME: If we didn't need to check alternate decls here, we could avoid
@@ -3232,6 +3232,7 @@
           consumer.foundDecl(alternate, DeclVisibilityKind::DynamicLookup);
         }
       }
+      return true;
     });
   }
 }
diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp
index e88a2a9..e539e2b 100644
--- a/lib/ClangImporter/ImportDecl.cpp
+++ b/lib/ClangImporter/ImportDecl.cpp
@@ -349,11 +349,17 @@
 
 void ClangImporter::Implementation::forEachDistinctName(
     const clang::NamedDecl *decl,
-    llvm::function_ref<void(ImportedName, ImportNameVersion)> action) {
+    llvm::function_ref<bool(ImportedName, ImportNameVersion)> action) {
   using ImportNameKey = std::pair<DeclName, EffectiveClangContext>;
   SmallVector<ImportNameKey, 8> seenNames;
-  forEachImportNameVersionFromCurrent(CurrentVersion,
-                                      [&](ImportNameVersion nameVersion) {
+
+  ImportedName newName = importFullName(decl, CurrentVersion);
+  ImportNameKey key(newName, newName.getEffectiveContext());
+  if (action(newName, CurrentVersion))
+    seenNames.push_back(key);
+
+  CurrentVersion.forEachOtherImportNameVersion(
+      [&](ImportNameVersion nameVersion) {
     // Check to see if the name is different.
     ImportedName newName = importFullName(decl, nameVersion);
     ImportNameKey key(newName, newName.getEffectiveContext());
@@ -365,8 +371,8 @@
     });
     if (seen)
       return;
-    seenNames.push_back(key);
-    action(newName, nameVersion);
+    if (action(newName, nameVersion))
+      seenNames.push_back(key);
   });
 }
 
@@ -1927,7 +1933,7 @@
                                 Optional<ImportedName> &correctSwiftName) {
       ImportNameVersion canonicalVersion = getActiveSwiftVersion();
       if (isa<clang::TypeDecl>(D) || isa<clang::ObjCContainerDecl>(D)) {
-        canonicalVersion = ImportNameVersion::ForTypes;
+        canonicalVersion = ImportNameVersion::forTypes();
       }
       correctSwiftName = None;
 
@@ -2113,36 +2119,38 @@
         // If we're importing a global as a member, we need to provide the
         // effective context.
         Impl.printSwiftName(
-            correctSwiftName,
+            correctSwiftName, getActiveSwiftVersion(),
             /*fullyQualified=*/correctSwiftName.importAsMember(), os);
       }
 
-      unsigned majorVersion = majorVersionNumberForNameVersion(getVersion());
       DeclAttribute *attr;
-      if (isActiveSwiftVersion() || getVersion() == ImportNameVersion::Raw) {
+      if (isActiveSwiftVersion() || getVersion() == ImportNameVersion::raw()) {
         // "Raw" is the Objective-C name, which was never available in Swift.
         // Variants within the active version are usually declarations that
         // have been superseded, like the accessors of a property.
         attr = AvailableAttr::createPlatformAgnostic(
             ctx, /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()),
             PlatformAgnosticAvailabilityKind::UnavailableInSwift);
-      } else if (getVersion() < getActiveSwiftVersion()) {
-        // A Swift 2 name, for example, was obsoleted in Swift 3.
-        attr = AvailableAttr::createPlatformAgnostic(
-            ctx, /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()),
-            PlatformAgnosticAvailabilityKind::SwiftVersionSpecific,
-            clang::VersionTuple(majorVersion + 1));
       } else {
-        // Future names are introduced in their future version.
-        assert(getVersion() > getActiveSwiftVersion());
-        attr = new (ctx) AvailableAttr(
-            SourceLoc(), SourceRange(), PlatformKind::none,
-            /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()),
-            /*Introduced*/clang::VersionTuple(majorVersion), SourceRange(),
-            /*Deprecated*/clang::VersionTuple(), SourceRange(),
-            /*Obsoleted*/clang::VersionTuple(), SourceRange(),
-            PlatformAgnosticAvailabilityKind::SwiftVersionSpecific,
-            /*Implicit*/false);
+        unsigned majorVersion = getVersion().majorVersionNumber();
+        if (getVersion() < getActiveSwiftVersion()) {
+          // A Swift 2 name, for example, was obsoleted in Swift 3.
+          attr = AvailableAttr::createPlatformAgnostic(
+              ctx, /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()),
+              PlatformAgnosticAvailabilityKind::SwiftVersionSpecific,
+              clang::VersionTuple(majorVersion + 1));
+        } else {
+          // Future names are introduced in their future version.
+          assert(getVersion() > getActiveSwiftVersion());
+          attr = new (ctx) AvailableAttr(
+              SourceLoc(), SourceRange(), PlatformKind::none,
+              /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()),
+              /*Introduced*/clang::VersionTuple(majorVersion), SourceRange(),
+              /*Deprecated*/clang::VersionTuple(), SourceRange(),
+              /*Obsoleted*/clang::VersionTuple(), SourceRange(),
+              PlatformAgnosticAvailabilityKind::SwiftVersionSpecific,
+              /*Implicit*/false);
+        }
       }
 
       decl->getAttrs().add(attr);
@@ -2663,31 +2671,33 @@
         case EnumKind::Unknown:
           Impl.forEachDistinctName(constant,
                                    [&](ImportedName newName,
-                                       ImportNameVersion nameVersion) {
+                                       ImportNameVersion nameVersion) -> bool {
             Decl *imported = Impl.importDecl(constant, nameVersion);
             if (!imported)
-              return;
+              return false;
             if (nameVersion == getActiveSwiftVersion())
               enumeratorDecl = imported;
             else
               variantDecls.push_back(imported);
+            return true;
           });
           break;
         case EnumKind::Options:
           Impl.forEachDistinctName(constant,
                                    [&](ImportedName newName,
-                                       ImportNameVersion nameVersion) {
+                                       ImportNameVersion nameVersion) -> bool {
             if (!contextIsEnum(newName))
-              return;
+              return true;
             SwiftDeclConverter converter(Impl, nameVersion);
             Decl *imported =
                 converter.importOptionConstant(constant, decl, result);
             if (!imported)
-              return;
+              return false;
             if (nameVersion == getActiveSwiftVersion())
               enumeratorDecl = imported;
             else
               variantDecls.push_back(imported);
+            return true;
           });
           break;
         case EnumKind::Enum: {
@@ -2735,18 +2745,19 @@
 
           Impl.forEachDistinctName(constant,
                                    [&](ImportedName newName,
-                                       ImportNameVersion nameVersion) {
+                                       ImportNameVersion nameVersion) -> bool {
             if (nameVersion == getActiveSwiftVersion())
-              return;
+              return true;
             if (!contextIsEnum(newName))
-              return;
+              return true;
             SwiftDeclConverter converter(Impl, nameVersion);
             Decl *imported =
                 converter.importEnumCase(constant, decl, cast<EnumDecl>(result),
                                          enumeratorDecl);
             if (!imported)
-              return;
+              return false;
             variantDecls.push_back(imported);
+            return true;
           });
           break;
         }
@@ -4823,7 +4834,7 @@
   // we don't care.
   Decl *importedDecl = nullptr;
   if (getVersion() >= getActiveSwiftVersion())
-    importedDecl = Impl.importDecl(decl, ImportNameVersion::ForTypes);
+    importedDecl = Impl.importDecl(decl, ImportNameVersion::forTypes());
   if (!importedDecl && getVersion() != getActiveSwiftVersion())
     importedDecl = Impl.importDecl(decl, getActiveSwiftVersion());
   auto typeDecl = dyn_cast_or_null<TypeDecl>(importedDecl);
@@ -6894,9 +6905,7 @@
   {
     // Render a swift_name string.
     llvm::raw_svector_ostream os(renamed);
-    printSwiftName(importedName,
-                   /*fullyQualified=*/true,
-                   os);
+    printSwiftName(importedName, CurrentVersion, /*fullyQualified=*/true, os);
   }
 
   return SwiftContext.AllocateCopy(StringRef(renamed));
@@ -8023,9 +8032,9 @@
       continue;
 
     forEachDistinctName(
-        decl, [&](ImportedName newName, ImportNameVersion nameVersion) {
-          addMemberAndAlternatesToExtension(decl, newName, nameVersion, ext);
-        });
+        decl, [&](ImportedName newName, ImportNameVersion nameVersion) -> bool {
+      return addMemberAndAlternatesToExtension(decl, newName, nameVersion, ext);
+    });
   }
 }
 
@@ -8043,29 +8052,30 @@
   return result;
 }
 
-void ClangImporter::Implementation::addMemberAndAlternatesToExtension(
+bool ClangImporter::Implementation::addMemberAndAlternatesToExtension(
     clang::NamedDecl *decl, ImportedName newName, ImportNameVersion nameVersion,
     ExtensionDecl *ext) {
   // Quickly check the context and bail out if it obviously doesn't
   // belong here.
   if (auto *importDC = newName.getEffectiveContext().getAsDeclContext())
     if (importDC->isTranslationUnit())
-      return;
+      return true;
 
   // Then try to import the decl under the specified name.
   auto *member = importDecl(decl, nameVersion);
   if (!member)
-    return;
+    return false;
 
   member = findMemberThatWillLandInAnExtensionContext(member);
   if (!member || member->getDeclContext() != ext)
-    return;
+    return true;
   ext->addMember(member);
 
   for (auto alternate : getAlternateDecls(member)) {
     if (alternate->getDeclContext() == ext)
       ext->addMember(alternate);
   }
+  return true;
 }
 
 static ExtensionDecl *
@@ -8127,26 +8137,27 @@
     const clang::NamedDecl *nd, SmallVectorImpl<Decl *> &members) {
   llvm::SmallPtrSet<Decl *, 4> knownAlternateMembers;
   forEachDistinctName(
-      nd, [&](ImportedName name, ImportNameVersion nameVersion) {
-        auto member = importDecl(nd, nameVersion);
-        if (!member)
-          return;
+      nd, [&](ImportedName name, ImportNameVersion nameVersion) -> bool {
+    auto member = importDecl(nd, nameVersion);
+    if (!member)
+      return false;
 
-        // If there are alternate declarations for this member, add them.
-        for (auto alternate : getAlternateDecls(member)) {
-          if (alternate->getDeclContext() == member->getDeclContext() &&
-              knownAlternateMembers.insert(alternate).second) {
-            members.push_back(alternate);
-          }
-        }
+    // If there are alternate declarations for this member, add them.
+    for (auto alternate : getAlternateDecls(member)) {
+      if (alternate->getDeclContext() == member->getDeclContext() &&
+          knownAlternateMembers.insert(alternate).second) {
+        members.push_back(alternate);
+      }
+    }
 
-        // If this declaration shouldn't be visible, don't add it to
-        // the list.
-        if (shouldSuppressDeclImport(nd))
-          return;
+    // If this declaration shouldn't be visible, don't add it to
+    // the list.
+    if (shouldSuppressDeclImport(nd))
+      return true;
 
-        members.push_back(member);
-      });
+    members.push_back(member);
+    return true;
+  });
 }
 
 void ClangImporter::Implementation::collectMembersToAdd(
diff --git a/lib/ClangImporter/ImportEnumInfo.cpp b/lib/ClangImporter/ImportEnumInfo.cpp
index 4dfb3ee..f9ac7cc 100644
--- a/lib/ClangImporter/ImportEnumInfo.cpp
+++ b/lib/ClangImporter/ImportEnumInfo.cpp
@@ -68,7 +68,7 @@
   // If API notes have /removed/ a FlagEnum or EnumExtensibility attribute,
   // then we don't need to check the macros.
   for (auto *attr : decl->specific_attrs<clang::SwiftVersionedAttr>()) {
-    if (!attr->getVersion().empty())
+    if (!attr->getIsReplacedByActive())
       continue;
     if (isa<clang::FlagEnumAttr>(attr->getAttrToAdd()) ||
         isa<clang::EnumExtensibilityAttr>(attr->getAttrToAdd())) {
diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp
index 2d40e02..161f1e9 100644
--- a/lib/ClangImporter/ImportName.cpp
+++ b/lib/ClangImporter/ImportName.cpp
@@ -54,39 +54,6 @@
 using clang::CompilerInstance;
 using clang::CompilerInvocation;
 
-ImportNameVersion
-importer::nameVersionFromOptions(const LangOptions &langOpts) {
-  auto languageVersion = langOpts.EffectiveLanguageVersion;
-  switch (languageVersion[0]) {
-  default:
-    llvm_unreachable("unknown swift language version");
-  case 1:
-  case 2:
-    return ImportNameVersion::Swift2;
-  case 3:
-    return ImportNameVersion::Swift3;
-  // Fixme: Figure out the importing story for 5 instead of falling back to 4.
-  case 4:
-  case 5:
-    return ImportNameVersion::Swift4;
-  }
-}
-
-unsigned importer::majorVersionNumberForNameVersion(ImportNameVersion version) {
-  switch (version) {
-  case ImportNameVersion::Raw:
-    return 0;
-  case ImportNameVersion::Swift2:
-    return 2;
-  case ImportNameVersion::Swift3:
-    return 3;
-  case ImportNameVersion::Swift4:
-    return 4;
-  }
-
-  llvm_unreachable("Unhandled ImportNameVersion in switch.");
-}
-
 
 /// Determine whether the given Clang selector matches the given
 /// selector pieces.
@@ -318,10 +285,9 @@
 
 /// Will recursively print out the fully qualified context for the given name.
 /// Ends with a trailing "."
-static void
-printFullContextPrefix(ImportedName name,
-                       llvm::raw_ostream &os,
-                       ClangImporter::Implementation &Impl) {
+static void printFullContextPrefix(ImportedName name, ImportNameVersion version,
+                                   llvm::raw_ostream &os,
+                                   ClangImporter::Implementation &Impl) {
   const clang::NamedDecl *newDeclContextNamed = nullptr;
   switch (name.getEffectiveContext().getKind()) {
   case EffectiveClangContext::UnresolvedContext:
@@ -347,12 +313,13 @@
 
   // Now, let's print out the parent
   assert(newDeclContextNamed && "should of been set");
-  auto parentName = Impl.importFullName(newDeclContextNamed, name.getVersion());
-  printFullContextPrefix(parentName, os, Impl);
+  auto parentName = Impl.importFullName(newDeclContextNamed, version);
+  printFullContextPrefix(parentName, version, os, Impl);
   os << parentName.getDeclName() << ".";
 }
 
 void ClangImporter::Implementation::printSwiftName(ImportedName name,
+                                                   ImportNameVersion version,
                                                    bool fullyQualified,
                                                    llvm::raw_ostream &os) {
   // Property accessors.
@@ -376,7 +343,7 @@
   }
 
   if (fullyQualified)
-    printFullContextPrefix(name, os, *this);
+    printFullContextPrefix(name, version, os, *this);
 
   // Base name.
   os << name.getDeclName().getBaseName();
@@ -578,17 +545,63 @@
   return None;
 }
 
-template <typename A>
-static bool matchesVersion(A *versionedAttr, ImportNameVersion version) {
-  clang::VersionTuple attrVersion = versionedAttr->getVersion();
-  if (attrVersion.empty())
-    return version == ImportNameVersion::LAST_VERSION;
-  return attrVersion.getMajor() == majorVersionNumberForNameVersion(version);
+namespace {
+/// Aggregate struct for the common members of clang::SwiftVersionedAttr and
+/// clang::SwiftVersionedRemovalAttr.
+///
+/// For a SwiftVersionedRemovalAttr, the Attr member will be null.
+struct VersionedSwiftNameInfo {
+  const clang::SwiftNameAttr *Attr;
+  clang::VersionTuple Version;
+  bool IsReplacedByActive;
+};
+
+/// The action to take upon seeing a particular versioned swift_name annotation.
+enum class VersionedSwiftNameAction {
+  /// This annotation is not interesting.
+  Ignore,
+  /// This annotation is better than whatever we have so far.
+  Use,
+  /// This annotation is better than nothing, but that's all; don't bother
+  /// recording its version.
+  UseAsFallback,
+  /// This annotation itself isn't interesting, but its version shows that the
+  /// correct answer is whatever's currently active.
+  ResetToActive
+};
+} // end anonymous namespace
+
+static VersionedSwiftNameAction
+checkVersionedSwiftName(VersionedSwiftNameInfo info,
+                        clang::VersionTuple bestSoFar,
+                        ImportNameVersion requestedVersion) {
+  if (!bestSoFar.empty() && bestSoFar <= info.Version)
+    return VersionedSwiftNameAction::Ignore;
+
+  if (info.IsReplacedByActive) {
+    // We know that there are no versioned names between the active version and
+    // a replacement version, because otherwise /that/ name would be active.
+    // So if replacement < requested, we want to use the old value that was
+    // replaced (but with very low priority), and otherwise we want to use the
+    // new value that is now active. (Special case: replacement = 0 means that
+    // a header annotation was replaced by an unversioned API notes annotation.)
+    if (info.Version.empty() ||
+        info.Version.getMajor() >= requestedVersion.majorVersionNumber()) {
+      return VersionedSwiftNameAction::ResetToActive;
+    }
+    if (bestSoFar.empty())
+      return VersionedSwiftNameAction::UseAsFallback;
+    return VersionedSwiftNameAction::Ignore;
+  }
+
+  if (info.Version.getMajor() < requestedVersion.majorVersionNumber())
+    return VersionedSwiftNameAction::Ignore;
+  return VersionedSwiftNameAction::Use;
 }
 
-const clang::SwiftNameAttr *
-importer::findSwiftNameAttr(const clang::Decl *decl,
-                            ImportNameVersion version) {
+
+static const clang::SwiftNameAttr *
+findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
 #ifndef NDEBUG
   if (Optional<const clang::Decl *> def = getDefinitionForClangTypeDecl(decl)) {
     assert((*def == nullptr || *def == decl) &&
@@ -596,30 +609,64 @@
   }
 #endif
 
-  if (version == ImportNameVersion::Raw)
+  if (version == ImportNameVersion::raw())
     return nullptr;
 
   // Handle versioned API notes for Swift 3 and later. This is the common case.
-  if (version != ImportNameVersion::Swift2) {
+  if (version > ImportNameVersion::swift2()) {
+    const auto *activeAttr = decl->getAttr<clang::SwiftNameAttr>();
+    const clang::SwiftNameAttr *result = activeAttr;
+    clang::VersionTuple bestSoFar;
     for (auto *attr : decl->attrs()) {
+      VersionedSwiftNameInfo info;
+
       if (auto *versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr)) {
-        if (!matchesVersion(versionedAttr, version))
+        auto *added =
+          dyn_cast<clang::SwiftNameAttr>(versionedAttr->getAttrToAdd());
+        if (!added)
           continue;
-        if (auto *added =
-              dyn_cast<clang::SwiftNameAttr>(versionedAttr->getAttrToAdd())) {
-          return added;
-        }
+
+        info = {added, versionedAttr->getVersion(),
+                versionedAttr->getIsReplacedByActive()};
+
+      } else if (auto *removeAttr =
+                   dyn_cast<clang::SwiftVersionedRemovalAttr>(attr)) {
+        if (removeAttr->getAttrKindToRemove() != clang::attr::SwiftName)
+          continue;
+        info = {nullptr, removeAttr->getVersion(),
+                removeAttr->getIsReplacedByActive()};
+
+      } else {
+        continue;
       }
 
-      if (auto *removeAttr = dyn_cast<clang::SwiftVersionedRemovalAttr>(attr)) {
-        if (!matchesVersion(removeAttr, version))
-          continue;
-        if (removeAttr->getAttrKindToRemove() == clang::attr::SwiftName)
-          return nullptr;
+      switch (checkVersionedSwiftName(info, bestSoFar, version)) {
+      case VersionedSwiftNameAction::Ignore:
+        continue;
+      case VersionedSwiftNameAction::Use:
+        result = info.Attr;
+        bestSoFar = info.Version;
+        break;
+      case VersionedSwiftNameAction::UseAsFallback:
+        // HACK: If there's a swift_name attribute in the headers /and/ in the
+        // unversioned API notes /and/ in the active versioned API notes, there
+        // will be two "replacement" attributes, one for each of the first two
+        // cases. Prefer the first one we see, because that turns out to be the
+        // one from the API notes, which matches the semantics when there are no
+        // versioned API notes. (This isn't very principled but there's at least
+        // a test to tell us if it changes.)
+        if (result == activeAttr)
+          result = info.Attr;
+        assert(bestSoFar.empty());
+        break;
+      case VersionedSwiftNameAction::ResetToActive:
+        result = activeAttr;
+        bestSoFar = info.Version;
+        break;
       }
     }
 
-    return decl->getAttr<clang::SwiftNameAttr>();
+    return result;
   }
 
   // The remainder of this function emulates the limited form of swift_name
@@ -843,7 +890,7 @@
     case EnumKind::Enum:
     case EnumKind::Options:
       // Enums are mapped to Swift enums, Options to Swift option sets.
-      if (version != ImportNameVersion::Raw) {
+      if (version != ImportNameVersion::raw()) {
         res = cast<clang::DeclContext>(enumDecl);
         break;
       }
@@ -960,7 +1007,7 @@
     switch (nameImporter.getEnumKind(ED)) {
     case EnumKind::Enum:
     case EnumKind::Options:
-      if (version != ImportNameVersion::Raw)
+      if (version != ImportNameVersion::raw())
         break;
       LLVM_FALLTHROUGH;
     case EnumKind::Constants:
@@ -1125,7 +1172,7 @@
 static bool suppressFactoryMethodAsInit(const clang::ObjCMethodDecl *method,
                                         ImportNameVersion version,
                                         CtorInitializerKind initKind) {
-  return (version == ImportNameVersion::Raw || method->isPropertyAccessor()) &&
+  return (version == ImportNameVersion::raw() || method->isPropertyAccessor()) &&
          (initKind == CtorInitializerKind::Factory ||
           initKind == CtorInitializerKind::ConvenienceFactory);
 }
@@ -1136,7 +1183,7 @@
   ImportedName result;
 
   /// Whether we want a Swift 3 or later name
-  bool swift3OrLaterName = version >= ImportNameVersion::Swift3;
+  bool swift3OrLaterName = version > ImportNameVersion::swift2();
 
   // Objective-C categories and extensions don't have names, despite
   // being "named" declarations.
@@ -1516,7 +1563,7 @@
 
   // Enumeration constants may have common prefixes stripped.
   bool strippedPrefix = false;
-  if (version != ImportNameVersion::Raw && isa<clang::EnumConstantDecl>(D)) {
+  if (version != ImportNameVersion::raw() && isa<clang::EnumConstantDecl>(D)) {
     auto enumDecl = cast<clang::EnumDecl>(D->getDeclContext());
     auto enumInfo = getEnumInfo(enumDecl);
 
@@ -1723,7 +1770,6 @@
   }
   ++ImportNameNumCacheMisses;
   auto res = importNameImpl(decl, version, givenName);
-  res.setVersion(version);
   if (!givenName)
     importNameCache[key] = res;
   return res;
diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h
index b8f33cc..f993b02 100644
--- a/lib/ClangImporter/ImportName.h
+++ b/lib/ClangImporter/ImportName.h
@@ -20,6 +20,7 @@
 #include "ImportEnumInfo.h"
 #include "SwiftLookupTable.h"
 #include "swift/Basic/StringExtras.h"
+#include "swift/Basic/Version.h"
 #include "swift/AST/ASTContext.h"
 #include "swift/AST/Decl.h"
 #include "swift/AST/ForeignErrorConvention.h"
@@ -40,78 +41,88 @@
 enum { NumImportedAccessorKindBits = 3 };
 
 /// The name version
-enum class ImportNameVersion : unsigned {
-  /// Names as they appear in C/ObjC
-  Raw = 0,
+class ImportNameVersion : public RelationalOperationsBase<ImportNameVersion> {
+  unsigned rawValue;
+  friend llvm::DenseMapInfo<ImportNameVersion>;
 
-  /// Names as they appeared in Swift 2 family
-  Swift2,
+  enum AsConstExpr_t { AsConstExpr };
 
-  /// Names as they appeared in Swift 3 family
-  Swift3,
+  constexpr ImportNameVersion() : rawValue(0) {}
+  constexpr ImportNameVersion(unsigned version, AsConstExpr_t)
+      : rawValue(version) {}
+  explicit ImportNameVersion(unsigned version) : rawValue(version) {
+    assert(version >= 2 && "only Swift 2 and later are supported");
+  }
+public:
+  /// Map a language version into an import name version.
+  static ImportNameVersion fromOptions(const LangOptions &langOpts) {
+    return ImportNameVersion(langOpts.EffectiveLanguageVersion[0]);
+  }
 
-  /// Names as they appeared in Swift 4 family
-  Swift4,
+  unsigned majorVersionNumber() const {
+    assert(*this != ImportNameVersion::raw());
+    return rawValue;
+  }
 
-  /// A placeholder for the latest version, to be used in loops and such.
-  LAST_VERSION = Swift4,
+  bool operator==(ImportNameVersion other) const {
+    return rawValue == other.rawValue;
+  }
+  bool operator<(ImportNameVersion other) const {
+    return rawValue < other.rawValue;
+  }
+
+  /// Calls \p action for each name version other than this one, first going
+  /// backwards until ImportNameVersion::raw(), and then going forwards to
+  /// ImportNameVersion::maxVersion().
+  ///
+  /// This is the most useful order for importing compatibility stubs.
+  void forEachOtherImportNameVersion(
+      llvm::function_ref<void(ImportNameVersion)> action) const {
+    assert(*this >= ImportNameVersion::swift2());
+
+    ImportNameVersion nameVersion = *this;
+    while (nameVersion > ImportNameVersion::swift2()) {
+      --nameVersion.rawValue;
+      action(nameVersion);
+    }
+
+    action(ImportNameVersion::raw());
+
+    nameVersion = *this;
+    while (nameVersion < ImportNameVersion::maxVersion()) {
+      ++nameVersion.rawValue;
+      action(nameVersion);
+    }
+  }
+
+  /// Names as they appear in C/ObjC.
+  static constexpr inline ImportNameVersion raw() {
+    return ImportNameVersion{};
+  }
+
+  /// Names as they appeared in Swift 2 family.
+  static constexpr inline ImportNameVersion swift2() {
+    return ImportNameVersion{2, AsConstExpr};
+  }
+
+  /// The latest supported version.
+  ///
+  /// FIXME: All other version information is in Version.h. Can this go there
+  /// instead?
+  static constexpr inline ImportNameVersion maxVersion() {
+    return ImportNameVersion{5, AsConstExpr};
+  }
 
   /// The version which should be used for importing types, which need to have
   /// one canonical definition.
   ///
   /// FIXME: Is this supposed to be the /newest/ version, or a canonical
   /// version that lasts forever as part of the ABI?
-  ForTypes = Swift4
+  static constexpr inline ImportNameVersion forTypes() {
+    return ImportNameVersion::maxVersion();
+  }
 };
 
-static inline ImportNameVersion &operator++(ImportNameVersion &value) {
-  assert(value != ImportNameVersion::LAST_VERSION);
-  value = static_cast<ImportNameVersion>(static_cast<unsigned>(value) + 1);
-  return value;
-}
-
-static inline ImportNameVersion &operator--(ImportNameVersion &value) {
-  assert(value != ImportNameVersion::Raw);
-  value = static_cast<ImportNameVersion>(static_cast<unsigned>(value) - 1);
-  return value;
-}
-
-/// Calls \p action for each name version, starting with \p current, then going
-/// backwards until ImportNameVersion::Raw, and then finally going forwards to
-/// ImportNameVersion::LAST_VERSION.
-///
-/// This is the most useful order for importing compatibility stubs.
-static inline void forEachImportNameVersionFromCurrent(
-    ImportNameVersion current,
-    llvm::function_ref<void(ImportNameVersion)> action) {
-  action(current);
-  ImportNameVersion nameVersion = current;
-  while (nameVersion != ImportNameVersion::Raw) {
-    --nameVersion;
-    action(nameVersion);
-  }
-  nameVersion = current;
-  while (nameVersion != ImportNameVersion::LAST_VERSION) {
-    ++nameVersion;
-    action(nameVersion);
-  }
-}
-
-/// Calls \p action for each name version, starting with ImportNameVersion::Raw
-/// and going forwards.
-static inline void
-forEachImportNameVersion(llvm::function_ref<void(ImportNameVersion)> action) {
-  auto limit = static_cast<unsigned>(ImportNameVersion::LAST_VERSION);
-  for (unsigned raw = 0; raw <= limit; ++raw)
-    action(static_cast<ImportNameVersion>(raw));
-}
-
-/// Map a language version into an import name version.
-ImportNameVersion nameVersionFromOptions(const LangOptions &langOpts);
-
-/// Map an import name version into a language version.
-unsigned majorVersionNumberForNameVersion(ImportNameVersion version);
-
 /// Describes a name that was imported from Clang.
 class ImportedName {
   friend class NameImporter;
@@ -141,11 +152,6 @@
     /// For an initializer, the kind of initializer to import.
     CtorInitializerKind initKind;
 
-    /// The version of Swift this name corresponds to.
-    ///
-    /// \see ImportNameVersion
-    unsigned rawVersion : 2;
-
     /// What kind of accessor this name refers to, if any.
     ImportedAccessorKind accessorKind : NumImportedAccessorKindBits;
 
@@ -167,9 +173,9 @@
 
     Info()
         : errorInfo(), selfIndex(), initKind(CtorInitializerKind::Designated),
-          rawVersion(), accessorKind(ImportedAccessorKind::None),
-          hasCustomName(false), droppedVariadic(false), importAsMember(false),
-          hasSelfIndex(false), hasErrorInfo(false) {}
+          accessorKind(ImportedAccessorKind::None), hasCustomName(false),
+          droppedVariadic(false), importAsMember(false), hasSelfIndex(false),
+          hasErrorInfo(false) {}
   } info;
 
 public:
@@ -189,16 +195,6 @@
     effectiveContext = ctx;
   }
 
-  /// The highest version of Swift that this name comes from
-  ImportNameVersion getVersion() const {
-    return static_cast<ImportNameVersion>(info.rawVersion);
-  }
-
-  void setVersion(ImportNameVersion version) {
-    info.rawVersion = static_cast<unsigned>(version);
-    assert(getVersion() == version && "not enough bits");
-  }
-
   /// For an initializer, the kind of initializer to import.
   CtorInitializerKind getInitKind() const { return info.initKind; }
 
@@ -272,11 +268,6 @@
 /// in "Notification", or it there would be nothing left.
 StringRef stripNotification(StringRef name);
 
-/// Find the swift_name attribute associated with this declaration, if any,
-/// appropriate for \p version.
-const clang::SwiftNameAttr *findSwiftNameAttr(const clang::Decl *decl,
-                                              ImportNameVersion version);
-
 /// Class to determine the Swift name of foreign entities. Currently fairly
 /// stateless and borrows from the ClangImporter::Implementation, but in the
 /// future will be more self-contained and encapsulated.
@@ -387,7 +378,7 @@
     return (ImportNameVersion)DMIU::getTombstoneKey();
   }
   static unsigned getHashValue(const ImportNameVersion &Val) {
-    return DMIU::getHashValue((unsigned)Val);
+    return DMIU::getHashValue(Val.rawValue);
   }
   static bool isEqual(const ImportNameVersion &LHS,
                       const ImportNameVersion &RHS) {
diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h
index 2d63bcd..d904d35 100644
--- a/lib/ClangImporter/ImporterImpl.h
+++ b/lib/ClangImporter/ImporterImpl.h
@@ -641,7 +641,9 @@
 
   /// Print an imported name as a string suitable for the swift_name attribute,
   /// or the 'Rename' field of AvailableAttr.
-  void printSwiftName(importer::ImportedName, bool fullyQualified,
+  void printSwiftName(importer::ImportedName name,
+                      importer::ImportNameVersion version,
+                      bool fullyQualified,
                       llvm::raw_ostream &os);
 
   /// \brief Import the given Clang identifier into Swift.
@@ -1130,7 +1132,14 @@
   void insertMembersAndAlternates(const clang::NamedDecl *nd,
                                   SmallVectorImpl<Decl *> &members);
   void loadAllMembersIntoExtension(Decl *D, uint64_t extra);
-  void addMemberAndAlternatesToExtension(
+
+  /// Imports \p decl under \p nameVersion with the name \p newName, and adds
+  /// it and its alternates to \p ext.
+  ///
+  /// \returns true if \p decl was successfully imported, whether or not it was
+  /// ultimately added to \p ext. This matches the behavior of
+  /// forEachDistinctName's callback.
+  bool addMemberAndAlternatesToExtension(
       clang::NamedDecl *decl, importer::ImportedName newName,
       importer::ImportNameVersion nameVersion, ExtensionDecl *ext);
 
@@ -1221,11 +1230,14 @@
   /// will eventually reference that declaration, the contexts will still be
   /// considered distinct.
   ///
-  /// The names are generated in the same order as
-  /// forEachImportNameVersionFromCurrent. The current name is always first.
+  /// If \p action returns false, the current name will \e not be added to the
+  /// set of seen names.
+  ///
+  /// The active name is always first, followed by the other names in the order
+  /// of ImportNameVersion::forEachOtherImportNameVersion.
   void forEachDistinctName(
       const clang::NamedDecl *decl,
-      llvm::function_ref<void(importer::ImportedName,
+      llvm::function_ref<bool(importer::ImportedName,
                               importer::ImportNameVersion)> action);
 
   /// Dump the Swift-specific name lookup tables we generate.
diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp
index 176509e..5e4d6f6 100644
--- a/lib/ClangImporter/SwiftLookupTable.cpp
+++ b/lib/ClangImporter/SwiftLookupTable.cpp
@@ -1625,8 +1625,8 @@
   }
 
   // If we have a name to import as, add this entry to the table.
-  ImportNameVersion currentVersion =
-      nameVersionFromOptions(nameImporter.getLangOpts());
+  auto currentVersion =
+      ImportNameVersion::fromOptions(nameImporter.getLangOpts());
   if (auto importedName = nameImporter.importName(named, currentVersion)) {
     SmallPtrSet<DeclName, 8> distinctNames;
     distinctNames.insert(importedName.getDeclName());
@@ -1640,9 +1640,8 @@
                               ArrayRef<Identifier>()),
                      named, importedName.getEffectiveContext());
 
-    forEachImportNameVersion([&] (ImportNameVersion alternateVersion) {
-      if (alternateVersion == currentVersion)
-        return;
+    currentVersion.forEachOtherImportNameVersion(
+        [&](ImportNameVersion alternateVersion) {
       auto alternateName = nameImporter.importName(named, alternateVersion);
       if (!alternateName)
         return;
diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp
index 665b7a2..a46e359 100644
--- a/lib/Demangling/Demangler.cpp
+++ b/lib/Demangling/Demangler.cpp
@@ -1297,15 +1297,65 @@
     case 'k': {
       auto nodeKind = c == 'K' ? Node::Kind::KeyPathGetterThunkHelper
                                : Node::Kind::KeyPathSetterThunkHelper;
-      auto type = popNode();
-      auto sigOrDecl = popNode();
-      if (sigOrDecl &&
-          sigOrDecl->getKind() == Node::Kind::DependentGenericSignature) {
-        auto decl = popNode();
-        return createWithChildren(nodeKind, decl, sigOrDecl, type);
+      std::vector<NodePointer> types;
+      auto node = popNode();
+      if (!node || node->getKind() != Node::Kind::Type)
+        return nullptr;
+      do {
+        types.push_back(node);
+        node = popNode();
+      } while (node && node->getKind() == Node::Kind::Type);
+      
+      NodePointer result;
+      if (node) {
+        if (node->getKind() == Node::Kind::DependentGenericSignature) {
+          auto decl = popNode();
+          result = createWithChildren(nodeKind, decl, /*sig*/ node);
+        } else {
+          result = createWithChild(nodeKind, /*decl*/ node);
+        }
       } else {
-        return createWithChildren(nodeKind, sigOrDecl, type);
+        return nullptr;
       }
+      for (auto i = types.rbegin(), e = types.rend(); i != e; ++i) {
+        result->addChild(*i, *this);
+      }
+      return result;
+    }
+    case 'H':
+    case 'h': {
+      auto nodeKind = c == 'H' ? Node::Kind::KeyPathEqualsThunkHelper
+                               : Node::Kind::KeyPathHashThunkHelper;
+      NodePointer genericSig = nullptr;
+      std::vector<NodePointer> types;
+      
+      auto node = popNode();
+      if (node) {
+        if (node->getKind() == Node::Kind::DependentGenericSignature) {
+          genericSig = node;
+        } else if (node->getKind() == Node::Kind::Type) {
+          types.push_back(node);
+        } else {
+          return nullptr;
+        }
+      } else {
+        return nullptr;
+      }
+      
+      while (auto node = popNode()) {
+        if (node->getKind() != Node::Kind::Type) {
+          return nullptr;
+        }
+        types.push_back(node);
+      }
+      
+      NodePointer result = createNode(nodeKind);
+      for (auto i = types.rbegin(), e = types.rend(); i != e; ++i) {
+        result->addChild(*i, *this);
+      }
+      if (genericSig)
+        result->addChild(genericSig, *this);
+      return result;
     }
     case 'v': {
       int Idx = demangleIndex();
diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp
index 1a617b3..7ed8fae 100644
--- a/lib/Demangling/NodePrinter.cpp
+++ b/lib/Demangling/NodePrinter.cpp
@@ -342,6 +342,8 @@
     case Node::Kind::Initializer:
     case Node::Kind::KeyPathGetterThunkHelper:
     case Node::Kind::KeyPathSetterThunkHelper:
+    case Node::Kind::KeyPathEqualsThunkHelper:
+    case Node::Kind::KeyPathHashThunkHelper:
     case Node::Kind::LazyProtocolWitnessTableAccessor:
     case Node::Kind::LazyProtocolWitnessTableCacheVariable:
     case Node::Kind::LocalDeclName:
@@ -1238,6 +1240,29 @@
       print(Node->getChild(2));
     }
     return nullptr;
+  case Node::Kind::KeyPathEqualsThunkHelper:
+  case Node::Kind::KeyPathHashThunkHelper: {
+    Printer << "key path index "
+         << (Node->getKind() == Node::Kind::KeyPathEqualsThunkHelper
+               ? "equality" : "hash")
+         << " operator for ";
+   
+    auto lastChild = Node->getChild(Node->getNumChildren() - 1);
+    auto lastType = Node->getNumChildren();
+    if (lastChild->getKind() == Node::Kind::DependentGenericSignature) {
+      print(lastChild);
+      lastType--;
+    }
+    
+    Printer << "(";
+    for (unsigned i = 0; i < lastType; ++i) {
+      if (i != 0)
+        Printer << ", ";
+      print(Node->getChild(i));
+    }
+    Printer << ")";
+    return nullptr;
+  }
   case Node::Kind::FieldOffset: {
     print(Node->getChild(0)); // directness
     Printer << "field offset for ";
diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp
index 0f790ca..cb5e0d2 100644
--- a/lib/Demangling/OldRemangler.cpp
+++ b/lib/Demangling/OldRemangler.cpp
@@ -1716,6 +1716,16 @@
   mangleChildNodes(node);
 }
 
+void Remangler::mangleKeyPathEqualsThunkHelper(Node *node) {
+  Out << "TH";
+  mangleChildNodes(node);
+}
+
+void Remangler::mangleKeyPathHashThunkHelper(Node *node) {
+  Out << "Th";
+  mangleChildNodes(node);
+}
+
 void Remangler::mangleProtocolListWithClass(Node *node) {
   Out << "Xc";
   mangleChildNode(node, 1);
diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp
index 13110e5..194fa1c 100644
--- a/lib/Demangling/Remangler.cpp
+++ b/lib/Demangling/Remangler.cpp
@@ -1517,6 +1517,16 @@
   Buffer << "Tk";
 }
 
+void Remangler::mangleKeyPathEqualsThunkHelper(Node *node) {
+  mangleChildNodes(node);
+  Buffer << "TH";
+}
+
+void Remangler::mangleKeyPathHashThunkHelper(Node *node) {
+  mangleChildNodes(node);
+  Buffer << "Th";
+}
+
 void Remangler::mangleReturnType(Node *node) {
   mangleArgumentTuple(node);
 }
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index 22bcded..eaff447 100644
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -687,14 +687,6 @@
   // serialized ASTs.
   Arguments.push_back("-parse-as-library");
 
-  // Merge serialized SIL from partial modules.
-  Arguments.push_back("-sil-merge-partial-modules");
-
-  // Disable SIL optimization passes; we've already optimized the code in each
-  // partial mode.
-  Arguments.push_back("-disable-diagnostic-passes");
-  Arguments.push_back("-disable-sil-perf-optzns");
-
   addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
                         Arguments);
   context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 40461ac..0a12441 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -957,9 +957,6 @@
   Opts.EnableExperimentalPropertyBehaviors |=
     Args.hasArg(OPT_enable_experimental_property_behaviors);
 
-  Opts.EnableExperimentalKeyPathComponents |=
-    Args.hasArg(OPT_enable_experimental_keypath_components);
-
   Opts.EnableClassResilience |=
     Args.hasArg(OPT_enable_class_resilience);
 
diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp
index c161dd0..d6465b0 100644
--- a/lib/IDE/SyntaxModel.cpp
+++ b/lib/IDE/SyntaxModel.cpp
@@ -165,6 +165,15 @@
         break;
       }
 
+      case tok::unknown: {
+        if (Tok.getRawText().startswith("\"")) {
+          // This is an invalid string literal
+          Kind = SyntaxNodeKind::String;
+          break;
+        }
+        continue;
+      }
+
       default:
         continue;
       }
diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp
index 4558772..3f789d9 100644
--- a/lib/IRGen/GenHeap.cpp
+++ b/lib/IRGen/GenHeap.cpp
@@ -837,7 +837,7 @@
 
 /// Emit a unary call to perform a ref-counting operation.
 ///
-/// \param fn - expected signature 'void (T)'
+/// \param fn - expected signature 'void (T)' or 'T (T)'
 static void emitUnaryRefCountCall(IRGenFunction &IGF,
                                   llvm::Constant *fn,
                                   llvm::Value *value) {
@@ -847,9 +847,13 @@
 
   // Instead of casting the input, we cast the function type.
   // This tends to produce less IR, but might be evil.
-  if (value->getType() != getTypeOfFunction(fn)->getParamType(0)) {
+  auto origFnType = getTypeOfFunction(fn);
+  if (value->getType() != origFnType->getParamType(0)) {
+    auto resultTy = origFnType->getReturnType() == IGF.IGM.VoidTy
+                        ? IGF.IGM.VoidTy
+                        : value->getType();
     llvm::FunctionType *fnType =
-      llvm::FunctionType::get(IGF.IGM.VoidTy, value->getType(), false);
+      llvm::FunctionType::get(resultTy, value->getType(), false);
     fn = llvm::ConstantExpr::getBitCast(fn, fnType->getPointerTo());
   }
   
@@ -861,7 +865,7 @@
 
 /// Emit a copy-like call to perform a ref-counting operation.
 ///
-/// \param fn - expected signature 'void (T, T)'
+/// \param fn - expected signature 'void (T, T)' or 'T (T, T)'
 static void emitCopyLikeCall(IRGenFunction &IGF,
                              llvm::Constant *fn,
                              llvm::Value *dest,
@@ -875,10 +879,14 @@
 
   // Instead of casting the inputs, we cast the function type.
   // This tends to produce less IR, but might be evil.
-  if (dest->getType() != getTypeOfFunction(fn)->getParamType(0)) {
+  auto origFnType = getTypeOfFunction(fn);
+  if (dest->getType() != origFnType->getParamType(0)) {
     llvm::Type *paramTypes[] = { dest->getType(), dest->getType() };
+    auto resultTy = origFnType->getReturnType() == IGF.IGM.VoidTy
+                        ? IGF.IGM.VoidTy
+                        : dest->getType();
     llvm::FunctionType *fnType =
-      llvm::FunctionType::get(IGF.IGM.VoidTy, paramTypes, false);
+      llvm::FunctionType::get(resultTy, paramTypes, false);
     fn = llvm::ConstantExpr::getBitCast(fn, fnType->getPointerTo());
   }
   
@@ -923,7 +931,7 @@
 
 /// Emit a call to a function with a storeWeak-like signature.
 ///
-/// \param fn - expected signature 'void (Weak*, T)'
+/// \param fn - expected signature 'void (Weak*, T)' or 'Weak* (Weak*, T)'
 static void emitStoreWeakLikeCall(IRGenFunction &IGF,
                                   llvm::Constant *fn,
                                   llvm::Value *addr,
@@ -938,10 +946,14 @@
 
   // Instead of casting the inputs, we cast the function type.
   // This tends to produce less IR, but might be evil.
-  if (value->getType() != getTypeOfFunction(fn)->getParamType(1)) {
+  auto origFnType = getTypeOfFunction(fn);
+  if (value->getType() != origFnType->getParamType(1)) {
     llvm::Type *paramTypes[] = { addr->getType(), value->getType() };
+    auto resultTy = origFnType->getReturnType() == IGF.IGM.VoidTy
+                        ? IGF.IGM.VoidTy
+                        : addr->getType();
     llvm::FunctionType *fnType =
-      llvm::FunctionType::get(IGF.IGM.VoidTy, paramTypes, false);
+      llvm::FunctionType::get(resultTy, paramTypes, false);
     fn = llvm::ConstantExpr::getBitCast(fn, fnType->getPointerTo());
   }
 
diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp
index d58f606..3ecaad2 100644
--- a/lib/IRGen/GenKeyPath.cpp
+++ b/lib/IRGen/GenKeyPath.cpp
@@ -30,6 +30,7 @@
 #include "MetadataLayout.h"
 #include "ProtocolInfo.h"
 #include "StructLayout.h"
+#include "TypeInfo.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/IR/Module.h"
 #include "swift/SIL/SILInstruction.h"
@@ -47,15 +48,46 @@
 using namespace swift;
 using namespace irgen;
 
-enum GetterOrSetter {
+enum KeyPathAccessor {
   Getter,
   Setter,
+  Equals,
+  Hash,
 };
 
+static void
+bindPolymorphicArgumentsFromComponentIndices(IRGenFunction &IGF,
+                                     const KeyPathPatternComponent &component,
+                                     GenericEnvironment *genericEnv,
+                                     ArrayRef<GenericRequirement> requirements,
+                                     llvm::Value *args,
+                                     llvm::Value *size) {
+  if (!genericEnv)
+    return;
+  
+  // The generic environment is marshaled into the end of the component
+  // argument area inside the instance. Bind the generic information out of
+  // the buffer.
+  if (!component.getComputedPropertyIndices().empty()) {
+    auto genericArgsSize = llvm::ConstantInt::get(IGF.IGM.SizeTy,
+      requirements.size() * IGF.IGM.getPointerSize().getValue());
+
+    auto genericArgsOffset = IGF.Builder.CreateSub(size, genericArgsSize);
+    args = IGF.Builder.CreateInBoundsGEP(args, genericArgsOffset);
+  }
+  bindFromGenericRequirementsBuffer(IGF, requirements,
+    Address(args, IGF.IGM.getPointerAlignment()),
+    [&](CanType t) {
+      if (!genericEnv)
+        return t;
+      return genericEnv->mapTypeIntoContext(t)->getCanonicalType();
+    });
+}
+
 static llvm::Function *
 getAccessorForComputedComponent(IRGenModule &IGM,
                                 const KeyPathPatternComponent &component,
-                                GetterOrSetter whichAccessor,
+                                KeyPathAccessor whichAccessor,
                                 GenericEnvironment *genericEnv,
                                 ArrayRef<GenericRequirement> requirements) {
   SILFunction *accessor;
@@ -66,6 +98,12 @@
   case Setter:
     accessor = component.getComputedPropertySetter();
     break;
+  case Equals:
+    accessor = component.getComputedPropertyIndexEquals();
+    break;
+  case Hash:
+    accessor = component.getComputedPropertyIndexHash();
+    break;
   }
   
   auto accessorFn = IGM.getAddrOfSILFunction(accessor, NotForDefinition);
@@ -80,37 +118,75 @@
   // Otherwise, we need a thunk to unmarshal the generic environment from the
   // argument area. It'd be nice to have a good way to represent this
   // directly in SIL, of course...
-  auto thunkType = llvm::FunctionType::get(
-      IGM.VoidTy,
-      { /*sret or newValue*/ accessorFnTy->getFunctionParamType(0),
-        /*base*/ accessorFnTy->getFunctionParamType(1),
-        /*arg*/ IGM.Int8PtrTy },
-      /*vararg*/ false);
   const char *thunkName;
-  unsigned numArgsToForward = 2;
+  unsigned numArgsToForward;
+
   switch (whichAccessor) {
   case Getter:
     thunkName = "keypath_get";
+    numArgsToForward = 2;
     break;
   case Setter:
     thunkName = "keypath_set";
+    numArgsToForward = 2;
+    break;
+  case Equals:
+    thunkName = "keypath_equals";
+    numArgsToForward = 2;
+    break;
+  case Hash:
+    thunkName = "keypath_hash";
+    numArgsToForward = 1;
     break;
   }
+
+  SmallVector<llvm::Type *, 4> thunkParams;
+  for (unsigned i = 0; i < numArgsToForward; ++i)
+    thunkParams.push_back(accessorFnTy->getFunctionParamType(i));
+  
+  switch (whichAccessor) {
+  case Getter:
+  case Setter:
+    thunkParams.push_back(IGM.Int8PtrTy);
+    break;
+  case Equals:
+  case Hash:
+    break;
+  }
+  thunkParams.push_back(IGM.SizeTy);
+
+  auto thunkType = llvm::FunctionType::get(IGM.VoidTy, thunkParams,
+                                           /*vararg*/ false);
   
   auto accessorThunk = llvm::Function::Create(thunkType,
     llvm::GlobalValue::PrivateLinkage, thunkName, IGM.getModule());
   accessorThunk->setAttributes(IGM.constructInitialAttributes());
-  // Original accessor's args should be @in or @out, meaning they won't be
-  // captured or aliased.
-  accessorThunk->addAttribute(1, llvm::Attribute::NoCapture);
-  accessorThunk->addAttribute(1, llvm::Attribute::NoAlias);
-  accessorThunk->addAttribute(2, llvm::Attribute::NoCapture);
-  accessorThunk->addAttribute(2, llvm::Attribute::NoAlias);
-  // Getter's output is sret.
-  if (whichAccessor == Getter)
-    accessorThunk->addAttribute(1, llvm::Attribute::StructRet);
   accessorThunk->setCallingConv(IGM.SwiftCC);
-  
+
+  switch (whichAccessor) {
+  case Getter:
+    // Original accessor's args should be @in or @out, meaning they won't be
+    // captured or aliased.
+    accessorThunk->addAttribute(1, llvm::Attribute::NoCapture);
+    accessorThunk->addAttribute(1, llvm::Attribute::NoAlias);
+    accessorThunk->addAttribute(2, llvm::Attribute::NoCapture);
+    accessorThunk->addAttribute(2, llvm::Attribute::NoAlias);
+    // Output is sret.
+    accessorThunk->addAttribute(1, llvm::Attribute::StructRet);
+    break;
+  case Setter:
+    // Original accessor's args should be @in or @out, meaning they won't be
+    // captured or aliased.
+    accessorThunk->addAttribute(1, llvm::Attribute::NoCapture);
+    accessorThunk->addAttribute(1, llvm::Attribute::NoAlias);
+    accessorThunk->addAttribute(2, llvm::Attribute::NoCapture);
+    accessorThunk->addAttribute(2, llvm::Attribute::NoAlias);
+    break;
+  case Equals:
+  case Hash:
+    break;
+  }
+
   {
     IRGenFunction IGF(IGM, accessorThunk);
     if (IGM.DebugInfo)
@@ -120,29 +196,37 @@
     Explosion forwardedArgs;
     forwardedArgs.add(params.claim(numArgsToForward));
     
-    // The generic environment is marshaled into the beginning of the component
-    // argument area inside the instance. Bind the generic information out of
-    // the buffer, and advance past it.
-    auto componentArgsBuf = params.claimNext();
-    bindFromGenericRequirementsBuffer(IGF, requirements,
-      Address(componentArgsBuf, IGM.getPointerAlignment()),
-      [&](CanType t) {
-        if (!genericEnv)
-          return t;
-        return genericEnv->mapTypeIntoContext(t)->getCanonicalType();
-      });
-      
-    /* TODO: If the underlying accessor wants index arguments, advance the
-     * pointer past the generic requirements here to pass down. */
-     
+    llvm::Value *componentArgsBuf;
+    switch (whichAccessor) {
+    case Getter:
+    case Setter:
+      // The component arguments are passed alongside the base being projected.
+      componentArgsBuf = params.claimNext();
+      // Pass the argument pointer down to the underlying function.
+      if (!component.getComputedPropertyIndices().empty()) {
+        forwardedArgs.add(componentArgsBuf);
+      }
+      break;
+    case Equals:
+    case Hash:
+      // We're operating directly on the component argument buffer.
+      componentArgsBuf = forwardedArgs.getAll()[0];
+      break;
+    }
+    auto componentArgsBufSize = params.claimNext();
+    bindPolymorphicArgumentsFromComponentIndices(IGF, component,
+                                                 genericEnv, requirements,
+                                                 componentArgsBuf,
+                                                 componentArgsBufSize);
+    
     // Use the bound generic metadata to form a call to the original generic
     // accessor.
-    WitnessMetadata witnessMetadata;
+    WitnessMetadata ignoreWitnessMetadata;
     auto forwardingSubs = genericEnv->getGenericSignature()->getSubstitutionMap(
       genericEnv->getForwardingSubstitutions());
     emitPolymorphicArguments(IGF, accessor->getLoweredFunctionType(),
                              forwardingSubs,
-                             &witnessMetadata,
+                             &ignoreWitnessMetadata,
                              forwardedArgs);
     auto fnPtr = FunctionPointer::forDirect(IGM, accessorFn,
                                           accessor->getLoweredFunctionType());
@@ -171,19 +255,55 @@
     
   {
     IRGenFunction IGF(IGM, layoutFn);
-    // TODO: We would need to unmarshal generic arguments to be able to
-    // compute the layout of dependent subscript indexes.
-    (void)IGF.collectParameters().claimNext();
+    // Unmarshal the generic environment from the argument buffer.
+    auto parameters = IGF.collectParameters();
+    auto args = parameters.claimNext();
     
-    // Base size is one pointer for each generic requirement; base alignment
-    // is pointer alignment.
-    llvm::Value *size = llvm::ConstantInt::get(IGM.SizeTy,
-      IGM.getPointerSize().getValue() * requirements.size());
-    llvm::Value *alignMask = llvm::ConstantInt::get(IGM.SizeTy,
-      IGM.getPointerAlignment().getValue() - 1);
+    if (genericEnv) {
+      bindFromGenericRequirementsBuffer(IGF, requirements,
+        Address(args, IGF.IGM.getPointerAlignment()),
+        [&](CanType t) {
+          if (!genericEnv)
+            return t;
+          return genericEnv->mapTypeIntoContext(t)->getCanonicalType();
+        });
+    }
+    
+    // Run through the captured index types to determine the size and alignment
+    // needed. Start with pointer alignment for the generic environment.
+    llvm::Value *size = llvm::ConstantInt::get(IGM.SizeTy, 0);
+    llvm::Value *alignMask = llvm::ConstantInt::get(IGM.SizeTy, 0);
+
+    for (auto &index : component.getComputedPropertyIndices()) {
+      auto ty = genericEnv
+        ? genericEnv->mapTypeIntoContext(IGM.getSILModule(), index.LoweredType)
+        : index.LoweredType;
+      auto &ti = IGM.getTypeInfo(ty);
+      auto indexSize = ti.getSize(IGF, ty);
+      auto indexAlign = ti.getAlignmentMask(IGF, ty);
       
-    // TODO: Combine layout of captured index values
+      auto notIndexAlign = IGF.Builder.CreateNot(indexAlign);
+      
+      size = IGF.Builder.CreateAdd(size, indexAlign);
+      size = IGF.Builder.CreateAnd(size, notIndexAlign);
+      size = IGF.Builder.CreateAdd(size, indexSize);
+      
+      alignMask = IGF.Builder.CreateOr(alignMask, indexAlign);
+    }
     
+    // If there's generic environment to capture, then it's stored as a block
+    // of pointer-aligned words after the captured values.
+    
+    auto genericsSize = llvm::ConstantInt::get(IGM.SizeTy,
+      IGM.getPointerSize().getValue() * requirements.size());
+    auto genericsAlign = llvm::ConstantInt::get(IGM.SizeTy,
+      IGM.getPointerAlignment().getValue() - 1);
+    auto notGenericsAlign = llvm::ConstantExpr::getNot(genericsAlign);
+    size = IGF.Builder.CreateAdd(size, genericsAlign);
+    size = IGF.Builder.CreateAnd(size, notGenericsAlign);
+    size = IGF.Builder.CreateAdd(size, genericsSize);
+    alignMask = IGF.Builder.CreateOr(alignMask, genericsAlign);
+
     llvm::Value *retValue = IGF.Builder.CreateInsertValue(
       llvm::UndefValue::get(retTy), size, 0);
     retValue = IGF.Builder.CreateInsertValue(
@@ -202,27 +322,184 @@
                                     ArrayRef<GenericRequirement> requirements) {
   // If the only thing we're capturing is generic environment, then we can
   // use a prefab witness table from the runtime.
-  // TODO: If there were subscript indexes, we'd need to generate something.
-  if (auto existing =
-        IGM.Module.getNamedGlobal("swift_keyPathGenericWitnessTable"))
-    return existing;
+  if (component.getComputedPropertyIndices().empty()) {
+    if (auto existing =
+          IGM.Module.getNamedGlobal("swift_keyPathGenericWitnessTable"))
+      return existing;
+    
+    auto linkInfo = LinkInfo::get(IGM, "swift_keyPathGenericWitnessTable",
+                                  SILLinkage::PublicExternal,
+                                  /*fragile*/ false,
+                                  /*sil only*/ false,
+                                  NotForDefinition,
+                                  /*weak imported*/ false);
+    
+    return createVariable(IGM, linkInfo,
+                          IGM.Int8PtrTy, IGM.getPointerAlignment());
+  }
   
-  auto linkInfo = LinkInfo::get(IGM, "swift_keyPathGenericWitnessTable",
-                                SILLinkage::PublicExternal,
-                                /*fragile*/ false,
-                                /*sil only*/ false,
-                                NotForDefinition,
-                                /*weak imported*/ false);
+  // Are the index values trivial?
+  bool isTrivial = true;
+  for (auto &component : component.getComputedPropertyIndices()) {
+    auto ty = genericEnv
+      ? genericEnv->mapTypeIntoContext(IGM.getSILModule(), component.LoweredType)
+      : component.LoweredType;
+    auto &ti = IGM.getTypeInfo(ty);
+    isTrivial &= ti.isPOD(ResilienceExpansion::Minimal);
+  }
   
-  return createVariable(IGM, linkInfo,
-                        IGM.Int8PtrTy, IGM.getPointerAlignment());
+  llvm::Constant *destroy;
+  llvm::Constant *copy;
+  if (isTrivial) {
+    // We can use prefab witnesses for handling trivial copying and destruction.
+    // A null destructor witness signals that the payload is trivial.
+    destroy = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
+    copy = IGM.getCopyKeyPathTrivialIndicesFn();
+  } else {
+    // Generate a destructor for this set of indices.
+    {
+      auto destroyType = llvm::FunctionType::get(IGM.VoidTy,
+                                                 {IGM.Int8PtrTy, IGM.SizeTy},
+                                                 /*vararg*/ false);
+      auto destroyFn = llvm::Function::Create(destroyType,
+        llvm::GlobalValue::PrivateLinkage, "keypath_destroy", IGM.getModule());
+      destroy = destroyFn;
+      
+      IRGenFunction IGF(IGM, destroyFn);
+      if (IGM.DebugInfo)
+        IGM.DebugInfo->emitArtificialFunction(IGF, destroyFn);
+    
+      auto params = IGF.collectParameters();
+      auto componentArgsBuf = params.claimNext();
+      auto componentArgsBufSize = params.claimNext();
+      bindPolymorphicArgumentsFromComponentIndices(IGF, component,
+                                                   genericEnv, requirements,
+                                                   componentArgsBuf,
+                                                   componentArgsBufSize);
+      
+      llvm::Value *offset = nullptr;
+      for (auto &component : component.getComputedPropertyIndices()) {
+        auto ty = genericEnv
+          ? genericEnv->mapTypeIntoContext(IGM.getSILModule(),
+                                           component.LoweredType)
+          : component.LoweredType;
+        auto &ti = IGM.getTypeInfo(ty);
+        if (offset) {
+          auto align = ti.getAlignmentMask(IGF, ty);
+          auto notAlign = IGF.Builder.CreateNot(align);
+          offset = IGF.Builder.CreateAdd(offset, align);
+          offset = IGF.Builder.CreateAnd(offset, notAlign);
+        } else {
+          offset = llvm::ConstantInt::get(IGM.SizeTy, 0);
+        }
+        auto elt = IGF.Builder.CreateInBoundsGEP(componentArgsBuf, offset);
+        auto eltAddr = ti.getAddressForPointer(
+          IGF.Builder.CreateBitCast(elt, ti.getStorageType()->getPointerTo()));
+        ti.destroy(IGF, eltAddr, ty);
+        auto size = ti.getSize(IGF, ty);
+        offset = IGF.Builder.CreateAdd(offset, size);
+      }
+      IGF.Builder.CreateRetVoid();
+    }
+    // Generate a copier for this set of indices.
+    {
+      auto copyType = llvm::FunctionType::get(IGM.VoidTy,
+                                              {IGM.Int8PtrTy, IGM.Int8PtrTy,
+                                               IGM.SizeTy},
+                                              /*vararg*/ false);
+      auto copyFn = llvm::Function::Create(copyType,
+        llvm::GlobalValue::PrivateLinkage, "keypath_copy", IGM.getModule());
+      copy = copyFn;
+      
+      IRGenFunction IGF(IGM, copyFn);
+      if (IGM.DebugInfo)
+        IGM.DebugInfo->emitArtificialFunction(IGF, copyFn);
+    
+      auto params = IGF.collectParameters();
+      auto sourceArgsBuf = params.claimNext();
+      auto destArgsBuf = params.claimNext();
+      auto componentArgsBufSize = params.claimNext();
+      bindPolymorphicArgumentsFromComponentIndices(IGF, component,
+                                                   genericEnv, requirements,
+                                                   sourceArgsBuf,
+                                                   componentArgsBufSize);
+      
+      // Copy over the index values.
+      llvm::Value *offset = nullptr;
+      for (auto &component : component.getComputedPropertyIndices()) {
+        auto ty = genericEnv
+          ? genericEnv->mapTypeIntoContext(IGM.getSILModule(),
+                                           component.LoweredType)
+          : component.LoweredType;
+        auto &ti = IGM.getTypeInfo(ty);
+        if (offset) {
+          auto align = ti.getAlignmentMask(IGF, ty);
+          auto notAlign = IGF.Builder.CreateNot(align);
+          offset = IGF.Builder.CreateAdd(offset, align);
+          offset = IGF.Builder.CreateAnd(offset, notAlign);
+        } else {
+          offset = llvm::ConstantInt::get(IGM.SizeTy, 0);
+        }
+        auto sourceElt = IGF.Builder.CreateInBoundsGEP(sourceArgsBuf, offset);
+        auto destElt = IGF.Builder.CreateInBoundsGEP(destArgsBuf, offset);
+        auto sourceEltAddr = ti.getAddressForPointer(
+          IGF.Builder.CreateBitCast(sourceElt,
+                                    ti.getStorageType()->getPointerTo()));
+        auto destEltAddr = ti.getAddressForPointer(
+          IGF.Builder.CreateBitCast(destElt,
+                                    ti.getStorageType()->getPointerTo()));
+        
+        ti.initializeWithCopy(IGF, destEltAddr, sourceEltAddr, ty);
+        auto size = ti.getSize(IGF, ty);
+        offset = IGF.Builder.CreateAdd(offset, size);
+      }
+      
+      // Copy over the generic environment.
+      if (genericEnv) {
+        auto envAlignMask = llvm::ConstantInt::get(IGM.SizeTy,
+          IGM.getPointerAlignment().getMaskValue());
+        auto notAlignMask = IGF.Builder.CreateNot(envAlignMask);
+        offset = IGF.Builder.CreateAdd(offset, envAlignMask);
+        offset = IGF.Builder.CreateAnd(offset, notAlignMask);
+        
+        auto sourceEnv = IGF.Builder.CreateInBoundsGEP(sourceArgsBuf, offset);
+        auto destEnv = IGF.Builder.CreateInBoundsGEP(destArgsBuf, offset);
+        
+        IGF.Builder.CreateMemCpy(destEnv, sourceEnv,
+          IGM.getPointerSize().getValue() * requirements.size(),
+          IGM.getPointerAlignment().getValue());
+      }
+      
+      IGF.Builder.CreateRetVoid();
+    }
+  }
+  
+  auto equals = getAccessorForComputedComponent(IGM, component, Equals,
+                                                genericEnv, requirements);
+  auto hash = getAccessorForComputedComponent(IGM, component, Hash,
+                                              genericEnv, requirements);
+  
+  auto witnesses = llvm::ConstantStruct::getAnon({destroy, copy, equals, hash});
+  return new llvm::GlobalVariable(IGM.Module, witnesses->getType(),
+                                  /*constant*/ true,
+                                  llvm::GlobalValue::PrivateLinkage,
+                                  witnesses,
+                                  "keypath_witnesses");
 }
 
+/// Information about each index operand for a key path pattern that is used
+/// to lay out and consume the argument packet.
+struct KeyPathIndexOperand {
+  SILType LoweredType;
+  const KeyPathPatternComponent *LastUser;
+};
+
 static llvm::Constant *
 getInitializerForComputedComponent(IRGenModule &IGM,
-                                   const KeyPathPatternComponent &component,
-                                   GenericEnvironment *genericEnv,
-                                   ArrayRef<GenericRequirement> requirements) {
+           const KeyPathPatternComponent &component,
+           ArrayRef<KeyPathIndexOperand> operands,
+           GenericEnvironment *genericEnv,
+           ArrayRef<GenericRequirement> requirements) {
   auto fnTy = llvm::FunctionType::get(IGM.VoidTy,
     { /*src*/ IGM.Int8PtrTy,
       /*dest*/ IGM.Int8PtrTy }, /*vararg*/ false);
@@ -233,16 +510,113 @@
   {
     IRGenFunction IGF(IGM, initFn);
     auto params = IGF.collectParameters();
+    // Pointer to the argument packet passed into swift_getKeyPath
     auto src = params.claimNext();
+    // Pointer to the destination component's argument buffer
     auto dest = params.claimNext();
     
-    // Transfer all of the requirements into the destination instance.
-    IGF.Builder.CreateMemCpy(dest, src,
-                         IGM.getPointerSize().getValue() * requirements.size(),
-                         IGM.getPointerAlignment().getValue());
+    SmallVector<Address, 4> srcAddresses;
+    int lastOperandNeeded = -1;
+    for (auto &index : component.getComputedPropertyIndices()) {
+      lastOperandNeeded = std::max(lastOperandNeeded, (int)index.Operand);
+    }
     
-    // TODO: Copy over subscript index values.
+    llvm::Value *offset;
     
+    if (genericEnv) {
+      // We'll copy over the generic environment after we copy in the indexes.
+      offset = llvm::ConstantInt::get(IGM.SizeTy,
+        IGM.getPointerSize().getValue() * requirements.size());
+
+      // Bind the generic environment from the argument buffer.
+      bindFromGenericRequirementsBuffer(IGF, requirements,
+        Address(src, IGF.IGM.getPointerAlignment()),
+        [&](CanType t) {
+          if (!genericEnv)
+            return t;
+          return genericEnv->mapTypeIntoContext(t)->getCanonicalType();
+        });
+
+    } else {
+      offset = llvm::ConstantInt::get(IGM.SizeTy, 0);
+    }
+    
+    // Figure out the offsets of the operands in the source buffer.
+    for (int i = 0; i <= lastOperandNeeded; ++i) {
+      auto ty = genericEnv
+        ? genericEnv->mapTypeIntoContext(IGM.getSILModule(),
+                                         operands[i].LoweredType)
+        : operands[i].LoweredType;
+      
+      auto &ti = IGM.getTypeInfo(ty);
+
+      if (i != 0 || genericEnv) {
+        auto alignMask = ti.getAlignmentMask(IGF, ty);
+        auto notAlignMask = IGF.Builder.CreateNot(alignMask);
+        offset = IGF.Builder.CreateAdd(offset, alignMask);
+        offset = IGF.Builder.CreateAnd(offset, notAlignMask);
+      }
+      
+      auto ptr = IGF.Builder.CreateInBoundsGEP(src, offset);
+      auto addr = ti.getAddressForPointer(IGF.Builder.CreateBitCast(
+        ptr, ti.getStorageType()->getPointerTo()));
+      srcAddresses.push_back(addr);
+      
+      auto size = ti.getSize(IGF, ty);
+      offset = IGF.Builder.CreateAdd(offset, size);
+    }
+    
+    offset = llvm::ConstantInt::get(IGM.SizeTy, 0);
+    
+    // Transfer the operands we want into the destination buffer.
+    for (unsigned i : indices(component.getComputedPropertyIndices())) {
+      auto &index = component.getComputedPropertyIndices()[i];
+      
+      auto ty = genericEnv
+        ? genericEnv->mapTypeIntoContext(IGM.getSILModule(),
+                                         index.LoweredType)
+        : index.LoweredType;
+      
+      auto &ti = IGM.getTypeInfo(ty);
+      
+      if (i != 0) {
+        auto alignMask = ti.getAlignmentMask(IGF, ty);
+        auto notAlignMask = IGF.Builder.CreateNot(alignMask);
+        offset = IGF.Builder.CreateAdd(offset, alignMask);
+        offset = IGF.Builder.CreateAnd(offset, notAlignMask);
+      }
+      
+      auto ptr = IGF.Builder.CreateInBoundsGEP(dest, offset);
+      auto destAddr = ti.getAddressForPointer(IGF.Builder.CreateBitCast(
+        ptr, ti.getStorageType()->getPointerTo()));
+      
+      // The last component using an operand can move the value out of the
+      // buffer.
+      if (&component == operands[index.Operand].LastUser) {
+        ti.initializeWithTake(IGF, destAddr, srcAddresses[index.Operand], ty);
+      } else {
+        ti.initializeWithCopy(IGF, destAddr, srcAddresses[index.Operand], ty);
+      }
+      auto size = ti.getSize(IGF, ty);
+      offset = IGF.Builder.CreateAdd(offset, size);
+    }
+    
+    // Transfer the generic environment.
+    if (genericEnv) {
+      auto destGenericEnv = dest;
+      if (!component.getComputedPropertyIndices().empty()) {
+        auto genericEnvAlignMask = llvm::ConstantInt::get(IGM.SizeTy,
+          IGM.getPointerAlignment().getMaskValue());
+        auto notGenericEnvAlignMask = IGF.Builder.CreateNot(genericEnvAlignMask);
+        offset = IGF.Builder.CreateAdd(offset, genericEnvAlignMask);
+        offset = IGF.Builder.CreateAnd(offset, notGenericEnvAlignMask);
+        destGenericEnv = IGF.Builder.CreateInBoundsGEP(dest, offset);
+      }
+      
+      IGF.Builder.CreateMemCpy(destGenericEnv, src,
+                           IGM.getPointerSize().getValue() * requirements.size(),
+                           IGM.getPointerAlignment().getValue());
+    }
     IGF.Builder.CreateRetVoid();
   }
   return initFn;
@@ -374,6 +748,28 @@
            && "must be pointer-aligned here");
   };
   
+  // Collect the order and types of any captured index operands, which will
+  // determine the layout of the buffer that gets passed to the initializer
+  // for each component.
+  SmallVector<KeyPathIndexOperand, 4> operands;
+  operands.resize(pattern->getNumOperands());
+  for (auto &component : pattern->getComponents()) {
+    switch (component.getKind()) {
+    case KeyPathPatternComponent::Kind::GettableProperty:
+    case KeyPathPatternComponent::Kind::SettableProperty:
+      for (auto &index : component.getComputedPropertyIndices()) {
+        operands[index.Operand].LoweredType = index.LoweredType;
+        operands[index.Operand].LastUser = &component;
+      }
+      break;
+    case KeyPathPatternComponent::Kind::StoredProperty:
+    case KeyPathPatternComponent::Kind::OptionalChain:
+    case KeyPathPatternComponent::Kind::OptionalForce:
+    case KeyPathPatternComponent::Kind::OptionalWrap:
+      break;
+    }
+  }
+  
   for (unsigned i : indices(pattern->getComponents())) {
     assertPointerAlignment();
     SILType loweredBaseTy;
@@ -585,7 +981,7 @@
           fields.add(getAddrOfSILFunction(component.getComputedPropertySetter(),
                                           NotForDefinition));
       } else {
-        // If there's generic context (TODO: or subscript indexes), embed as
+        // If there's generic context or subscript indexes, embed as
         // arguments in the component. Thunk the SIL-level accessors to give the
         // runtime implementation a polymorphically-callable interface.
         
@@ -611,7 +1007,7 @@
         
         // Add an initializer function that copies generic arguments out of the
         // pattern argument buffer into the instantiated object.
-        fields.add(getInitializerForComputedComponent(*this, component,
+        fields.add(getInitializerForComputedComponent(*this, component, operands,
                                                      genericEnv, requirements));
       }
       break;
@@ -657,4 +1053,3 @@
   KeyPathPatterns.insert({pattern, patternVar});
   return patternVar;
 }
-
diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp
index 33e2b70..9252938 100644
--- a/lib/IRGen/IRGenSIL.cpp
+++ b/lib/IRGen/IRGenSIL.cpp
@@ -4597,25 +4597,76 @@
   auto pattern = IGM.getAddrOfKeyPathPattern(I->getPattern(), I->getLoc());
   // Build up the argument vector to instantiate the pattern here.
   llvm::Value *args;
-  if (!I->getSubstitutions().empty()) {
+  if (!I->getSubstitutions().empty() || !I->getAllOperands().empty()) {
     auto sig = I->getPattern()->getGenericSignature();
-    auto subs = sig->getSubstitutionMap(I->getSubstitutions());
+    SubstitutionMap subs;
+    if (sig)
+      subs = sig->getSubstitutionMap(I->getSubstitutions());
 
     SmallVector<GenericRequirement, 4> requirements;
     enumerateGenericSignatureRequirements(sig,
             [&](GenericRequirement reqt) { requirements.push_back(reqt); });
 
-    auto argsBufTy = llvm::ArrayType::get(IGM.TypeMetadataPtrTy,
-                                          requirements.size());
-    auto argsBuf = createAlloca(argsBufTy, IGM.getPointerAlignment(),
-                                "keypath_args");
-    emitInitOfGenericRequirementsBuffer(*this, requirements, argsBuf,
-      [&](GenericRequirement reqt) -> llvm::Value * {
-        return emitGenericRequirementFromSubstitutions(*this, sig,
-                                         *IGM.getSwiftModule(),
-                                         reqt, subs);
-      });
-    args = Builder.CreateBitCast(argsBuf.getAddress(), IGM.Int8PtrTy);
+    llvm::Value *argsBufSize;
+    llvm::Value *argsBufAlign;
+    
+    if (!I->getSubstitutions().empty()) {
+      argsBufSize = llvm::ConstantInt::get(IGM.SizeTy,
+                       IGM.getPointerSize().getValue() * requirements.size());
+      argsBufAlign = llvm::ConstantInt::get(IGM.SizeTy,
+                       IGM.getPointerAlignment().getMaskValue());
+    } else {
+      argsBufSize = llvm::ConstantInt::get(IGM.SizeTy, 0);
+      argsBufAlign = llvm::ConstantInt::get(IGM.SizeTy, 0);
+    }
+    
+    SmallVector<llvm::Value *, 4> operandOffsets;
+    for (unsigned i : indices(I->getAllOperands())) {
+      auto operand = I->getAllOperands()[i].get();
+      auto &ti = getTypeInfo(operand->getType());
+      auto ty = operand->getType();
+      auto alignMask = ti.getAlignmentMask(*this, ty);
+      if (i != 0) {
+        auto notAlignMask = Builder.CreateNot(alignMask);
+        argsBufSize = Builder.CreateAdd(argsBufSize, alignMask);
+        argsBufSize = Builder.CreateAnd(argsBufSize, notAlignMask);
+      }
+      operandOffsets.push_back(argsBufSize);
+      auto size = ti.getSize(*this, ty);
+      argsBufSize = Builder.CreateAdd(argsBufSize, size);
+      argsBufAlign = Builder.CreateOr(argsBufAlign, alignMask);
+    }
+
+    auto argsBufInst = Builder.CreateAlloca(IGM.Int8Ty, argsBufSize);
+    // TODO: over-alignment?
+    argsBufInst->setAlignment(16);
+    
+    Address argsBuf(argsBufInst, Alignment(16));
+    
+    if (!I->getSubstitutions().empty()) {
+      emitInitOfGenericRequirementsBuffer(*this, requirements, argsBuf,
+        [&](GenericRequirement reqt) -> llvm::Value * {
+          return emitGenericRequirementFromSubstitutions(*this, sig,
+                                           *IGM.getSwiftModule(),
+                                           reqt, subs);
+        });
+    }
+    
+    for (unsigned i : indices(I->getAllOperands())) {
+      auto operand = I->getAllOperands()[i].get();
+      auto &ti = getTypeInfo(operand->getType());
+      auto ptr = Builder.CreateInBoundsGEP(argsBufInst, operandOffsets[i]);
+      auto addr = ti.getAddressForPointer(
+        Builder.CreateBitCast(ptr, ti.getStorageType()->getPointerTo()));
+      if (operand->getType().isAddress()) {
+        ti.initializeWithTake(*this, addr, getLoweredAddress(operand),
+                              operand->getType());
+      } else {
+        Explosion operandValue = getLoweredExplosion(operand);
+        cast<LoadableTypeInfo>(ti).initialize(*this, operandValue, addr);
+      }
+    }
+    args = argsBufInst;
   } else {
     // No arguments necessary, so the argument ought to be ignored by any
     // callbacks in the pattern.
diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp
index fae55b8..0e1526f 100644
--- a/lib/ParseSIL/ParseSIL.cpp
+++ b/lib/ParseSIL/ParseSIL.cpp
@@ -27,6 +27,7 @@
 #include "swift/SIL/SILDebugScope.h"
 #include "swift/SIL/SILModule.h"
 #include "swift/SIL/SILUndef.h"
+#include "swift/SIL/TypeLowering.h"
 #include "swift/Subsystems.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/SaveAndRestore.h"
@@ -139,7 +140,7 @@
     SILModule &SILMod;
     SILParserTUState &TUState;
     SILFunction *F = nullptr;
-    GenericEnvironment *GenericEnv = nullptr;
+    GenericEnvironment *ContextGenericEnv = nullptr;
     FunctionOwnershipEvaluator OwnershipEvaluator;
 
   private:
@@ -281,6 +282,12 @@
       TypeLoc = P.Tok.getLoc();
       return parseASTType(result);
     }
+    bool parseASTType(CanType &result,
+                      SourceLoc &TypeLoc,
+                      GenericEnvironment *env) {
+      TypeLoc = P.Tok.getLoc();
+      return parseASTType(result, env);
+    }
     bool parseSILOwnership(ValueOwnershipKind &OwnershipKind) {
       // We parse here @ <identifier>.
       if (!P.consumeIf(tok::at_sign)) {
@@ -294,8 +301,9 @@
                                       diag::expected_sil_value_ownership_kind);
     }
     bool parseSILType(SILType &Result,
-                      GenericEnvironment *&genericEnv,
-                      bool IsFuncDecl = false);
+                      GenericEnvironment *&parsedGenericEnv,
+                      bool IsFuncDecl = false,
+                      GenericEnvironment *parentGenericEnv = nullptr);
     bool parseSILType(SILType &Result) {
       GenericEnvironment *IgnoredEnv;
       return parseSILType(Result, IgnoredEnv);
@@ -305,9 +313,10 @@
       return parseSILType(Result);
     }
     bool parseSILType(SILType &Result, SourceLoc &TypeLoc,
-                      GenericEnvironment *&GenericEnv) {
+                      GenericEnvironment *&parsedGenericEnv,
+                      GenericEnvironment *parentGenericEnv = nullptr) {
       TypeLoc = P.Tok.getLoc();
-      return parseSILType(Result, GenericEnv);
+      return parseSILType(Result, parsedGenericEnv, false, parentGenericEnv);
     }
     /// @}
 
@@ -971,7 +980,7 @@
          "Unexpected stage during parsing!");
 
   if (GenericEnv == nullptr)
-    GenericEnv = this->GenericEnv;
+    GenericEnv = ContextGenericEnv;
 
   if (!DC)
     DC = &P.SF;
@@ -1041,9 +1050,10 @@
 ///     '$' '*'? attribute-list (generic-params)? type
 ///
 bool SILParser::parseSILType(SILType &Result,
-                             GenericEnvironment *&GenericEnv,
-                             bool IsFuncDecl){
-  GenericEnv = nullptr;
+                             GenericEnvironment *&ParsedGenericEnv,
+                             bool IsFuncDecl,
+                             GenericEnvironment *OuterGenericEnv) {
+  ParsedGenericEnv = nullptr;
 
   if (P.parseToken(tok::sil_dollar, diag::expected_sil_type))
     return true;
@@ -1108,12 +1118,12 @@
   // Save the top-level function generic environment if there was one.
   if (auto fnType = dyn_cast<FunctionTypeRepr>(TyR.get()))
     if (auto env = fnType->getGenericEnvironment())
-      GenericEnv = env;
+      ParsedGenericEnv = env;
   
   // Apply attributes to the type.
   TypeLoc Ty = P.applyAttributeToType(TyR.get(), attrs, specifier, specifierLoc);
 
-  if (performTypeLocChecking(Ty, /*IsSILType=*/true, nullptr))
+  if (performTypeLocChecking(Ty, /*IsSILType=*/true, OuterGenericEnv))
     return true;
 
   Result = SILType::getPrimitiveType(Ty.getType()->getCanonicalType(),
@@ -2417,6 +2427,7 @@
     GenericEnvironment *patternEnv = nullptr;
     CanType rootType;
     StringRef objcString;
+    SmallVector<SILType, 4> operandTypes;
     {
       Scope genericsScope(&P, ScopeKind::Generics);
       generics = P.maybeParseGenericParams().getPtrOrNull();
@@ -2472,6 +2483,9 @@
           VarDecl *idProperty = nullptr;
           SILFunction *getter = nullptr;
           SILFunction *setter = nullptr;
+          SILFunction *equals = nullptr;
+          SILFunction *hash = nullptr;
+          SmallVector<KeyPathPatternComponent::Index, 4> indexes;
           while (true) {
             Identifier subKind;
             SourceLoc subKindLoc;
@@ -2505,6 +2519,88 @@
               bool isSetter = subKind.str()[0] == 's';
               if (parseSILFunctionRef(InstLoc, isSetter ? setter : getter))
                 return true;
+            } else if (subKind.str() == "indices") {
+              if (P.parseToken(tok::l_square,
+                               diag::expected_tok_in_sil_instr, "["))
+                return true;
+              
+              while (true) {
+                unsigned index;
+                CanType formalTy;
+                SILType loweredTy;
+                if (P.parseToken(tok::oper_prefix,
+                                 diag::expected_tok_in_sil_instr, "%")
+                    || P.parseToken(tok::sil_dollar,
+                                    diag::expected_tok_in_sil_instr, "$"))
+                  return true;
+                
+                if (!P.Tok.is(tok::integer_literal)
+                    || P.Tok.getText().getAsInteger(0, index))
+                  return true;
+                
+                P.consumeToken(tok::integer_literal);
+                
+                SourceLoc formalTyLoc;
+                SourceLoc loweredTyLoc;
+                GenericEnvironment *ignoredParsedEnv;
+                if (P.parseToken(tok::colon,
+                                 diag::expected_tok_in_sil_instr, ":")
+                    || P.parseToken(tok::sil_dollar,
+                                    diag::expected_tok_in_sil_instr, "$")
+                    || parseASTType(formalTy, formalTyLoc, patternEnv)
+                    || P.parseToken(tok::colon,
+                                    diag::expected_tok_in_sil_instr, ":")
+                    || parseSILType(loweredTy, loweredTyLoc,
+                                    ignoredParsedEnv, patternEnv))
+                  return true;
+                
+                if (patternEnv)
+                  loweredTy = SILType::getPrimitiveType(
+                    patternEnv
+                      ->mapTypeOutOfContext(loweredTy.getSwiftRValueType())
+                      ->getCanonicalType(),
+                    loweredTy.getCategory());
+
+                // Formal type must be hashable.
+                auto proto = P.Context.getProtocol(KnownProtocolKind::Hashable);
+                Type contextFormalTy = formalTy;
+                if (patternEnv)
+                  contextFormalTy = patternEnv->mapTypeIntoContext(formalTy);
+                auto lookup = P.SF.getParentModule()->lookupConformance(
+                                                        contextFormalTy, proto);
+                if (!lookup) {
+                  P.diagnose(formalTyLoc,
+                             diag::sil_keypath_index_not_hashable,
+                             formalTy);
+                  return true;
+                }
+                auto conformance = ProtocolConformanceRef(*lookup);
+                
+                indexes.push_back({index, formalTy, loweredTy, conformance});
+                
+                operandTypes.resize(index+1);
+                if (operandTypes[index] && operandTypes[index] != loweredTy) {
+                  P.diagnose(loweredTyLoc,
+                             diag::sil_keypath_index_operand_type_conflict,
+                             index,
+                             operandTypes[index].getSwiftRValueType(),
+                             loweredTy.getSwiftRValueType());
+                  return true;
+                }
+                operandTypes[index] = loweredTy;
+                
+                if (P.consumeIf(tok::comma))
+                  continue;
+                if (P.consumeIf(tok::r_square))
+                  break;
+                return true;
+              }
+            } else if (subKind.str() == "indices_equals") {
+              if (parseSILFunctionRef(InstLoc, equals))
+                return true;
+            } else if (subKind.str() == "indices_hash") {
+              if (parseSILFunctionRef(InstLoc, hash))
+                return true;
             } else {
               P.diagnose(subKindLoc, diag::sil_keypath_unknown_component_kind,
                          subKind);
@@ -2542,15 +2638,24 @@
           else
             llvm_unreachable("no id?!");
           
-          // TODO: indexes
+          auto indexesCopy = P.Context.AllocateCopy(indexes);
+          
+          if (!indexes.empty() && (!equals || !hash)) {
+            P.diagnose(componentLoc,
+                       diag::sil_keypath_computed_property_missing_part,
+                       isSettable);
+          }
+          
           if (isSettable) {
             components.push_back(
                 KeyPathPatternComponent::forComputedSettableProperty(
-                                   id, getter, setter, {}, componentTy));
+                                   id, getter, setter,
+                                   indexesCopy, equals, hash, componentTy));
           } else {
             components.push_back(
                 KeyPathPatternComponent::forComputedGettableProperty(
-                                   id, getter, {}, componentTy));
+                                   id, getter,
+                                   indexesCopy, equals, hash, componentTy));
           }
         } else if (componentKind.str() == "optional_wrap"
                      || componentKind.str() == "optional_chain"
@@ -2595,7 +2700,7 @@
     
     SmallVector<ParsedSubstitution, 4> parsedSubs;
     SmallVector<Substitution, 4> subs;
-    if (parseSubstitutions(parsedSubs, GenericEnv))
+    if (parseSubstitutions(parsedSubs, ContextGenericEnv))
       return true;
     
     if (!parsedSubs.empty()) {
@@ -2608,6 +2713,40 @@
         return true;
     }
     
+    SubstitutionMap subMap;
+    if (patternEnv && patternEnv->getGenericSignature())
+      subMap = patternEnv->getGenericSignature()->getSubstitutionMap(subs);
+    
+    SmallVector<SILValue, 4> operands;
+    
+    if (P.consumeIf(tok::l_paren)) {
+      Lowering::GenericContextScope scope(SILMod.Types,
+        patternEnv ? patternEnv->getGenericSignature()->getCanonicalSignature()
+                   : nullptr);
+      while (true) {
+        SILValue v;
+        
+        if (operands.size() >= operandTypes.size()
+            || !operandTypes[operands.size()]) {
+          P.diagnose(P.Tok, diag::sil_keypath_no_use_of_operand_in_pattern,
+                     operands.size());
+          return true;
+        }
+        
+        auto ty = operandTypes[operands.size()].subst(SILMod, subMap);
+        
+        if (parseValueRef(v, ty, RegularLocation(P.Tok.getLoc()), B))
+          return true;
+        operands.push_back(v);
+        
+        if (P.consumeIf(tok::comma))
+          continue;
+        if (P.consumeIf(tok::r_paren))
+          break;
+        return true;
+      }
+    }
+    
     if (parseSILDebugLocation(InstLoc, B))
       return true;
 
@@ -2618,7 +2757,7 @@
                      rootType, components.back().getComponentType(),
                      components, objcString);
 
-    ResultVal = B.createKeyPath(InstLoc, pattern, subs, Ty);
+    ResultVal = B.createKeyPath(InstLoc, pattern, subs, operands, Ty);
     break;
   }
 
@@ -4793,7 +4932,7 @@
     if (P.consumeIf(tok::l_brace)) {
       isDefinition = true;
 
-      FunctionState.GenericEnv = GenericEnv;
+      FunctionState.ContextGenericEnv = GenericEnv;
       FunctionState.F->setGenericEnvironment(GenericEnv);
 
       if (GenericEnv && !SpecAttrs.empty()) {
@@ -5309,7 +5448,7 @@
   auto conf = WitnessState.parseProtocolConformance(proto,
                                                     witnessEnv,
                                                     false/*localScope*/);
-  WitnessState.GenericEnv = witnessEnv;
+  WitnessState.ContextGenericEnv = witnessEnv;
 
   NormalProtocolConformance *theConformance = conf ?
       dyn_cast<NormalProtocolConformance>(conf) : nullptr;
diff --git a/lib/SIL/LoopInfo.cpp b/lib/SIL/LoopInfo.cpp
index 416ffb6..1e2cc87 100644
--- a/lib/SIL/LoopInfo.cpp
+++ b/lib/SIL/LoopInfo.cpp
@@ -60,10 +60,11 @@
   }
 
   // We can't have a phi of two openexistential instructions of different UUID.
-  SILInstruction *OEI = dyn_cast<OpenExistentialAddrInst>(I);
-  if (OEI || (OEI = dyn_cast<OpenExistentialRefInst>(I)) ||
-      (OEI = dyn_cast<OpenExistentialMetatypeInst>(I))) {
-    for (auto *UI : OEI->getUses())
+  if (isa<OpenExistentialAddrInst>(I) || isa<OpenExistentialRefInst>(I) ||
+      isa<OpenExistentialMetatypeInst>(I) ||
+      isa<OpenExistentialValueInst>(I) || isa<OpenExistentialBoxInst>(I) ||
+      isa<OpenExistentialBoxValueInst>(I)) {
+    for (auto *UI : I->getUses())
       if (!contains(UI->getUser()))
         return false;
     return true;
diff --git a/lib/SIL/SILDeclRef.cpp b/lib/SIL/SILDeclRef.cpp
index 6fc3285..1acb152 100644
--- a/lib/SIL/SILDeclRef.cpp
+++ b/lib/SIL/SILDeclRef.cpp
@@ -15,6 +15,8 @@
 #include "swift/AST/AnyFunctionRef.h"
 #include "swift/AST/ASTContext.h"
 #include "swift/AST/ASTMangler.h"
+#include "swift/AST/Initializer.h"
+#include "swift/AST/ParameterList.h"
 #include "swift/ClangImporter/ClangImporter.h"
 #include "swift/ClangImporter/ClangModule.h"
 #include "swift/SIL/SILLinkage.h"
@@ -538,6 +540,19 @@
     dc = closure->getLocalContext();
   else {
     auto *d = getDecl();
+
+    // Default argument generators are serialized if the function was
+    // type-checked in Swift 4 mode.
+    if (kind == SILDeclRef::Kind::DefaultArgGenerator) {
+      auto *afd = cast<AbstractFunctionDecl>(d);
+      switch (afd->getDefaultArgumentResilienceExpansion()) {
+      case ResilienceExpansion::Minimal:
+        return IsSerialized;
+      case ResilienceExpansion::Maximal:
+        return IsNotSerialized;
+      }
+    }
+
     dc = getDecl()->getInnermostDeclContext();
 
     // Enum element constructors are serialized if the enum is
diff --git a/lib/SIL/SILInstruction.cpp b/lib/SIL/SILInstruction.cpp
index 72e37d2..90fc0f3 100644
--- a/lib/SIL/SILInstruction.cpp
+++ b/lib/SIL/SILInstruction.cpp
@@ -928,6 +928,7 @@
     return true;
 
   case ValueKind::UnconditionalCheckedCastAddrInst:
+  case ValueKind::UnconditionalCheckedCastValueInst:
     return true;
 
   case ValueKind::CheckedCastAddrBranchInst: {
diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp
index 6c51e50..1f4fc47 100644
--- a/lib/SIL/SILInstructions.cpp
+++ b/lib/SIL/SILInstructions.cpp
@@ -2052,6 +2052,11 @@
     case KeyPathPatternComponent::ComputedPropertyId::Property:
       return;
     }
+    
+    if (auto equals = component.getComputedPropertyIndexEquals())
+      forFunction(equals);
+    if (auto hash = component.getComputedPropertyIndexHash())
+      forFunction(hash);
   }
 }
 
@@ -2078,6 +2083,7 @@
     return existing;
   
   // Determine the number of operands.
+  int maxOperandNo = -1;
   for (auto component : components) {
     switch (component.getKind()) {
     case KeyPathPatternComponent::Kind::StoredProperty:
@@ -2088,14 +2094,15 @@
       
     case KeyPathPatternComponent::Kind::GettableProperty:
     case KeyPathPatternComponent::Kind::SettableProperty:
-      assert(component.getComputedPropertyIndices().empty()
-             && "todo");
+      for (auto &index : component.getComputedPropertyIndices()) {
+        maxOperandNo = std::max(maxOperandNo, (int)index.Operand);
+      }
     }
   }
   
   auto newPattern = KeyPathPattern::create(M, signature, rootType, valueType,
                                            components, objcString,
-                                           0 /*todo: num operands*/);
+                                           maxOperandNo + 1);
   M.KeyPathPatterns.InsertNode(newPattern, insertPos);
   return newPattern;
 }
@@ -2183,8 +2190,12 @@
         break;
       }
       }
-      assert(component.getComputedPropertyIndices().empty()
-             && "todo");
+      for (auto &index : component.getComputedPropertyIndices()) {
+        ID.AddInteger(index.Operand);
+        ID.AddPointer(index.FormalType.getPointer());
+        ID.AddPointer(index.LoweredType.getOpaqueValue());
+        ID.AddPointer(index.Hashable.getOpaqueValue());
+      }
       break;
     }
   }
@@ -2194,23 +2205,35 @@
 KeyPathInst::create(SILDebugLocation Loc,
                     KeyPathPattern *Pattern,
                     SubstitutionList Subs,
+                    ArrayRef<SILValue> Args,
                     SILType Ty,
                     SILFunction &F) {
-  auto totalSize = totalSizeToAlloc<Substitution>(Subs.size());
+  assert(Args.size() == Pattern->getNumOperands()
+         && "number of key path args doesn't match pattern");
+
+  auto totalSize = totalSizeToAlloc<Substitution, Operand>
+    (Subs.size(), Args.size());
   void *mem = F.getModule().allocateInst(totalSize, alignof(Substitution));
-  return ::new (mem) KeyPathInst(Loc, Pattern, Subs, Ty);
+  return ::new (mem) KeyPathInst(Loc, Pattern, Subs, Args, Ty);
 }
 
 KeyPathInst::KeyPathInst(SILDebugLocation Loc,
                          KeyPathPattern *Pattern,
                          SubstitutionList Subs,
+                         ArrayRef<SILValue> Args,
                          SILType Ty)
   : SILInstruction(ValueKind::KeyPathInst, Loc, Ty),
-    Pattern(Pattern), NumSubstitutions(Subs.size())
+    Pattern(Pattern), NumSubstitutions(Subs.size()),
+    NumOperands(Pattern->getNumOperands())
 {
   auto *subsBuf = getTrailingObjects<Substitution>();
   std::uninitialized_copy(Subs.begin(), Subs.end(), subsBuf);
   
+  auto *operandsBuf = getTrailingObjects<Operand>();
+  for (unsigned i = 0; i < Args.size(); ++i) {
+    ::new ((void*)&operandsBuf[i]) Operand(this, Args[i]);
+  }
+  
   // Increment the use of any functions referenced from the keypath pattern.
   for (auto component : Pattern->getComponents()) {
     component.incrementRefCounts();
@@ -2224,8 +2247,7 @@
 
 MutableArrayRef<Operand>
 KeyPathInst::getAllOperands() {
-  // TODO: subscript indexes
-  return {};
+  return {getTrailingObjects<Operand>(), NumOperands};
 }
 
 KeyPathInst::~KeyPathInst() {
@@ -2236,7 +2258,9 @@
   for (auto component : Pattern->getComponents()) {
     component.decrementRefCounts();
   }
-  // TODO: destroy operands
+  // Destroy operands.
+  for (auto &operand : getAllOperands())
+    operand.~Operand();
 }
 
 KeyPathPattern *KeyPathInst::getPattern() const {
diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp
index 914d589..7a01698 100644
--- a/lib/SIL/SILOwnershipVerifier.cpp
+++ b/lib/SIL/SILOwnershipVerifier.cpp
@@ -449,8 +449,6 @@
 NO_OPERAND_INST(StrongRetainUnowned)
 NO_OPERAND_INST(UnownedRetain)
 NO_OPERAND_INST(Unreachable)
-// TODO: Some key path components will have operands
-NO_OPERAND_INST(KeyPath)
 #undef NO_OPERAND_INST
 
 /// Instructions whose arguments are always compatible with one convention.
@@ -1144,6 +1142,18 @@
   return {true, UseLifetimeConstraint::MustBeLive};
 }
 
+OwnershipUseCheckerResult
+OwnershipCompatibilityUseChecker::visitKeyPathInst(KeyPathInst *I) {
+  // KeyPath moves the value in memory out of address operands, but the
+  // ownership checker doesn't reason about that yet.
+  if (isAddressOrTrivialType()) {
+    return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
+            UseLifetimeConstraint::MustBeLive};
+  }
+  return {compatibleWithOwnership(ValueOwnershipKind::Owned),
+          UseLifetimeConstraint::MustBeInvalidated};
+}
+
 //===----------------------------------------------------------------------===//
 //                            Builtin Use Checker
 //===----------------------------------------------------------------------===//
diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp
index 3ff1f37..586da8c 100644
--- a/lib/SIL/SILPrinter.cpp
+++ b/lib/SIL/SILPrinter.cpp
@@ -1907,8 +1907,26 @@
           *this << " : "
                 << component.getComputedPropertySetter()->getLoweredType();
         }
-        assert(component.getComputedPropertyIndices().empty()
-               && "todo");
+        
+        if (!component.getComputedPropertyIndices().empty()) {
+          *this << ", indices [";
+          interleave(component.getComputedPropertyIndices(),
+            [&](const KeyPathPatternComponent::Index &i) {
+              *this << "%$" << i.Operand << " : $"
+                    << i.FormalType << " : "
+                    << i.LoweredType;
+            }, [&]{
+              *this << ", ";
+            });
+          *this << "], indices_equals ";
+          component.getComputedPropertyIndexEquals()->printName(PrintState.OS);
+          *this << " : "
+                << component.getComputedPropertyIndexEquals()->getLoweredType();
+          *this << ", indices_hash ";
+          component.getComputedPropertyIndexHash()->printName(PrintState.OS);
+          *this << " : "
+                << component.getComputedPropertyIndexHash()->getLoweredType();
+        }
         break;
       }
       case KeyPathPatternComponent::Kind::OptionalWrap:
@@ -1938,6 +1956,18 @@
       *this << ' ';
       printSubstitutions(KPI->getSubstitutions());
     }
+    if (!KPI->getAllOperands().empty()) {
+      *this << " (";
+      
+      interleave(KPI->getAllOperands(),
+        [&](const Operand &operand) {
+          *this << Ctx.getID(operand.get());
+        }, [&]{
+          *this << ", ";
+        });
+      
+      *this << ")";
+    }
   }
 };
 } // end anonymous namespace
diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp
index 7fd2e1f..9da7a7b 100644
--- a/lib/SIL/SILVerifier.cpp
+++ b/lib/SIL/SILVerifier.cpp
@@ -3748,8 +3748,7 @@
           
         case KeyPathPatternComponent::Kind::GettableProperty:
         case KeyPathPatternComponent::Kind::SettableProperty: {
-          require(component.getComputedPropertyIndices().empty(),
-                  "subscripts not implemented");
+          bool hasIndices = !component.getComputedPropertyIndices().empty();
         
           // Getter should be <Sig...> @convention(thin) (@in Base) -> @out Result
           {
@@ -3760,14 +3759,25 @@
                       SILFunctionTypeRepresentation::Thin,
                     "getter should be a thin function");
             
-              // TODO: indexes
-            require(substGetterType->getNumParameters() == 1,
+            require(substGetterType->getNumParameters() == 1 + hasIndices,
                     "getter should have one parameter");
-            auto baseParam = substGetterType->getSelfParameter();
-            require(baseParam.getConvention() == ParameterConvention::Indirect_In,
+            auto baseParam = substGetterType->getParameters()[0];
+            require(baseParam.getConvention() ==
+                      ParameterConvention::Indirect_In,
                     "getter base parameter should be @in");
             require(baseParam.getType() == loweredBaseTy.getSwiftRValueType(),
                     "getter base parameter should match base of component");
+            
+            if (hasIndices) {
+              auto indicesParam = substGetterType->getParameters()[1];
+              require(indicesParam.getConvention()
+                        == ParameterConvention::Direct_Unowned,
+                      "indices pointer should be trivial");
+              require(indicesParam.getType()->getAnyNominal()
+                        == C.getUnsafeRawPointerDecl(),
+                      "indices pointer should be an UnsafeRawPointer");
+            }
+
             require(substGetterType->getNumResults() == 1,
                     "getter should have one result");
             auto result = substGetterType->getResults()[0];
@@ -3788,22 +3798,33 @@
             
             require(substSetterType->getRepresentation() ==
                       SILFunctionTypeRepresentation::Thin,
-                    "getter should be a thin function");
+                    "setter should be a thin function");
             
-            // TODO: indexes
-            require(substSetterType->getNumParameters() == 2,
+            require(substSetterType->getNumParameters() == 2 + hasIndices,
                     "setter should have two parameters");
-            auto baseParam = substSetterType->getSelfParameter();
+
+            auto newValueParam = substSetterType->getParameters()[0];
+            require(newValueParam.getConvention() ==
+                      ParameterConvention::Indirect_In,
+                    "setter value parameter should be @in");
+
+            auto baseParam = substSetterType->getParameters()[1];
             require(baseParam.getConvention() ==
                       ParameterConvention::Indirect_In
                     || baseParam.getConvention() ==
                         ParameterConvention::Indirect_Inout,
                     "setter base parameter should be @in or @inout");
-            auto newValueParam = substSetterType->getParameters()[0];
-            require(newValueParam.getConvention() ==
-                      ParameterConvention::Indirect_In,
-                    "setter value parameter should be @in");
             
+            if (hasIndices) {
+              auto indicesParam = substSetterType->getParameters()[2];
+              require(indicesParam.getConvention()
+                        == ParameterConvention::Direct_Unowned,
+                      "indices pointer should be trivial");
+              require(indicesParam.getType()->getAnyNominal()
+                        == C.getUnsafeRawPointerDecl(),
+                      "indices pointer should be an UnsafeRawPointer");
+            }
+
             require(newValueParam.getType() ==
                       loweredComponentTy.getSwiftRValueType(),
                     "setter value should match the maximal abstraction of the "
@@ -3812,6 +3833,86 @@
             require(substSetterType->getNumResults() == 0,
                     "setter should have no results");
           }
+          
+          for (auto &index : component.getComputedPropertyIndices()) {
+            auto opIndex = index.Operand;
+            auto contextType =
+              index.LoweredType.subst(F.getModule(), patternSubs);
+            requireSameType(contextType,
+                            KPI->getAllOperands()[opIndex].get()->getType(),
+                            "operand must match type required by pattern");
+            require(isLoweringOf(index.LoweredType, index.FormalType),
+                    "pattern index formal type doesn't match lowered type");
+          }
+          
+          if (!component.getComputedPropertyIndices().empty()) {
+            // Equals should be
+            // <Sig...> @convention(thin) (RawPointer, RawPointer) -> Bool
+            {
+              auto equals = component.getComputedPropertyIndexEquals();
+              require(equals, "key path pattern with indexes must have equals "
+                              "operator");
+              
+              auto substEqualsType = equals->getLoweredFunctionType()
+                ->substGenericArgs(F.getModule(), KPI->getSubstitutions());
+              
+              require(substEqualsType->getParameters().size() == 2,
+                      "must have two arguments");
+              for (unsigned i = 0; i < 2; ++i) {
+                auto param = substEqualsType->getParameters()[i];
+                require(param.getConvention()
+                          == ParameterConvention::Direct_Unowned,
+                        "indices pointer should be trivial");
+                require(param.getType()->getAnyNominal()
+                          == C.getUnsafeRawPointerDecl(),
+                        "indices pointer should be an UnsafeRawPointer");
+              }
+              
+              require(substEqualsType->getResults().size() == 1,
+                      "must have one result");
+              
+              require(substEqualsType->getResults()[0].getConvention()
+                        == ResultConvention::Unowned,
+                      "result should be unowned");
+              require(substEqualsType->getResults()[0].getType()->getAnyNominal()
+                        == C.getBoolDecl(),
+                      "result should be Bool");
+            }
+            {
+              // Hash should be
+              // <Sig...> @convention(thin) (RawPointer) -> Int
+              auto hash = component.getComputedPropertyIndexHash();
+              require(hash, "key path pattern with indexes must have hash "
+                            "operator");
+              
+              auto substHashType = hash->getLoweredFunctionType()
+                ->substGenericArgs(F.getModule(), KPI->getSubstitutions());
+              
+              require(substHashType->getParameters().size() == 1,
+                      "must have two arguments");
+              auto param = substHashType->getParameters()[0];
+              require(param.getConvention()
+                        == ParameterConvention::Direct_Unowned,
+                      "indices pointer should be trivial");
+              require(param.getType()->getAnyNominal()
+                        == C.getUnsafeRawPointerDecl(),
+                      "indices pointer should be an UnsafeRawPointer");
+              
+              require(substHashType->getResults().size() == 1,
+                      "must have one result");
+              
+              require(substHashType->getResults()[0].getConvention()
+                        == ResultConvention::Unowned,
+                      "result should be unowned");
+              require(substHashType->getResults()[0].getType()->getAnyNominal()
+                        == C.getIntDecl(),
+                      "result should be Int");
+            }
+          } else {
+            require(!component.getComputedPropertyIndexEquals()
+                    && !component.getComputedPropertyIndexHash(),
+                    "component without indexes must not have equals/hash");
+          }
 
           break;
         }
diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp
index a698928..b9207d0 100644
--- a/lib/SIL/TypeLowering.cpp
+++ b/lib/SIL/TypeLowering.cpp
@@ -1116,7 +1116,7 @@
   class OpaqueValueTypeLowering : public LeafLoadableTypeLowering {
   public:
     OpaqueValueTypeLowering(SILType type)
-      : LeafLoadableTypeLowering(type, IsAddressOnly, IsReferenceCounted) {}
+      : LeafLoadableTypeLowering(type, IsAddressOnly, IsNotReferenceCounted) {}
 
     void emitCopyInto(SILBuilder &B, SILLocation loc,
                       SILValue src, SILValue dest, IsTake_t isTake,
diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp
index fd15d36..5dfc2e1 100644
--- a/lib/SILGen/SILGen.cpp
+++ b/lib/SILGen/SILGen.cpp
@@ -649,11 +649,10 @@
 
 void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) {
   // Emit any default argument generators.
-  {
-    auto paramLists = AFD->getParameterLists();
-    if (AFD->getDeclContext()->isTypeContext())
-      paramLists = paramLists.slice(1);
-    emitDefaultArgGenerators(AFD, paramLists);
+  if (!isa<DestructorDecl>(AFD)) {
+    unsigned paramListIndex = AFD->getDeclContext()->isTypeContext() ? 1 : 0;
+    auto *paramList = AFD->getParameterLists()[paramListIndex];
+    emitDefaultArgGenerators(AFD, paramList);
   }
 
   // If this is a function at global scope, it may close over a global variable.
@@ -1015,15 +1014,13 @@
 }
 
 void SILGenModule::emitDefaultArgGenerators(SILDeclRef::Loc decl,
-                                        ArrayRef<ParameterList*> paramLists) {
+                                            ParameterList *paramList) {
   unsigned index = 0;
-  for (auto paramList : paramLists) {
-    for (auto param : *paramList) {
-      if (auto defaultArg = param->getDefaultValue())
-        emitDefaultArgGenerator(SILDeclRef::getDefaultArgGenerator(decl, index),
-                                defaultArg, param->getDefaultArgumentKind());
-      ++index;
-    }
+  for (auto param : *paramList) {
+    if (auto defaultArg = param->getDefaultValue())
+      emitDefaultArgGenerator(SILDeclRef::getDefaultArgGenerator(decl, index),
+                              defaultArg, param->getDefaultArgumentKind());
+    ++index;
   }
 }
 
diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h
index ed0f26f..5f41b31 100644
--- a/lib/SILGen/SILGen.h
+++ b/lib/SILGen/SILGen.h
@@ -271,9 +271,9 @@
   /// Emits the stored property initializer for the given pattern.
   void emitStoredPropertyInitialization(PatternBindingDecl *pd, unsigned i);
 
-  /// Emits the default argument generator for the given function.
+  /// Emits default argument generators for the given parameter list.
   void emitDefaultArgGenerators(SILDeclRef::Loc decl,
-                                ArrayRef<ParameterList*> paramLists);
+                                ParameterList *paramList);
 
   /// Emits the curry thunk between two uncurry levels of a function.
   void emitCurryThunk(SILDeclRef thunk);
diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp
index c370c88..38d9033 100644
--- a/lib/SILGen/SILGenConvert.cpp
+++ b/lib/SILGen/SILGenConvert.cpp
@@ -577,9 +577,9 @@
 
       FormalEvaluationScope writebackScope(*this);
       ManagedValue nsError =
-          emitRValueForPropertyLoad(
+          emitRValueForStorageLoad(
               loc, nativeError, concreteFormalType,
-              /*super*/ false, nsErrorVar, nsErrorVarSubstitutions,
+              /*super*/ false, nsErrorVar, RValue(), nsErrorVarSubstitutions,
               AccessSemantics::Ordinary, nsErrorType, SGFContext())
               .getAsSingleValue(*this, loc);
 
diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp
index 3be283f..b482405 100644
--- a/lib/SILGen/SILGenExpr.cpp
+++ b/lib/SILGen/SILGenExpr.cpp
@@ -31,6 +31,7 @@
 #include "swift/AST/ForeignErrorConvention.h"
 #include "swift/AST/GenericEnvironment.h"
 #include "swift/AST/ASTMangler.h"
+#include "swift/AST/ProtocolConformance.h"
 #include "swift/AST/SubstitutionMap.h"
 #include "swift/AST/Types.h"
 #include "swift/Basic/SourceManager.h"
@@ -1050,9 +1051,13 @@
 }
 
 static AbstractionPattern
-getOrigFormalRValueType(SILGenFunction &SGF, VarDecl *field) {
-  auto origType = SGF.SGM.Types.getAbstractionPattern(field);
-  return origType.getReferenceStorageReferentType();
+getFormalStorageAbstractionPattern(SILGenFunction &SGF, AbstractStorageDecl *field) {
+  if (auto var = dyn_cast<VarDecl>(field)) {
+    auto origType = SGF.SGM.Types.getAbstractionPattern(var);
+    return origType.getReferenceStorageReferentType();
+  }
+  auto sub = cast<SubscriptDecl>(field);
+  return SGF.SGM.Types.getAbstractionPattern(sub);
 }
 
 static SILDeclRef getRValueAccessorDeclRef(SILGenFunction &SGF,
@@ -1153,31 +1158,33 @@
 
 /// Produce a singular RValue for a load from the specified property.  This is
 /// designed to work with RValue ManagedValue bases that are either +0 or +1.
-RValue SILGenFunction::emitRValueForPropertyLoad(
+RValue SILGenFunction::emitRValueForStorageLoad(
     SILLocation loc, ManagedValue base, CanType baseFormalType,
-    bool isSuper, VarDecl *field, SubstitutionList substitutions,
+    bool isSuper, AbstractStorageDecl *storage, RValue indexes,
+    SubstitutionList substitutions,
     AccessSemantics semantics, Type propTy, SGFContext C,
     bool isBaseGuaranteed) {
   AccessStrategy strategy =
-    field->getAccessStrategy(semantics, AccessKind::Read);
+    storage->getAccessStrategy(semantics, AccessKind::Read);
 
   // If we should call an accessor of some kind, do so.
   if (strategy != AccessStrategy::Storage) {
-    auto accessor = getRValueAccessorDeclRef(*this, field, strategy);
+    auto accessor = getRValueAccessorDeclRef(*this, storage, strategy);
     ArgumentSource baseRV = prepareAccessorBaseArg(loc, base,
                                                    baseFormalType,
                                                    accessor);
 
     AbstractionPattern origFormalType =
-      getOrigFormalRValueType(*this, field);
+      getFormalStorageAbstractionPattern(*this, storage);
     auto substFormalType = propTy->getCanonicalType();
 
-    return emitRValueWithAccessor(*this, loc, field, substitutions,
-                                  std::move(baseRV), RValue(),
+    return emitRValueWithAccessor(*this, loc, storage, substitutions,
+                                  std::move(baseRV), std::move(indexes),
                                   isSuper, strategy, accessor,
                                   origFormalType, substFormalType, C);
   }
-
+  assert(isa<VarDecl>(storage) && "only properties should have storage");
+  auto field = cast<VarDecl>(storage);
   assert(field->hasStorage() &&
          "Cannot directly access value without storage");
 
@@ -1211,7 +1218,7 @@
   auto &lowering = getTypeLowering(substFormalType);
 
   // Check for an abstraction difference.
-  AbstractionPattern origFormalType = getOrigFormalRValueType(*this, field);
+  AbstractionPattern origFormalType = getFormalStorageAbstractionPattern(*this, field);
   bool hasAbstractionChange = false;
   auto &abstractedTL = getTypeLowering(origFormalType, substFormalType);
   if (!origFormalType.isExactType(substFormalType)) {
@@ -2392,12 +2399,12 @@
     assert(baseFormalType->isMaterializable());
     
     RValue result =
-      SGF.emitRValueForPropertyLoad(Expr, base, baseFormalType,
-                                    Expr->isSuper(),
-                                    Field,
-                                    Expr->getMember().getSubstitutions(),
-                                    Expr->getAccessSemantics(),
-                                    Expr->getType(), Context);
+      SGF.emitRValueForStorageLoad(Expr, base, baseFormalType,
+                                   Expr->isSuper(),
+                                   Field, {},
+                                   Expr->getMember().getSubstitutions(),
+                                   Expr->getAccessSemantics(),
+                                   Expr->getType(), Context);
     return result;
   }
 
@@ -2434,13 +2441,13 @@
     // And then emit our property using whether or not base is at +0 to
     // discriminate whether or not the base was guaranteed.
     RValue result =
-        SGF.emitRValueForPropertyLoad(Expr, base, baseFormalType,
-                                      Expr->isSuper(),
-                                      Field,
-                                      Expr->getMember().getSubstitutions(),
-                                      Expr->getAccessSemantics(),
-                                      Expr->getType(), Context,
-                                      base.isPlusZeroRValueOrTrivial());
+        SGF.emitRValueForStorageLoad(Expr, base, baseFormalType,
+                                     Expr->isSuper(),
+                                     Field, {},
+                                     Expr->getMember().getSubstitutions(),
+                                     Expr->getAccessSemantics(),
+                                     Expr->getType(), Context,
+                                     base.isPlusZeroRValueOrTrivial());
     return std::move(result);
   }
 };
@@ -2887,17 +2894,19 @@
 
 static ManagedValue
 emitKeyPathRValueBase(SILGenFunction &subSGF,
-                     VarDecl *property,
-                     SILLocation loc,
-                     SILValue paramArg,
-                     CanType &baseType) {
+                      AbstractStorageDecl *storage,
+                      SILLocation loc,
+                      SILValue paramArg,
+                      CanType &baseType,
+                      SubstitutionList &subs,
+                      SmallVectorImpl<Substitution> &subsBuf) {
   auto paramOrigValue = subSGF.emitManagedRValueWithCleanup(paramArg);
   auto paramSubstValue = subSGF.emitOrigToSubstValue(loc, paramOrigValue,
                                              AbstractionPattern::getOpaque(),
                                              baseType);
   
   // Upcast a class instance to the property's declared type if necessary.
-  if (auto propertyClass = dyn_cast<ClassDecl>(property->getDeclContext())) {
+  if (auto propertyClass = dyn_cast<ClassDecl>(storage->getDeclContext())) {
     if (baseType->getClassOrBoundGenericClass() != propertyClass) {
       baseType = baseType->getSuperclassForDecl(propertyClass)
         ->getCanonicalType();
@@ -2907,8 +2916,10 @@
   }
   // …or pop open an existential container.
   else if (baseType->isAnyExistentialType()) {
-    ArchetypeType *opened;
-    baseType = baseType->openAnyExistentialType(opened)->getCanonicalType();
+    auto opened = subs[0].getReplacement()->castTo<ArchetypeType>();
+    assert(opened->isOpenedExistential());
+
+    baseType = opened->getCanonicalType();
     auto openedOpaqueValue = subSGF.emitOpenExistential(loc, paramSubstValue,
                                    opened, subSGF.SGM.getLoweredType(baseType),
                                    AccessKind::Read);
@@ -2923,13 +2934,57 @@
   return paramSubstValue;
 }
 
+/// Helper function to load the captured indexes out of a key path component
+/// in order to invoke the accessors on that key path. A component with captured
+/// indexes passes down a pointer to those captures to the accessor thunks,
+/// which we can copy out of to produce values we can pass to the real
+/// accessor functions.
+static RValue loadIndexValuesForKeyPathComponent(SILGenFunction &SGF,
+                         SILLocation loc,
+                         ArrayRef<KeyPathPatternComponent::Index> indexes,
+                         SILValue pointer) {
+  // If no indexes, do nothing.
+  if (indexes.empty())
+    return RValue();
+  
+  SmallVector<TupleTypeElt, 2> indexElts;
+  for (auto &elt : indexes) {
+    indexElts.push_back(SGF.F.mapTypeIntoContext(elt.FormalType));
+  }
+  
+  auto indexTupleTy = TupleType::get(indexElts, SGF.getASTContext())
+                        ->getCanonicalType();
+  RValue indexValue(indexTupleTy);
+  
+  auto indexLoweredTy = SGF.getLoweredType(indexTupleTy);
+  auto addr = SGF.B.createPointerToAddress(loc, pointer,
+                                           indexLoweredTy.getAddressType(),
+                                           /*isStrict*/ false);
+  
+  for (unsigned i : indices(indexes)) {
+    SILValue eltAddr = addr;
+    if (indexes.size() > 1) {
+      eltAddr = SGF.B.createTupleElementAddr(loc, eltAddr, i);
+    }
+    auto ty = SGF.F.mapTypeIntoContext(indexes[i].LoweredType);
+    auto value = SGF.emitLoad(loc, eltAddr,
+                              SGF.getTypeLowering(ty),
+                              SGFContext(), IsNotTake);
+    indexValue.addElement(SGF, value, indexes[i].FormalType, loc);
+  }
+  
+  return indexValue;
+}
+
 static SILFunction *getOrCreateKeyPathGetter(SILGenFunction &SGF,
-                                             SILLocation loc,
-                                             VarDecl *property,
-                                             AccessStrategy strategy,
-                                             GenericEnvironment *genericEnv,
-                                             CanType baseType,
-                                             CanType propertyType) {
+                         SILLocation loc,
+                         AbstractStorageDecl *property,
+                         SubstitutionList subs,
+                         AccessStrategy strategy,
+                         GenericEnvironment *genericEnv,
+                         ArrayRef<KeyPathPatternComponent::Index> indexes,
+                         CanType baseType,
+                         CanType propertyType) {
   auto genericSig = genericEnv
     ? genericEnv->getGenericSignature()->getCanonicalSignature()
     : nullptr;
@@ -2944,8 +2999,15 @@
                                        propertyType);
   }
   
-  SILParameterInfo param(loweredBaseTy.getSwiftRValueType(),
-                         ParameterConvention::Indirect_In);
+  SmallVector<SILParameterInfo, 2> params;
+  params.push_back({loweredBaseTy.getSwiftRValueType(),
+                    ParameterConvention::Indirect_In});
+  auto &C = SGF.getASTContext();
+  if (!indexes.empty())
+    params.push_back({C.getUnsafeRawPointerDecl()->getDeclaredType()
+                                                 ->getCanonicalType(),
+                      ParameterConvention::Direct_Unowned});
+  
   SILResultInfo result(loweredPropTy.getSwiftRValueType(),
                        ResultConvention::Indirect);
   
@@ -2953,11 +3015,19 @@
     SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin,
                              /*pseudogeneric*/ false),
     ParameterConvention::Direct_Unowned,
-    param, result, None, SGF.getASTContext());
+    params, result, None, SGF.getASTContext());
   
   // Find the function and see if we already created it.
+  SmallVector<CanType, 2> interfaceSubs;
+  for (auto &sub : subs) {
+    interfaceSubs.push_back((genericEnv
+      ? genericEnv->mapTypeOutOfContext(sub.getReplacement())
+      : sub.getReplacement())
+      ->getCanonicalType());
+  }
   auto name = Mangle::ASTMangler()
-    .mangleKeyPathGetterThunkHelper(property, genericSig, baseType);
+    .mangleKeyPathGetterThunkHelper(property, genericSig, baseType,
+                                    interfaceSubs);
   auto thunk = SGF.SGM.M.getOrCreateSharedFunction(loc, name,
                                                    signature,
                                                    IsBare,
@@ -2980,28 +3050,35 @@
   SILGenFunction subSGF(SGM, *thunk);
   auto entry = thunk->begin();
   auto resultArgTy = result.getSILStorageType();
-  auto paramArgTy = param.getSILStorageType();
+  auto baseArgTy = params[0].getSILStorageType();
   if (genericEnv) {
     resultArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, resultArgTy);
-    paramArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, paramArgTy);
+    baseArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, baseArgTy);
   }
   auto resultArg = entry->createFunctionArgument(resultArgTy);
-  auto paramArg = entry->createFunctionArgument(paramArgTy);
+  auto baseArg = entry->createFunctionArgument(baseArgTy);
+  SILValue indexPtrArg;
+  if (!indexes.empty()) {
+    auto indexArgTy = params[1].getSILStorageType();
+    indexPtrArg = entry->createFunctionArgument(indexArgTy);
+  }
   
   Scope scope(subSGF, loc);
   
+  SmallVector<Substitution, 2> subsBuf;
+  
   auto paramSubstValue = emitKeyPathRValueBase(subSGF, property,
-                                               loc, paramArg,
-                                               baseType);
-
-  auto subs = baseType->getContextSubstitutionMap(subSGF.SGM.M.getSwiftModule(),
-               property->getInnermostDeclContext()->getInnermostTypeContext());
-  SmallVector<Substitution, 4> subsList;
-  if (subs.getGenericSignature())
-    subs.getGenericSignature()->getSubstitutions(subs, subsList);
-  auto resultSubst = subSGF.emitRValueForPropertyLoad(loc, paramSubstValue,
-                                   baseType, /*super*/false, property,
-                                   subsList, AccessSemantics::Ordinary,
+                                               loc, baseArg,
+                                               baseType, subs, subsBuf);
+  
+  RValue indexValue = loadIndexValuesForKeyPathComponent(subSGF, loc,
+                                                         indexes,
+                                                         indexPtrArg);
+  
+  auto resultSubst = subSGF.emitRValueForStorageLoad(loc, paramSubstValue,
+                                   baseType, /*super*/false,
+                                   property, std::move(indexValue),
+                                   subs, AccessSemantics::Ordinary,
                                    propertyType, SGFContext())
     .getAsSingleValue(subSGF, loc);
   if (resultSubst.getType().getAddressType() != resultArg->getType())
@@ -3018,12 +3095,14 @@
 }
 
 SILFunction *getOrCreateKeyPathSetter(SILGenFunction &SGF,
-                                      SILLocation loc,
-                                      VarDecl *property,
-                                      AccessStrategy strategy,
-                                      GenericEnvironment *genericEnv,
-                                      CanType baseType,
-                                      CanType propertyType) {
+                          SILLocation loc,
+                          AbstractStorageDecl *property,
+                          SubstitutionList subs,
+                          AccessStrategy strategy,
+                          GenericEnvironment *genericEnv,
+                          ArrayRef<KeyPathPatternComponent::Index> indexes,
+                          CanType baseType,
+                          CanType propertyType) {
   auto genericSig = genericEnv
     ? genericEnv->getGenericSignature()->getCanonicalSignature()
     : nullptr;
@@ -3038,27 +3117,43 @@
                                        propertyType);
   }
   
-  SILParameterInfo propParam(loweredPropTy.getSwiftRValueType(),
-                             ParameterConvention::Indirect_In);
+  auto &C = SGF.getASTContext();
   
-  SILParameterInfo baseParam(loweredBaseTy.getSwiftRValueType(),
-                             property->isSetterMutating()
-                             ? ParameterConvention::Indirect_Inout
-                             : ParameterConvention::Indirect_In);
+  SmallVector<SILParameterInfo, 3> params;
+  // property value
+  params.push_back({loweredPropTy.getSwiftRValueType(),
+                    ParameterConvention::Indirect_In});
+  // base
+  params.push_back({loweredBaseTy.getSwiftRValueType(),
+                    property->isSetterMutating()
+                      ? ParameterConvention::Indirect_Inout
+                      : ParameterConvention::Indirect_In});
+  // indexes
+  if (!indexes.empty())
+    params.push_back({C.getUnsafeRawPointerDecl()->getDeclaredType()
+                                                 ->getCanonicalType(),
+                      ParameterConvention::Direct_Unowned});
   
   auto signature = SILFunctionType::get(genericSig,
     SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin,
                              /*pseudogeneric*/ false),
     ParameterConvention::Direct_Unowned,
-    {propParam, baseParam}, {}, None, SGF.getASTContext());
+    params, {}, None, SGF.getASTContext());
   
   // Mangle the name of the thunk to see if we already created it.
   SmallString<64> nameBuf;
   
-  // Find the function and see if we already created it.
+  SmallVector<CanType, 2> interfaceSubs;
+  for (auto &sub : subs) {
+    interfaceSubs.push_back((genericEnv
+      ? genericEnv->mapTypeOutOfContext(sub.getReplacement())
+      : sub.getReplacement())
+      ->getCanonicalType());
+  }
   auto name = Mangle::ASTMangler().mangleKeyPathSetterThunkHelper(property,
-                                                                  genericSig,
-                                                                  baseType);
+                                                                genericSig,
+                                                                baseType,
+                                                                interfaceSubs);
   auto thunk = SGF.SGM.M.getOrCreateSharedFunction(loc, name,
                                                    signature,
                                                    IsBare,
@@ -3080,16 +3175,26 @@
   
   SILGenFunction subSGF(SGM, *thunk);
   auto entry = thunk->begin();
-  auto valueArgTy = propParam.getSILStorageType();
-  auto baseArgTy = baseParam.getSILStorageType();
+  auto valueArgTy = params[0].getSILStorageType();
+  auto baseArgTy = params[1].getSILStorageType();
   if (genericEnv) {
     valueArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, valueArgTy);
     baseArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, baseArgTy);
   }
   auto valueArg = entry->createFunctionArgument(valueArgTy);
   auto baseArg = entry->createFunctionArgument(baseArgTy);
+  SILValue indexPtrArg;
   
+  if (!indexes.empty()) {
+    auto indexArgTy = params[2].getSILStorageType();
+    indexPtrArg = entry->createFunctionArgument(indexArgTy);
+  }
+
   Scope scope(subSGF, loc);
+
+  RValue indexValue = loadIndexValuesForKeyPathComponent(subSGF, loc,
+                                                         indexes,
+                                                         indexPtrArg);
   
   auto valueOrig = subSGF.emitManagedRValueWithCleanup(valueArg);
   auto valueSubst = subSGF.emitOrigToSubstValue(loc, valueOrig,
@@ -3097,10 +3202,12 @@
                                                 propertyType);
   
   LValue lv;
+  SmallVector<Substitution, 2> subsBuf;
+
   if (!property->isSetterMutating()) {
     auto baseSubst = emitKeyPathRValueBase(subSGF, property,
                                            loc, baseArg,
-                                           baseType);
+                                           baseType, subs, subsBuf);
 
     lv = LValue::forValue(baseSubst, baseType);
   } else {
@@ -3111,8 +3218,9 @@
     
     // Open an existential lvalue, if necessary.
     if (baseType->isAnyExistentialType()) {
-      ArchetypeType *opened;
-      baseType = baseType->openAnyExistentialType(opened)->getCanonicalType();
+      auto opened = subs[0].getReplacement()->castTo<ArchetypeType>();
+      assert(opened->isOpenedExistential());
+      baseType = opened->getCanonicalType();
       lv = subSGF.emitOpenExistentialLValue(loc, std::move(lv),
                                             CanArchetypeType(opened),
                                             baseType,
@@ -3120,16 +3228,18 @@
     }
   }
 
-  auto subs = baseType->getContextSubstitutionMap(subSGF.SGM.M.getSwiftModule(),
-               property->getInnermostDeclContext()->getInnermostTypeContext());
-  SmallVector<Substitution, 4> subsList;
-  if (subs.getGenericSignature())
-    subs.getGenericSignature()->getSubstitutions(subs, subsList);
-
   LValueOptions lvOptions;
-  lv.addMemberVarComponent(subSGF, loc, property, subsList, lvOptions,
-                           /*super*/ false, AccessKind::Write,
-                           AccessSemantics::Ordinary, strategy, propertyType);
+  if (auto var = dyn_cast<VarDecl>(property)) {
+    lv.addMemberVarComponent(subSGF, loc, var, subs, lvOptions,
+                             /*super*/ false, AccessKind::Write,
+                             AccessSemantics::Ordinary, strategy, propertyType);
+  } else {
+    auto sub = cast<SubscriptDecl>(property);
+    lv.addMemberSubscriptComponent(subSGF, loc, sub, subs, lvOptions,
+                                   /*super*/ false, AccessKind::Write,
+                                   AccessSemantics::Ordinary, strategy, propertyType,
+                                   std::move(indexValue));
+  }
 
   subSGF.emitAssignToLValue(loc,
     RValue(subSGF, loc, propertyType, valueSubst),
@@ -3141,14 +3251,341 @@
   return thunk;
 }
 
+static void
+getOrCreateKeyPathEqualsAndHash(SILGenFunction &SGF,
+                              SILLocation loc,
+                              GenericEnvironment *genericEnv,
+                              ArrayRef<KeyPathPatternComponent::Index> indexes,
+                              SILFunction *&equals,
+                              SILFunction *&hash) {
+  if (indexes.empty()) {
+    equals = nullptr;
+    hash = nullptr;
+    return;
+  }
+  
+  auto genericSig = genericEnv
+    ? genericEnv->getGenericSignature()->getCanonicalSignature()
+    : nullptr;
+
+  auto &C = SGF.getASTContext();
+  auto unsafeRawPointerTy = C.getUnsafeRawPointerDecl()->getDeclaredType()
+                                                       ->getCanonicalType();
+  auto boolTy = C.getBoolDecl()->getDeclaredType()->getCanonicalType();
+  auto intTy = C.getIntDecl()->getDeclaredType()->getCanonicalType();
+
+  auto hashableProto = C.getProtocol(KnownProtocolKind::Hashable);
+
+  SmallVector<CanType, 4> indexTypes;
+  indexTypes.reserve(indexes.size());
+  for (auto &index : indexes)
+    indexTypes.push_back(index.FormalType);
+
+  SmallVector<TupleTypeElt, 2> indexElts;
+  for (auto &elt : indexes) {
+    indexElts.push_back(SGF.F.mapTypeIntoContext(elt.FormalType));
+  }
+
+  auto indexTupleTy = TupleType::get(indexElts, SGF.getASTContext())
+                        ->getCanonicalType();
+  RValue indexValue(indexTupleTy);
+
+  auto indexLoweredTy = SGF.getLoweredType(indexTupleTy);
+  auto &SGM = SGF.SGM;
+  // Get or create the equals witness
+  [&unsafeRawPointerTy, &boolTy, &genericSig, &C, &indexTypes, &equals, &loc,
+   &SGM, &genericEnv, &indexLoweredTy, &hashableProto, &indexes]{
+    // (RawPointer, RawPointer) -> Bool
+    SmallVector<SILParameterInfo, 2> params;
+    params.push_back({unsafeRawPointerTy,
+                      ParameterConvention::Direct_Unowned});
+    params.push_back({unsafeRawPointerTy,
+                      ParameterConvention::Direct_Unowned});
+    
+    SmallVector<SILResultInfo, 1> results;
+    results.push_back({boolTy, ResultConvention::Unowned});
+    
+    auto signature = SILFunctionType::get(genericSig,
+      SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin,
+                               /*pseudogeneric*/ false),
+      ParameterConvention::Direct_Unowned,
+      params, results, None, C);
+    
+    // Mangle the name of the thunk to see if we already created it.
+    SmallString<64> nameBuf;
+    
+    auto name = Mangle::ASTMangler().mangleKeyPathEqualsHelper(indexTypes,
+                                                               genericSig);
+    equals = SGM.M.getOrCreateSharedFunction(loc, name,
+                                             signature,
+                                             IsBare,
+                                             IsNotTransparent,
+                                             IsNotSerialized,
+                                             IsThunk);
+    if (!equals->empty()) {
+      return;
+    }
+    
+    SILGenFunction subSGF(SGM, *equals);
+    equals->setGenericEnvironment(genericEnv);
+    auto entry = equals->begin();
+    auto lhsPtr = entry->createFunctionArgument(params[0].getSILStorageType());
+    auto rhsPtr = entry->createFunctionArgument(params[1].getSILStorageType());
+
+    Scope scope(subSGF, loc);
+
+    auto lhsAddr = subSGF.B.createPointerToAddress(loc, lhsPtr,
+                                             indexLoweredTy.getAddressType(),
+                                             /*isStrict*/ false);
+    auto rhsAddr = subSGF.B.createPointerToAddress(loc, rhsPtr,
+                                             indexLoweredTy.getAddressType(),
+                                             /*isStrict*/ false);
+
+    // Compare each pair of index values using the == witness from the
+    // conformance.
+    auto equatableProtocol = C.getProtocol(KnownProtocolKind::Equatable);
+    auto equalsMethod = equatableProtocol->lookupDirect(C.Id_EqualsOperator)[0];
+    auto equalsRef = SILDeclRef(equalsMethod);
+    auto equalsTy = subSGF.SGM.Types.getConstantType(equalsRef);
+    
+    auto hashableSig = C.getExistentialSignature(
+      hashableProto->getDeclaredType()->getCanonicalType(),
+      SGM.M.getSwiftModule());
+    
+    auto isFalseBB = subSGF.createBasicBlock();
+    auto i1Ty = SILType::getBuiltinIntegerType(1, C);
+    for (unsigned i : indices(indexes)) {
+      auto &index = indexes[i];
+      
+      auto formalTy = index.FormalType;
+      auto hashable = index.Hashable;
+      if (genericEnv) {
+        formalTy = genericEnv->mapTypeIntoContext(formalTy)->getCanonicalType();
+        hashable = hashable.subst(index.FormalType,
+          [&](Type t) -> Type { return genericEnv->mapTypeIntoContext(t); },
+          LookUpConformanceInSignature(*genericSig));
+      }
+      
+      // Get the Equatable conformance from the Hashable conformance
+      auto subMap = hashableSig->getSubstitutionMap(
+        Substitution(formalTy, hashable));
+      auto equatable = *subMap
+        .lookupConformance(CanType(hashableSig->getGenericParams()[0]),
+                           equatableProtocol);
+      auto equatableSub = Substitution(formalTy,
+                   C.AllocateCopy(ArrayRef<ProtocolConformanceRef>(equatable)));
+    
+      auto equalsWitness = subSGF.B.createWitnessMethod(loc,
+        formalTy, equatable,
+        equalsRef, equalsTy);
+      
+      auto equalsSubstTy = equalsTy.castTo<SILFunctionType>()
+        ->substGenericArgs(SGM.M, equatableSub);
+      auto equalsInfo = CalleeTypeInfo(equalsSubstTy,
+                                       AbstractionPattern(boolTy), boolTy,
+                                       None,
+                                       ImportAsMemberStatus());
+      
+      Scope branchScope(subSGF, loc);
+      
+      SILValue lhsEltAddr = lhsAddr;
+      SILValue rhsEltAddr = rhsAddr;
+      if (indexes.size() > 1) {
+        lhsEltAddr = subSGF.B.createTupleElementAddr(loc, lhsEltAddr, i);
+        rhsEltAddr = subSGF.B.createTupleElementAddr(loc, rhsEltAddr, i);
+      }
+      auto lhsArg = subSGF.emitLoad(loc, lhsEltAddr,
+             subSGF.getTypeLowering(AbstractionPattern::getOpaque(), formalTy),
+             SGFContext(), IsNotTake);
+      auto rhsArg = subSGF.emitLoad(loc, rhsEltAddr,
+             subSGF.getTypeLowering(AbstractionPattern::getOpaque(), formalTy),
+             SGFContext(), IsNotTake);
+      
+      if (!lhsArg.getType().isAddress()) {
+        auto lhsBuf = subSGF.emitTemporaryAllocation(loc, lhsArg.getType());
+        lhsArg.forwardInto(subSGF, loc, lhsBuf);
+        lhsArg = subSGF.emitManagedBufferWithCleanup(lhsBuf);
+
+        auto rhsBuf = subSGF.emitTemporaryAllocation(loc, rhsArg.getType());
+        rhsArg.forwardInto(subSGF, loc, rhsBuf);
+        rhsArg = subSGF.emitManagedBufferWithCleanup(rhsBuf);
+      }
+
+      auto metaty = CanMetatypeType::get(formalTy,
+                                         MetatypeRepresentation::Thick);
+      auto metatyValue = ManagedValue::forUnmanaged(subSGF.B.createMetatype(loc,
+        SILType::getPrimitiveObjectType(metaty)));
+      SILValue isEqual;
+      {
+        auto equalsResultPlan = ResultPlanBuilder::computeResultPlan(subSGF,
+          equalsInfo, loc, SGFContext());
+        ArgumentScope argScope(subSGF, loc);
+        isEqual = subSGF.emitApply(std::move(equalsResultPlan),
+                               std::move(argScope),
+                               loc,
+                               ManagedValue::forUnmanaged(equalsWitness),
+                               equatableSub,
+                               {lhsArg, rhsArg, metatyValue},
+                               equalsInfo,
+                               ApplyOptions::None,
+                               SGFContext())
+          .getUnmanagedSingleValue(subSGF, loc);
+      }
+      
+      branchScope.pop();
+      
+      auto isEqualI1 = subSGF.B.createStructExtract(loc, isEqual,
+        C.getBoolDecl()->getStoredProperties().front(), i1Ty);
+      
+      auto isTrueBB = subSGF.createBasicBlock();
+      
+      subSGF.B.createCondBranch(loc, isEqualI1, isTrueBB, isFalseBB);
+      
+      subSGF.B.emitBlock(isTrueBB);
+    }
+    
+    auto returnBB = subSGF.createBasicBlock(FunctionSection::Postmatter);
+    
+    SILValue trueValue = subSGF.B.createIntegerLiteral(loc, i1Ty, 1);
+    subSGF.B.createBranch(loc, returnBB, trueValue);
+    
+    subSGF.B.emitBlock(isFalseBB);
+    SILValue falseValue = subSGF.B.createIntegerLiteral(loc, i1Ty, 0);
+    subSGF.B.createBranch(loc, returnBB, falseValue);
+    
+    subSGF.B.emitBlock(returnBB);
+    scope.pop();
+    SILValue returnVal = returnBB->createPHIArgument(i1Ty,
+                                                   ValueOwnershipKind::Trivial);
+    auto returnBoolVal = subSGF.B.createStruct(loc,
+      SILType::getPrimitiveObjectType(boolTy), returnVal);
+    subSGF.B.createReturn(loc, returnBoolVal);
+  }();
+
+  // Get or create the hash witness
+  [&unsafeRawPointerTy, &intTy, &genericSig, &C, &indexTypes, &hash, &loc,
+   &SGM, &genericEnv, &indexLoweredTy, &hashableProto, &indexes]{
+    // (RawPointer) -> Int
+    SmallVector<SILParameterInfo, 1> params;
+    params.push_back({unsafeRawPointerTy,
+                      ParameterConvention::Direct_Unowned});
+    
+    SmallVector<SILResultInfo, 1> results;
+    results.push_back({intTy, ResultConvention::Unowned});
+    
+    auto signature = SILFunctionType::get(genericSig,
+      SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin,
+                               /*pseudogeneric*/ false),
+      ParameterConvention::Direct_Unowned,
+      params, results, None, C);
+    
+    // Mangle the name of the thunk to see if we already created it.
+    SmallString<64> nameBuf;
+    
+    auto name = Mangle::ASTMangler().mangleKeyPathHashHelper(indexTypes,
+                                                             genericSig);
+    hash = SGM.M.getOrCreateSharedFunction(loc, name,
+                                           signature,
+                                           IsBare,
+                                           IsNotTransparent,
+                                           IsNotSerialized,
+                                           IsThunk);
+    if (!hash->empty()) {
+      return;
+    }
+    
+    SILGenFunction subSGF(SGM, *hash);
+    hash->setGenericEnvironment(genericEnv);
+    auto entry = hash->begin();
+    auto indexPtr = entry->createFunctionArgument(params[0].getSILStorageType());
+
+    Scope scope(subSGF, loc);
+
+    auto hashMethod = cast<VarDecl>(
+      hashableProto->lookupDirect(C.Id_hashValue)[0])
+                   ->getGetter();
+    auto hashRef = SILDeclRef(hashMethod);
+    auto hashTy = subSGF.SGM.Types.getConstantType(hashRef);
+
+    SILValue hashCode;
+
+    // TODO: Combine hashes of the indexes. There isn't a great hash combining
+    // interface in the standard library to do this yet.
+    {
+      auto &index = indexes[0];
+      
+      SILValue indexAddr = subSGF.B.createPointerToAddress(loc, indexPtr,
+                                             indexLoweredTy.getAddressType(),
+                                             /*isStrict*/ false);
+      if (indexes.size() > 1) {
+        indexAddr = subSGF.B.createTupleElementAddr(loc, indexAddr, 0);
+      }
+      
+      auto formalTy = index.FormalType;
+      auto hashable = index.Hashable;
+      if (genericEnv) {
+        formalTy = genericEnv->mapTypeIntoContext(formalTy)->getCanonicalType();
+        hashable = hashable.subst(index.FormalType,
+          [&](Type t) -> Type { return genericEnv->mapTypeIntoContext(t); },
+          LookUpConformanceInSignature(*genericSig));
+      }
+      
+      // Get the Equatable conformance from the Hashable conformance
+      auto hashableSub = Substitution(formalTy,
+                   C.AllocateCopy(ArrayRef<ProtocolConformanceRef>(hashable)));
+
+      auto hashWitness = subSGF.B.createWitnessMethod(loc,
+        formalTy, hashable,
+        hashRef, hashTy);
+      
+      auto hashSubstTy = hashTy.castTo<SILFunctionType>()
+        ->substGenericArgs(SGM.M, hashableSub);
+      auto hashInfo = CalleeTypeInfo(hashSubstTy,
+                                     AbstractionPattern(intTy), intTy,
+                                     None,
+                                     ImportAsMemberStatus());
+
+      auto arg = subSGF.emitLoad(loc, indexAddr,
+        subSGF.getTypeLowering(AbstractionPattern::getOpaque(), formalTy),
+        SGFContext(), IsNotTake);
+      
+      if (!arg.getType().isAddress()) {
+        auto buf = subSGF.emitTemporaryAllocation(loc, arg.getType());
+        arg.forwardInto(subSGF, loc, buf);
+        arg = subSGF.emitManagedBufferWithCleanup(buf);
+      }
+      
+      {
+        auto hashResultPlan = ResultPlanBuilder::computeResultPlan(subSGF,
+          hashInfo, loc, SGFContext());
+        ArgumentScope argScope(subSGF, loc);
+        hashCode = subSGF.emitApply(std::move(hashResultPlan),
+                               std::move(argScope),
+                               loc,
+                               ManagedValue::forUnmanaged(hashWitness),
+                               hashableSub,
+                               {arg},
+                               hashInfo,
+                               ApplyOptions::None,
+                               SGFContext())
+          .getUnmanagedSingleValue(subSGF, loc);
+      }
+    }
+    scope.pop();
+    subSGF.B.createReturn(loc, hashCode);
+  }();
+  
+  return;
+}
+
 static KeyPathPatternComponent::ComputedPropertyId
 getIdForKeyPathComponentComputedProperty(SILGenFunction &SGF,
-                                         VarDecl *property,
+                                         AbstractStorageDecl *storage,
                                          AccessStrategy strategy) {
   switch (strategy) {
   case AccessStrategy::Storage:
     // Identify reabstracted stored properties by the property itself.
-    return property;
+    return cast<VarDecl>(storage);
   case AccessStrategy::Addressor:
   case AccessStrategy::DirectToAccessor: {
     // Identify the property using its (unthunked) getter. For a
@@ -3157,17 +3594,17 @@
     // TODO: If the getter has shared linkage (say it's synthesized for a
     // Clang-imported thing), we'll need some other sort of
     // stable identifier.
-    auto getterRef = SILDeclRef(property->getGetter(), SILDeclRef::Kind::Func);
+    auto getterRef = SILDeclRef(storage->getGetter(), SILDeclRef::Kind::Func);
     return SGF.SGM.getFunction(getterRef, NotForDefinition);
   }
   case AccessStrategy::DispatchToAccessor: {
     // Identify the property by its vtable or wtable slot.
     // Use the foreign selector if the decl is ObjC-imported, dynamic, or
     // otherwise requires objc_msgSend for its ABI.
-    return SILDeclRef(property->getGetter(), SILDeclRef::Kind::Func,
+    return SILDeclRef(storage->getGetter(), SILDeclRef::Kind::Func,
                       ResilienceExpansion::Minimal,
                       /*curried*/ false,
-                      /*foreign*/ property->requiresForeignGetterAndSetter());
+                      /*foreign*/ storage->requiresForeignGetterAndSetter());
   }
   case AccessStrategy::BehaviorStorage:
     llvm_unreachable("unpossible");
@@ -3184,14 +3621,7 @@
   // subscript indexes.
   SmallVector<KeyPathPatternComponent, 4> loweredComponents;
   auto loweredTy = SGF.getLoweredType(E->getType());
-  
-  auto unsupported = [&](StringRef message) -> RValue {
-    SGF.SGM.diagnose(E->getLoc(), diag::not_implemented, message);
-    
-    auto undef = SILUndef::get(loweredTy, SGF.SGM.M);
-    return RValue(SGF, E, ManagedValue::forUnmanaged(undef));
-  };
-  
+
   CanType rootTy = E->getType()->castTo<BoundGenericType>()->getGenericArgs()[0]
     ->getCanonicalType();
   
@@ -3202,6 +3632,7 @@
   }
   
   auto baseTy = rootTy;
+  SmallVector<SILValue, 4> operands;
   
   for (auto &component : E->getComponents()) {
     switch (auto kind = component.getKind()) {
@@ -3239,24 +3670,26 @@
         auto id = getIdForKeyPathComponentComputedProperty(SGF, decl,
                                                            strategy);
         auto getter = getOrCreateKeyPathGetter(SGF, SILLocation(E),
-                 decl, strategy,
+                 decl, component.getDeclRef().getSubstitutions(),
+                 strategy,
                  needsGenericContext ? SGF.F.getGenericEnvironment() : nullptr,
+                 {},
                  oldBaseTy, baseTy);
         
         if (decl->isSettable(decl->getDeclContext())) {
           auto setter = getOrCreateKeyPathSetter(SGF, SILLocation(E),
-                 decl, strategy,
+                 decl, component.getDeclRef().getSubstitutions(),
+                 strategy,
                  needsGenericContext ? SGF.F.getGenericEnvironment() : nullptr,
+                 {},
                  oldBaseTy, baseTy);
           loweredComponents.push_back(
             KeyPathPatternComponent::forComputedSettableProperty(id,
-                                                                 getter, setter,
-                                                                 {}, baseTy));
+              getter, setter, {}, nullptr, nullptr, baseTy));
         } else {
           loweredComponents.push_back(
             KeyPathPatternComponent::forComputedGettableProperty(id,
-                                                                 getter,
-                                                                 {}, baseTy));
+              getter, {}, nullptr, nullptr, baseTy));
         }
         break;
       }
@@ -3292,8 +3725,95 @@
       break;
     }
         
-    case KeyPathExpr::Component::Kind::Subscript:
-      return unsupported("subscript key path component");
+    case KeyPathExpr::Component::Kind::Subscript: {
+      auto decl = cast<SubscriptDecl>(component.getDeclRef().getDecl());
+      auto strategy = decl->getAccessStrategy(AccessSemantics::Ordinary,
+                                              AccessKind::ReadWrite);
+      auto oldBaseTy = baseTy;
+      auto baseSubscriptTy =
+        decl->getInterfaceType()->castTo<AnyFunctionType>();
+      if (auto genSubscriptTy = baseSubscriptTy->getAs<GenericFunctionType>())
+        baseSubscriptTy = genSubscriptTy
+          ->substGenericArgs(component.getDeclRef().getSubstitutions());
+      auto baseSubscriptInterfaceTy = cast<AnyFunctionType>(
+        SGF.F.mapTypeOutOfContext(baseSubscriptTy)->getCanonicalType());
+      
+      baseTy = baseSubscriptInterfaceTy.getResult();
+
+      // Capturing an index value dependent on the generic context means we
+      // need the generic context captured in the key path.
+      needsGenericContext |=
+          component.getIndexExpr()->getType()->hasArchetype()
+        | baseTy->hasTypeParameter();
+      
+      // Evaluate the index arguments.
+      SmallVector<RValue, 2> indexValues;
+      auto indexResult = visit(component.getIndexExpr(), SGFContext());
+      if (auto tup = indexResult.getType()->getAs<TupleType>()) {
+        std::move(indexResult).extractElements(indexValues);
+      } else {
+        indexValues.push_back(std::move(indexResult));
+      }
+
+      SmallVector<KeyPathPatternComponent::Index, 4> indexPatterns;
+      SILFunction *indexEquals = nullptr, *indexHash = nullptr;
+      for (unsigned i : indices(indexValues)) {
+        auto hashable = component.getSubscriptIndexHashableConformances()[i];
+        assert(hashable.isAbstract() ||
+          hashable.getConcrete()->getType()->isEqual(indexValues[i].getType()));
+        auto &value = indexValues[i];
+        
+        auto indexTy = SGF.F.mapTypeOutOfContext(value.getType())->getCanonicalType();
+        auto indexLoweredTy = SGF.getLoweredType(value.getType());
+        indexLoweredTy = SILType::getPrimitiveType(
+          SGF.F.mapTypeOutOfContext(indexLoweredTy.getSwiftRValueType())
+             ->getCanonicalType(),
+          indexLoweredTy.getCategory());
+        indexPatterns.push_back({(unsigned)operands.size(),
+                 indexTy, indexLoweredTy,
+                 hashable});
+        operands.push_back(
+          std::move(indexValues[i]).forwardAsSingleValue(SGF, E));
+      }
+      getOrCreateKeyPathEqualsAndHash(SGF, SILLocation(E),
+               needsGenericContext ? SGF.F.getGenericEnvironment() : nullptr,
+               indexPatterns,
+               indexEquals, indexHash);
+
+      auto id = getIdForKeyPathComponentComputedProperty(SGF, decl, strategy);
+      auto getter = getOrCreateKeyPathGetter(SGF, SILLocation(E),
+               decl, component.getDeclRef().getSubstitutions(),
+               strategy,
+               needsGenericContext ? SGF.F.getGenericEnvironment() : nullptr,
+               indexPatterns,
+               oldBaseTy, baseTy);
+    
+      auto indexPatternsCopy = SGF.getASTContext().AllocateCopy(indexPatterns);
+      if (decl->isSettable()) {
+        auto setter = getOrCreateKeyPathSetter(SGF, SILLocation(E),
+               decl, component.getDeclRef().getSubstitutions(),
+               strategy,
+               needsGenericContext ? SGF.F.getGenericEnvironment() : nullptr,
+               indexPatterns,
+               oldBaseTy, baseTy);
+        loweredComponents.push_back(
+          KeyPathPatternComponent::forComputedSettableProperty(id,
+                                                             getter, setter,
+                                                             indexPatternsCopy,
+                                                             indexEquals,
+                                                             indexHash,
+                                                             baseTy));
+      } else {
+        loweredComponents.push_back(
+          KeyPathPatternComponent::forComputedGettableProperty(id,
+                                                             getter,
+                                                             indexPatternsCopy,
+                                                             indexEquals,
+                                                             indexHash,
+                                                             baseTy));
+      }
+      break;
+    }
         
     case KeyPathExpr::Component::Kind::Invalid:
     case KeyPathExpr::Component::Kind::UnresolvedProperty:
@@ -3319,6 +3839,7 @@
                                      needsGenericContext
                                        ? SGF.F.getForwardingSubstitutions()
                                        : SubstitutionList(),
+                                     operands,
                                      loweredTy);
   auto value = SGF.emitManagedRValueWithCleanup(keyPath);
   return RValue(SGF, E, value);
diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h
index 681d125..1c93cef 100644
--- a/lib/SILGen/SILGenFunction.h
+++ b/lib/SILGen/SILGenFunction.h
@@ -1057,14 +1057,15 @@
   /// \arg isBaseGuaranteed This should /only/ be set to true if we know that
   /// the base value will stay alive as long as the returned RValue implying
   /// that it is safe to load/use values as +0.
-  RValue emitRValueForPropertyLoad(SILLocation loc,
-                                   ManagedValue base,
-                                   CanType baseFormalType,
-                                   bool isSuper, VarDecl *property,
-                                   SubstitutionList substitutions,
-                                   AccessSemantics semantics, Type propTy,
-                                   SGFContext C,
-                                   bool isBaseGuaranteed = false);
+  RValue emitRValueForStorageLoad(SILLocation loc,
+                                  ManagedValue base,
+                                  CanType baseFormalType,
+                                  bool isSuper, AbstractStorageDecl *storage,
+                                  RValue indexes,
+                                  SubstitutionList substitutions,
+                                  AccessSemantics semantics, Type propTy,
+                                  SGFContext C,
+                                  bool isBaseGuaranteed = false);
 
   void emitCaptures(SILLocation loc,
                     AnyFunctionRef TheClosure,
diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp
index 8715fc1..802c014 100644
--- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp
+++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp
@@ -49,9 +49,10 @@
 ///    that the release occurs in the epilog after any retains associated with
 ///    @owned return values.
 ///
-/// 3. We do not support specialization of closures with arguments passed using
-///    any indirect calling conventions besides @inout and @inout_aliasable.
-///    This is a temporary limitation.
+/// 3. In !useLoweredAddresses mode, we do not support specialization of closures
+///    with arguments passed using any indirect calling conventions besides
+///    @inout and @inout_aliasable.  This is a temporary limitation that goes
+///    away with sil-opaque-values.
 //===----------------------------------------------------------------------===//
 
 #define DEBUG_TYPE "closure-specialization"
@@ -581,8 +582,9 @@
     ParameterConvention ParamConv;
     if (PInfo.isFormalIndirect()) {
       ParamConv = PInfo.getConvention();
-      assert(ParamConv == ParameterConvention::Indirect_Inout ||
-             ParamConv == ParameterConvention::Indirect_InoutAliasable);
+      assert(!SILModuleConventions(M).useLoweredAddresses()
+             || ParamConv == ParameterConvention::Indirect_Inout
+             || ParamConv == ParameterConvention::Indirect_InoutAliasable);
     } else {
       ParamConv = ClosedOverFunConv.getSILType(PInfo).isTrivial(M)
                       ? ParameterConvention::Direct_Unowned
diff --git a/lib/SILOptimizer/IPO/EagerSpecializer.cpp b/lib/SILOptimizer/IPO/EagerSpecializer.cpp
index c549b0e..9df86d7 100644
--- a/lib/SILOptimizer/IPO/EagerSpecializer.cpp
+++ b/lib/SILOptimizer/IPO/EagerSpecializer.cpp
@@ -626,7 +626,9 @@
         ReInfo.createSpecializedType(SubstitutedType, Builder.getModule());
   }
 
-  assert(OrigArgs.size() == ReInfo.getNumArguments() && "signature mismatch");
+  assert(!substConv.useLoweredAddresses()
+         || OrigArgs.size() == ReInfo.getNumArguments() &&
+         "signature mismatch");
 
   CallArgs.reserve(OrigArgs.size());
   SILValue StoreResultTo;
diff --git a/lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp
index 9ee8f8f..70ba575 100644
--- a/lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp
+++ b/lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp
@@ -967,18 +967,17 @@
 
 static void createArgumentRelease(SILBuilder &Builder, ArgumentDescriptor &AD) {
   auto &F = Builder.getFunction();
-  if (AD.PInfo->getConvention() == ParameterConvention::Direct_Owned) {
-    Builder.createReleaseValue(RegularLocation(SourceLoc()),
-                               F.getArguments()[AD.Index],
-                               Builder.getDefaultAtomicity());
-    return;
-  }
-  if (AD.PInfo->getConvention() == ParameterConvention::Indirect_In) {
+  SILArgument *Arg = F.getArguments()[AD.Index];
+  if (Arg->getType().isAddress()) {
+    assert(AD.PInfo->getConvention() == ParameterConvention::Indirect_In
+           && F.getConventions().useLoweredAddresses());
     Builder.createDestroyAddr(RegularLocation(SourceLoc()),
                               F.getArguments()[AD.Index]);
     return;
   }
-  llvm_unreachable("Parameter convention is not supported");
+  Builder.createReleaseValue(RegularLocation(SourceLoc()),
+                             F.getArguments()[AD.Index],
+                             Builder.getDefaultAtomicity());
 }
 
 /// Set up epilogue work for the thunk arguments based in the given argument.
diff --git a/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp b/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp
index b5fd8c9..3d8267f 100644
--- a/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp
+++ b/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp
@@ -173,7 +173,8 @@
 
   // If we have an address only type, do nothing.
   SILType Type = Value->getType();
-  assert(Type.isLoadable(Module) &&
+  assert(!SILModuleConventions(Module).useLoweredAddresses()
+         || Type.isLoadable(Module) &&
          "release_value should never be called on a non-loadable type.");
 
   if (!shouldExpand(Module, Type.getObjectType()))
@@ -199,8 +200,9 @@
 
   // If we have an address only type, do nothing.
   SILType Type = Value->getType();
-  assert(Type.isLoadable(Module) && "Copy Value can only be called on loadable "
-         "types.");
+  assert(!SILModuleConventions(Module).useLoweredAddresses()
+         || Type.isLoadable(Module) &&
+         "Copy Value can only be called on loadable types.");
 
   if (!shouldExpand(Module, Type.getObjectType()))
     return false;
diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp
index d1e143d..2115aef 100644
--- a/lib/Sema/CSApply.cpp
+++ b/lib/Sema/CSApply.cpp
@@ -3919,7 +3919,12 @@
       E->setMethod(method);
       return E;
     }
-
+    
+  private:
+    // Key path components we need to
+    SmallVector<std::pair<KeyPathExpr *, unsigned>, 4>
+      KeyPathSubscriptComponents;
+  public:
     Expr *visitKeyPathExpr(KeyPathExpr *E) {
       if (E->isObjC()) {
         cs.setType(E, cs.getType(E->getObjCStringLiteralExpr()));
@@ -4069,12 +4074,17 @@
           resolvedTy = simplifyType(resolvedTy);
           
           auto ref = ConcreteDeclRef(cs.getASTContext(), subscript, subs);
+          
           component = KeyPathExpr::Component
             ::forSubscriptWithPrebuiltIndexExpr(ref,
                                             origComponent.getIndexExpr(),
                                             origComponent.getSubscriptLabels(),
                                             resolvedTy,
-                                            origComponent.getLoc());
+                                            origComponent.getLoc(),
+                                            {});
+          // Save a reference to the component so we can do a post-pass to check
+          // the Hashable conformance of the indexes.
+          KeyPathSubscriptComponents.push_back({E, resolvedComponents.size()});
           break;
         }
         case KeyPathExpr::Component::Kind::OptionalChain: {
@@ -4216,6 +4226,46 @@
           .fixItInsert(cast->getStartLoc(), "(")
           .fixItInsertAfter(cast->getEndLoc(), ")");
       }
+      
+      // Look at key path subscript components to verify that they're hashable.
+      for (auto componentRef : KeyPathSubscriptComponents) {
+        auto &component = componentRef.first
+                                  ->getMutableComponents()[componentRef.second];
+        // We need to be able to hash the captured index values in order for
+        // KeyPath itself to be hashable, so check that all of the subscript
+        // index components are hashable and collect their conformances here.
+        SmallVector<ProtocolConformanceRef, 2> hashables;
+        bool allIndexesHashable = true;
+        ArrayRef<TupleTypeElt> indexTypes;
+        TupleTypeElt singleIndexTypeBuf;
+        if (auto tup = component.getIndexExpr()->getType()
+                                               ->getAs<TupleType>()) {
+          indexTypes = tup->getElements();
+        } else {
+          singleIndexTypeBuf = component.getIndexExpr()->getType();
+          indexTypes = singleIndexTypeBuf;
+        }
+      
+        auto hashable =
+          cs.getASTContext().getProtocol(KnownProtocolKind::Hashable);
+        for (auto indexType : indexTypes) {
+          auto conformance =
+            cs.TC.conformsToProtocol(indexType.getType(), hashable,
+                                     cs.DC, ConformanceCheckFlags::Used);
+          if (!conformance) {
+            cs.TC.diagnose(component.getIndexExpr()->getLoc(),
+                           diag::expr_keypath_subscript_index_not_hashable,
+                           indexType.getType());
+            allIndexesHashable = false;
+            continue;
+          }
+          hashables.push_back(*conformance);
+        }
+
+        if (allIndexesHashable) {
+          component.setSubscriptIndexHashableConformances(hashables);
+        }
+      }
 
       // Set the final types on the expression.
       cs.setExprTypes(result);
diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp
index 3b0a42a..30370f1 100644
--- a/lib/Sema/CSBindings.cpp
+++ b/lib/Sema/CSBindings.cpp
@@ -142,37 +142,6 @@
       typeVar, constraints, ConstraintGraph::GatheringKind::EquivalenceClass);
 
   PotentialBindings result;
-  Optional<unsigned> lastSupertypeIndex;
-
-  // Local function to add a potential binding to the list of bindings,
-  // coalescing supertype bounds when we are able to compute the meet.
-  auto addPotentialBinding = [&](PotentialBinding binding,
-                                 bool allowJoinMeet = true) {
-    assert(!binding.BindingType->is<ErrorType>());
-    // If this is a non-defaulted supertype binding, check whether we can
-    // combine it with another supertype binding by computing the 'join' of the
-    // types.
-    if (binding.Kind == AllowedBindingKind::Supertypes &&
-        !binding.BindingType->hasTypeVariable() && !binding.DefaultedProtocol &&
-        !binding.isDefaultableBinding() && allowJoinMeet) {
-      if (lastSupertypeIndex) {
-        // Can we compute a join?
-        auto &lastBinding = result.Bindings[*lastSupertypeIndex];
-        auto lastType = lastBinding.BindingType->getWithoutSpecifierType();
-        auto bindingType = binding.BindingType->getWithoutSpecifierType();
-        if (auto join = Type::join(lastType, bindingType)) {
-          // Replace the last supertype binding with the join. We're done.
-          lastBinding.BindingType = join;
-          return;
-        }
-      }
-
-      // Record this as the most recent supertype index.
-      lastSupertypeIndex = result.Bindings.size();
-    }
-
-    result.Bindings.push_back(std::move(binding));
-  };
 
   // Consider each of the constraints related to this type variable.
   llvm::SmallPtrSet<CanType, 4> exactTypes;
@@ -284,8 +253,8 @@
           continue;
 
         result.foundLiteralBinding(constraint->getProtocol());
-        addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
-                             constraint->getProtocol()});
+        result.addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
+                                    constraint->getProtocol()});
         continue;
       }
 
@@ -311,8 +280,8 @@
       if (!matched) {
         result.foundLiteralBinding(constraint->getProtocol());
         exactTypes.insert(defaultType->getCanonicalType());
-        addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
-                             constraint->getProtocol()});
+        result.addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
+                                    constraint->getProtocol()});
       }
 
       continue;
@@ -484,10 +453,12 @@
     }
 
     if (exactTypes.insert(type->getCanonicalType()).second)
-      addPotentialBinding({type, kind, None}, /*allowJoinMeet=*/!adjustedIUO);
+      result.addPotentialBinding({type, kind, None},
+                                 /*allowJoinMeet=*/!adjustedIUO);
     if (alternateType &&
         exactTypes.insert(alternateType->getCanonicalType()).second)
-      addPotentialBinding({alternateType, kind, None}, /*allowJoinMeet=*/false);
+      result.addPotentialBinding({alternateType, kind, None},
+                                 /*allowJoinMeet=*/false);
   }
 
   // If we have any literal constraints, check whether there is already a
@@ -565,7 +536,7 @@
       continue;
 
     ++result.NumDefaultableBindings;
-    addPotentialBinding(
+    result.addPotentialBinding(
         {type, AllowedBindingKind::Exact, None, constraint->getLocator()});
   }
 
diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h
index e1a82c7..ceae16c 100644
--- a/lib/Sema/ConstraintSystem.h
+++ b/lib/Sema/ConstraintSystem.h
@@ -2575,6 +2575,9 @@
     /// Is this type variable on the RHS of a BindParam constraint?
     bool IsRHSOfBindParam = false;
 
+    /// Tracks the position of the last known supertype in the group.
+    Optional<unsigned> lastSupertypeIndex;
+
     /// Determine whether the set of bindings is non-empty.
     explicit operator bool() const { return !Bindings.empty(); }
 
@@ -2619,6 +2622,38 @@
       }
     }
 
+    /// \brief Add a potential binding to the list of bindings,
+    /// coalescing supertype bounds when we are able to compute the meet.
+    void addPotentialBinding(PotentialBinding binding,
+                             bool allowJoinMeet = true) {
+      assert(!binding.BindingType->is<ErrorType>());
+
+      // If this is a non-defaulted supertype binding,
+      // check whether we can combine it with another
+      // supertype binding by computing the 'join' of the types.
+      if (binding.Kind == AllowedBindingKind::Supertypes &&
+          !binding.BindingType->hasTypeVariable() &&
+          !binding.DefaultedProtocol && !binding.isDefaultableBinding() &&
+          allowJoinMeet) {
+        if (lastSupertypeIndex) {
+          // Can we compute a join?
+          auto &lastBinding = Bindings[*lastSupertypeIndex];
+          auto lastType = lastBinding.BindingType->getWithoutSpecifierType();
+          auto bindingType = binding.BindingType->getWithoutSpecifierType();
+          if (auto join = Type::join(lastType, bindingType)) {
+            // Replace the last supertype binding with the join. We're done.
+            lastBinding.BindingType = join;
+            return;
+          }
+        }
+
+        // Record this as the most recent supertype index.
+        lastSupertypeIndex = Bindings.size();
+      }
+
+      Bindings.push_back(std::move(binding));
+    }
+
     void dump(llvm::raw_ostream &out,
               unsigned indent = 0) const LLVM_ATTRIBUTE_USED {
       out.indent(indent);
diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp
index 77b4566..162a3f9 100644
--- a/lib/Sema/TypeCheckConstraints.cpp
+++ b/lib/Sema/TypeCheckConstraints.cpp
@@ -1460,12 +1460,6 @@
 
         expr = UDE->getBase();
       } else if (auto SE = dyn_cast<SubscriptExpr>(expr)) {
-        if (!TC.Context.LangOpts.EnableExperimentalKeyPathComponents) {
-          TC.diagnose(SE->getLoc(),
-                      diag::expr_swift_keypath_unimplemented_component,
-                      "subscript");
-        }
-      
         // .[0] or just plain [0]
         components.push_back(
             KeyPathExpr::Component::forUnresolvedSubscriptWithPrebuiltIndexExpr(
diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp
index ecd7f5a..4d85138 100644
--- a/lib/Sema/TypeCheckStmt.cpp
+++ b/lib/Sema/TypeCheckStmt.cpp
@@ -1279,14 +1279,6 @@
                                   ParameterList *params,
                                   unsigned &nextArgIndex,
                                   AbstractFunctionDecl *func) {
-  // In Swift 4 mode, default argument bodies are inlined into the
-  // caller.
-  auto expansion = func->getResilienceExpansion();
-  if (!tc.Context.isSwiftVersion3() &&
-      func->getFormalAccessScope(/*useDC=*/nullptr,
-                                 /*respectVersionedAttr=*/true).isPublic())
-    expansion = ResilienceExpansion::Minimal;
-
   for (auto &param : *params) {
     ++nextArgIndex;
     if (!param->getDefaultValue() || !param->hasType() ||
@@ -1296,9 +1288,6 @@
     Expr *e = param->getDefaultValue();
     auto initContext = param->getDefaultArgumentInitContext();
 
-    cast<DefaultArgumentInitializer>(initContext)
-        ->changeResilienceExpansion(expansion);
-
     // Type-check the initializer, then flag that we did so.
     auto resultTy = tc.typeCheckExpression(
         e, initContext, TypeLoc::withoutLoc(param->getType()),
@@ -1317,6 +1306,24 @@
   }
 }
 
+/// Check the default arguments that occur within this pattern.
+static void checkDefaultArguments(TypeChecker &tc,
+                                  AbstractFunctionDecl *func) {
+  // In Swift 4 mode, default argument bodies are inlined into the
+  // caller.
+  auto expansion = func->getResilienceExpansion();
+  if (!tc.Context.isSwiftVersion3() &&
+      func->getFormalAccessScope(/*useDC=*/nullptr,
+                                 /*respectVersionedAttr=*/true).isPublic())
+    expansion = ResilienceExpansion::Minimal;
+
+  func->setDefaultArgumentResilienceExpansion(expansion);
+
+  unsigned nextArgIndex = 0;
+  for (auto paramList : func->getParameterLists())
+    checkDefaultArguments(tc, paramList, nextArgIndex, func);
+}
+
 bool TypeChecker::typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD,
                                                      SourceLoc EndTypeCheckLoc) {
   validateDecl(AFD);
@@ -1358,10 +1365,7 @@
 // named function or an anonymous func expression.
 bool TypeChecker::typeCheckFunctionBodyUntil(FuncDecl *FD,
                                              SourceLoc EndTypeCheckLoc) {
-  // Check the default argument definitions.
-  unsigned nextArgIndex = 0;
-  for (auto paramList : FD->getParameterLists())
-    checkDefaultArguments(*this, paramList, nextArgIndex, FD);
+  checkDefaultArguments(*this, FD);
 
   // Clang imported inline functions do not have a Swift body to
   // typecheck.
@@ -1464,10 +1468,7 @@
 
 bool TypeChecker::typeCheckConstructorBodyUntil(ConstructorDecl *ctor,
                                                 SourceLoc EndTypeCheckLoc) {
-  // Check the default argument definitions.
-  unsigned nextArgIndex = 0;
-  for (auto paramList : ctor->getParameterLists())
-    checkDefaultArguments(*this, paramList, nextArgIndex, ctor);
+  checkDefaultArguments(*this, ctor);
 
   BraceStmt *body = ctor->getBody();
   if (!body)
diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp
index 7003372..e8c1470 100644
--- a/lib/Serialization/Deserialization.cpp
+++ b/lib/Serialization/Deserialization.cpp
@@ -1942,6 +1942,17 @@
   return None;
 }
 
+static
+Optional<swift::ResilienceExpansion> getActualResilienceExpansion(uint8_t raw) {
+  switch (serialization::ResilienceExpansion(raw)) {
+  case serialization::ResilienceExpansion::Minimal:
+    return swift::ResilienceExpansion::Minimal;
+  case serialization::ResilienceExpansion::Maximal:
+    return swift::ResilienceExpansion::Maximal;
+  }
+  return None;
+}
+
 void ModuleFile::configureStorage(AbstractStorageDecl *decl,
                                   unsigned rawStorageKind,
                                   serialization::DeclID getter,
@@ -2570,6 +2581,7 @@
     TypeID interfaceID;
     DeclID overriddenID;
     bool needsNewVTableEntry, firstTimeRequired;
+    uint8_t rawDefaultArgumentResilienceExpansion;
     unsigned numArgNames;
     ArrayRef<uint64_t> argNameAndDependencyIDs;
 
@@ -2581,6 +2593,7 @@
                                                overriddenID,
                                                rawAccessLevel,
                                                needsNewVTableEntry,
+                                               rawDefaultArgumentResilienceExpansion,
                                                firstTimeRequired,
                                                numArgNames,
                                                argNameAndDependencyIDs);
@@ -2687,6 +2700,16 @@
     if (auto overriddenCtor = cast_or_null<ConstructorDecl>(overridden.get()))
       ctor->setOverriddenDecl(overriddenCtor);
     ctor->setNeedsNewVTableEntry(needsNewVTableEntry);
+
+    if (auto defaultArgumentResilienceExpansion = getActualResilienceExpansion(
+            rawDefaultArgumentResilienceExpansion)) {
+      ctor->setDefaultArgumentResilienceExpansion(
+          *defaultArgumentResilienceExpansion);
+    } else {
+      error();
+      return nullptr;
+    }
+
     break;
   }
 
@@ -2825,6 +2848,7 @@
     DeclID overriddenID;
     DeclID accessorStorageDeclID;
     bool needsNewVTableEntry;
+    uint8_t rawDefaultArgumentResilienceExpansion;
     ArrayRef<uint64_t> nameAndDependencyIDs;
 
     decls_block::FuncLayout::readRecord(scratch, contextID, isImplicit,
@@ -2837,6 +2861,7 @@
                                         numNameComponentsBiased,
                                         rawAddressorKind, rawAccessLevel,
                                         needsNewVTableEntry,
+                                        rawDefaultArgumentResilienceExpansion,
                                         nameAndDependencyIDs);
 
     // Resolve the name ids.
@@ -2974,6 +2999,16 @@
       fn->setImplicit();
     fn->setDynamicSelf(hasDynamicSelf);
     fn->setNeedsNewVTableEntry(needsNewVTableEntry);
+
+    if (auto defaultArgumentResilienceExpansion = getActualResilienceExpansion(
+            rawDefaultArgumentResilienceExpansion)) {
+      fn->setDefaultArgumentResilienceExpansion(
+          *defaultArgumentResilienceExpansion);
+    } else {
+      error();
+      return nullptr;
+    }
+
     break;
   }
 
diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp
index a2a9d23..4000929 100644
--- a/lib/Serialization/DeserializeSIL.cpp
+++ b/lib/Serialization/DeserializeSIL.cpp
@@ -707,7 +707,7 @@
          "Expect 5 numbers for SILDeclRef");
   SILDeclRef DRef(cast<ValueDecl>(MF->getDecl(ListOfValues[NextIdx])),
                   (SILDeclRef::Kind)ListOfValues[NextIdx+1],
-                  (ResilienceExpansion)ListOfValues[NextIdx+2],
+                  (swift::ResilienceExpansion)ListOfValues[NextIdx+2],
                   /*isCurried=*/false, ListOfValues[NextIdx+4] > 0);
   if (ListOfValues[NextIdx+3] < DRef.getUncurryLevel())
     DRef = DRef.asCurried();
@@ -2106,7 +2106,6 @@
     auto valueTy = MF->getType(ListOfValues[nextValue++]);
     auto numComponents = ListOfValues[nextValue++];
     auto numOperands = ListOfValues[nextValue++];
-    assert(numOperands == 0 && "operands not implemented yet");
     auto numSubstitutions = ListOfValues[nextValue++];
     auto objcString = MF->getIdentifier(ListOfValues[nextValue++]).str();
     auto numGenericParams = ListOfValues[nextValue++];
@@ -2118,6 +2117,7 @@
     }
     
     SmallVector<KeyPathPatternComponent, 4> components;
+    components.reserve(numComponents);
     while (numComponents-- > 0) {
       auto kind =
         (KeyPathComponentKindEncoding)ListOfValues[nextValue++];
@@ -2142,6 +2142,36 @@
         }
       };
       
+      ArrayRef<KeyPathPatternComponent::Index> indices;
+      SILFunction *indicesEquals = nullptr;
+      SILFunction *indicesHash = nullptr;
+      
+      auto handleComputedIndices = [&] {
+        SmallVector<KeyPathPatternComponent::Index, 4> indicesBuf;
+        auto numIndexes = ListOfValues[nextValue++];
+        indicesBuf.reserve(numIndexes);
+        while (numIndexes-- > 0) {
+          unsigned operand = ListOfValues[nextValue++];
+          auto formalType = MF->getType(ListOfValues[nextValue++]);
+          auto loweredType = MF->getType(ListOfValues[nextValue++]);
+          auto loweredCategory = (SILValueCategory)ListOfValues[nextValue++];
+          auto conformance = MF->readConformance(SILCursor);
+          indicesBuf.push_back({
+            operand, formalType->getCanonicalType(),
+            SILType::getPrimitiveType(loweredType->getCanonicalType(),
+                                      loweredCategory),
+            conformance});
+        }
+        
+        indices = MF->getContext().AllocateCopy(indicesBuf);
+        if (!indices.empty()) {
+          auto indicesEqualsName = MF->getIdentifier(ListOfValues[nextValue++]);
+          auto indicesHashName = MF->getIdentifier(ListOfValues[nextValue++]);
+          indicesEquals = getFuncForReference(indicesEqualsName.str());
+          indicesHash = getFuncForReference(indicesHashName.str());
+        }
+      };
+      
       switch (kind) {
       case KeyPathComponentKindEncoding::StoredProperty: {
         auto decl = cast<VarDecl>(MF->getDecl(ListOfValues[nextValue++]));
@@ -2153,9 +2183,10 @@
         auto id = handleComputedId();
         auto getterName = MF->getIdentifier(ListOfValues[nextValue++]);
         auto getter = getFuncForReference(getterName.str());
+        handleComputedIndices();
         components.push_back(
-          KeyPathPatternComponent::forComputedGettableProperty(id, getter, {},
-                                                               type));
+          KeyPathPatternComponent::forComputedGettableProperty(
+            id, getter, indices, indicesEquals, indicesHash, type));
         break;
       }
       case KeyPathComponentKindEncoding::SettableProperty: {
@@ -2164,10 +2195,10 @@
         auto getter = getFuncForReference(getterName.str());
         auto setterName = MF->getIdentifier(ListOfValues[nextValue++]);
         auto setter = getFuncForReference(setterName.str());
+        handleComputedIndices();
         components.push_back(
-          KeyPathPatternComponent::forComputedSettableProperty(id,
-                                                               getter, setter,
-                                                               {}, type));
+          KeyPathPatternComponent::forComputedSettableProperty(
+            id, getter, setter, indices, indicesEquals, indicesHash, type));
         break;
       }
       case KeyPathComponentKindEncoding::OptionalChain:
@@ -2205,7 +2236,18 @@
                                        components,
                                        objcString);
     
-    ResultVal = Builder.createKeyPath(Loc, pattern, substitutions, kpTy);
+    SmallVector<SILValue, 4> operands;
+    
+    operands.reserve(numOperands);
+    while (numOperands-- > 0) {
+      auto opValue = ListOfValues[nextValue++];
+      auto opTy = MF->getType(ListOfValues[nextValue++]);
+      auto opCat = (SILValueCategory)ListOfValues[nextValue++];
+      operands.push_back(getLocalValue(opValue, getSILType(opTy, opCat)));
+    }
+    
+    ResultVal = Builder.createKeyPath(Loc, pattern,
+                                      substitutions, operands, kpTy);
     break;
   }
   case ValueKind::MarkUninitializedBehaviorInst:
diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp
index dd008b0..0389a92 100644
--- a/lib/Serialization/Serialization.cpp
+++ b/lib/Serialization/Serialization.cpp
@@ -1014,6 +1014,15 @@
   llvm_unreachable("bad addressor kind");
 }
 
+static uint8_t getRawStableResilienceExpansion(swift::ResilienceExpansion e) {
+  switch (e) {
+  case swift::ResilienceExpansion::Minimal:
+    return uint8_t(serialization::ResilienceExpansion::Minimal);
+  case swift::ResilienceExpansion::Maximal:
+    return uint8_t(serialization::ResilienceExpansion::Maximal);
+  }
+}
+
 void Serializer::writeParameterList(const ParameterList *PL) {
   using namespace decls_block;
 
@@ -2959,6 +2968,9 @@
     uint8_t rawAccessLevel = getRawStableAccessLevel(fn->getFormalAccess());
     uint8_t rawAddressorKind =
       getRawStableAddressorKind(fn->getAddressorKind());
+    uint8_t rawDefaultArgumentResilienceExpansion =
+      getRawStableResilienceExpansion(
+          fn->getDefaultArgumentResilienceExpansion());
     Type ty = fn->getInterfaceType();
 
     for (auto dependency : collectDependenciesFromType(ty->getCanonicalType()))
@@ -2987,6 +2999,7 @@
                            rawAddressorKind,
                            rawAccessLevel,
                            fn->needsNewVTableEntry(),
+                           rawDefaultArgumentResilienceExpansion,
                            nameComponentsAndDependencies);
 
     writeGenericParams(fn->getGenericParams());
@@ -3099,6 +3112,9 @@
       nameComponentsAndDependencies.push_back(addTypeRef(dependency));
 
     uint8_t rawAccessLevel = getRawStableAccessLevel(ctor->getFormalAccess());
+    uint8_t rawDefaultArgumentResilienceExpansion =
+        getRawStableResilienceExpansion(
+            ctor->getDefaultArgumentResilienceExpansion());
 
     bool firstTimeRequired = ctor->isRequired();
     if (auto *overridden = ctor->getOverriddenDecl())
@@ -3122,6 +3138,7 @@
                                   addDeclRef(ctor->getOverriddenDecl()),
                                   rawAccessLevel,
                                   ctor->needsNewVTableEntry(),
+                                  rawDefaultArgumentResilienceExpansion,
                                   firstTimeRequired,
                                   ctor->getFullName().getArgumentNames().size(),
                                   nameComponentsAndDependencies);
diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp
index b8e2ce4..b759524 100644
--- a/lib/Serialization/SerializeSIL.cpp
+++ b/lib/Serialization/SerializeSIL.cpp
@@ -1865,6 +1865,8 @@
       ListOfValues.push_back(0);
     }
     
+    SmallVector<ProtocolConformanceRef, 4> hashableConformances;
+    
     for (auto &component : pattern->getComponents()) {
       auto handleComponentCommon = [&](KeyPathComponentKindEncoding kind) {
         ListOfValues.push_back((unsigned)kind);
@@ -1889,6 +1891,25 @@
           break;
         }
       };
+      auto handleComputedIndices
+        = [&](const KeyPathPatternComponent &component) {
+          auto indices = component.getComputedPropertyIndices();
+          ListOfValues.push_back(indices.size());
+          for (auto &index : indices) {
+            ListOfValues.push_back(index.Operand);
+            ListOfValues.push_back(S.addTypeRef(index.FormalType));
+            ListOfValues.push_back(
+              S.addTypeRef(index.LoweredType.getSwiftRValueType()));
+            ListOfValues.push_back((unsigned)index.LoweredType.getCategory());
+            hashableConformances.push_back(index.Hashable);
+          }
+          if (!indices.empty()) {
+            ListOfValues.push_back(
+              addSILFunctionRef(component.getComputedPropertyIndexEquals()));
+            ListOfValues.push_back(
+              addSILFunctionRef(component.getComputedPropertyIndexHash()));
+          }
+        };
     
       switch (component.getKind()) {
       case KeyPathPatternComponent::Kind::StoredProperty:
@@ -1900,8 +1921,7 @@
         handleComputedId(component.getComputedPropertyId());
         ListOfValues.push_back(
                       addSILFunctionRef(component.getComputedPropertyGetter()));
-        assert(component.getComputedPropertyIndices().empty()
-               && "indices not implemented");
+        handleComputedIndices(component);
         break;
       case KeyPathPatternComponent::Kind::SettableProperty:
         handleComponentCommon(KeyPathComponentKindEncoding::SettableProperty);
@@ -1910,8 +1930,7 @@
                       addSILFunctionRef(component.getComputedPropertyGetter()));
         ListOfValues.push_back(
                       addSILFunctionRef(component.getComputedPropertySetter()));
-        assert(component.getComputedPropertyIndices().empty()
-               && "indices not implemented");
+        handleComputedIndices(component);
         break;
       case KeyPathPatternComponent::Kind::OptionalChain:
         handleComponentCommon(KeyPathComponentKindEncoding::OptionalChain);
@@ -1925,12 +1944,21 @@
       }
     }
     
-    assert(KPI->getAllOperands().empty() && "operands not implemented yet");
+    for (auto &operand : KPI->getAllOperands()) {
+      auto value = operand.get();
+      ListOfValues.push_back(addValueRef(value));
+      ListOfValues.push_back(S.addTypeRef(value->getType().getSwiftRValueType()));
+      ListOfValues.push_back((unsigned)value->getType().getCategory());
+    }
+    
     SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord,
          SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(),
          S.addTypeRef(KPI->getType().getSwiftRValueType()),
          (unsigned)KPI->getType().getCategory(),
          ListOfValues);
+    for (auto conformance : hashableConformances) {
+      S.writeConformance(conformance, SILAbbrCodes);
+    }
     S.writeGenericRequirements(reqts, SILAbbrCodes);
     S.writeSubstitutions(KPI->getSubstitutions(), SILAbbrCodes);
 
diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift
index 23c621f..61e0a4d 100644
--- a/stdlib/public/core/KeyPath.swift
+++ b/stdlib/public/core/KeyPath.swift
@@ -472,9 +472,6 @@
         return false
       }
       if let arg1 = argument1, let arg2 = argument2 {
-        // TODO: Sizes may differ if one key path was formed in a context
-        // capturing generic arguments and one wasn't.
-        _sanityCheck(arg1.data.count == arg2.data.count)
         return arg1.witnesses.pointee.equals(
           arg1.data.baseAddress.unsafelyUnwrapped,
           arg2.data.baseAddress.unsafelyUnwrapped,
@@ -556,23 +553,26 @@
 internal final class MutatingWritebackBuffer<CurValue, NewValue> {
   let previous: AnyObject?
   let base: UnsafeMutablePointer<CurValue>
-  let set: @convention(thin) (NewValue, inout CurValue, UnsafeRawPointer) -> ()
+  let set: @convention(thin) (NewValue, inout CurValue, UnsafeRawPointer, Int) -> ()
   let argument: UnsafeRawPointer
+  let argumentSize: Int
   var value: NewValue
 
   deinit {
-    set(value, &base.pointee, argument)
+    set(value, &base.pointee, argument, argumentSize)
   }
 
   init(previous: AnyObject?,
        base: UnsafeMutablePointer<CurValue>,
-       set: @escaping @convention(thin) (NewValue, inout CurValue, UnsafeRawPointer) -> (),
+       set: @escaping @convention(thin) (NewValue, inout CurValue, UnsafeRawPointer, Int) -> (),
        argument: UnsafeRawPointer,
+       argumentSize: Int,
        value: NewValue) {
     self.previous = previous
     self.base = base
     self.set = set
     self.argument = argument
+    self.argumentSize = argumentSize
     self.value = value
   }
 }
@@ -581,23 +581,26 @@
 internal final class NonmutatingWritebackBuffer<CurValue, NewValue> {
   let previous: AnyObject?
   let base: CurValue
-  let set: @convention(thin) (NewValue, CurValue, UnsafeRawPointer) -> ()
+  let set: @convention(thin) (NewValue, CurValue, UnsafeRawPointer, Int) -> ()
   let argument: UnsafeRawPointer
+  let argumentSize: Int
   var value: NewValue
 
   deinit {
-    set(value, base, argument)
+    set(value, base, argument, argumentSize)
   }
 
   init(previous: AnyObject?,
        base: CurValue,
-       set: @escaping @convention(thin) (NewValue, CurValue, UnsafeRawPointer) -> (),
+       set: @escaping @convention(thin) (NewValue, CurValue, UnsafeRawPointer, Int) -> (),
        argument: UnsafeRawPointer,
+       argumentSize: Int,
        value: NewValue) {
     self.previous = previous
     self.base = base
     self.set = set
     self.argument = argument
+    self.argumentSize = argumentSize
     self.value = value
   }
 }
@@ -929,7 +932,6 @@
     case .computed:
       // Fields are pointer-aligned after the header
       componentSize += Header.pointerAlignmentSkew
-      // TODO: nontrivial arguments need to be copied by value witness
       buffer.storeBytes(of: _computedIDValue,
                         toByteOffset: MemoryLayout<Int>.size,
                         as: Int.self)
@@ -1015,9 +1017,11 @@
          .mutatingGetSet(id: _, get: let rawGet, set: _, argument: let argument),
          .nonmutatingGetSet(id: _, get: let rawGet, set: _, argument: let argument):
       typealias Getter
-        = @convention(thin) (CurValue, UnsafeRawPointer) -> NewValue
+        = @convention(thin) (CurValue, UnsafeRawPointer, Int) -> NewValue
       let get = unsafeBitCast(rawGet, to: Getter.self)
-      return .continue(get(base, argument?.data.baseAddress ?? rawGet))
+      return .continue(get(base,
+                           argument?.data.baseAddress ?? rawGet,
+                           argument?.data.count ?? 0))
 
     case .optionalChain:
       // TODO: IUO shouldn't be a first class type
@@ -1079,9 +1083,9 @@
     case .mutatingGetSet(id: _, get: let rawGet, set: let rawSet,
                          argument: let argument):
       typealias Getter
-        = @convention(thin) (CurValue, UnsafeRawPointer) -> NewValue
+        = @convention(thin) (CurValue, UnsafeRawPointer, Int) -> NewValue
       typealias Setter
-        = @convention(thin) (NewValue, inout CurValue, UnsafeRawPointer) -> ()
+        = @convention(thin) (NewValue, inout CurValue, UnsafeRawPointer, Int) -> ()
       let get = unsafeBitCast(rawGet, to: Getter.self)
       let set = unsafeBitCast(rawSet, to: Setter.self)
 
@@ -1089,11 +1093,13 @@
         mutating: base.assumingMemoryBound(to: CurValue.self))
 
       let argValue = argument?.data.baseAddress ?? rawGet
+      let argSize = argument?.data.count ?? 0
       let writeback = MutatingWritebackBuffer(previous: keepAlive,
-                                       base: baseTyped,
-                                       set: set,
-                                       argument: argValue,
-                                       value: get(baseTyped.pointee, argValue))
+                               base: baseTyped,
+                               set: set,
+                               argument: argValue,
+                               argumentSize: argSize,
+                               value: get(baseTyped.pointee, argValue, argSize))
       keepAlive = writeback
       // A maximally-abstracted, final, stored class property should have
       // a stable address.
@@ -1107,20 +1113,22 @@
            "nonmutating component should not appear in the middle of mutation")
 
       typealias Getter
-        = @convention(thin) (CurValue, UnsafeRawPointer) -> NewValue
+        = @convention(thin) (CurValue, UnsafeRawPointer, Int) -> NewValue
       typealias Setter
-        = @convention(thin) (NewValue, CurValue, UnsafeRawPointer) -> ()
+        = @convention(thin) (NewValue, CurValue, UnsafeRawPointer, Int) -> ()
 
       let get = unsafeBitCast(rawGet, to: Getter.self)
       let set = unsafeBitCast(rawSet, to: Setter.self)
 
       let baseValue = base.assumingMemoryBound(to: CurValue.self).pointee
       let argValue = argument?.data.baseAddress ?? rawGet
+      let argSize = argument?.data.count ?? 0
       let writeback = NonmutatingWritebackBuffer(previous: keepAlive,
-                                             base: baseValue,
-                                             set: set,
-                                             argument: argValue,
-                                             value: get(baseValue, argValue))
+                                       base: baseValue,
+                                       set: set,
+                                       argument: argValue,
+                                       argumentSize: argSize,
+                                       value: get(baseValue, argValue, argSize))
       keepAlive = writeback
       // A maximally-abstracted, final, stored class property should have
       // a stable address.
diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm
index e87e0e1..1a0d38c 100644
--- a/stdlib/public/runtime/SwiftObject.mm
+++ b/stdlib/public/runtime/SwiftObject.mm
@@ -869,7 +869,8 @@
           !objectUsesNativeSwiftReferenceCounting(value));
 }
 
-void swift::swift_unknownUnownedInit(UnownedReference *dest, void *value) {
+UnownedReference *swift::swift_unknownUnownedInit(UnownedReference *dest,
+                                                  void *value) {
   if (!value) {
     dest->Value = nullptr;
   } else if (isObjCForUnownedReference(value)) {
@@ -877,9 +878,11 @@
   } else {
     swift_unownedInit(dest, (HeapObject*) value);
   }
+  return dest;
 }
 
-void swift::swift_unknownUnownedAssign(UnownedReference *dest, void *value) {
+UnownedReference *swift::swift_unknownUnownedAssign(UnownedReference *dest,
+                                                    void *value) {
   if (!value) {
     swift_unknownUnownedDestroy(dest);
     dest->Value = nullptr;
@@ -898,6 +901,7 @@
       swift_unownedAssign(dest, (HeapObject*) value);
     }
   }
+  return dest;
 }
 
 void *swift::swift_unknownUnownedLoadStrong(UnownedReference *ref) {
@@ -941,8 +945,8 @@
   }
 }
 
-void swift::swift_unknownUnownedCopyInit(UnownedReference *dest,
-                                         UnownedReference *src) {
+UnownedReference *swift::swift_unknownUnownedCopyInit(UnownedReference *dest,
+                                                      UnownedReference *src) {
   assert(dest != src);
   if (!src->Value) {
     dest->Value = nullptr;
@@ -951,17 +955,19 @@
   } else {
     swift_unownedCopyInit(dest, src);
   }
+  return dest;
 }
 
-void swift::swift_unknownUnownedTakeInit(UnownedReference *dest,
-                                         UnownedReference *src) {
+UnownedReference *swift::swift_unknownUnownedTakeInit(UnownedReference *dest,
+                                                      UnownedReference *src) {
   assert(dest != src);
   dest->Value = src->Value;
+  return dest;
 }
 
-void swift::swift_unknownUnownedCopyAssign(UnownedReference *dest,
-                                           UnownedReference *src) {
-  if (dest == src) return;
+UnownedReference *swift::swift_unknownUnownedCopyAssign(UnownedReference *dest,
+                                                        UnownedReference *src) {
+  if (dest == src) return dest;
 
   if (auto objcSrc = dyn_cast<ObjCUnownedReference>(src)) {
     if (auto objcDest = dyn_cast<ObjCUnownedReference>(dest)) {
@@ -969,7 +975,7 @@
       objc_destroyWeak(&objcDest->storage()->WeakRef);
       objc_copyWeak(&objcDest->storage()->WeakRef,
                     &objcSrc->storage()->WeakRef);
-      return;
+      return dest;
     }
 
     swift_unownedDestroy(dest);
@@ -982,15 +988,17 @@
       swift_unownedCopyAssign(dest, src);
     }
   }
+  return dest;
 }
 
-void swift::swift_unknownUnownedTakeAssign(UnownedReference *dest,
-                                           UnownedReference *src) {
+UnownedReference *swift::swift_unknownUnownedTakeAssign(UnownedReference *dest,
+                                                        UnownedReference *src) {
   assert(dest != src);
 
   // There's not really anything more efficient to do here than this.
   swift_unknownUnownedDestroy(dest);
   dest->Value = src->Value;
+  return dest;
 }
 
 bool swift::swift_unknownUnownedIsEqual(UnownedReference *ref, void *value) {
@@ -1012,12 +1020,14 @@
 /************************** UNKNOWN WEAK REFERENCES **************************/
 /*****************************************************************************/
 
-void swift::swift_unknownWeakInit(WeakReference *ref, void *value) {
-  return ref->unknownInit(value);
+WeakReference *swift::swift_unknownWeakInit(WeakReference *ref, void *value) {
+  ref->unknownInit(value);
+  return ref;
 }
 
-void swift::swift_unknownWeakAssign(WeakReference *ref, void *value) {
-  return ref->unknownAssign(value);
+WeakReference *swift::swift_unknownWeakAssign(WeakReference *ref, void *value) {
+  ref->unknownAssign(value);
+  return ref;
 }
 
 void *swift::swift_unknownWeakLoadStrong(WeakReference *ref) {
@@ -1032,19 +1042,25 @@
   ref->unknownDestroy();
 }
 
-void swift::swift_unknownWeakCopyInit(WeakReference *dest, WeakReference *src) {
+WeakReference *swift::swift_unknownWeakCopyInit(WeakReference *dest,
+                                                WeakReference *src) {
   dest->unknownCopyInit(src);
+  return dest;
 }
-void swift::swift_unknownWeakTakeInit(WeakReference *dest, WeakReference *src) {
+WeakReference *swift::swift_unknownWeakTakeInit(WeakReference *dest,
+                                                WeakReference *src) {
   dest->unknownTakeInit(src);
+  return dest;
 }
-void swift::swift_unknownWeakCopyAssign(WeakReference *dest,
-                                        WeakReference *src) {
+WeakReference *swift::swift_unknownWeakCopyAssign(WeakReference *dest,
+                                                  WeakReference *src) {
   dest->unknownCopyAssign(src);
+  return dest;
 }
-void swift::swift_unknownWeakTakeAssign(WeakReference *dest,
-                                        WeakReference *src) {
+WeakReference *swift::swift_unknownWeakTakeAssign(WeakReference *dest,
+                                                  WeakReference *src) {
   dest->unknownTakeAssign(src);
+  return dest;
 }
 
 // SWIFT_OBJC_INTEROP
diff --git a/stdlib/public/stubs/KeyPaths.cpp b/stdlib/public/stubs/KeyPaths.cpp
index 889ec10..053dc20 100644
--- a/stdlib/public/stubs/KeyPaths.cpp
+++ b/stdlib/public/stubs/KeyPaths.cpp
@@ -15,8 +15,8 @@
 #include <cstdint>
 #include <cstring>
 
-SWIFT_CC(swift)
-static void copyGenericArguments(const void *src, void *dest, size_t bytes) {
+SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
+void swift_copyKeyPathTrivialIndices(const void *src, void *dest, size_t bytes) {
   memcpy(dest, src, bytes);
 }
 
@@ -42,7 +42,7 @@
 SWIFT_RUNTIME_EXPORT
 void *(swift_keyPathGenericWitnessTable[]) = {
   nullptr, // no destructor necessary
-  (void*)(uintptr_t)copyGenericArguments,
+  (void*)(uintptr_t)swift_copyKeyPathTrivialIndices,
   (void*)(uintptr_t)equateGenericArguments,
   (void*)(uintptr_t)hashGenericArguments,
 };
diff --git a/stdlib/public/stubs/UnicodeNormalization.cpp b/stdlib/public/stubs/UnicodeNormalization.cpp
index 950f2e4..2c9fb2f 100644
--- a/stdlib/public/stubs/UnicodeNormalization.cpp
+++ b/stdlib/public/stubs/UnicodeNormalization.cpp
@@ -336,9 +336,9 @@
 #if defined(__CYGWIN__) || defined( _MSC_VER) || defined(__linux__)
   return ptr_cast<swift::__swift_stdlib_UBreakIterator>(
       ubrk_open(static_cast<UBreakIteratorType>(type), locale,
-		reinterpret_cast<const UChar*>(text), textLength,
+                reinterpret_cast<const UChar *>(text), textLength,
                 ptr_cast<UErrorCode>(status)));
-#else      
+#else
   return ptr_cast<swift::__swift_stdlib_UBreakIterator>(
       ubrk_open(static_cast<UBreakIteratorType>(type), locale, text, textLength,
                 ptr_cast<UErrorCode>(status)));
diff --git a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.apinotes b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.apinotes
index 6fd1676..af8b3b3 100644
--- a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.apinotes
+++ b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.apinotes
@@ -65,6 +65,23 @@
 Tags:
   - Name: InnerInSwift4
     SwiftName: Outer.Inner
+Globals:
+  - Name: multiVersionedGlobal34Notes
+    SwiftName: multiVersionedGlobal34Notes_NEW
+  - Name: multiVersionedGlobal34Both
+    SwiftName: multiVersionedGlobal34Both_NEW
+  - Name: multiVersionedGlobal345Notes
+    SwiftName: multiVersionedGlobal345Notes_NEW
+  - Name: multiVersionedGlobal345Both
+    SwiftName: multiVersionedGlobal345Both_NEW
+  - Name: multiVersionedGlobal4Notes
+    SwiftName: multiVersionedGlobal4Notes_NEW
+  - Name: multiVersionedGlobal4Both
+    SwiftName: multiVersionedGlobal4Both_NEW
+  - Name: multiVersionedGlobal45Notes
+    SwiftName: multiVersionedGlobal45Notes_NEW
+  - Name: multiVersionedGlobal45Both
+    SwiftName: multiVersionedGlobal45Both_NEW
 SwiftVersions:
   - Version: 3.0
     Classes:
@@ -207,3 +224,72 @@
         SwiftName: aliasRenamedSwift3
       - Name: OptionyEnumRenamed
         SwiftName: renamedSwift3
+    Globals:
+      - Name: multiVersionedGlobal34
+        SwiftName: multiVersionedGlobal34_3
+      - Name: multiVersionedGlobal34Header
+        SwiftName: multiVersionedGlobal34Header_3
+      - Name: multiVersionedGlobal34Notes
+        SwiftName: multiVersionedGlobal34Notes_3
+      - Name: multiVersionedGlobal34Both
+        SwiftName: multiVersionedGlobal34Both_3
+      - Name: multiVersionedGlobal345
+        SwiftName: multiVersionedGlobal345_3
+      - Name: multiVersionedGlobal345Header
+        SwiftName: multiVersionedGlobal345Header_3
+      - Name: multiVersionedGlobal345Notes
+        SwiftName: multiVersionedGlobal345Notes_3
+      - Name: multiVersionedGlobal345Both
+        SwiftName: multiVersionedGlobal345Both_3
+  - Version: 5
+    Globals:
+      - Name: multiVersionedGlobal345
+        SwiftName: multiVersionedGlobal345_5
+      - Name: multiVersionedGlobal345Header
+        SwiftName: multiVersionedGlobal345Header_5
+      - Name: multiVersionedGlobal345Notes
+        SwiftName: multiVersionedGlobal345Notes_5
+      - Name: multiVersionedGlobal345Both
+        SwiftName: multiVersionedGlobal345Both_5
+      - Name: multiVersionedGlobal45
+        SwiftName: multiVersionedGlobal45_5
+      - Name: multiVersionedGlobal45Header
+        SwiftName: multiVersionedGlobal45Header_5
+      - Name: multiVersionedGlobal45Notes
+        SwiftName: multiVersionedGlobal45Notes_5
+      - Name: multiVersionedGlobal45Both
+        SwiftName: multiVersionedGlobal45Both_5
+  - Version: 4 # Versions are deliberately ordered as "3, 5, 4" to catch bugs.
+    Globals:
+      - Name: multiVersionedGlobal34
+        SwiftName: multiVersionedGlobal34_4
+      - Name: multiVersionedGlobal34Header
+        SwiftName: multiVersionedGlobal34Header_4
+      - Name: multiVersionedGlobal34Notes
+        SwiftName: multiVersionedGlobal34Notes_4
+      - Name: multiVersionedGlobal34Both
+        SwiftName: multiVersionedGlobal34Both_4
+      - Name: multiVersionedGlobal345
+        SwiftName: multiVersionedGlobal345_4
+      - Name: multiVersionedGlobal345Header
+        SwiftName: multiVersionedGlobal345Header_4
+      - Name: multiVersionedGlobal345Notes
+        SwiftName: multiVersionedGlobal345Notes_4
+      - Name: multiVersionedGlobal345Both
+        SwiftName: multiVersionedGlobal345Both_4
+      - Name: multiVersionedGlobal4
+        SwiftName: multiVersionedGlobal4_4
+      - Name: multiVersionedGlobal4Header
+        SwiftName: multiVersionedGlobal4Header_4
+      - Name: multiVersionedGlobal4Notes
+        SwiftName: multiVersionedGlobal4Notes_4
+      - Name: multiVersionedGlobal4Both
+        SwiftName: multiVersionedGlobal4Both_4
+      - Name: multiVersionedGlobal45
+        SwiftName: multiVersionedGlobal45_4
+      - Name: multiVersionedGlobal45Header
+        SwiftName: multiVersionedGlobal45Header_4
+      - Name: multiVersionedGlobal45Notes
+        SwiftName: multiVersionedGlobal45Notes_4
+      - Name: multiVersionedGlobal45Both
+        SwiftName: multiVersionedGlobal45Both_4
diff --git a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h
index c3a23fd..43c932c 100644
--- a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h
+++ b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/APINotesFrameworkTest.h
@@ -32,6 +32,7 @@
 
 #import <APINotesFrameworkTest/Classes.h>
 #import <APINotesFrameworkTest/Enums.h>
+#import <APINotesFrameworkTest/Globals.h>
 #import <APINotesFrameworkTest/ImportAsMember.h>
 #import <APINotesFrameworkTest/Properties.h>
 #import <APINotesFrameworkTest/Protocols.h>
diff --git a/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/Globals.h b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/Globals.h
new file mode 100644
index 0000000..059ecd7
--- /dev/null
+++ b/test/APINotes/Inputs/custom-frameworks/APINotesFrameworkTest.framework/Headers/Globals.h
@@ -0,0 +1,23 @@
+#pragma clang assume_nonnull begin
+
+int multiVersionedGlobal4;
+int multiVersionedGlobal4Notes;
+int multiVersionedGlobal4Header __attribute__((swift_name("multiVersionedGlobal4Header_NEW")));
+int multiVersionedGlobal4Both __attribute__((swift_name("multiVersionedGlobal4Both_OLD")));
+
+int multiVersionedGlobal34;
+int multiVersionedGlobal34Notes;
+int multiVersionedGlobal34Header __attribute__((swift_name("multiVersionedGlobal34Header_NEW")));
+int multiVersionedGlobal34Both __attribute__((swift_name("multiVersionedGlobal34Both_OLD")));
+
+int multiVersionedGlobal45;
+int multiVersionedGlobal45Notes;
+int multiVersionedGlobal45Header __attribute__((swift_name("multiVersionedGlobal45Header_NEW")));
+int multiVersionedGlobal45Both __attribute__((swift_name("multiVersionedGlobal45Both_OLD")));
+
+int multiVersionedGlobal345;
+int multiVersionedGlobal345Notes;
+int multiVersionedGlobal345Header __attribute__((swift_name("multiVersionedGlobal345Header_NEW")));
+int multiVersionedGlobal345Both __attribute__((swift_name("multiVersionedGlobal345Both_OLD")));
+
+#pragma clang assume_nonnull end
diff --git a/test/APINotes/versioned-multi.swift b/test/APINotes/versioned-multi.swift
new file mode 100644
index 0000000..d479c3c
--- /dev/null
+++ b/test/APINotes/versioned-multi.swift
@@ -0,0 +1,297 @@
+// RUN: %empty-directory(%t)
+
+// RUN: %target-swift-ide-test -F %S/Inputs/custom-frameworks -print-module -source-filename %s -module-to-print=APINotesFrameworkTest -function-definitions=false -print-regular-comments -swift-version 3 | %FileCheck -check-prefix=CHECK-SWIFT-3 %s
+
+// RUN: %target-swift-ide-test -F %S/Inputs/custom-frameworks -print-module -source-filename %s -module-to-print=APINotesFrameworkTest -function-definitions=false -swift-version 4 | %FileCheck -check-prefix=CHECK-SWIFT-4 %s
+
+// RUN: %target-swift-ide-test -F %S/Inputs/custom-frameworks -print-module -source-filename %s -module-to-print=APINotesFrameworkTest -function-definitions=false -swift-version 5 | %FileCheck -check-prefix=CHECK-SWIFT-5 %s
+
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal4: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal4_4: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Notes_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal4Notes: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal4Notes_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal4Notes_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal4Notes_NEW: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Header_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal4Header: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal4Header_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal4Header_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal4Header_NEW: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Both_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal4Both: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal4Both_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal4Both_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal4Both_NEW: Int32
+
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal34_3: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 4, renamed: "multiVersionedGlobal34_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34_4: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Notes_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Notes: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal34Notes_3: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 4, renamed: "multiVersionedGlobal34Notes_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Notes_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal34Notes_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Notes_NEW: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Header_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Header: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal34Header_3: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 4, renamed: "multiVersionedGlobal34Header_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Header_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal34Header_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Header_NEW: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Both_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Both: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal34Both_3: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 4, renamed: "multiVersionedGlobal34Both_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Both_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal34Both_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal34Both_NEW: Int32
+
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal45: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal45_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal45_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal45_5: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Notes_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal45Notes: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal45Notes_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal45Notes_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal45Notes_5: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Header_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal45Header: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal45Header_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal45Header_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal45Header_5: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Both_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal45Both: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal45Both_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal45Both_4")
+// CHECK-SWIFT-3: var multiVersionedGlobal45Both_5: Int32
+
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal345_3: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 4, renamed: "multiVersionedGlobal345_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal345_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345_5: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Notes_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Notes: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal345Notes_3: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 4, renamed: "multiVersionedGlobal345Notes_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Notes_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal345Notes_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Notes_5: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Header_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Header: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal345Header_3: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 4, renamed: "multiVersionedGlobal345Header_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Header_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal345Header_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Header_5: Int32
+// CHECK-SWIFT-3: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Both_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Both: Int32
+// CHECK-SWIFT-3: var multiVersionedGlobal345Both_3: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 4, renamed: "multiVersionedGlobal345Both_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Both_4: Int32
+// CHECK-SWIFT-3: @available(swift, introduced: 5, renamed: "multiVersionedGlobal345Both_3")
+// CHECK-SWIFT-3: var multiVersionedGlobal345Both_5: Int32
+
+
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal4: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal4_4: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal4Notes: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal4Notes_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal4Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal4Notes_NEW: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal4Header: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal4Header_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal4Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal4Header_NEW: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal4Both: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal4Both_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal4Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal4Both_NEW: Int32
+
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34_3: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal34_4: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Notes: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Notes_3: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal34Notes_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal34Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Notes_NEW: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Header: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Header_3: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal34Header_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal34Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Header_NEW: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Both: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Both_3: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal34Both_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal34Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal34Both_NEW: Int32
+
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal45: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal45_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal45_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal45_5: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal45Notes: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal45Notes_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal45Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal45Notes_5: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal45Header: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal45Header_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal45Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal45Header_5: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal45Both: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal45Both_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal45Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal45Both_5: Int32
+
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345_3: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal345_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal345_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345_5: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Notes: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Notes_3: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal345Notes_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal345Notes_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Notes_5: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Header: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Header_3: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal345Header_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal345Header_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Header_5: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Both: Int32
+// CHECK-SWIFT-4: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Both_3: Int32
+// CHECK-SWIFT-4: var multiVersionedGlobal345Both_4: Int32
+// CHECK-SWIFT-4: @available(swift, introduced: 5, renamed: "multiVersionedGlobal345Both_4")
+// CHECK-SWIFT-4: var multiVersionedGlobal345Both_5: Int32
+
+
+// CHECK-SWIFT-5: var multiVersionedGlobal4: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal4")
+// CHECK-SWIFT-5: var multiVersionedGlobal4_4: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Notes_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal4Notes: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal4Notes_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal4Notes_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal4Notes_NEW: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Header_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal4Header: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal4Header_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal4Header_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal4Header_NEW: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal4Both_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal4Both: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal4Both_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal4Both_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal4Both_NEW: Int32
+
+// CHECK-SWIFT-5: var multiVersionedGlobal34: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34")
+// CHECK-SWIFT-5: var multiVersionedGlobal34_3: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34")
+// CHECK-SWIFT-5: var multiVersionedGlobal34_4: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Notes_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Notes: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Notes_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Notes_3: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34Notes_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Notes_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal34Notes_NEW: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Header_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Header: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Header_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Header_3: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34Header_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Header_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal34Header_NEW: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal34Both_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Both: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal34Both_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Both_3: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal34Both_NEW")
+// CHECK-SWIFT-5: var multiVersionedGlobal34Both_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal34Both_NEW: Int32
+
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal45: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal45_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal45_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal45_5: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Notes_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal45Notes: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal45Notes_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal45Notes_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal45Notes_5: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Header_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal45Header: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal45Header_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal45Header_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal45Header_5: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal45Both_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal45Both: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal45Both_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal45Both_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal45Both_5: Int32
+
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345_3: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal345_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal345_5: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Notes_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Notes: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Notes_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Notes_3: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal345Notes_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Notes_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal345Notes_5: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Header_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Header: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Header_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Header_3: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal345Header_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Header_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal345Header_5: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 3, renamed: "multiVersionedGlobal345Both_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Both: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 4, renamed: "multiVersionedGlobal345Both_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Both_3: Int32
+// CHECK-SWIFT-5: @available(swift, obsoleted: 5, renamed: "multiVersionedGlobal345Both_5")
+// CHECK-SWIFT-5: var multiVersionedGlobal345Both_4: Int32
+// CHECK-SWIFT-5: var multiVersionedGlobal345Both_5: Int32
diff --git a/test/IDE/coloring.swift b/test/IDE/coloring.swift
index 24e915f..f9797b3 100644
--- a/test/IDE/coloring.swift
+++ b/test/IDE/coloring.swift
@@ -227,6 +227,15 @@
   // CHECK: <str>"This is string </str>\<anchor>(</anchor>genFn({(a:<type>Int</type> -> <type>Int</type>) <kw>in</kw> a})<anchor>)</anchor><str> interpolation"</str>
   "This is string \(genFn({(a:Int -> Int) in a})) interpolation"
 
+  // CHECK: <str>"This is unterminated</str>
+  "This is unterminated
+
+  // CHECK: <str>"This is unterminated with ignored \(interpolation) in it</str>
+  "This is unterminated with ignored \(interpolation) in it
+
+  // CHECK: <str>"This is terminated with invalid \(interpolation" + "in it"</str>
+  "This is terminated with invalid \(interpolation" + "in it"
+
   // CHECK: <str>"""
   // CHECK-NEXT: This is a multiline string.
   // CHECK-NEXT: """</str>
@@ -236,9 +245,19 @@
 
   // CHECK: <str>"""
   // CHECK-NEXT: This is a multiline</str>\<anchor>(</anchor> <str>"interpolated"</str> <anchor>)</anchor><str>string
+  // CHECK-NEXT: </str>\<anchor>(</anchor>
+  // CHECK-NEXT: <str>"""
+  // CHECK-NEXT: inner
+  // CHECK-NEXT: """</str>
+  // CHECK-NEXT: <anchor>)</anchor><str>
   // CHECK-NEXT: """</str>
   """
       This is a multiline\( "interpolated" )string
+   \(
+   """
+    inner
+   """
+   )
    """
 
   // CHECK: <str>"</str>\<anchor>(</anchor><int>1</int><anchor>)</anchor>\<anchor>(</anchor><int>1</int><anchor>)</anchor><str>"</str>
diff --git a/test/IDE/unterminated_multiline.swift b/test/IDE/unterminated_multiline.swift
new file mode 100644
index 0000000..4fd7a60
--- /dev/null
+++ b/test/IDE/unterminated_multiline.swift
@@ -0,0 +1,16 @@
+// RUN: %target-swift-ide-test -syntax-coloring -source-filename %s | %FileCheck %s
+// RUN: %target-swift-ide-test -syntax-coloring -typecheck -source-filename %s | %FileCheck %s
+
+// CHECK: <kw>let</kw> x = <str>"""
+// CHECK-NEXT: This is an unterminated
+// CHECK-NEXT: \( "multiline" )
+// CHECK-NEXT: string followed by code
+// CHECK-NEXT: ""
+// CHECK-NEXT: func foo() {}
+// CHECK-NEXT: </str>
+let x = """
+  This is an unterminated
+  \( "multiline" )
+  string followed by code
+  ""
+func foo() {}
diff --git a/test/IRGen/existentials_objc.sil b/test/IRGen/existentials_objc.sil
index 420bd81..746707c 100644
--- a/test/IRGen/existentials_objc.sil
+++ b/test/IRGen/existentials_objc.sil
@@ -80,7 +80,7 @@
   // CHECK: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[U1]], i32 0, i32 1
   // CHECK: store i8** %1, i8*** [[T0]], align 8
   // CHECK: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[U1]], i32 0, i32 0
-  // CHECK: call void @swift_unknownUnownedInit(%swift.unowned* [[T0]], %objc_object* %0)
+  // CHECK: call %swift.unowned* @swift_unknownUnownedInit(%swift.unowned* [[T0]], %objc_object* %0)
 
   // CHECK: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[U1]], i32 0, i32 0
   // CHECK: [[T1:%.*]] = call %objc_object* @swift_unknownUnownedLoadStrong(%swift.unowned* [[T0]])
@@ -112,7 +112,7 @@
   // CHECK: [[DEST_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 1
   // CHECK: store i8** [[SRC_WITNESS]], i8*** [[DEST_WITNESS_ADDR]]
   // CHECK: [[DEST_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 0
-  // CHECK: call void @swift_unknownWeakInit(%swift.weak* [[DEST_REF_ADDR]], %objc_object* [[SRC_REF]])
+  // CHECK: call %swift.weak* @swift_unknownWeakInit(%swift.weak* [[DEST_REF_ADDR]], %objc_object* [[SRC_REF]])
   store_weak %a to [initialization] %w : $*@sil_weak CP?
 
   // CHECK: [[SRC_REF:%.*]] = inttoptr {{.*}} %objc_object*
@@ -120,7 +120,7 @@
   // CHECK: [[DEST_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 1
   // CHECK: store i8** [[SRC_WITNESS]], i8*** [[DEST_WITNESS_ADDR]]
   // CHECK: [[DEST_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 0
-  // CHECK: call void @swift_unknownWeakAssign(%swift.weak* [[DEST_REF_ADDR]], %objc_object* [[SRC_REF]])
+  // CHECK: call %swift.weak* @swift_unknownWeakAssign(%swift.weak* [[DEST_REF_ADDR]], %objc_object* [[SRC_REF]])
   store_weak %a to                  %w : $*@sil_weak CP?
 
   // CHECK: [[SRC_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 0
@@ -137,7 +137,7 @@
 
   // CHECK: [[DEST_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* [[V]], i32 0, i32 0
   // CHECK: [[SRC_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 0
-  // CHECK: call void @swift_unknownWeakTakeInit(%swift.weak* [[DEST_REF_ADDR]], %swift.weak* [[SRC_REF_ADDR]])
+  // CHECK: call %swift.weak* @swift_unknownWeakTakeInit(%swift.weak* [[DEST_REF_ADDR]], %swift.weak* [[SRC_REF_ADDR]])
   // CHECK: [[SRC_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 1
   // CHECK: [[WITNESS:%.*]] = load i8**, i8*** [[SRC_WITNESS_ADDR]], align 8
   // CHECK: [[DEST_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* [[V]], i32 0, i32 1
@@ -146,7 +146,7 @@
 
   // CHECK: [[DEST_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* [[V]], i32 0, i32 0
   // CHECK: [[SRC_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 0
-  // CHECK: call void @swift_unknownWeakTakeAssign(%swift.weak* [[DEST_REF_ADDR]], %swift.weak* [[SRC_REF_ADDR]])
+  // CHECK: call %swift.weak* @swift_unknownWeakTakeAssign(%swift.weak* [[DEST_REF_ADDR]], %swift.weak* [[SRC_REF_ADDR]])
   // CHECK: [[SRC_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 1
   // CHECK: [[WITNESS:%.*]] = load i8**, i8*** [[SRC_WITNESS_ADDR]], align 8
   // CHECK: [[DEST_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* [[V]], i32 0, i32 1
@@ -155,7 +155,7 @@
 
   // CHECK: [[DEST_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* [[V]], i32 0, i32 0
   // CHECK: [[SRC_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 0
-  // CHECK: call void @swift_unknownWeakCopyInit(%swift.weak* [[DEST_REF_ADDR]], %swift.weak* [[SRC_REF_ADDR]])
+  // CHECK: call %swift.weak* @swift_unknownWeakCopyInit(%swift.weak* [[DEST_REF_ADDR]], %swift.weak* [[SRC_REF_ADDR]])
   // CHECK: [[SRC_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 1
   // CHECK: [[WITNESS:%.*]] = load i8**, i8*** [[SRC_WITNESS_ADDR]], align 8
   // CHECK: [[DEST_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* [[V]], i32 0, i32 1
@@ -164,7 +164,7 @@
 
   // CHECK: [[DEST_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* [[V]], i32 0, i32 0
   // CHECK: [[SRC_REF_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 0
-  // CHECK: call void @swift_unknownWeakCopyAssign(%swift.weak* [[DEST_REF_ADDR]], %swift.weak* [[SRC_REF_ADDR]])
+  // CHECK: call %swift.weak* @swift_unknownWeakCopyAssign(%swift.weak* [[DEST_REF_ADDR]], %swift.weak* [[SRC_REF_ADDR]])
   // CHECK: [[SRC_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 1
   // CHECK: [[WITNESS:%.*]] = load i8**, i8*** [[SRC_WITNESS_ADDR]], align 8
   // CHECK: [[DEST_WITNESS_ADDR:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* [[V]], i32 0, i32 1
diff --git a/test/IRGen/keypaths.sil b/test/IRGen/keypaths.sil
index 4cb4074..b7dbb0e 100644
--- a/test/IRGen/keypaths.sil
+++ b/test/IRGen/keypaths.sil
@@ -6,19 +6,25 @@
 sil_stage canonical
 import Swift
 
-struct S {
+struct S: Hashable {
   var x: Int
   let y: String
   var z: C
   var reabstracted: () -> ()
+
+  var hashValue: Int { get }
+  static func ==(_: S, _: S) -> Bool
 }
-class C {
+class C: Hashable {
   final var x: Int
   final let y: String
   final var z: S
   var w: Int { get set }
 
   init()
+
+  var hashValue: Int { get }
+  static func ==(_: C, _: C) -> Bool
 }
 
 sil_vtable C {}
@@ -260,23 +266,20 @@
 // CHECK-LABEL: define{{( protected)?}} swiftcc void @stored_property_generics(%swift.type* %T, %swift.type* %U)
 sil @stored_property_generics : $@convention(thin) <T, U> () -> () {
 entry:
-  // CHECK: [[PTR:%.*]] = bitcast [1 x %swift.type*]* [[ARGS:%.*]] to
+  // CHECK: [[PTR:%.*]] = bitcast i8* [[ARGS:%.*]] to
   // CHECK: store %swift.type* %T, %swift.type** [[PTR]]
-  // CHECK: [[ARGS_I8:%.*]] = bitcast [1 x %swift.type*]* [[ARGS]] to
-  // CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_I]] to i8*), i8* [[ARGS_I8]])
+  // CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_I]] to i8*), i8* [[ARGS]])
   %i = keypath $KeyPath<Gen<T,T>, T>, <A> (root $Gen<A, A>; stored_property #Gen.x : $A) <T>
 
-  // CHECK: [[PTR:%.*]] = bitcast [1 x %swift.type*]* [[ARGS:%.*]] to
+  // CHECK: [[PTR:%.*]] = bitcast i8* [[ARGS:%.*]] to
   // CHECK: store %swift.type* %U, %swift.type** [[PTR]]
-  // CHECK: [[ARGS_I8:%.*]] = bitcast [1 x %swift.type*]* [[ARGS]] to
-  // CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_J]] to i8*), i8* [[ARGS_I8]])
+  // CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_J]] to i8*), i8* [[ARGS]])
   %j = keypath $KeyPath<Gen<U,U>, U>, <A> (root $Gen<A, A>; stored_property #Gen.y : $A) <U>
 
-  // CHECK: [[PTR:%.*]] = bitcast [1 x %swift.type*]* [[ARGS:%.*]] to
+  // CHECK: [[PTR:%.*]] = bitcast i8* [[ARGS:%.*]] to
   // CHECK: [[FOO_T:%.*]] = call %swift.type* @_T08keypaths3FooVMa(%swift.type* %T)
   // CHECK: store %swift.type* [[FOO_T]], %swift.type** [[PTR]]
-  // CHECK: [[ARGS_I8:%.*]] = bitcast [1 x %swift.type*]* [[ARGS]] to
-  // CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_I]] to i8*), i8* [[ARGS_I8]])
+  // CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_I]] to i8*), i8* [[ARGS]])
   %i2 = keypath $KeyPath<Gen<Foo<T>,Foo<T>>, Foo<T>>, <A> (root $Gen<A, A>; stored_property #Gen.x : $A) <Foo<T>>
 
   return undef : $()
@@ -304,3 +307,105 @@
 
 sil @n_get : $@convention(thin) <UU, TT> (@in TT) -> @out UU
 sil @n_set : $@convention(thin) <UU, TT> (@in UU, @in TT) -> ()
+
+sil @computed_property_indices : $@convention(thin) (C, S, C, S, C, S) -> () {
+entry(%0 : $C, %1 : $S, %2 : $C, %3 : $S, %4 : $C, %5 : $S):
+  %o = keypath $WritableKeyPath<S, C>, (
+    root $S;
+    settable_property $C,
+      id @o_get : $@convention(thin) (@in S, UnsafeRawPointer) -> @out C,
+      getter @o_get : $@convention(thin) (@in S, UnsafeRawPointer) -> @out C,
+      setter @o_set : $@convention(thin) (@in C, @in S, UnsafeRawPointer) -> (),
+      indices [%$0 : $C : $C],
+      indices_equals @o_equals : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool,
+      indices_hash @o_hash : $@convention(thin) (UnsafeRawPointer) -> Int
+  ) (%0)
+  %p = keypath $WritableKeyPath<S, C>, (
+    root $S;
+    settable_property $C,
+      id @o_get : $@convention(thin) (@in S, UnsafeRawPointer) -> @out C,
+      getter @o_get : $@convention(thin) (@in S, UnsafeRawPointer) -> @out C,
+      setter @o_set : $@convention(thin) (@in C, @in S, UnsafeRawPointer) -> (),
+      indices [%$0 : $S : $S, %$1 : $C : $C],
+      indices_equals @o_equals : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool,
+      indices_hash @o_hash : $@convention(thin) (UnsafeRawPointer) -> Int
+  ) (%1, %2)
+  %r = keypath $WritableKeyPath<S, S>, (
+    root $S;
+    settable_property $C,
+      id @o_get : $@convention(thin) (@in S, UnsafeRawPointer) -> @out C,
+      getter @o_get : $@convention(thin) (@in S, UnsafeRawPointer) -> @out C,
+      setter @o_set : $@convention(thin) (@in C, @in S, UnsafeRawPointer) -> (),
+      indices [%$0 : $S : $S, %$1 : $C : $C],
+      indices_equals @o_equals : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool,
+      indices_hash @o_hash : $@convention(thin) (UnsafeRawPointer) -> Int;
+    settable_property $S,
+      id @r_get : $@convention(thin) (@in C, UnsafeRawPointer) -> @out S,
+      getter @r_get : $@convention(thin) (@in C, UnsafeRawPointer) -> @out S,
+      setter @r_set : $@convention(thin) (@in S, @in C, UnsafeRawPointer) -> (),
+      indices [%$2 : $S : $S],
+      indices_equals @o_equals : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool,
+      indices_hash @o_hash : $@convention(thin) (UnsafeRawPointer) -> Int
+  ) (%3, %4, %5)
+
+  return undef : $()
+}
+
+sil @o_get : $@convention(thin) (@in S, UnsafeRawPointer) -> @out C
+sil @o_set : $@convention(thin) (@in C, @in S, UnsafeRawPointer) -> ()
+sil @o_equals : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool
+sil @o_hash : $@convention(thin) (UnsafeRawPointer) -> Int
+
+sil @r_get : $@convention(thin) (@in C, UnsafeRawPointer) -> @out S
+sil @r_set : $@convention(thin) (@in S, @in C, UnsafeRawPointer) -> ()
+
+sil @generic_computed_property_indices : $@convention(thin) <A: Hashable, B: Hashable> (@in A, @in B, @in A, @in B, @in A, @in B) -> () {
+entry(%0 : $*A, %1 : $*B, %2 : $*A, %3 : $*B, %4 : $*A, %5 : $*B):
+  %s = keypath $WritableKeyPath<A, B>, <X: Hashable, Y: Hashable> (
+    root $X;
+    settable_property $Y,
+      id @s_get : $@convention(thin) <T: Hashable, U: Hashable> (@in T, UnsafeRawPointer) -> @out U,
+      getter @s_get : $@convention(thin) <T: Hashable, U: Hashable> (@in T, UnsafeRawPointer) -> @out U,
+      setter @s_set : $@convention(thin) <T: Hashable, U: Hashable> (@in U, @in T, UnsafeRawPointer) -> (),
+      indices [%$0 : $X : $*X],
+      indices_equals @s_equals : $@convention(thin) <T: Hashable, U: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool,
+      indices_hash @s_hash : $@convention(thin) <T: Hashable, U: Hashable> (UnsafeRawPointer) -> Int
+  ) <A, B> (%0)
+  %t = keypath $WritableKeyPath<A, B>, <X: Hashable, Y: Hashable> (
+    root $X;
+    settable_property $Y,
+      id @s_get : $@convention(thin) <T: Hashable, U: Hashable> (@in T, UnsafeRawPointer) -> @out U,
+      getter @s_get : $@convention(thin) <T: Hashable, U: Hashable> (@in T, UnsafeRawPointer) -> @out U,
+      setter @s_set : $@convention(thin) <T: Hashable, U: Hashable> (@in U, @in T, UnsafeRawPointer) -> (),
+      indices [%$0 : $Y : $*Y, %$1 : $X : $*X],
+      indices_equals @s_equals : $@convention(thin) <T: Hashable, U: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool,
+      indices_hash @s_hash : $@convention(thin) <T: Hashable, U: Hashable> (UnsafeRawPointer) -> Int
+  ) <A, B> (%1, %2)
+  %v = keypath $WritableKeyPath<A, A>, <X: Hashable, Y: Hashable> (
+    root $X;
+    settable_property $Y,
+      id @s_get : $@convention(thin) <T: Hashable, U: Hashable> (@in T, UnsafeRawPointer) -> @out U,
+      getter @s_get : $@convention(thin) <T: Hashable, U: Hashable> (@in T, UnsafeRawPointer) -> @out U,
+      setter @s_set : $@convention(thin) <T: Hashable, U: Hashable> (@in U, @in T, UnsafeRawPointer) -> (),
+      indices [%$0 : $Y : $*Y, %$1 : $X : $*X],
+      indices_equals @s_equals : $@convention(thin) <T: Hashable, U: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool,
+      indices_hash @s_hash : $@convention(thin) <T: Hashable, U: Hashable> (UnsafeRawPointer) -> Int;
+    settable_property $X,
+      id @v_get : $@convention(thin) <T: Hashable, U: Hashable> (@in U, UnsafeRawPointer) -> @out T,
+      getter @v_get : $@convention(thin) <T: Hashable, U: Hashable> (@in U, UnsafeRawPointer) -> @out T,
+      setter @v_set : $@convention(thin) <T: Hashable, U: Hashable> (@in T, @in U, UnsafeRawPointer) -> (),
+      indices [%$2 : $Y : $*Y],
+      indices_equals @s_equals : $@convention(thin) <T: Hashable, U: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool,
+      indices_hash @s_hash : $@convention(thin) <T: Hashable, U: Hashable> (UnsafeRawPointer) -> Int
+  ) <A, B> (%3, %4, %5)
+
+  return undef : $()
+}
+
+sil @s_get : $@convention(thin) <A: Hashable, B: Hashable> (@in A, UnsafeRawPointer) -> @out B
+sil @s_set : $@convention(thin) <A: Hashable, B: Hashable> (@in B, @in A, UnsafeRawPointer) -> ()
+sil @s_equals : $@convention(thin) <A: Hashable, B: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool
+sil @s_hash : $@convention(thin) <A: Hashable, B: Hashable> (UnsafeRawPointer) -> Int
+
+sil @v_get : $@convention(thin) <A: Hashable, B: Hashable> (@in B, UnsafeRawPointer) -> @out A
+sil @v_set : $@convention(thin) <A: Hashable, B: Hashable> (@in A, @in B, UnsafeRawPointer) -> ()
diff --git a/test/IRGen/unowned_objc.sil b/test/IRGen/unowned_objc.sil
index 73ddf86..0d65eb5 100644
--- a/test/IRGen/unowned_objc.sil
+++ b/test/IRGen/unowned_objc.sil
@@ -58,12 +58,12 @@
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 1
   // CHECK-NEXT: store i8** [[PP:%1]], i8*** [[T0]], align 8
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 0
-  // CHECK-NEXT: call void @swift_unknownUnownedInit(%swift.unowned* [[T0]], [[UNKNOWN]]* [[PV:%0]])
+  // CHECK-NEXT: call %swift.unowned* @swift_unknownUnownedInit(%swift.unowned* [[T0]], [[UNKNOWN]]* [[PV:%0]])
   store_unowned %p to [initialization] %x : $*@sil_unowned P
 
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 0
   // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 0
-  // CHECK-NEXT: call void @swift_unknownUnownedCopyInit(%swift.unowned* [[T0]], %swift.unowned* [[T1]])
+  // CHECK-NEXT: call %swift.unowned* @swift_unknownUnownedCopyInit(%swift.unowned* [[T0]], %swift.unowned* [[T1]])
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 1
   // CHECK-NEXT: [[WT:%.*]] = load i8**, i8*** [[T0]], align 8
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 1
@@ -81,7 +81,7 @@
 
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 0
   // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 0
-  // CHECK-NEXT: call void @swift_unknownUnownedCopyAssign(%swift.unowned* [[T0]], %swift.unowned* [[T1]])
+  // CHECK-NEXT: call %swift.unowned* @swift_unknownUnownedCopyAssign(%swift.unowned* [[T0]], %swift.unowned* [[T1]])
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 1
   // CHECK-NEXT: [[WT:%.*]] = load i8**, i8*** [[T0]], align 8
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 1
@@ -91,12 +91,12 @@
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 1
   // CHECK-NEXT: store i8** [[QP:%3]], i8*** [[T0]], align 8
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 0
-  // CHECK-NEXT: call void @swift_unknownUnownedAssign(%swift.unowned* [[T0]], [[UNKNOWN]]* [[QV:%2]])
+  // CHECK-NEXT: call %swift.unowned* @swift_unknownUnownedAssign(%swift.unowned* [[T0]], [[UNKNOWN]]* [[QV:%2]])
   store_unowned %q to %y : $*@sil_unowned P
 
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 0
   // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 0
-  // CHECK-NEXT: call void @swift_unknownUnownedTakeAssign(%swift.unowned* [[T0]], %swift.unowned* [[T1]])
+  // CHECK-NEXT: call %swift.unowned* @swift_unknownUnownedTakeAssign(%swift.unowned* [[T0]], %swift.unowned* [[T1]])
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 1
   // CHECK-NEXT: [[WT:%.*]] = load i8**, i8*** [[T0]], align 8
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 1
@@ -105,7 +105,7 @@
 
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 0
   // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 0
-  // CHECK-NEXT: call void @swift_unknownUnownedTakeInit(%swift.unowned* [[T0]], %swift.unowned* [[T1]])
+  // CHECK-NEXT: call %swift.unowned* @swift_unknownUnownedTakeInit(%swift.unowned* [[T0]], %swift.unowned* [[T1]])
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[Y]], i32 0, i32 1
   // CHECK-NEXT: [[WT:%.*]] = load i8**, i8*** [[T0]], align 8
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 1
@@ -121,7 +121,7 @@
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 1
   // CHECK-NEXT: store i8** [[TP]], i8*** [[T0]], align 8
   // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[UREF]], [[UREF]]* [[X]], i32 0, i32 0
-  // CHECK-NEXT: call void @swift_unknownUnownedInit(%swift.unowned* [[T0]], [[UNKNOWN]]* [[TV]])
+  // CHECK-NEXT: call %swift.unowned* @swift_unknownUnownedInit(%swift.unowned* [[T0]], [[UNKNOWN]]* [[TV]])
   store_unowned %t1 to [initialization] %x : $*@sil_unowned P
 
   // CHECK-NEXT: call void @swift_unknownRelease([[UNKNOWN]]* [[TV]])
diff --git a/test/IRGen/weak.sil b/test/IRGen/weak.sil
index f9ebfdf..b4270f2 100644
--- a/test/IRGen/weak.sil
+++ b/test/IRGen/weak.sil
@@ -47,9 +47,9 @@
 // CHECK-NEXT: [[T0:%.*]] = call [[C]]* bitcast ([[REF]]* ([[WEAK]]*)* @swift_weakLoadStrong to [[C]]* ([[WEAK]]*)*)([[WEAK]]* [[X]])
 // CHECK-NEXT: %3 = ptrtoint  %T4weak1CC* %2 to i64
 // CHECK-NEXT: %4 = inttoptr
-// CHECK-NEXT: call void bitcast ([[WEAK]]* ([[WEAK]]*, [[REF]]*)* @swift_weakAssign to void ([[WEAK]]*, [[C]]*)*)([[WEAK]]* [[X]], [[C]]* %4)
-// CHECK-NEXT: %5 = inttoptr i64 %3 to %swift.refcounted*
-// CHECK-NEXT: call void @swift_rt_swift_release([[REF]]* %5)
+// CHECK-NEXT: call [[WEAK]]* bitcast ([[WEAK]]* ([[WEAK]]*, [[REF]]*)* @swift_weakAssign to [[WEAK]]* ([[WEAK]]*, [[C]]*)*)([[WEAK]]* [[X]], [[C]]* %4)
+// CHECK-NEXT: %6 = inttoptr i64 %3 to %swift.refcounted*
+// CHECK-NEXT: call void @swift_rt_swift_release([[REF]]* %6)
 // CHECK-NEXT: ret void
 
 struct B {
@@ -76,7 +76,7 @@
 // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds { [[WEAK]], i8** }, { [[WEAK]], i8** }* [[X]], i32 0, i32 1
 // CHECK-NEXT: store i8** [[TMPTAB]], i8*** [[T0]], align 8
 // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds { [[WEAK]], i8** }, { [[WEAK]], i8** }* [[X]], i32 0, i32 0
-// CHECK-NEXT: call void @swift_unknownWeakAssign([[WEAK]]* [[T0]], [[UNKNOWN]]* [[TMPOBJ]])
+// CHECK-NEXT: call [[WEAK]]* @swift_unknownWeakAssign([[WEAK]]* [[T0]], [[UNKNOWN]]* [[TMPOBJ]])
 // CHECK: call void @_T0SqWe
 
 sil @test_weak_alloc_stack : $@convention(thin) (Optional<P>) -> () {
@@ -95,7 +95,7 @@
 // CHECK: [[T0:%.*]] = getelementptr inbounds { [[WEAK]], i8** }, { [[WEAK]], i8** }* [[X]], i32 0, i32 1
 // CHECK-NEXT: store i8** [[TMPTAB:%.*]], i8*** [[T0]], align 8
 // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds { [[WEAK]], i8** }, { [[WEAK]], i8** }* [[X]], i32 0, i32 0
-// CHECK-NEXT: call void @swift_unknownWeakInit([[WEAK]]* [[T0]], [[UNKNOWN]]* [[TMPOBJ:%.*]])
+// CHECK-NEXT: call [[WEAK]]* @swift_unknownWeakInit([[WEAK]]* [[T0]], [[UNKNOWN]]* [[TMPOBJ:%.*]])
 // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds { [[WEAK]], i8** }, { [[WEAK]], i8** }* [[X]], i32 0, i32 0
 // CHECK-NEXT: call void @swift_unknownWeakDestroy([[WEAK]]* [[T0]])
 // CHECK-NEXT: bitcast
diff --git a/test/IRGen/weak_class_protocol.sil b/test/IRGen/weak_class_protocol.sil
index ad60a85..75a1877 100644
--- a/test/IRGen/weak_class_protocol.sil
+++ b/test/IRGen/weak_class_protocol.sil
@@ -14,7 +14,7 @@
 // CHECK:         [[WTABLE_SLOT:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 1
 // CHECK:         store i8** [[WTABLE]], i8*** [[WTABLE_SLOT]], align 8
 // CHECK:         [[INSTANCE_SLOT:%.*]] = getelementptr inbounds { %swift.weak, i8** }, { %swift.weak, i8** }* %0, i32 0, i32 0
-// CHECK-objc:    call void @swift_unknownWeakAssign(%swift.weak* [[INSTANCE_SLOT]], %objc_object* [[INSTANCE]]) {{#[0-9]+}}
+// CHECK-objc:    call %swift.weak* @swift_unknownWeakAssign(%swift.weak* [[INSTANCE_SLOT]], %objc_object* [[INSTANCE]]) {{#[0-9]+}}
 // CHECK-native:  call %swift.weak* @swift_weakAssign(%swift.weak* [[INSTANCE_SLOT]], %swift.refcounted* [[INSTANCE]]) {{#[0-9]+}}
 // CHECK:         ret void
 // CHECK:       }
diff --git a/test/SIL/Parser/keypath.sil b/test/SIL/Parser/keypath.sil
index 6fb730a..a8b99ff 100644
--- a/test/SIL/Parser/keypath.sil
+++ b/test/SIL/Parser/keypath.sil
@@ -4,12 +4,15 @@
 
 import Swift
 
-struct S {
+struct S: Hashable {
   var x: Int
   let y: String
   var z: C
+
+  var hashValue: Int { get }
+  static func ==(x: S, y: S) -> Bool
 }
-class C {
+class C: Hashable {
   final var x: Int
   final let y: String
   final var z: S
@@ -18,6 +21,9 @@
   var overridable: Int {
     get set
   }
+
+  var hashValue: Int { get }
+  static func ==(x: C, y: C) -> Bool
 }
 
 protocol P {}
@@ -63,6 +69,15 @@
 sil @set_c_int : $@convention(thin) (@in Int, @in C) -> ()
 sil @get_fns_fnc : $@convention(thin) (@in @callee_owned (@in S) -> @out S) -> @out @callee_owned (@in C) -> @out C
 sil @set_fns_fnc : $@convention(thin) (@in @callee_owned (@in C) -> @out C, @in @callee_owned (@in S) -> @out S) -> ()
+sil @get_s_int_subs : $@convention(thin) (@in S, UnsafeRawPointer) -> @out Int
+sil @set_s_int_subs : $@convention(thin) (@in Int, @in S, UnsafeRawPointer) -> ()
+sil @subs_eq : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool
+sil @subs_hash : $@convention(thin) (UnsafeRawPointer) -> Int
+sil @get_gen_int_subs : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (@in A, UnsafeRawPointer) -> @out C
+sil @set_gen_int_subs : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (@in C, @in A, UnsafeRawPointer) -> ()
+sil @gen_subs_eq : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool
+sil @gen_subs_hash : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (UnsafeRawPointer) -> Int
+
 
 // CHECK-LABEL: sil shared @computed_properties
 sil shared @computed_properties : $@convention(thin) () -> () {
@@ -101,3 +116,23 @@
 
   return undef : $()
 }
+
+// CHECK-LABEL: sil @indexes
+sil @indexes : $@convention(thin) (S, C) -> () {
+// CHECK: bb0([[S:%.*]] : $S, [[C:%.*]] : $C):
+entry(%s : $S, %c : $C):
+  // CHECK: keypath $KeyPath<S, Int>, (root $S; settable_property $Int, id @id_a : $@convention(thin) () -> (), getter @get_s_int_subs : $@convention(thin) (@in S, UnsafeRawPointer) -> @out Int, setter @set_s_int_subs : $@convention(thin) (@in Int, @in S, UnsafeRawPointer) -> (), indices [%$0 : $S : $S, %$1 : $C : $C], indices_equals @subs_eq : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @subs_hash : $@convention(thin) (UnsafeRawPointer) -> Int) ([[S]], [[C]])
+  %a = keypath $KeyPath<S, Int>, (root $S; settable_property $Int, id @id_a : $@convention(thin) () -> (), getter @get_s_int_subs : $@convention(thin) (@in S, UnsafeRawPointer) -> @out Int, setter @set_s_int_subs : $@convention(thin) (@in Int, @in S, UnsafeRawPointer) -> (), indices [%$0 : $S : $S, %$1 : $C : $C], indices_equals @subs_eq : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @subs_hash : $@convention(thin) (UnsafeRawPointer) -> Int) (%s, %c)
+  // CHECK: [[T:%.*]] = alloc_stack
+  %t = alloc_stack $S
+  // CHECK: [[D:%.*]] = alloc_stack
+  %d = alloc_stack $C
+  // CHECK: keypath $KeyPath<S, Int>, <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (root $τ_0_0; settable_property $τ_0_2, id @id_a : $@convention(thin) () -> (), getter @get_gen_int_subs : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (@in τ_0_0, UnsafeRawPointer) -> @out τ_0_2, setter @set_gen_int_subs : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (@in τ_0_2, @in τ_0_0, UnsafeRawPointer) -> (), indices [%$0 : $τ_0_0 : $*τ_0_0, %$1 : $τ_0_1 : $*τ_0_1], indices_equals @gen_subs_eq : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @gen_subs_hash : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (UnsafeRawPointer) -> Int) <S, C, Int> ([[T]], [[D]])
+  %b = keypath $KeyPath<S, Int>, <τ_0_0: Hashable, Y: Hashable, Z: Hashable> (root $τ_0_0; settable_property $Z, id @id_a : $@convention(thin) () -> (), getter @get_gen_int_subs : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (@in A, UnsafeRawPointer) -> @out C, setter @set_gen_int_subs : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (@in C, @in A, UnsafeRawPointer) -> (), indices [%$0 : $τ_0_0 : $*τ_0_0, %$1 : $Y : $*Y], indices_equals @gen_subs_eq : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @gen_subs_hash : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (UnsafeRawPointer) -> Int) <S, C, Int> (%t, %d)
+
+  dealloc_stack %d : $*C
+  dealloc_stack %t : $*S
+
+  return undef : $()
+}
+
diff --git a/test/SIL/Serialization/keypath.sil b/test/SIL/Serialization/keypath.sil
index cfed287..53989c9 100644
--- a/test/SIL/Serialization/keypath.sil
+++ b/test/SIL/Serialization/keypath.sil
@@ -9,12 +9,15 @@
 
 import Swift
 
-struct S {
+struct S: Hashable {
   var x: Int
   let y: String
   var z: C
+
+  var hashValue: Int { get }
+  static func ==(x: S, y: S) -> Bool
 }
-class C {
+class C: Hashable {
   final var x: Int
   final let y: String
   final var z: S
@@ -23,6 +26,9 @@
   var overridable: Int {
     get set
   }
+
+  var hashValue: Int { get }
+  static func ==(x: C, y: C) -> Bool
 }
 
 protocol P {}
@@ -68,6 +74,14 @@
 sil @set_c_int : $@convention(thin) (@in Int, @in C) -> ()
 sil @get_fns_fnc : $@convention(thin) (@in @callee_owned (@in S) -> @out S) -> @out @callee_owned (@in C) -> @out C
 sil @set_fns_fnc : $@convention(thin) (@in @callee_owned (@in C) -> @out C, @in @callee_owned (@in S) -> @out S) -> ()
+sil @get_s_int_subs : $@convention(thin) (@in S, UnsafeRawPointer) -> @out Int
+sil @set_s_int_subs : $@convention(thin) (@in Int, @in S, UnsafeRawPointer) -> ()
+sil @subs_eq : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool
+sil @subs_hash : $@convention(thin) (UnsafeRawPointer) -> Int
+sil @get_gen_int_subs : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (@in A, UnsafeRawPointer) -> @out C
+sil @set_gen_int_subs : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (@in C, @in A, UnsafeRawPointer) -> ()
+sil @gen_subs_eq : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool
+sil @gen_subs_hash : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (UnsafeRawPointer) -> Int
 
 // CHECK-LABEL: sil shared @computed_properties
 sil shared @computed_properties : $@convention(thin) () -> () {
@@ -107,6 +121,25 @@
   return undef : $()
 }
 
+// CHECK-LABEL: sil shared @indexes
+sil shared @indexes : $@convention(thin) (S, C) -> () {
+// CHECK: bb0([[S:%.*]] : $S, [[C:%.*]] : $C):
+entry(%s : $S, %c : $C):
+  // CHECK: keypath $KeyPath<S, Int>, (root $S; settable_property $Int, id @id_a : $@convention(thin) () -> (), getter @get_s_int_subs : $@convention(thin) (@in S, UnsafeRawPointer) -> @out Int, setter @set_s_int_subs : $@convention(thin) (@in Int, @in S, UnsafeRawPointer) -> (), indices [%$0 : $S : $S, %$1 : $C : $C], indices_equals @subs_eq : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @subs_hash : $@convention(thin) (UnsafeRawPointer) -> Int) ([[S]], [[C]])
+  %a = keypath $KeyPath<S, Int>, (root $S; settable_property $Int, id @id_a : $@convention(thin) () -> (), getter @get_s_int_subs : $@convention(thin) (@in S, UnsafeRawPointer) -> @out Int, setter @set_s_int_subs : $@convention(thin) (@in Int, @in S, UnsafeRawPointer) -> (), indices [%$0 : $S : $S, %$1 : $C : $C], indices_equals @subs_eq : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @subs_hash : $@convention(thin) (UnsafeRawPointer) -> Int) (%s, %c)
+  // CHECK: [[T:%.*]] = alloc_stack
+  %t = alloc_stack $S
+  // CHECK: [[D:%.*]] = alloc_stack
+  %d = alloc_stack $C
+  // CHECK: keypath $KeyPath<S, Int>, <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (root $τ_0_0; settable_property $τ_0_2, id @id_a : $@convention(thin) () -> (), getter @get_gen_int_subs : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (@in τ_0_0, UnsafeRawPointer) -> @out τ_0_2, setter @set_gen_int_subs : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (@in τ_0_2, @in τ_0_0, UnsafeRawPointer) -> (), indices [%$0 : $τ_0_0 : $*τ_0_0, %$1 : $τ_0_1 : $*τ_0_1], indices_equals @gen_subs_eq : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @gen_subs_hash : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : Hashable, τ_0_1 : Hashable, τ_0_2 : Hashable> (UnsafeRawPointer) -> Int) <S, C, Int> ([[T]], [[D]])
+  %b = keypath $KeyPath<S, Int>, <τ_0_0: Hashable, Y: Hashable, Z: Hashable> (root $τ_0_0; settable_property $Z, id @id_a : $@convention(thin) () -> (), getter @get_gen_int_subs : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (@in A, UnsafeRawPointer) -> @out C, setter @set_gen_int_subs : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (@in C, @in A, UnsafeRawPointer) -> (), indices [%$0 : $τ_0_0 : $*τ_0_0, %$1 : $Y : $*Y], indices_equals @gen_subs_eq : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @gen_subs_hash : $@convention(thin) <A: Hashable, B: Hashable, C: Hashable> (UnsafeRawPointer) -> Int) <S, C, Int> (%t, %d)
+
+  dealloc_stack %d : $*C
+  dealloc_stack %t : $*S
+
+  return undef : $()
+}
+
 sil @serialize_all : $@convention(thin) () -> () {
 entry:
   %0 = function_ref @stored_properties : $@convention(thin) () -> ()
@@ -114,6 +147,7 @@
   %2 = function_ref @computed_properties : $@convention(thin) () -> ()
   %3 = function_ref @computed_properties_generic : $@convention(thin) <D: P, E: Q, F: R> () -> ()
   %4 = function_ref @optional : $@convention(thin) () -> ()
+  %5 = function_ref @indexes : $@convention(thin) (S, C) -> ()
 
   unreachable
 }
diff --git a/test/SILGen/Inputs/default_arguments_other.swift b/test/SILGen/Inputs/default_arguments_other.swift
new file mode 100644
index 0000000..a2a71e0
--- /dev/null
+++ b/test/SILGen/Inputs/default_arguments_other.swift
@@ -0,0 +1 @@
+public func otherDefaultArguments(x: Int = 0) {}
diff --git a/test/SILGen/default_arguments.swift b/test/SILGen/default_arguments.swift
index f2ec010..75eb08a 100644
--- a/test/SILGen/default_arguments.swift
+++ b/test/SILGen/default_arguments.swift
@@ -1,5 +1,8 @@
-// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-silgen %s | %FileCheck %s
-// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-silgen %s | %FileCheck %s --check-prefix=NEGATIVE
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-silgen -swift-version 3 %s | %FileCheck %s
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-silgen -swift-version 3 %s | %FileCheck %s --check-prefix=NEGATIVE
+
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-silgen -swift-version 4 %s | %FileCheck %s
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-silgen -swift-version 4 %s | %FileCheck %s --check-prefix=NEGATIVE
 
 // __FUNCTION__ used as top-level parameter produces the module name.
 // CHECK-LABEL: sil @main
diff --git a/test/SILGen/default_arguments_serialized.swift b/test/SILGen/default_arguments_serialized.swift
new file mode 100644
index 0000000..3391474
--- /dev/null
+++ b/test/SILGen/default_arguments_serialized.swift
@@ -0,0 +1,60 @@
+// RUN: %empty-directory(%t)
+// RUN: %target-swift-frontend -emit-module-path %t/default_arguments_other.swiftmodule -emit-module -swift-version 4 -primary-file %S/Inputs/default_arguments_other.swift
+
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-silgen -swift-version 3 -I %t %s | %FileCheck %s --check-prefix=SWIFT3 --check-prefix=CHECK
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-silgen -swift-version 4 -I %t %s | %FileCheck %s --check-prefix=SWIFT4 --check-prefix=CHECK
+
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-sil -O -swift-version 3 -I %t %s | %FileCheck %s --check-prefix=OPT
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -enable-sil-ownership -emit-sil -O -swift-version 4 -I %t %s | %FileCheck %s --check-prefix=OPT
+
+// Check that default arguments are serialized in Swift 4 mode.
+
+import default_arguments_other
+
+// CHECK-LABEL: sil @_T028default_arguments_serialized0A6StringSSyF : $@convention(thin) () -> @owned String
+public func defaultString() -> String { return "hi" }
+
+// SWIFT3-LABEL: sil @_T028default_arguments_serialized19hasDefaultArgumentsySi1x_SS1ytFfA_ : $@convention(thin) () -> Int
+// SWIFT4-LABEL: sil [serialized] @_T028default_arguments_serialized19hasDefaultArgumentsySi1x_SS1ytFfA_ : $@convention(thin) () -> Int
+
+// SWIFT3-LABEL: sil @_T028default_arguments_serialized19hasDefaultArgumentsySi1x_SS1ytFfA0_ : $@convention(thin) () -> @owned String
+// SWIFT4-LABEL: sil [serialized] @_T028default_arguments_serialized19hasDefaultArgumentsySi1x_SS1ytFfA0_ : $@convention(thin) () -> @owned String
+
+public func hasDefaultArguments(x: Int = 0, y: String = defaultString()) {}
+
+// CHECK-LABEL: sil @_T028default_arguments_serialized21callsDefaultArgumentsyyF : $@convention(thin) () -> ()
+// CHECK: function_ref @_T028default_arguments_serialized19hasDefaultArgumentsySi1x_SS1ytF : $@convention(thin) (Int, @owned String) -> ()
+// CHECK: function_ref @_T028default_arguments_serialized19hasDefaultArgumentsySi1x_SS1ytFfA_ : $@convention(thin) () -> Int
+// CHECK: function_ref @_T028default_arguments_serialized19hasDefaultArgumentsySi1x_SS1ytFfA0_ : $@convention(thin) () -> @owned String
+// CHECK: apply
+// CHECK: return
+public func callsDefaultArguments() {
+  hasDefaultArguments()
+}
+
+// When calling a default argument generator for a function in another module
+// that was built in Swift 4 mode, we should always treat it as serialized,
+// even if *this* module is built in Swift 3 mode.
+
+// CHECK-LABEL: sil @_T028default_arguments_serialized26callsOtherDefaultArgumentsyyF : $@convention(thin) () -> ()
+// CHECK: function_ref @_T023default_arguments_other0C16DefaultArgumentsySi1x_tF : $@convention(thin) (Int) -> ()
+// CHECK: function_ref @_T023default_arguments_other0C16DefaultArgumentsySi1x_tFfA_ : $@convention(thin) () -> Int
+// CHECK: apply
+// CHECK: return
+
+// Make sure the optimizer inlines the default argument generator from the
+// other module.
+
+// OPT-LABEL: sil @_T028default_arguments_serialized26callsOtherDefaultArgumentsyyF : $@convention(thin) () -> ()
+// OPT: [[FN:%.*]] = function_ref @_T023default_arguments_other0C16DefaultArgumentsySi1x_tF : $@convention(thin) (Int) -> () // user: %3
+// OPT: [[INT_VAL:%.*]] = integer_literal [[INT_TYPE:\$Builtin.Int(32|64)]], 0
+// OPT: [[INT:%.*]] = struct $Int ([[INT_VAL]] : [[INT_TYPE]]
+// OPT: apply [[FN]]([[INT]]) : $@convention(thin) (Int) -> ()
+// OPT: return
+public func callsOtherDefaultArguments() {
+  otherDefaultArguments()
+}
+
+// CHECK-LABEL: sil @_T023default_arguments_other0C16DefaultArgumentsySi1x_tF : $@convention(thin) (Int) -> ()
+
+// CHECK-LABEL: sil [serialized] @_T023default_arguments_other0C16DefaultArgumentsySi1x_tFfA_ : $@convention(thin) () -> Int
diff --git a/test/SILGen/keypath_application.swift b/test/SILGen/keypath_application.swift
index 4df7f8e..88cd272 100644
--- a/test/SILGen/keypath_application.swift
+++ b/test/SILGen/keypath_application.swift
@@ -1,4 +1,4 @@
-// RUN: %target-swift-frontend -enable-experimental-keypath-components -emit-silgen -enable-sil-ownership %s | %FileCheck %s
+// RUN: %target-swift-frontend -emit-silgen -enable-sil-ownership %s | %FileCheck %s
 
 class A {}
 class B {}
diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift
index 8ba3f4f..4f7247b 100644
--- a/test/SILGen/keypaths.swift
+++ b/test/SILGen/keypaths.swift
@@ -1,4 +1,4 @@
-// RUN: %target-swift-frontend -enable-experimental-keypath-components -emit-silgen -enable-sil-ownership %s | %FileCheck %s
+// RUN: %target-swift-frontend -emit-silgen -enable-sil-ownership %s | %FileCheck %s
 
 struct S<T> {
   var x: T
@@ -253,7 +253,7 @@
   }
 }
 
-// CHECK-LABEL: sil hidden @{{.*}}iuoKeyPaths
+// CHECK-LABEL: sil hidden @{{.*}}11iuoKeyPaths
 func iuoKeyPaths() {
   // CHECK: = keypath $WritableKeyPath<IUOProperty, Int>,
   // CHECK-SAME: stored_property #IUOProperty.iuo
@@ -266,3 +266,59 @@
   // CHECK-SAME: stored_property #IUOBlob.x
   _ = \IUOProperty.iuo!.x
 }
+
+struct Subscripts<T> {
+  subscript() -> T {
+    get { fatalError() }
+    set { fatalError() }
+  }
+  subscript(generic x: T) -> T {
+    get { fatalError() }
+    set { fatalError() }
+  }
+  subscript(concrete x: String) -> String {
+    get { fatalError() }
+    set { fatalError() }
+  }
+  subscript(x: String, y: String) -> String {
+    get { fatalError() }
+    set { fatalError() }
+  }
+  subscript<U>(subGeneric z: U) -> U {
+    get { fatalError() }
+    set { fatalError() }
+  }
+  subscript(mutable x: T) -> T {
+    get { fatalError() }
+    set { fatalError() }
+  }
+}
+
+// CHECK-LABEL: sil hidden @{{.*}}10subscripts
+func subscripts<T: Hashable, U: Hashable>(x: T, y: U, s: String) {
+  _ = \Subscripts<T>.[]
+  _ = \Subscripts<T>.[generic: x]
+  _ = \Subscripts<T>.[concrete: s]
+  _ = \Subscripts<T>.[s, s]
+  _ = \Subscripts<T>.[subGeneric: s]
+  _ = \Subscripts<T>.[subGeneric: x]
+  _ = \Subscripts<T>.[subGeneric: y]
+
+  _ = \Subscripts<U>.[]
+  _ = \Subscripts<U>.[generic: y]
+  _ = \Subscripts<U>.[concrete: s]
+  _ = \Subscripts<U>.[s, s]
+  _ = \Subscripts<U>.[subGeneric: s]
+  _ = \Subscripts<U>.[subGeneric: x]
+  _ = \Subscripts<U>.[subGeneric: y]
+
+  _ = \Subscripts<String>.[]
+  _ = \Subscripts<String>.[generic: s]
+  _ = \Subscripts<String>.[concrete: s]
+  _ = \Subscripts<String>.[s, s]
+  _ = \Subscripts<String>.[subGeneric: s]
+  _ = \Subscripts<String>.[subGeneric: x]
+  _ = \Subscripts<String>.[subGeneric: y]
+
+  _ = \Subscripts<T>.[s, s].count
+}
diff --git a/test/SILGen/keypaths_objc.swift b/test/SILGen/keypaths_objc.swift
index 8bbaf94..90b29fc 100644
--- a/test/SILGen/keypaths_objc.swift
+++ b/test/SILGen/keypaths_objc.swift
@@ -1,5 +1,5 @@
-// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-keypath-components -emit-silgen -import-objc-header %S/Inputs/keypaths_objc.h %s | %FileCheck %s
-// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-keypath-components -emit-ir -import-objc-header %S/Inputs/keypaths_objc.h %s
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -import-objc-header %S/Inputs/keypaths_objc.h %s | %FileCheck %s
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-ir -import-objc-header %S/Inputs/keypaths_objc.h %s
 // REQUIRES: objc_interop
 
 import Foundation
diff --git a/test/SILOptimizer/closure_specialize_opaque.sil b/test/SILOptimizer/closure_specialize_opaque.sil
new file mode 100644
index 0000000..840a9d6
--- /dev/null
+++ b/test/SILOptimizer/closure_specialize_opaque.sil
@@ -0,0 +1,42 @@
+// RUN: %target-sil-opt -enable-sil-opaque-values -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -closure-specialize %s | %FileCheck %s
+
+struct TestView {}
+struct TestRange {}
+struct TestSlice {}
+
+// helper
+sil @closure : $@convention(thin) (@inout TestView, TestRange, @in TestSlice) -> () {
+bb0(%0 : $*TestView, %1 : $TestRange, %2 : $TestSlice):
+  %1284 = tuple ()
+  return %1284 : $()
+}
+
+// helper
+sil @thunk : $@convention(thin) (@inout TestView, @owned @callee_owned (@inout TestView) -> ()) -> @out () {
+bb0(%0 : $*TestView, %1 : $@callee_owned (@inout TestView) ->()):
+  %call = apply %1(%0) : $@callee_owned (@inout TestView) -> ()
+  %1284 = tuple ()
+  return %1284 : $()
+}
+
+// Test that ClosureSpecializer can handle captured @in args, in addition to direct args.
+//
+// CHECK-LABEL: sil @testSpecializeThunk : $@convention(thin) (@inout TestView, TestRange, @in TestSlice) -> () {
+// CHECK: bb0(%0 : $*TestView, %1 : $TestRange, %2 : $TestSlice):
+// CHECK:   [[CLOSURE:%.*]] = function_ref @closure : $@convention(thin) (@inout TestView, TestRange, @in TestSlice) -> ()
+// CHECK:   [[SPECIALIZED:%.*]] = function_ref @_T05thunk7closure4main9TestRangeVAC0D5SliceVTf1nc_n : $@convention(thin) (@inout TestView, TestRange, @in TestSlice) -> @out () // user: %6
+// CHECK:   [[THUNK:%.*]] = function_ref @thunk : $@convention(thin) (@inout TestView, @owned @callee_owned (@inout TestView) -> ()) -> @out ()
+// CHECK:   [[CALL:%.*]] = apply [[SPECIALIZED]](%0, %1, %2) : $@convention(thin) (@inout TestView, TestRange, @in TestSlice) -> @out ()
+// CHECK:   %{{.*}} = tuple ()
+// CHECK:   return %{{.*}} : $()
+// CHECK-LABEL: } // end sil function 'testSpecializeThunk'
+sil @testSpecializeThunk : $@convention(thin) (@inout TestView, TestRange, @in TestSlice) -> () {
+bb0(%0 : $*TestView, %1 : $TestRange, %2 : $TestSlice):
+  %closurefn = function_ref @closure : $@convention(thin) (@inout TestView, TestRange, @in TestSlice) -> ()
+  %pa = partial_apply %closurefn(%1, %2) : $@convention(thin) (@inout TestView, TestRange, @in TestSlice) -> ()
+  %thunk = function_ref @thunk : $@convention(thin) (@inout TestView, @owned @callee_owned (@inout TestView) -> ()) -> @out ()
+  strong_retain %pa : $@callee_owned (@inout TestView) -> ()
+  %call = apply %thunk(%0, %pa) : $@convention(thin) (@inout TestView, @owned @callee_owned (@inout TestView) -> ()) -> @out ()
+  %1284 = tuple ()
+  return %1284 : $()
+}
diff --git a/test/SILOptimizer/funcsig_opaque.sil b/test/SILOptimizer/funcsig_opaque.sil
new file mode 100644
index 0000000..bcd8f47
--- /dev/null
+++ b/test/SILOptimizer/funcsig_opaque.sil
@@ -0,0 +1,44 @@
+// RUN: %target-sil-opt -enable-sil-opaque-values -assume-parsing-unqualified-ownership-sil -function-signature-opts %s | %FileCheck %s
+
+import Builtin
+
+struct R<T> {
+  var base: T
+}
+
+struct S {
+  var val: Builtin.Int32
+}
+
+sil @testAggregateArgHelper : $@convention(thin) (@owned R<S>) -> ()
+
+// Test owned-to-guaranteed transformation of opaque arguments.
+//
+// CHECK-LABEL: sil [thunk] [always_inline] @testAggregateArg : $@convention(thin) (@in R<S>) -> @out () {
+// CHECK: bb0(%0 : $R<S>):
+// CHECK:   [[F:%.*]] = function_ref @_T016testAggregateArgTf4g_n : $@convention(thin) (@in_guaranteed R<S>) -> @out ()
+// CHECK:   [[CALL:%.*]] = apply [[F]](%0) : $@convention(thin) (@in_guaranteed R<S>) -> @out ()
+// CHECK:   release_value %0 : $R<S>
+// CHECK:   return [[CALL]] : $()
+// CHECK-LABEL: } // end sil function 'testAggregateArg'
+//
+// CHECK-LABEL: sil shared @_T016testAggregateArgTf4g_n : $@convention(thin) (@in_guaranteed R<S>) -> @out () {
+// CHECK: bb0(%0 : $R<S>):
+// CHECK:   [[COPY:%.*]] = copy_value %0 : $R<S>
+// CHECK:   retain_value %0 : $R<S>
+// CHECK:   [[F:%.*]] = function_ref @testAggregateArgHelper : $@convention(thin) (@owned R<S>) -> ()
+// CHECK:   [[CALL:%.*]] = apply [[F]]([[COPY]]) : $@convention(thin) (@owned R<S>) -> ()
+// CHECK:   destroy_value %0 : $R<S>
+// CHECK:   return %{{.*}} : $()
+// CHECK-LABEL: } // end sil function '_T016testAggregateArgTf4g_n'
+sil @testAggregateArg : $@convention(thin) (@in R<S>) -> @out () {
+bb0(%0 : $R<S>):
+  %9 = copy_value %0 : $R<S>
+  retain_value %0 : $R<S>
+  %10 = function_ref @testAggregateArgHelper : $@convention(thin) (@owned R<S>) -> ()
+  %12 = apply %10(%9) : $@convention(thin) (@owned R<S>) -> ()
+  destroy_value %0 : $R<S>
+  release_value %0 : $R<S>
+  %1284 = tuple ()
+  return %1284 : $()
+}
diff --git a/test/SILOptimizer/sil_combine_opaque.sil b/test/SILOptimizer/sil_combine_opaque.sil
new file mode 100644
index 0000000..e99bbf9
--- /dev/null
+++ b/test/SILOptimizer/sil_combine_opaque.sil
@@ -0,0 +1,25 @@
+// RUN: %target-sil-opt -enable-sil-opaque-values -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -sil-combine %s | %FileCheck %s
+
+struct S<T> {
+  var t : T
+}
+
+// Test SIL combine's handling of retain_value/release_value.
+//
+// CHECK-LABEL: sil @testSpecializeOpaque : $@convention(thin) <T> (@in S<T>) -> () {
+// CHECK-LABEL: bb0(%0 : $S<T>):
+// CHECK:   retain_value %0 : $S<T>
+// CHECK:   [[F:%.*]] = function_ref @testSpecializeOpaque : $@convention(thin) <τ_0_0> (@in S<τ_0_0>) -> ()
+// CHECK:   [[CALL:%.*]] = apply [[F]]<T>(%0) : $@convention(thin) <τ_0_0> (@in S<τ_0_0>) -> ()
+// CHECK:   release_value %0 : $S<T>
+// CHECK:   return %{{.*}} : $()
+// CHECK-LABEL: } // end sil function 'testSpecializeOpaque'
+sil @testSpecializeOpaque : $@convention(thin) <T> (@in S<T>) -> () {
+bb0(%0 : $S<T>):
+  %retain = retain_value %0 : $S<T>
+  %f = function_ref @testSpecializeOpaque : $@convention(thin) <T> (@in S<T>) -> ()
+  %call = apply %f<T>(%0) : $@convention(thin) <τ_0_0> (@in S<τ_0_0>) -> ()
+  %release = release_value %0 : $S<T>
+  %999 = tuple ()
+  return %999 : $()
+}
diff --git a/test/SourceKit/DocumentStructure/mark_edit.swift.response b/test/SourceKit/DocumentStructure/mark_edit.swift.response
index 62e0e85..de19dc3 100644
--- a/test/SourceKit/DocumentStructure/mark_edit.swift.response
+++ b/test/SourceKit/DocumentStructure/mark_edit.swift.response
@@ -13,8 +13,6 @@
   ]
 }
 {
-  key.offset: 0,
-  key.length: 3,
   key.diagnostic_stage: source.diagnostic.stage.swift.parse,
   key.substructure: [
     {
diff --git a/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-block-comment.swift b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-block-comment.swift
new file mode 100644
index 0000000..d3fe128
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-block-comment.swift
@@ -0,0 +1,5 @@
+  
+let x = /*
+
+*/ 2
+func foo() {}
diff --git a/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-chained-comment.swift b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-chained-comment.swift
new file mode 100644
index 0000000..42a1b54
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-chained-comment.swift
@@ -0,0 +1,5 @@
+let y = /*
+
+*// /*
+
+*/ 2
diff --git a/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-disjoint-effect.swift b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-disjoint-effect.swift
new file mode 100644
index 0000000..b6e9f63
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-disjoint-effect.swift
@@ -0,0 +1,6 @@
+func getIt 
+struct MyStruct {
+  func getChildStruct() -> String {
+    return ""
+  }
+}
diff --git a/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-multiline-string.swift b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-multiline-string.swift
new file mode 100644
index 0000000..85143bd
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-multiline-string.swift
@@ -0,0 +1,12 @@
+let a = "value"
+let x = """
+
+\(
+a
+) 
+
+   
+
+func foo () -> String {
+  return "foo"
+}
diff --git a/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-nested-token.swift b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-nested-token.swift
new file mode 100644
index 0000000..69c1432
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-nested-token.swift
@@ -0,0 +1,19 @@
+/// This function does stuff
+///
+/// - parameter first: The first parameter
+///
+/// - returns: The return value
+func foo(first: Int) -> String {
+  return ""
+}
+
+let x = "Changing this string should only affect this line"
+
+/// This function does other stuff
+///
+/// - parameter first: The first parameter
+///
+/// - returns: The return value
+func bar(first: Int) -> String {
+  return ""
+}
diff --git a/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-remove.swift b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-remove.swift
new file mode 100644
index 0000000..b4ac2a5
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-edit-remove.swift
@@ -0,0 +1,4 @@
+let l = 2
+l
+\( 56
+)
diff --git a/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-multiple-edits.swift b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-multiple-edits.swift
new file mode 100644
index 0000000..6c18bdc
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-multiple-edits.swift
@@ -0,0 +1,14 @@
+ 
+/// A comment
+/// - Note: important things
+let x = 42
+
+struct Point {
+  let x: Int = x
+  let y: Int = x
+
+  func mag2() {
+    return x*x + y*y;
+  }
+}
+
diff --git a/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-partial-delete.swift b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-partial-delete.swift
new file mode 100644
index 0000000..def3663
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/Inputs/syntaxmap-partial-delete.swift
@@ -0,0 +1,2 @@
+
+let x = 421
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-block-comment.swift b/test/SourceKit/SyntaxMapData/syntaxmap-edit-block-comment.swift
new file mode 100644
index 0000000..2304819
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-block-comment.swift
@@ -0,0 +1,98 @@
+// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=4:2 -replace=" " -length=1  == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=4:2 -replace="/" -length=1 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=1:1 -replace="//" -length=2 | %sed_clean > %t.response
+// RUN: %FileCheck -input-file=%t.response %s
+
+// Initial state
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 34,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 3,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 7,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 11,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 18,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 20,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 25,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After removing the '/' from the comment close
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 11,
+// CHECK-NEXT: key.length: 23,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 11,
+// CHECK-NEXT:     key.length: 23
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After adding the '/' back to the comment close
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 11,
+// CHECK-NEXT: key.length: 23,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 11,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 18,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 20,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 25,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// after adding a single-line comment on the line above
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 3,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 0,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-chained-comment.swift b/test/SourceKit/SyntaxMapData/syntaxmap-edit-chained-comment.swift
new file mode 100644
index 0000000..2718461
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-chained-comment.swift
@@ -0,0 +1,70 @@
+// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-chained-comment.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-chained-comment.swift -pos=1:9 -replace=" " -length=1  == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-chained-comment.swift -pos=1:9 -replace="/" -length=1 | %sed_clean > %t.response
+// RUN: %FileCheck -input-file=%t.response %s
+
+// Initial state
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 25,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 0,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 4,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 8,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 16,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 23,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+
+// After replacing the '/' from the comment open with ' '
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 8,
+// CHECK-NEXT: key.length: 14,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 13,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After adding the '/' back to the comment open
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 8,
+// CHECK-NEXT: key.length: 14,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 8,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.comment,
+// CHECK-NEXT:     key.offset: 16,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response
index bc86f00..d0497be 100644
--- a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response
@@ -29,7 +29,7 @@
 }
 {
   key.offset: 1,
-  key.length: 36,
+  key.length: 33,
   key.diagnostic_stage: source.diagnostic.stage.swift.parse,
   key.syntaxmap: [
     {
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2
index ae9b915..4d92f16 100644
--- a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2
@@ -28,8 +28,6 @@
   ]
 }
 {
-  key.offset: 36,
-  key.length: 0,
   key.diagnostic_stage: source.diagnostic.stage.swift.parse,
   key.syntaxmap: [
   ]
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-disjoint-effect.swift b/test/SourceKit/SyntaxMapData/syntaxmap-edit-disjoint-effect.swift
new file mode 100644
index 0000000..5eccc0e
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-disjoint-effect.swift
@@ -0,0 +1,70 @@
+// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-disjoint-effect.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-disjoint-effect.swift -pos=1:11 -replace='(' -length=1 | %sed_clean > %t.response
+// RUN: %FileCheck -input-file=%t.response %s
+
+// Original contents
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 86,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 0,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 5,
+// CHECK-NEXT:     key.length: 5
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 12,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 19,
+// CHECK-NEXT:     key.length: 8
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 32,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 37,
+// CHECK-NEXT:     key.length: 14
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 57,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 70,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 77,
+// CHECK-NEXT:     key.length: 2
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After opening the parameter list (without closing it)
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 12,
+// CHECK-NEXT: key.length: 51,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 12,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   }
+
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-multiline-string.swift b/test/SourceKit/SyntaxMapData/syntaxmap-edit-multiline-string.swift
new file mode 100644
index 0000000..e75fb30
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-multiline-string.swift
@@ -0,0 +1,137 @@
+// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-multiline-string.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-multiline-string.swift -pos=8:1 -replace='"""' -length=3 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-multiline-string.swift -pos=6:2 -replace=')' -length=1 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-multiline-string.swift -pos=2:10 -replace=' ' -length=1 | %sed_clean > %t.response
+// RUN: %FileCheck -input-file=%t.response %s
+
+// Original file contents
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 84,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 0,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 4,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 8,
+// CHECK-NEXT:     key.length: 7
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 16,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 20,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 24,
+// CHECK-NEXT:     key.length: 60
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After terminating the multiline string
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 24,
+// CHECK-NEXT: key.length: 60,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 24,
+// CHECK-NEXT:     key.length: 5
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string_interpolation_anchor,
+// CHECK-NEXT:     key.offset: 30,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 32,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string_interpolation_anchor,
+// CHECK-NEXT:     key.offset: 34,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 35,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 43,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 48,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 58,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 69,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 76,
+// CHECK-NEXT:     key.length: 5
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After adding a character after the interpolation
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 35,
+// CHECK-NEXT: key.length: 6,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 35,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After replacing the middle opening quote with a space
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 24,
+// CHECK-NEXT: key.length: 60,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 24,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 32,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 38,
+// CHECK-NEXT:     key.length: 46
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-nested-token.swift b/test/SourceKit/SyntaxMapData/syntaxmap-edit-nested-token.swift
new file mode 100644
index 0000000..f266bff
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-nested-token.swift
@@ -0,0 +1,200 @@
+// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-nested-token.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-nested-token.swift -pos=10:43 -replace='impact' -length=6 | %sed_clean > %t.response
+// RUN: %FileCheck -input-file=%t.response %s
+
+// Original file contents
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 386,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 0,
+// CHECK-NEXT:     key.length: 29
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 29,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 33,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment.field,
+// CHECK-NEXT:     key.offset: 39,
+// CHECK-NEXT:     key.length: 9
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 48,
+// CHECK-NEXT:     key.length: 28
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 76,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 80,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment.field,
+// CHECK-NEXT:     key.offset: 86,
+// CHECK-NEXT:     key.length: 7
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 93,
+// CHECK-NEXT:     key.length: 19
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 112,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 117,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 121,
+// CHECK-NEXT:     key.length: 5
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 128,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 136,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 147,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 154,
+// CHECK-NEXT:     key.length: 2
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 160,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 164,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 168,
+// CHECK-NEXT:     key.length: 51
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 221,
+// CHECK-NEXT:     key.length: 35
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 256,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 260,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment.field,
+// CHECK-NEXT:     key.offset: 266,
+// CHECK-NEXT:     key.length: 9
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 275,
+// CHECK-NEXT:     key.length: 28
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 303,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 307,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment.field,
+// CHECK-NEXT:     key.offset: 313,
+// CHECK-NEXT:     key.length: 7
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 320,
+// CHECK-NEXT:     key.length: 19
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 339,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 344,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 348,
+// CHECK-NEXT:     key.length: 5
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 355,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 363,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 374,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 381,
+// CHECK-NEXT:     key.length: 2
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After editing a string in between nested comments
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 168,
+// CHECK-NEXT: key.length: 51,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.string,
+// CHECK-NEXT:     key.offset: 168,
+// CHECK-NEXT:     key.length: 51
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-remove.swift b/test/SourceKit/SyntaxMapData/syntaxmap-edit-remove.swift
new file mode 100644
index 0000000..b6314c2
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-remove.swift
@@ -0,0 +1,57 @@
+// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-remove.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-remove.swift -pos=3:3 -length=1 -replace='' == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-remove.swift -pos=2:1 -replace='' -length=1 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-remove.swift -pos=1:9 -length=1 -replace='' | %sed_clean > %t.response
+// RUN: %FileCheck -input-file=%t.response %s
+
+// Initial state
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 20,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 0,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 4,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 8,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 10,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 15,
+// CHECK-NEXT:     key.length: 2
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After removing the space before the '56'
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT: ],
+
+// After deleting the identifier 'l'
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT: ],
+
+// After deleting the last token on the first line
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT: ],
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit.swift.response b/test/SourceKit/SyntaxMapData/syntaxmap-edit.swift.response
index 39c0de8..0f55ca4 100644
--- a/test/SourceKit/SyntaxMapData/syntaxmap-edit.swift.response
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit.swift.response
@@ -41,16 +41,11 @@
   ]
 }
 {
-  key.offset: 81,
-  key.length: 15,
+  key.offset: 87,
+  key.length: 6,
   key.diagnostic_stage: source.diagnostic.stage.swift.parse,
   key.syntaxmap: [
     {
-      key.kind: source.lang.swift.syntaxtype.keyword,
-      key.offset: 81,
-      key.length: 5
-    },
-    {
       key.kind: source.lang.swift.syntaxtype.identifier,
       key.offset: 87,
       key.length: 6
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-multiple-edits.swift b/test/SourceKit/SyntaxMapData/syntaxmap-multiple-edits.swift
new file mode 100644
index 0000000..cf14f45
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-multiple-edits.swift
@@ -0,0 +1,251 @@
+// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-multiple-edits.swift == -req=edit -print-raw-response -pos=6:13 -length=1 -replace=" " %S/Inputs/syntaxmap-multiple-edits.swift == -req="edit" -pos=14:1 -length=0 -replace="let y = 2" -print-raw-response %S/Inputs/syntaxmap-multiple-edits.swift == -req="edit" -pos=8:10 -length=7 -replace='Int64 = 3; let z = 2' -print-raw-response %S/Inputs/syntaxmap-multiple-edits.swift == -req="edit" -pos=4:9 -length=2 -replace='50 * 95 - 100' -print-raw-response %S/Inputs/syntaxmap-multiple-edits.swift == -req="edit" -pos=1:1 -length=0 -replace='func firstFunc(x: Int) {}' -print-raw-response %S/Inputs/syntaxmap-multiple-edits.swift | %sed_clean > %t.response
+// RUN: %FileCheck -input-file=%t.response %s
+
+// Initial syntax map
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 152,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 2,
+// CHECK-NEXT:     key.length: 14
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 16,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment.field,
+// CHECK-NEXT:     key.offset: 22,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.doccomment,
+// CHECK-NEXT:     key.offset: 26,
+// CHECK-NEXT:     key.length: 19
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 45,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 49,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 53,
+// CHECK-NEXT:     key.length: 2
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 57,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 64,
+// CHECK-NEXT:     key.length: 5
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 74,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 78,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 81,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 87,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 91,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 95,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 98,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 104,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 109,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 114,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 127,
+// CHECK-NEXT:     key.length: 6
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 134,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 136,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 140,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 142,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After replacing a space with a space
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT: ],
+
+// After adding code at the end of the file
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 151,
+// CHECK-NEXT: key.length: 9,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 151,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 155,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 159,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+
+// After inserting more than we removed
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 98,
+// CHECK-NEXT: key.length: 20,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 98,
+// CHECK-NEXT:     key.length: 5
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 106,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 109,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 113,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 117,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After inserting less than we removed
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 53,
+// CHECK-NEXT: key.length: 13,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 53,
+// CHECK-NEXT:     key.length: 2
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 58,
+// CHECK-NEXT:     key.length: 2
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 63,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+// After adding code at the start of the file
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 21,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 0,
+// CHECK-NEXT:     key.length: 4
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 5,
+// CHECK-NEXT:     key.length: 9
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 15,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.typeidentifier,
+// CHECK-NEXT:     key.offset: 18,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-partial-delete.swift b/test/SourceKit/SyntaxMapData/syntaxmap-partial-delete.swift
new file mode 100644
index 0000000..3f065ea
--- /dev/null
+++ b/test/SourceKit/SyntaxMapData/syntaxmap-partial-delete.swift
@@ -0,0 +1,39 @@
+// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-partial-delete.swift == -req=edit -print-raw-response -pos=2:10 -length=2 -replace='' %S/Inputs/syntaxmap-partial-delete.swift | %sed_clean > %t.response
+// RUN: %FileCheck -input-file=%t.response %s
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 0,
+// CHECK-NEXT: key.length: 13,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.keyword,
+// CHECK-NEXT:     key.offset: 1,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.identifier,
+// CHECK-NEXT:     key.offset: 5,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 9,
+// CHECK-NEXT:     key.length: 3
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
+
+
+// After removing 2 chars from number literal
+
+// CHECK: {{^}}{
+// CHECK-NEXT: key.offset: 9,
+// CHECK-NEXT: key.length: 1,
+// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
+// CHECK-NEXT: key.syntaxmap: [
+// CHECK-NEXT:   {
+// CHECK-NEXT:     key.kind: source.lang.swift.syntaxtype.number,
+// CHECK-NEXT:     key.offset: 9,
+// CHECK-NEXT:     key.length: 1
+// CHECK-NEXT:   }
+// CHECK-NEXT: ],
diff --git a/test/expr/unary/keypath/keypath-unimplemented.swift b/test/expr/unary/keypath/keypath-unimplemented.swift
index b4fdaa3..4791e87 100644
--- a/test/expr/unary/keypath/keypath-unimplemented.swift
+++ b/test/expr/unary/keypath/keypath-unimplemented.swift
@@ -9,10 +9,6 @@
   var i = 0
 }
 
-func unsupportedComponents() {
-  _ = \A.[0] // expected-error{{key path support for subscript components is not implemented}}
-}
-
 // rdar://problem/32209039 - Improve diagnostic when unsupported tuple element references are used in key path literals
 let _ = \(Int, String).0 // expected-error {{key path cannot reference tuple elements}}
 let _ = \(a: Int, b: String).b // expected-error {{key path cannot reference tuple elements}}
diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift
index fdbb755..f45b9b4 100644
--- a/test/expr/unary/keypath/keypath.swift
+++ b/test/expr/unary/keypath/keypath.swift
@@ -1,10 +1,21 @@
-// RUN: %target-swift-frontend -typecheck -parse-as-library -enable-experimental-keypath-components  %s -verify
+// RUN: %target-swift-frontend -typecheck -parse-as-library %s -verify
 
-struct Sub {}
-struct OptSub {}
+struct Sub: Hashable {
+  static func ==(_: Sub, _: Sub) -> Bool { return true }
+  var hashValue: Int { return 0 }
+}
+struct OptSub: Hashable {
+  static func ==(_: OptSub, _: OptSub) -> Bool { return true }
+  var hashValue: Int { return 0 }
+}
+struct NonHashableSub {}
+
 struct Prop {
   subscript(sub: Sub) -> A { get { return A() } set { } }
   subscript(optSub: OptSub) -> A? { get { return A() } set { } }
+  subscript(nonHashableSub: NonHashableSub) -> A { get { return A() } set { } }
+  subscript(a: Sub, b: Sub) -> A { get { return A() } set { } }
+  subscript(a: Sub, b: NonHashableSub) -> A { get { return A() } set { } }
 
   var nonMutatingProperty: B {
     get { fatalError() }
@@ -27,8 +38,10 @@
 struct B {}
 struct C<T> {
   var value: T
+  subscript() -> T { get { return value } }
   subscript(sub: Sub) -> T { get { return value } set { } }
-  subscript<U>(sub: U) -> U { get { return sub } set { } }
+  subscript<U: Hashable>(sub: U) -> U { get { return sub } set { } }
+  subscript<X>(noHashableConstraint sub: X) -> X { get { return sub } set { } }
 }
 
 extension Array where Element == A {
@@ -39,7 +52,8 @@
 
 func expect<T>(_ x: inout T, toHaveType _: Exactly<T>.Type) {}
 
-func testKeyPath(sub: Sub, optSub: OptSub, x: Int) {
+func testKeyPath(sub: Sub, optSub: OptSub,
+                 nonHashableSub: NonHashableSub, x: Int) {
   var a = \A.property
   expect(&a, toHaveType: Exactly<WritableKeyPath<A, Prop>>.self)
 
@@ -152,6 +166,21 @@
   let _: AnyKeyPath = \.property // expected-error{{ambiguous}}
   let _: AnyKeyPath = \C.value // expected-error{{cannot convert}} (need to improve diagnostic)
   let _: AnyKeyPath = \.value // expected-error{{ambiguous}}
+
+  let _ = \Prop.[nonHashableSub] // expected-error{{subscript index of type 'NonHashableSub' in a key path must be Hashable}}
+  let _ = \Prop.[sub, sub]
+  let _ = \Prop.[sub, nonHashableSub] // expected-error{{subscript index of type 'NonHashableSub' in a key path must be Hashable}}
+
+  let _ = \C<Int>.[]
+  let _ = \C<Int>.[sub]
+  let _ = \C<Int>.[noHashableConstraint: sub]
+  let _ = \C<Int>.[noHashableConstraint: nonHashableSub] // expected-error{{subscript index of type 'NonHashableSub' in a key path must be Hashable}}
+}
+
+func testKeyPathInGenericContext<H: Hashable, X>(hashable: H, anything: X) {
+  let _ = \C<Int>.[hashable]
+  let _ = \C<Int>.[noHashableConstraint: hashable]
+  let _ = \C<Int>.[noHashableConstraint: anything] // expected-error{{subscript index of type 'X' in a key path must be Hashable}}
 }
 
 func testDisembodiedStringInterpolation(x: Int) {
diff --git a/test/lit.cfg b/test/lit.cfg
index 1dbaefc..12a5315 100644
--- a/test/lit.cfg
+++ b/test/lit.cfg
@@ -908,17 +908,24 @@
 source_compiler_rt_libs(os.path.join(test_resource_dir, 'clang', 'lib',
                         platform.system().lower()))
 
-def check_runtime_feature(name):
+def check_runtime_libs(features_to_check):
     for lib in config.compiler_rt_libs:
-        if lib.startswith('libclang_rt.' + name + '_'):
-            config.available_features.add(name + '_runtime')
-            return
+        for (libname, feature) in features_to_check:
+            if lib.startswith("libclang_rt." + libname + "_"):
+                config.available_features.add(feature)
 
-check_runtime_feature('profile')
-check_runtime_feature('asan')
-check_runtime_feature('ubsan')
-check_runtime_feature('tsan')
-check_runtime_feature('safestack')
+runtime_libs = [
+    ('profile', 'profile_runtime'),
+    ('asan', 'asan_runtime'),
+    ('ubsan', 'ubsan_runtime'),
+    ('tsan', 'tsan_runtime'),
+    ('safestack', 'safestack_runtime')
+]
+
+if run_ptrsize != "32":
+    runtime_libs.append(('tsan', 'tsan_runtime'))
+
+check_runtime_libs(runtime_libs)
 
 if not getattr(config, 'target_run_simple_swift', None):
     config.target_run_simple_swift = (
diff --git a/test/stdlib/KeyPath.swift b/test/stdlib/KeyPath.swift
index fcfc008..478116e 100644
--- a/test/stdlib/KeyPath.swift
+++ b/test/stdlib/KeyPath.swift
@@ -1,5 +1,5 @@
 // RUN: %empty-directory(%t)
-// RUN: %target-build-swift %s -Xfrontend -enable-sil-ownership -Xfrontend -enable-experimental-keypath-components -o %t/a.out
+// RUN: %target-build-swift %s -Xfrontend -enable-sil-ownership -Xfrontend -g -o %t/a.out
 // RUN: %target-run %t/a.out
 // REQUIRES: executable_test
 
@@ -546,4 +546,125 @@
   expectEqual(kp1.hashValue, kp2.hashValue)
 }
 
+struct SubscriptResult<T: Hashable, U: Hashable> {
+  var canary = LifetimeTracked(3333)
+  var left: T
+  var right: U
+
+  init(left: T, right: U) {
+    self.left = left
+    self.right = right
+  }
+
+  subscript(left: T) -> Bool {
+    return self.left == left
+  }
+  subscript(right: U) -> Bool {
+    return self.right == right
+  }
+}
+
+struct Subscripts<T: Hashable> {
+  var canary = LifetimeTracked(4444)
+
+  subscript<U: Hashable>(x: T, y: U) -> SubscriptResult<T, U> {
+    return SubscriptResult(left: x, right: y)
+  }
+
+  subscript(x: Int, y: Int) -> Int {
+    return x + y
+  }
+}
+
+struct KeyA: Hashable {
+  var canary = LifetimeTracked(1111)
+  var value: String
+
+  init(value: String) { self.value = value }
+
+  static func ==(a: KeyA, b: KeyA) -> Bool { return a.value == b.value }
+  var hashValue: Int { return value.hashValue }
+}
+struct KeyB: Hashable {
+  var canary = LifetimeTracked(2222)
+
+  var value: Int
+
+  init(value: Int) { self.value = value }
+
+  static func ==(a: KeyB, b: KeyB) -> Bool { return a.value == b.value }
+  var hashValue: Int { return value.hashValue }
+}
+
+func fullGenericContext<T: Hashable, U: Hashable>(x: T, y: U) -> KeyPath<Subscripts<T>, SubscriptResult<T, U>> {
+  return \Subscripts<T>.[x, y]
+}
+
+func halfGenericContext<U: Hashable>(x: KeyA, y: U) -> KeyPath<Subscripts<KeyA>, SubscriptResult<KeyA, U>> {
+  return \Subscripts<KeyA>.[x, y]
+}
+
+func nonGenericContext(x: KeyA, y: KeyB) -> KeyPath<Subscripts<KeyA>, SubscriptResult<KeyA, KeyB>> {
+  return \Subscripts<KeyA>.[x, y]
+}
+
+keyPath.test("subscripts") {
+  let a = fullGenericContext(x: KeyA(value: "hey"), y: KeyB(value: 1738))
+  let b = halfGenericContext(x: KeyA(value: "hey"), y: KeyB(value: 1738))
+  let c = nonGenericContext(x: KeyA(value: "hey"), y: KeyB(value: 1738))
+
+  expectEqual(a, b)
+  expectEqual(a, c)
+  expectEqual(b, a)
+  expectEqual(b, c)
+  expectEqual(c, a)
+  expectEqual(c, b)
+  expectEqual(a.hashValue, b.hashValue)
+  expectEqual(a.hashValue, c.hashValue)
+  expectEqual(b.hashValue, a.hashValue)
+  expectEqual(b.hashValue, c.hashValue)
+  expectEqual(c.hashValue, a.hashValue)
+  expectEqual(c.hashValue, b.hashValue)
+
+  let base = Subscripts<KeyA>()
+
+  let kp2 = \SubscriptResult<KeyA, KeyB>.[KeyA(value: "hey")]
+
+  for kp in [a, b, c] {
+    let projected = base[keyPath: kp]
+    expectEqual(projected.left.value, "hey")
+    expectEqual(projected.right.value, 1738)
+
+    expectEqual(projected[keyPath: kp2], true)
+
+    let kp12 =
+      \Subscripts<KeyA>.[KeyA(value: "hey"), KeyB(value: 1738)][KeyA(value: "hey")]
+
+    let kp12a = kp.appending(path: kp2)
+
+    expectEqual(kp12, kp12a)
+    expectEqual(kp12a, kp12)
+    expectEqual(kp12.hashValue, kp12a.hashValue)
+  }
+
+  let ints = \Subscripts<KeyA>.[17, 38]
+  let ints2 = \Subscripts<KeyA>.[17, 38]
+  let ints3 = \Subscripts<KeyA>.[38, 17]
+  expectEqual(base[keyPath: ints], 17 + 38)
+
+  expectEqual(ints, ints2)
+  expectEqual(ints2, ints)
+  expectNotEqual(ints, ints3)
+  expectNotEqual(ints2, ints3)
+  expectNotEqual(ints3, ints)
+  expectNotEqual(ints3, ints2)
+
+  expectEqual(ints.hashValue, ints2.hashValue)
+
+  let ints_be = ints.appending(path: \Int.bigEndian)
+
+  expectEqual(base[keyPath: ints_be], (17 + 38).bigEndian)
+}
+
 runAllTests()
+
diff --git a/test/stdlib/KeyPathImplementation.swift b/test/stdlib/KeyPathImplementation.swift
index 7d166e2..8ccde01 100644
--- a/test/stdlib/KeyPathImplementation.swift
+++ b/test/stdlib/KeyPathImplementation.swift
@@ -1,5 +1,5 @@
 // RUN: %empty-directory(%t)
-// RUN: %target-build-swift %s -g -Xfrontend -enable-experimental-keypath-components -o %t/a.out
+// RUN: %target-build-swift %s -g -o %t/a.out
 // RUN: %target-run %t/a.out
 // REQUIRES: executable_test
 
diff --git a/test/stdlib/KeyPathObjC.swift b/test/stdlib/KeyPathObjC.swift
index aface86..787b325 100644
--- a/test/stdlib/KeyPathObjC.swift
+++ b/test/stdlib/KeyPathObjC.swift
@@ -1,5 +1,5 @@
 // RUN: %empty-directory(%t)
-// RUN: %target-build-swift %s -Xfrontend -enable-experimental-keypath-components -o %t/a.out
+// RUN: %target-build-swift %s -o %t/a.out
 // RUN: %target-run %t/a.out
 // REQUIRES: executable_test
 // REQUIRES: objc_interop
diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp
index 48aef12..67abda9 100644
--- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp
+++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp
@@ -258,110 +258,261 @@
   }
 }
 
-
 struct SwiftSyntaxToken {
-  unsigned Column;
+  unsigned Offset;
   unsigned Length:24;
   SyntaxNodeKind Kind:8;
 
-  SwiftSyntaxToken(unsigned Column, unsigned Length,
-                   SyntaxNodeKind Kind)
-    :Column(Column), Length(Length), Kind(Kind) { }
+  static SwiftSyntaxToken createInvalid() {
+    return {0, 0, SyntaxNodeKind::AttributeBuiltin};
+  }
+
+  SwiftSyntaxToken(unsigned Offset, unsigned Length, SyntaxNodeKind Kind)
+    : Offset(Offset), Length(Length), Kind(Kind) {}
+
+  unsigned endOffset() const { return Offset + Length; }
+
+  bool isInvalid() const { return Length == 0; }
+
+  bool operator==(const SwiftSyntaxToken &Other) const {
+    return Offset == Other.Offset && Length == Other.Length &&
+      Kind == Other.Kind;
+  }
+
+  bool operator!=(const SwiftSyntaxToken &Other) const {
+    return Offset != Other.Offset || Length != Other.Length ||
+      Kind != Other.Kind;
+  }
 };
 
-class SwiftSyntaxMap {
-  typedef std::vector<SwiftSyntaxToken> SwiftSyntaxLineMap;
-  std::vector<SwiftSyntaxLineMap> Lines;
+struct SwiftEditorCharRange {
+  unsigned Offset;
+  unsigned EndOffset;
 
-public:
-  bool matchesFirstTokenOnLine(unsigned Line,
-                               const SwiftSyntaxToken &Token) const {
-    assert(Line > 0);
-    if (Lines.size() < Line)
+  SwiftEditorCharRange(unsigned Offset, unsigned EndOffset) :
+    Offset(Offset), EndOffset(EndOffset) {}
+
+  SwiftEditorCharRange(SwiftSyntaxToken Token) :
+    Offset(Token.Offset), EndOffset(Token.endOffset()) {}
+
+  size_t length() const { return EndOffset - Offset; }
+  bool isEmpty() const { return Offset == EndOffset; }
+  bool intersects(const SwiftSyntaxToken &Token) const {
+    return this->Offset < (Token.endOffset()) && this->EndOffset > Token.Offset;
+  }
+  void extendToInclude(const SwiftEditorCharRange &Range) {
+    if (Range.Offset < Offset)
+      Offset = Range.Offset;
+    if (Range.EndOffset > EndOffset)
+      EndOffset = Range.EndOffset;
+  }
+  void extendToInclude(unsigned OtherOffset) {
+    extendToInclude({OtherOffset, OtherOffset});
+  }
+};
+
+/// Finds and represents the first mismatching tokens in two syntax maps,
+/// ignoring invalidated tokens.
+template <class Iter>
+struct TokenMismatch {
+  /// The begin and end iterators of the previous syntax map
+  Iter PrevTok, PrevEnd;
+  /// The begin and end iterators of the current syntax map
+  Iter CurrTok, CurrEnd;
+
+  TokenMismatch(Iter CurrTok, Iter CurrEnd, Iter PrevTok, Iter PrevEnd) :
+  PrevTok(PrevTok), PrevEnd(PrevEnd), CurrTok(CurrTok), CurrEnd(CurrEnd) {
+    skipInvalid();
+    while(advance());
+  }
+
+  /// Returns true if a mismatch was found
+  bool foundMismatch() const {
+    return CurrTok != CurrEnd || PrevTok != PrevEnd;
+  }
+
+  /// Returns the smallest start offset of the mismatched token ranges
+  unsigned mismatchStart() const {
+    assert(foundMismatch());
+    if (CurrTok != CurrEnd) {
+      if (PrevTok != PrevEnd)
+        return std::min(CurrTok->Offset, PrevTok->Offset);
+      return CurrTok->Offset;
+    }
+    return PrevTok->Offset;
+  }
+
+  /// Returns the largest end offset of the mismatched token ranges
+  unsigned mismatchEnd() const {
+    assert(foundMismatch());
+    if (CurrTok != CurrEnd) {
+      if (PrevTok != PrevEnd)
+        return std::max(CurrTok->endOffset(), PrevTok->endOffset());
+      return CurrTok->endOffset();
+    }
+    return PrevTok->endOffset();
+  }
+
+private:
+  void skipInvalid() {
+    while (PrevTok != PrevEnd && PrevTok->isInvalid())
+      ++PrevTok;
+  }
+
+  bool advance() {
+    if (CurrTok == CurrEnd || PrevTok == PrevEnd || *CurrTok != *PrevTok)
       return false;
+    ++CurrTok;
+    ++PrevTok;
+    skipInvalid();
+    return true;
+  }
+};
 
-    unsigned LineOffset = Line - 1;
-    const SwiftSyntaxLineMap &LineMap = Lines[LineOffset];
-    if (LineMap.empty())
-      return false;
+/// Represents a the syntax highlighted token ranges in a source file
+struct SwiftSyntaxMap {
+  std::vector<SwiftSyntaxToken> Tokens;
 
-    const SwiftSyntaxToken &Tok = LineMap.front();
-    if (Tok.Column == Token.Column && Tok.Length == Token.Length
-        && Tok.Kind == Token.Kind) {
-      return true;
-    }
-
-    return false;
+  explicit SwiftSyntaxMap(unsigned Capacity = 0) {
+    if (Capacity)
+      Tokens.reserve(Capacity);
   }
 
-  void addTokenForLine(unsigned Line, const SwiftSyntaxToken &Token) {
-    assert(Line > 0);
-    if (Lines.size() < Line) {
-      Lines.resize(Line);
-    }
-    unsigned LineOffset = Line - 1;
-    SwiftSyntaxLineMap &LineMap = Lines[LineOffset];
-    // FIXME: Assert this token is after the last one
-    LineMap.push_back(Token);
+  void addToken(const SwiftSyntaxToken &Token) {
+    assert(Tokens.empty() || Token.Offset >= Tokens.back().Offset);
+    Tokens.push_back(Token);
   }
 
-  void mergeTokenForLine(unsigned Line, const SwiftSyntaxToken &Token) {
-    assert(Line > 0);
-    if (Lines.size() < Line) {
-      Lines.resize(Line);
+  /// Merge this nested token into the last token that was added
+  void mergeToken(const SwiftSyntaxToken &Token) {
+    if (Tokens.empty()) {
+      Tokens.push_back(Token);
+      return;
     }
-    unsigned LineOffset = Line - 1;
-    SwiftSyntaxLineMap &LineMap = Lines[LineOffset];
-    if (!LineMap.empty()) {
-      auto &LastTok = LineMap.back();
-      mergeSplitRanges(LastTok.Column, LastTok.Length,
-                       Token.Column, Token.Length,
-                       [&](unsigned BeforeOff, unsigned BeforeLen,
-                           unsigned AfterOff, unsigned AfterLen) {
-        auto LastKind = LastTok.Kind;
-        LineMap.pop_back();
-        if (BeforeLen)
-          LineMap.emplace_back(BeforeOff, BeforeLen, LastKind);
-        LineMap.push_back(Token);
-        if (AfterLen)
-          LineMap.emplace_back(AfterOff, AfterLen, LastKind);
-      });
+    auto &LastTok = Tokens.back();
+    assert(LastTok.Offset <= Token.Offset);
+    mergeSplitRanges(LastTok.Offset, LastTok.Length, Token.Offset, Token.Length,
+                     [&](unsigned BeforeOff, unsigned BeforeLen,
+                         unsigned AfterOff, unsigned AfterLen) {
+                       auto LastKind = LastTok.Kind;
+                       Tokens.pop_back();
+                       if (BeforeLen)
+                         Tokens.emplace_back(BeforeOff, BeforeLen, LastKind);
+                       Tokens.push_back(Token);
+                       if (AfterLen)
+                         Tokens.emplace_back(AfterOff, AfterLen, LastKind);
+                     });
+  }
+
+  /// Adjusts the token offsets and lengths in this syntax map to account for
+  /// replacing \p Len bytes at the given \p Offset with \p NewLen bytes. Tokens
+  /// before the replacement stay the same, tokens after it are shifted, and
+  /// tokens that intersect it are 'removed' (really just marked invalid).
+  /// Clients are expected to match this behavior.
+  ///
+  /// Returns the union of the replaced range and the token ranges it
+  /// intersected, or nothing if no tokens were intersected.
+  llvm::Optional<SwiftEditorCharRange>
+  adjustForReplacement(unsigned Offset, unsigned Len, unsigned NewLen) {
+    unsigned ReplacedStart = Offset;
+    unsigned ReplacedEnd = Offset + Len;
+    bool TokenIntersected = false;
+    SwiftEditorCharRange Affected = { /*Offset=*/ReplacedStart,
+                                      /*EndOffset=*/ReplacedEnd};
+    // Adjust the tokens
+    auto Token = Tokens.begin();
+    while (Token != Tokens.end() && Token->endOffset() <= ReplacedStart) {
+      // Completely before the replaced range – no change needed
+      ++Token;
     }
-    else {
-      // Not overlapping, just add the new token to the end
-      LineMap.push_back(Token);
+
+    while (Token != Tokens.end() && Token->Offset < ReplacedEnd) {
+      // Intersecting the replaced range – extend Affected and invalidate
+      TokenIntersected = true;
+      Affected.extendToInclude(*Token);
+      *Token = SwiftSyntaxToken::createInvalid();
+      ++Token;
+    }
+
+    while (Token != Tokens.end()) {
+      // Completely after the replaced range - shift to account for NewLen
+      if (NewLen >= Len)
+        Token->Offset += NewLen - Len;
+      else
+        Token->Offset -= Len - NewLen;
+      ++Token;
+    }
+
+    // If the replaced range didn't intersect with any existing tokens, there's
+    // no need to report an affected range
+    if (!TokenIntersected)
+      return None;
+
+    // Update the end of the affected range to account for NewLen
+    if (NewLen >= Len) {
+      Affected.EndOffset += NewLen - Len;
+    } else {
+      Affected.EndOffset -= Len - NewLen;
+    }
+
+    return Affected;
+  }
+
+  /// Passes each token in this SwiftSyntaxMap to the given \p Consumer
+  void forEach(EditorConsumer &Consumer) {
+    for (auto &Token: Tokens) {
+      auto Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Token.Kind);
+      Consumer.handleSyntaxMap(Token.Offset, Token.Length, Kind);
     }
   }
 
-  void clearLineRange(unsigned StartLine, unsigned Length) {
-    assert(StartLine > 0);
-    unsigned LineOffset = StartLine - 1;
-    for (unsigned Line = LineOffset; Line < LineOffset + Length
-                                    && Line < Lines.size(); ++Line) {
-      Lines[Line].clear();
+  /// Finds the delta between the given SwiftSyntaxMap, \p Prev, and this one.
+  /// It passes each token not in \p Prev to the given \p Consumer and, if
+  /// needed, also expands or sets the given \p Affected range to cover all
+  /// non-matching tokens in the two lists.
+  ///
+  /// Returns true if this SwiftSyntaxMap is different to \p Prev.
+  bool forEachChanged(const SwiftSyntaxMap &Prev,
+                      llvm::Optional<SwiftEditorCharRange> &Affected,
+                      EditorConsumer &Consumer) const {
+    typedef std::vector<SwiftSyntaxToken>::const_iterator ForwardIt;
+    typedef std::vector<SwiftSyntaxToken>::const_reverse_iterator ReverseIt;
+
+    // Find the first pair of tokens that don't match
+    TokenMismatch<ForwardIt>
+    Forward(Tokens.begin(), Tokens.end(), Prev.Tokens.begin(), Prev.Tokens.end());
+
+    // Exit early if there was no mismatch
+    if (!Forward.foundMismatch())
+      return Affected && !Affected->isEmpty();
+
+    // Find the last pair of tokens that don't match
+    TokenMismatch<ReverseIt>
+    Backward(Tokens.rbegin(), Tokens.rend(), Prev.Tokens.rbegin(), Prev.Tokens.rend());
+    assert(Backward.foundMismatch());
+
+    // Set or extend the affected range to include the  mismatched range
+    SwiftEditorCharRange
+    MismatchRange = {Forward.mismatchStart(),Backward.mismatchEnd()};
+    if (!Affected) {
+      Affected = MismatchRange;
+    } else {
+      Affected->extendToInclude(MismatchRange);
     }
-  }
 
-  void removeLineRange(unsigned StartLine, unsigned Length) {
-    assert(StartLine > 0 && Length > 0);
-
-    if (StartLine < Lines.size()) {
-      unsigned EndLine = StartLine + Length - 1;
-      // Delete all syntax map data from start line through end line
-      Lines.erase(Lines.begin() + StartLine - 1,
-                  EndLine >= Lines.size() ? Lines.end()
-                                          : Lines.begin() + EndLine);
+    // Report all tokens in the affected range to the EditorConsumer
+    auto From = Forward.CurrTok;
+    auto To = Backward.CurrTok;
+    while (From != Tokens.begin() && (From-1)->Offset >= Affected->Offset)
+      --From;
+    while (To != Tokens.rbegin() && (To-1)->endOffset() <= Affected->EndOffset)
+      --To;
+    for (; From < To.base(); ++From) {
+      auto Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(From->Kind);
+      Consumer.handleSyntaxMap(From->Offset, From->Length, Kind);
     }
-  }
 
-  void insertLineRange(unsigned StartLine, unsigned Length) {
-    Lines.insert(StartLine <= Lines.size() ? Lines.begin() + StartLine - 1
-                                           : Lines.end(),
-                 Length, SwiftSyntaxLineMap());
-  }
-
-  void reset() {
-    Lines.clear();
+    return true;
   }
 };
 
@@ -373,8 +524,6 @@
     :Offset(Offset), Length(Length), Kind(Kind) { }
 };
 
-typedef std::pair<unsigned, unsigned> SwiftEditorCharRange;
-
 struct SwiftSemanticToken {
   unsigned ByteOffset;
   unsigned Length : 24;
@@ -849,9 +998,12 @@
   const std::string FilePath;
   EditableTextBufferRef EditableBuffer;
 
+  /// The list of syntax highlighted token offsets and ranges in the document
   SwiftSyntaxMap SyntaxMap;
-  LineRange EditedLineRange;
-  SwiftEditorCharRange AffectedRange;
+  /// The minimal range of syntax highlighted tokens affected by the last edit
+  llvm::Optional<SwiftEditorCharRange> AffectedRange;
+  /// Whether the last operation was an edit rather than a document open
+  bool Edited;
 
   std::vector<DiagnosticEntryInfo> ParserDiagnostics;
   RefPtr<SwiftDocumentSemanticInfo> SemanticInfo;
@@ -1180,121 +1332,39 @@
   }
 };
 
+/// Walks the syntax model to populate a given SwiftSyntaxMap with the token
+/// ranges to highlight and pass document structure information to the given
+/// EditorConsumer.
 class SwiftEditorSyntaxWalker: public ide::SyntaxModelWalker {
+  /// The syntax map to populate
   SwiftSyntaxMap &SyntaxMap;
-  LineRange EditedLineRange;
-  SwiftEditorCharRange &AffectedRange;
   SourceManager &SrcManager;
-  EditorConsumer &Consumer;
   unsigned BufferID;
   SwiftDocumentStructureWalker DocStructureWalker;
-  std::vector<EditorConsumerSyntaxMapEntry> ConsumerSyntaxMap;
+  /// The current token nesting level (e.g. for a field in a doc comment)
   unsigned NestingLevel = 0;
 public:
   SwiftEditorSyntaxWalker(SwiftSyntaxMap &SyntaxMap,
-                          LineRange EditedLineRange,
-                          SwiftEditorCharRange &AffectedRange,
                           SourceManager &SrcManager, EditorConsumer &Consumer,
                           unsigned BufferID)
-    : SyntaxMap(SyntaxMap), EditedLineRange(EditedLineRange),
-      AffectedRange(AffectedRange), SrcManager(SrcManager), Consumer(Consumer),
-      BufferID(BufferID),
+    : SyntaxMap(SyntaxMap), SrcManager(SrcManager), BufferID(BufferID),
       DocStructureWalker(SrcManager, BufferID, Consumer) { }
 
   bool walkToNodePre(SyntaxNode Node) override {
     if (Node.Kind == SyntaxNodeKind::CommentMarker)
       return DocStructureWalker.walkToNodePre(Node);
-
     ++NestingLevel;
-    SourceLoc StartLoc = Node.Range.getStart();
-    auto StartLineAndColumn = SrcManager.getLineAndColumn(StartLoc);
-    auto EndLineAndColumn = SrcManager.getLineAndColumn(Node.Range.getEnd());
-    unsigned StartLine = StartLineAndColumn.first;
-    unsigned EndLine = EndLineAndColumn.second > 1 ? EndLineAndColumn.first
-                                                   : EndLineAndColumn.first - 1;
-    unsigned Offset = SrcManager.getByteDistance(
-                           SrcManager.getLocForBufferStart(BufferID), StartLoc);
-    // Note that the length can span multiple lines.
-    unsigned Length = Node.Range.getByteLength();
 
-    SwiftSyntaxToken Token(StartLineAndColumn.second, Length,
-                           Node.Kind);
-    if (EditedLineRange.isValid()) {
-      if (StartLine < EditedLineRange.startLine()) {
-        if (EndLine < EditedLineRange.startLine()) {
-          // We're entirely before the edited range, no update needed.
-          return true;
-        }
+    auto End = SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(), BufferID),
+      Start = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(), BufferID);
 
-        // This token starts before the edited range, but doesn't end before it,
-        // we need to adjust edited line range and clear the affected syntax map
-        // line range.
-        unsigned AdjLineCount = EditedLineRange.startLine() - StartLine;
-        EditedLineRange.setRange(StartLine, AdjLineCount
-                                            + EditedLineRange.lineCount());
-        SyntaxMap.clearLineRange(StartLine, AdjLineCount);
-
-        // Also adjust the affected char range accordingly.
-        unsigned AdjCharCount = AffectedRange.first - Offset;
-        AffectedRange.first -= AdjCharCount;
-        AffectedRange.second += AdjCharCount;
-      }
-      else if (Offset > AffectedRange.first + AffectedRange.second) {
-        // We're passed the affected range and already synced up, just return.
-        return true;
-      }
-      else if (StartLine > EditedLineRange.endLine()) {
-        // We're after the edited line range, let's test if we're synced up.
-        if (SyntaxMap.matchesFirstTokenOnLine(StartLine, Token)) {
-          // We're synced up, mark the affected range and return.
-          AffectedRange.second =
-                 Offset - (StartLineAndColumn.second - 1) - AffectedRange.first;
-          return true;
-        }
-
-        // We're not synced up, continue replacing syntax map data on this line.
-        SyntaxMap.clearLineRange(StartLine, 1);
-        EditedLineRange.extendToIncludeLine(StartLine);
-      }
-
-      if (EndLine > StartLine) {
-        // The token spans multiple lines, make sure to replace syntax map data
-        // for affected lines.
-        EditedLineRange.extendToIncludeLine(EndLine);
-
-        unsigned LineCount = EndLine - StartLine + 1;
-        SyntaxMap.clearLineRange(StartLine, LineCount);
-      }
-
-    }
-
-    // Add the syntax map token.
-    if (NestingLevel > 1)
-      SyntaxMap.mergeTokenForLine(StartLine, Token);
-    else
-      SyntaxMap.addTokenForLine(StartLine, Token);
-
-    // Add consumer entry.
-    unsigned ByteOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(),
-                                                          BufferID);
-    UIdent Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Node.Kind);
     if (NestingLevel > 1) {
-      assert(!ConsumerSyntaxMap.empty());
-      auto &Last = ConsumerSyntaxMap.back();
-      mergeSplitRanges(Last.Offset, Last.Length, ByteOffset, Length,
-                       [&](unsigned BeforeOff, unsigned BeforeLen,
-                           unsigned AfterOff, unsigned AfterLen) {
-        auto LastKind = Last.Kind;
-        ConsumerSyntaxMap.pop_back();
-        if (BeforeLen)
-          ConsumerSyntaxMap.emplace_back(BeforeOff, BeforeLen, LastKind);
-        ConsumerSyntaxMap.emplace_back(ByteOffset, Length, Kind);
-        if (AfterLen)
-          ConsumerSyntaxMap.emplace_back(AfterOff, AfterLen, LastKind);
-      });
+      // We're nested inside the previously reported token - merge
+      SyntaxMap.mergeToken({Start, End - Start, Node.Kind});
+    } else {
+      // We're a top-level token, add it after the previous one
+      SyntaxMap.addToken({Start, End - Start, Node.Kind});
     }
-    else
-      ConsumerSyntaxMap.emplace_back(ByteOffset, Length, Kind);
 
     return true;
   }
@@ -1302,14 +1372,7 @@
   bool walkToNodePost(SyntaxNode Node) override {
     if (Node.Kind == SyntaxNodeKind::CommentMarker)
       return DocStructureWalker.walkToNodePost(Node);
-
-    if (--NestingLevel == 0) {
-      // We've unwound to the top level, so inform the consumer and drain
-      // the consumer syntax map queue.
-      for (auto &Entry: ConsumerSyntaxMap)
-        Consumer.handleSyntaxMap(Entry.Offset, Entry.Length, Entry.Kind);
-      ConsumerSyntaxMap.clear();
-    }
+    --NestingLevel;
 
     return true;
   }
@@ -1590,11 +1653,14 @@
 
   llvm::sys::ScopedLock L(Impl.AccessMtx);
 
+  Impl.Edited = false;
   Impl.EditableBuffer =
       new EditableTextBuffer(Impl.FilePath, Buf->getBuffer());
-  Impl.SyntaxMap.reset();
-  Impl.EditedLineRange.setRange(0,0);
-  Impl.AffectedRange = std::make_pair(0, Buf->getBufferSize());
+
+  // Reset the syntax map data and affected range
+  Impl.SyntaxMap.Tokens.clear();
+  Impl.AffectedRange = {0, static_cast<unsigned>(Buf->getBufferSize())};
+
   Impl.SemanticInfo =
       new SwiftDocumentSemanticInfo(Impl.FilePath, Impl.LangSupport);
   Impl.SemanticInfo->setCompilerArgs(Args);
@@ -1607,7 +1673,10 @@
 
   llvm::sys::ScopedLock L(Impl.AccessMtx);
 
+  Impl.Edited = true;
   llvm::StringRef Str = Buf->getBuffer();
+
+  // Update the buffer itself
   ImmutableTextSnapshotRef Snapshot =
       Impl.EditableBuffer->replace(Offset, Length, Str);
 
@@ -1633,37 +1702,12 @@
     }
   }
 
-  SourceManager &SrcManager = Impl.SyntaxInfo->getSourceManager();
-  unsigned BufID = Impl.SyntaxInfo->getBufferID();
-  SourceLoc StartLoc = SrcManager.getLocForBufferStart(BufID).getAdvancedLoc(
-                                                                        Offset);
-  unsigned StartLine = SrcManager.getLineAndColumn(StartLoc).first;
-  unsigned EndLine = SrcManager.getLineAndColumn(
-                                         StartLoc.getAdvancedLoc(Length)).first;
-
-  // Delete all syntax map data from start line through end line.
-  unsigned OldLineCount = EndLine - StartLine + 1;
-  Impl.SyntaxMap.removeLineRange(StartLine, OldLineCount);
-
-  // Insert empty syntax map data for replaced lines.
-  unsigned NewLineCount = Str.count('\n') + 1;
-  Impl.SyntaxMap.insertLineRange(StartLine, NewLineCount);
-
-  // Update the edited line range.
-  Impl.EditedLineRange.setRange(StartLine, NewLineCount);
-
-  ImmutableTextBufferRef ImmBuf = Snapshot->getBuffer();
-
-  // The affected range starts from the previous newline.
-  if (Offset > 0) {
-    auto AffectedRangeOffset = ImmBuf->getText().rfind('\n', Offset);
-    Impl.AffectedRange.first =
-      AffectedRangeOffset != StringRef::npos ? AffectedRangeOffset + 1 : 0;
-  }
-  else
-    Impl.AffectedRange.first = 0;
-
-  Impl.AffectedRange.second = ImmBuf->getText().size() - Impl.AffectedRange.first;
+  // Update the old syntax map offsets to account for the replaced range.
+  // Also set the initial AffectedRange to cover any tokens that
+  // the replaced range intersected. This allows for clients that split
+  // multi-line tokens at line boundaries, and ensure all parts of these tokens
+  // will be cleared.
+  Impl.AffectedRange = Impl.SyntaxMap.adjustForReplacement(Offset, Length, Str.size());
 
   return Snapshot;
 }
@@ -1716,17 +1760,34 @@
 
   ide::SyntaxModelContext ModelContext(Impl.SyntaxInfo->getSourceFile());
 
-  SwiftEditorSyntaxWalker SyntaxWalker(Impl.SyntaxMap,
-                                       Impl.EditedLineRange,
-                                       Impl.AffectedRange,
+  SwiftSyntaxMap NewMap = SwiftSyntaxMap(Impl.SyntaxMap.Tokens.size() + 16);
+
+  SwiftEditorSyntaxWalker SyntaxWalker(NewMap,
                                        Impl.SyntaxInfo->getSourceManager(),
                                        Consumer,
                                        Impl.SyntaxInfo->getBufferID());
-
   ModelContext.walk(SyntaxWalker);
 
-  Consumer.recordAffectedRange(Impl.AffectedRange.first,
-                               Impl.AffectedRange.second);
+  bool SawChanges = true;
+  if (Impl.Edited) {
+    // We're ansering an edit request. Report all highlighted token ranges not
+    // in the previous syntax map to the Consumer and extend the AffectedRange
+    // to contain all added/removed token ranges.
+    SawChanges = NewMap.forEachChanged(Impl.SyntaxMap, Impl.AffectedRange,
+                                       Consumer);
+  } else {
+    // The is an open/initialise. Report all highlighted token ranges to the
+    // Consumer.
+    NewMap.forEach(Consumer);
+  }
+  Impl.SyntaxMap = std::move(NewMap);
+
+  // Recording an affected length of 0 still results in the client updating its
+  // copy of the syntax map (by clearning all tokens on the line of the affected
+  // offset). We need to not record it at all to signal a no-op.
+  if (SawChanges)
+    Consumer.recordAffectedRange(Impl.AffectedRange->Offset,
+                                 Impl.AffectedRange->length());
 }
 
 void SwiftEditorDocument::readSemanticInfo(ImmutableTextSnapshotRef Snapshot,
diff --git a/unittests/runtime/weak.mm b/unittests/runtime/weak.mm
index 2e11a3a..faf19d2 100644
--- a/unittests/runtime/weak.mm
+++ b/unittests/runtime/weak.mm
@@ -277,7 +277,8 @@
   DestroyedObjCCount = 0;
 
   WeakReference ref1;
-  swift_unknownWeakInit(&ref1, o1);
+  auto res = swift_unknownWeakInit(&ref1, o1);
+  ASSERT_EQ(&ref1, res);
 
   void *tmp = swift_unknownWeakLoadStrong(&ref1);
   ASSERT_EQ(tmp, o1);
@@ -435,7 +436,8 @@
   ASSERT_EQ(nullptr, result);
 
   // ref2 = ref1 (nil -> nil)
-  swift_unknownUnownedCopyInit(&ref2, &ref1);
+  auto res = swift_unknownUnownedCopyInit(&ref2, &ref1);
+  ASSERT_EQ(&ref2, res);
   result = swift_unknownUnownedLoadStrong(&ref1);
   ASSERT_EQ(nullptr, result);
   result = swift_unknownUnownedLoadStrong(&ref2);
@@ -535,7 +537,8 @@
   ASSERT_EQ(nullptr, result);
 
   // ref2 = ref1 (nil -> nil)
-  swift_unknownUnownedTakeInit(&ref2, &ref1);
+  auto res = swift_unknownUnownedTakeInit(&ref2, &ref1);
+  ASSERT_EQ(&ref2, res);
   result = swift_unknownUnownedLoadStrong(&ref2);
   ASSERT_EQ(nullptr, result);
   swift_unknownUnownedDestroy(&ref2);
@@ -638,7 +641,8 @@
   swift_unknownRelease(result);
 
   // ref1 = ref2 (objc self transition)
-  swift_unknownUnownedCopyAssign(&ref1, &ref2);
+  auto res = swift_unknownUnownedCopyAssign(&ref1, &ref2);
+  ASSERT_EQ(&ref1, res);
   result = swift_unknownUnownedLoadStrong(&ref1);
   ASSERT_EQ(objc1, result);
   swift_unknownRelease(result);
@@ -766,7 +770,8 @@
   void *result;
 
   // ref1 = objc1
-  swift_unknownUnownedInit(&ref1, objc1);
+  auto res = swift_unknownUnownedInit(&ref1, objc1);
+  ASSERT_EQ(&ref1, res);
   result = swift_unknownUnownedLoadStrong(&ref1);
   ASSERT_EQ(objc1, result);
   swift_unknownRelease(result);
@@ -778,7 +783,8 @@
   swift_unknownRelease(result);
 
   // ref1 = ref2 (objc self transition)
-  swift_unknownUnownedTakeAssign(&ref1, &ref2);
+  res = swift_unknownUnownedTakeAssign(&ref1, &ref2);
+  ASSERT_EQ(&ref1, res);
   result = swift_unknownUnownedLoadStrong(&ref1);
   ASSERT_EQ(objc1, result);
   swift_unknownRelease(result);
@@ -928,3 +934,34 @@
   swift_unownedDestroy(&ref1);
   swift_unknownUnownedDestroy(&ref2);
 }
+
+TEST(WeakTest, unknownWeak) {
+  void *objc1 = make_objc_object();
+  HeapObject *swift1 = make_swift_object();
+
+  WeakReference ref1;
+  auto res = swift_unknownWeakInit(&ref1, objc1);
+  ASSERT_EQ(&ref1, res);
+
+  WeakReference ref2;
+  res = swift_unknownWeakCopyInit(&ref2, &ref1);
+  ASSERT_EQ(&ref2, res);
+
+  WeakReference ref3; // ref2 dead.
+  res = swift_unknownWeakTakeInit(&ref3, &ref2);
+  ASSERT_EQ(&ref3, res);
+
+  res = swift_unknownWeakAssign(&ref3, swift1);
+  ASSERT_EQ(&ref3, res);
+
+  res = swift_unknownWeakCopyAssign(&ref3, &ref1);
+  ASSERT_EQ(&ref3, res);
+
+  res = swift_unknownWeakTakeAssign(&ref3, &ref1);
+  ASSERT_EQ(&ref3, res);
+
+  swift_unknownWeakDestroy(&ref3);
+
+  swift_release(swift1);
+  swift_unknownRelease(objc1);
+}
diff --git a/utils/build-script b/utils/build-script
index a707aef..b4ba44d 100755
--- a/utils/build-script
+++ b/utils/build-script
@@ -268,80 +268,80 @@
         # iterate over all supported platforms.
 
         self.platforms_to_skip_build = set()
-        if args.skip_build_linux:
+        if not args.build_linux:
             self.platforms_to_skip_build.add(StdlibDeploymentTarget.Linux)
-        if args.skip_build_freebsd:
+        if not args.build_freebsd:
             self.platforms_to_skip_build.add(StdlibDeploymentTarget.FreeBSD)
-        if args.skip_build_cygwin:
+        if not args.build_cygwin:
             self.platforms_to_skip_build.add(StdlibDeploymentTarget.Cygwin)
-        if args.skip_build_osx:
+        if not args.build_osx:
             self.platforms_to_skip_build.add(StdlibDeploymentTarget.OSX)
-        if args.skip_build_ios_device:
+        if not args.build_ios_device:
             self.platforms_to_skip_build.add(StdlibDeploymentTarget.iOS)
-        if args.skip_build_ios_simulator:
+        if not args.build_ios_simulator:
             self.platforms_to_skip_build.add(
                 StdlibDeploymentTarget.iOSSimulator)
-        if args.skip_build_tvos_device:
+        if not args.build_tvos_device:
             self.platforms_to_skip_build.add(StdlibDeploymentTarget.AppleTV)
-        if args.skip_build_tvos_simulator:
+        if not args.build_tvos_simulator:
             self.platforms_to_skip_build.add(
                 StdlibDeploymentTarget.AppleTVSimulator)
-        if args.skip_build_watchos_device:
+        if not args.build_watchos_device:
             self.platforms_to_skip_build.add(StdlibDeploymentTarget.AppleWatch)
-        if args.skip_build_watchos_simulator:
+        if not args.build_watchos_simulator:
             self.platforms_to_skip_build.add(
                 StdlibDeploymentTarget.AppleWatchSimulator)
-        if args.skip_build_android:
+        if not args.build_android:
             self.platforms_to_skip_build.add(StdlibDeploymentTarget.Android)
 
         self.platforms_to_skip_test = set()
         self.platforms_archs_to_skip_test = set()
-        if args.skip_test_linux:
+        if not args.test_linux:
             self.platforms_to_skip_test.add(StdlibDeploymentTarget.Linux)
-        if args.skip_test_freebsd:
+        if not args.test_freebsd:
             self.platforms_to_skip_test.add(StdlibDeploymentTarget.FreeBSD)
-        if args.skip_test_cygwin:
+        if not args.test_cygwin:
             self.platforms_to_skip_test.add(StdlibDeploymentTarget.Cygwin)
-        if args.skip_test_osx:
+        if not args.test_osx:
             self.platforms_to_skip_test.add(StdlibDeploymentTarget.OSX)
-        if args.skip_test_ios_host:
+        if not args.test_ios_device:
             self.platforms_to_skip_test.add(StdlibDeploymentTarget.iOS)
         else:
             exit_rejecting_arguments("error: iOS device tests are not " +
                                      "supported in open-source Swift.")
-        if args.skip_test_ios_simulator:
+        if not args.test_ios_simulator:
             self.platforms_to_skip_test.add(
                 StdlibDeploymentTarget.iOSSimulator)
-        if args.skip_test_ios_32bit_simulator:
+        if not args.test_ios_32bit_simulator:
             self.platforms_archs_to_skip_test.add(
                 StdlibDeploymentTarget.iOSSimulator.i386)
-        if args.skip_test_tvos_host:
+        if not args.test_tvos_device:
             self.platforms_to_skip_test.add(StdlibDeploymentTarget.AppleTV)
         else:
             exit_rejecting_arguments("error: tvOS device tests are not " +
                                      "supported in open-source Swift.")
-        if args.skip_test_tvos_simulator:
+        if not args.test_tvos_simulator:
             self.platforms_to_skip_test.add(
                 StdlibDeploymentTarget.AppleTVSimulator)
-        if args.skip_test_watchos_host:
+        if not args.test_watchos_device:
             self.platforms_to_skip_test.add(StdlibDeploymentTarget.AppleWatch)
         else:
             exit_rejecting_arguments("error: watchOS device tests are not " +
                                      "supported in open-source Swift.")
-        if args.skip_test_watchos_simulator:
+        if not args.test_watchos_simulator:
             self.platforms_to_skip_test.add(
                 StdlibDeploymentTarget.AppleWatchSimulator)
 
-        if args.skip_test_android_host:
+        if not args.test_android_device:
             self.platforms_to_skip_test.add(StdlibDeploymentTarget.Android)
 
         self.platforms_to_skip_test_host = set()
-        if args.skip_test_ios_host:
+        if not args.test_ios_device:
             self.platforms_to_skip_test_host.add(StdlibDeploymentTarget.iOS)
-        if args.skip_test_tvos_host:
+        if not args.test_tvos_device:
             self.platforms_to_skip_test_host.add(
                 StdlibDeploymentTarget.AppleTV)
-        if args.skip_test_watchos_host:
+        if not args.test_watchos_device:
             self.platforms_to_skip_test_host.add(
                 StdlibDeploymentTarget.AppleWatch)
 
@@ -501,7 +501,7 @@
             impl_args += ["--skip-build-cmark",
                           "--skip-build-llvm",
                           "--skip-build-swift"]
-        if args.skip_build_benchmarks:
+        if not args.build_benchmarks:
             impl_args += ["--skip-build-benchmarks"]
         if not args.build_foundation:
             impl_args += ["--skip-build-foundation"]
@@ -532,27 +532,27 @@
         if args.build_swift_static_sdk_overlay:
             impl_args += ["--build-swift-static-sdk-overlay"]
 
-        if args.skip_build_linux:
+        if not args.build_linux:
             impl_args += ["--skip-build-linux"]
-        if args.skip_build_freebsd:
+        if not args.build_freebsd:
             impl_args += ["--skip-build-freebsd"]
-        if args.skip_build_cygwin:
+        if not args.build_cygwin:
             impl_args += ["--skip-build-cygwin"]
-        if args.skip_build_osx:
+        if not args.build_osx:
             impl_args += ["--skip-build-osx"]
-        if args.skip_build_ios_device:
+        if not args.build_ios_device:
             impl_args += ["--skip-build-ios-device"]
-        if args.skip_build_ios_simulator:
+        if not args.build_ios_simulator:
             impl_args += ["--skip-build-ios-simulator"]
-        if args.skip_build_tvos_device:
+        if not args.build_tvos_device:
             impl_args += ["--skip-build-tvos-device"]
-        if args.skip_build_tvos_simulator:
+        if not args.build_tvos_simulator:
             impl_args += ["--skip-build-tvos-simulator"]
-        if args.skip_build_watchos_device:
+        if not args.build_watchos_device:
             impl_args += ["--skip-build-watchos-device"]
-        if args.skip_build_watchos_simulator:
+        if not args.build_watchos_simulator:
             impl_args += ["--skip-build-watchos-simulator"]
-        if args.skip_build_android:
+        if not args.build_android:
             impl_args += ["--skip-build-android"]
 
         if not args.test and not args.long_test:
@@ -568,29 +568,29 @@
                           "--skip-test-libicu",
                           "--skip-test-playgroundlogger",
                           "--skip-test-playgroundsupport"]
-        if args.skip_test_linux:
+        if not args.test_linux:
             impl_args += ["--skip-test-linux"]
-        if args.skip_test_freebsd:
+        if not args.test_freebsd:
             impl_args += ["--skip-test-freebsd"]
-        if args.skip_test_cygwin:
+        if not args.test_cygwin:
             impl_args += ["--skip-test-cygwin"]
-        if args.skip_test_osx:
+        if not args.test_osx:
             impl_args += ["--skip-test-osx"]
-        if args.skip_test_ios_host:
+        if not args.test_ios_device:
             impl_args += ["--skip-test-ios-host"]
-        if args.skip_test_ios_simulator:
+        if not args.test_ios_simulator:
             impl_args += ["--skip-test-ios-simulator"]
-        if args.skip_test_ios_32bit_simulator:
+        if not args.test_ios_32bit_simulator:
             impl_args += ["--skip-test-ios-32bit-simulator"]
-        if args.skip_test_tvos_host:
+        if not args.test_tvos_device:
             impl_args += ["--skip-test-tvos-host"]
-        if args.skip_test_tvos_simulator:
+        if not args.test_tvos_simulator:
             impl_args += ["--skip-test-tvos-simulator"]
-        if args.skip_test_watchos_host:
+        if not args.test_watchos_device:
             impl_args += ["--skip-test-watchos-host"]
-        if args.skip_test_watchos_simulator:
+        if not args.test_watchos_simulator:
             impl_args += ["--skip-test-watchos-simulator"]
-        if args.skip_test_android_host:
+        if not args.test_android_device:
             impl_args += ["--skip-test-android-host"]
         if args.build_runtime_with_host_compiler:
             impl_args += ["--build-runtime-with-host-compiler"]
diff --git a/utils/build_swift/driver_arguments.py b/utils/build_swift/driver_arguments.py
index dab1b72..c902c00 100644
--- a/utils/build_swift/driver_arguments.py
+++ b/utils/build_swift/driver_arguments.py
@@ -144,15 +144,15 @@
 
     # Propagate global --skip-build
     if args.skip_build:
-        args.skip_build_linux = True
-        args.skip_build_freebsd = True
-        args.skip_build_cygwin = True
-        args.skip_build_osx = True
-        args.skip_build_ios = True
-        args.skip_build_tvos = True
-        args.skip_build_watchos = True
-        args.skip_build_android = True
-        args.skip_build_benchmarks = True
+        args.build_linux = False
+        args.build_freebsd = False
+        args.build_cygwin = False
+        args.build_osx = False
+        args.build_ios = False
+        args.build_tvos = False
+        args.build_watchos = False
+        args.build_android = False
+        args.build_benchmarks = False
         args.build_lldb = False
         args.build_llbuild = False
         args.build_swiftpm = False
@@ -165,20 +165,20 @@
 
     # --skip-{ios,tvos,watchos} or --skip-build-{ios,tvos,watchos} are
     # merely shorthands for --skip-build-{**os}-{device,simulator}
-    if not args.ios or args.skip_build_ios:
-        args.skip_build_ios_device = True
-        args.skip_build_ios_simulator = True
+    if not args.ios or not args.build_ios:
+        args.build_ios_device = False
+        args.build_ios_simulator = False
 
-    if not args.tvos or args.skip_build_tvos:
-        args.skip_build_tvos_device = True
-        args.skip_build_tvos_simulator = True
+    if not args.tvos or not args.build_tvos:
+        args.build_tvos_device = False
+        args.build_tvos_simulator = False
 
-    if not args.watchos or args.skip_build_watchos:
-        args.skip_build_watchos_device = True
-        args.skip_build_watchos_simulator = True
+    if not args.watchos or not args.build_watchos:
+        args.build_watchos_device = False
+        args.build_watchos_simulator = False
 
-    if not args.android or args.skip_build_android:
-        args.skip_build_android = True
+    if not args.android or not args.build_android:
+        args.build_android = False
 
     # --validation-test implies --test.
     if args.validation_test:
@@ -194,53 +194,53 @@
 
     # If none of tests specified skip swift stdlib test on all platforms
     if not args.test and not args.validation_test and not args.long_test:
-        args.skip_test_linux = True
-        args.skip_test_freebsd = True
-        args.skip_test_cygwin = True
-        args.skip_test_osx = True
-        args.skip_test_ios = True
-        args.skip_test_tvos = True
-        args.skip_test_watchos = True
+        args.test_linux = False
+        args.test_freebsd = False
+        args.test_cygwin = False
+        args.test_osx = False
+        args.test_ios = False
+        args.test_tvos = False
+        args.test_watchos = False
 
     # --skip-test-ios is merely a shorthand for host and simulator tests.
-    if args.skip_test_ios:
-        args.skip_test_ios_host = True
-        args.skip_test_ios_simulator = True
+    if not args.test_ios:
+        args.test_ios_device = False
+        args.test_ios_simulator = False
     # --skip-test-tvos is merely a shorthand for host and simulator tests.
-    if args.skip_test_tvos:
-        args.skip_test_tvos_host = True
-        args.skip_test_tvos_simulator = True
+    if not args.test_tvos:
+        args.test_tvos_device = False
+        args.test_tvos_simulator = False
     # --skip-test-watchos is merely a shorthand for host and simulator
     # --tests.
-    if args.skip_test_watchos:
-        args.skip_test_watchos_host = True
-        args.skip_test_watchos_simulator = True
+    if not args.test_watchos:
+        args.test_watchos_device = False
+        args.test_watchos_simulator = False
 
     # --skip-build-{ios,tvos,watchos}-{device,simulator} implies
     # --skip-test-{ios,tvos,watchos}-{host,simulator}
-    if args.skip_build_ios_device:
-        args.skip_test_ios_host = True
-    if args.skip_build_ios_simulator:
-        args.skip_test_ios_simulator = True
+    if not args.build_ios_device:
+        args.test_ios_device = False
+    if not args.build_ios_simulator:
+        args.test_ios_simulator = False
 
-    if args.skip_build_tvos_device:
-        args.skip_test_tvos_host = True
-    if args.skip_build_tvos_simulator:
-        args.skip_test_tvos_simulator = True
+    if not args.build_tvos_device:
+        args.test_tvos_device = False
+    if not args.build_tvos_simulator:
+        args.test_tvos_simulator = False
 
-    if args.skip_build_watchos_device:
-        args.skip_test_watchos_host = True
-    if args.skip_build_watchos_simulator:
-        args.skip_test_watchos_simulator = True
+    if not args.build_watchos_device:
+        args.test_watchos_device = False
+    if not args.build_watchos_simulator:
+        args.test_watchos_simulator = False
 
-    if args.skip_build_android:
-        args.skip_test_android_host = True
+    if not args.build_android:
+        args.test_android_device = False
 
     if not args.host_test:
-        args.skip_test_ios_host = True
-        args.skip_test_tvos_host = True
-        args.skip_test_watchos_host = True
-        args.skip_test_android_host = True
+        args.test_ios_device = False
+        args.test_tvos_device = False
+        args.test_watchos_device = False
+        args.test_android_device = False
 
     if args.build_subdir is None:
         args.build_subdir = \
@@ -259,7 +259,7 @@
                     if StdlibDeploymentTarget.Android.contains(tgt)]
     if not args.android and len(android_tgts) > 0:
         args.android = True
-        args.skip_build_android = True
+        args.build_android = False
 
 
 def create_argument_parser():
@@ -616,20 +616,24 @@
         iterations with -Onone", metavar='N', type=int, default=3)
     run_tests_group.add_argument(
         "--skip-test-osx",
-        help="skip testing Swift stdlibs for Mac OS X",
-        action=arguments.action.optional_bool)
+        dest='test_osx',
+        action=arguments.action.optional_false,
+        help="skip testing Swift stdlibs for Mac OS X")
     run_tests_group.add_argument(
         "--skip-test-linux",
-        help="skip testing Swift stdlibs for Linux",
-        action=arguments.action.optional_bool)
+        dest='test_linux',
+        action=arguments.action.optional_false,
+        help="skip testing Swift stdlibs for Linux")
     run_tests_group.add_argument(
         "--skip-test-freebsd",
-        help="skip testing Swift stdlibs for FreeBSD",
-        action=arguments.action.optional_bool)
+        dest='test_freebsd',
+        action=arguments.action.optional_false,
+        help="skip testing Swift stdlibs for FreeBSD")
     run_tests_group.add_argument(
         "--skip-test-cygwin",
-        help="skip testing Swift stdlibs for Cygwin",
-        action=arguments.action.optional_bool)
+        dest='test_cygwin',
+        action=arguments.action.optional_false,
+        help="skip testing Swift stdlibs for Cygwin")
     parser.add_argument(
         "--build-runtime-with-host-compiler",
         help="Use the host compiler, not the self-built one to compile the "
@@ -666,130 +670,155 @@
         action="store_true")
     run_build_group.add_argument(
         "--skip-build-linux",
-        help="skip building Swift stdlibs for Linux",
-        action=arguments.action.optional_bool)
+        dest='build_linux',
+        action=arguments.action.optional_false,
+        help="skip building Swift stdlibs for Linux")
     run_build_group.add_argument(
         "--skip-build-freebsd",
-        help="skip building Swift stdlibs for FreeBSD",
-        action=arguments.action.optional_bool)
+        dest='build_freebsd',
+        action=arguments.action.optional_false,
+        help="skip building Swift stdlibs for FreeBSD")
     run_build_group.add_argument(
         "--skip-build-cygwin",
-        help="skip building Swift stdlibs for Cygwin",
-        action=arguments.action.optional_bool)
+        dest='build_cygwin',
+        action=arguments.action.optional_false,
+        help="skip building Swift stdlibs for Cygwin")
     run_build_group.add_argument(
         "--skip-build-osx",
-        help="skip building Swift stdlibs for MacOSX",
-        action=arguments.action.optional_bool)
+        dest='build_osx',
+        action=arguments.action.optional_false,
+        help="skip building Swift stdlibs for MacOSX")
 
     run_build_group.add_argument(
         "--skip-build-ios",
-        help="skip building Swift stdlibs for iOS",
-        action=arguments.action.optional_bool)
+        dest='build_ios',
+        action=arguments.action.optional_false,
+        help="skip building Swift stdlibs for iOS")
     run_build_group.add_argument(
         "--skip-build-ios-device",
+        dest='build_ios_device',
+        action=arguments.action.optional_false,
         help="skip building Swift stdlibs for iOS devices "
-             "(i.e. build simulators only)",
-        action=arguments.action.optional_bool)
+             "(i.e. build simulators only)")
     run_build_group.add_argument(
         "--skip-build-ios-simulator",
+        dest='build_ios_simulator',
+        action=arguments.action.optional_false,
         help="skip building Swift stdlibs for iOS simulator "
-             "(i.e. build devices only)",
-        action=arguments.action.optional_bool)
+             "(i.e. build devices only)")
 
     run_build_group.add_argument(
         "--skip-build-tvos",
-        help="skip building Swift stdlibs for tvOS",
-        action=arguments.action.optional_bool)
+        dest='build_tvos',
+        action=arguments.action.optional_false,
+        help="skip building Swift stdlibs for tvOS")
     run_build_group.add_argument(
         "--skip-build-tvos-device",
+        dest='build_tvos_device',
+        action=arguments.action.optional_false,
         help="skip building Swift stdlibs for tvOS devices "
-             "(i.e. build simulators only)",
-        action=arguments.action.optional_bool)
+             "(i.e. build simulators only)")
     run_build_group.add_argument(
         "--skip-build-tvos-simulator",
+        dest='build_tvos_simulator',
+        action=arguments.action.optional_false,
         help="skip building Swift stdlibs for tvOS simulator "
-             "(i.e. build devices only)",
-        action=arguments.action.optional_bool)
+             "(i.e. build devices only)")
 
     run_build_group.add_argument(
         "--skip-build-watchos",
-        help="skip building Swift stdlibs for watchOS",
-        action=arguments.action.optional_bool)
+        dest='build_watchos',
+        action=arguments.action.optional_false,
+        help="skip building Swift stdlibs for watchOS")
     run_build_group.add_argument(
         "--skip-build-watchos-device",
+        dest='build_watchos_device',
+        action=arguments.action.optional_false,
         help="skip building Swift stdlibs for watchOS devices "
-             "(i.e. build simulators only)",
-        action=arguments.action.optional_bool)
+             "(i.e. build simulators only)")
     run_build_group.add_argument(
         "--skip-build-watchos-simulator",
+        dest='build_watchos_simulator',
+        action=arguments.action.optional_false,
         help="skip building Swift stdlibs for watchOS simulator "
-             "(i.e. build devices only)",
-        action=arguments.action.optional_bool)
+             "(i.e. build devices only)")
 
     run_build_group.add_argument(
         "--skip-build-android",
-        help="skip building Swift stdlibs for Android",
-        action=arguments.action.optional_bool)
+        dest='build_android',
+        action=arguments.action.optional_false,
+        help="skip building Swift stdlibs for Android")
 
     run_build_group.add_argument(
         "--skip-build-benchmarks",
-        help="skip building Swift Benchmark Suite",
-        action=arguments.action.optional_bool)
+        dest='build_benchmarks',
+        action=arguments.action.optional_false,
+        help="skip building Swift Benchmark Suite")
 
     skip_test_group = parser.add_argument_group(
         title="Skip testing specified targets")
     skip_test_group.add_argument(
         "--skip-test-ios",
+        dest='test_ios',
+        action=arguments.action.optional_false,
         help="skip testing all iOS targets. Equivalent to specifying both "
-             "--skip-test-ios-simulator and --skip-test-ios-host",
-        action=arguments.action.optional_bool)
+             "--skip-test-ios-simulator and --skip-test-ios-host")
     skip_test_group.add_argument(
         "--skip-test-ios-simulator",
-        help="skip testing iOS simulator targets",
-        action=arguments.action.optional_bool)
+        dest='test_ios_simulator',
+        action=arguments.action.optional_false,
+        help="skip testing iOS simulator targets")
     skip_test_group.add_argument(
         "--skip-test-ios-32bit-simulator",
-        help="skip testing iOS 32 bit simulator targets",
-        action=arguments.action.optional_bool,
-        default=False)
+        dest='test_ios_32bit_simulator',
+        action=arguments.action.optional_false,
+        help="skip testing iOS 32 bit simulator targets")
     skip_test_group.add_argument(
         "--skip-test-ios-host",
+        dest='test_ios_device',
+        action=arguments.action.optional_false,
         help="skip testing iOS device targets on the host machine (the phone "
-             "itself)",
-        action=arguments.action.optional_bool)
+             "itself)")
     skip_test_group.add_argument(
         "--skip-test-tvos",
+        dest='test_tvos',
+        action=arguments.action.optional_false,
         help="skip testing all tvOS targets. Equivalent to specifying both "
-             "--skip-test-tvos-simulator and --skip-test-tvos-host",
-        action=arguments.action.optional_bool)
+             "--skip-test-tvos-simulator and --skip-test-tvos-host")
     skip_test_group.add_argument(
         "--skip-test-tvos-simulator",
-        help="skip testing tvOS simulator targets",
-        action=arguments.action.optional_bool)
+        dest='test_tvos_simulator',
+        action=arguments.action.optional_false,
+        help="skip testing tvOS simulator targets")
     skip_test_group.add_argument(
         "--skip-test-tvos-host",
+        dest='test_tvos_device',
+        action=arguments.action.optional_false,
         help="skip testing tvOS device targets on the host machine (the TV "
-             "itself)",
-        action=arguments.action.optional_bool)
+             "itself)")
     skip_test_group.add_argument(
         "--skip-test-watchos",
+        dest='test_watchos',
+        action=arguments.action.optional_false,
         help="skip testing all tvOS targets. Equivalent to specifying both "
-             "--skip-test-watchos-simulator and --skip-test-watchos-host",
-        action=arguments.action.optional_bool)
+             "--skip-test-watchos-simulator and --skip-test-watchos-host")
     skip_test_group.add_argument(
         "--skip-test-watchos-simulator",
-        help="skip testing watchOS simulator targets",
-        action=arguments.action.optional_bool)
+        dest='test_watchos_simulator',
+        action=arguments.action.optional_false,
+        help="skip testing watchOS simulator targets")
     skip_test_group.add_argument(
         "--skip-test-watchos-host",
+        dest='test_watchos_device',
+        action=arguments.action.optional_false,
         help="skip testing watchOS device targets on the host machine (the "
-             "watch itself)",
-        action=arguments.action.optional_bool)
+             "watch itself)")
     skip_test_group.add_argument(
         "--skip-test-android-host",
+        dest='test_android_device',
+        action=arguments.action.optional_false,
         help="skip testing Android device targets on the host machine (the "
-             "phone itself)",
-        action=arguments.action.optional_bool)
+             "phone itself)")
 
     parser.add_argument(
         "-i", "--ios",
diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py
index 2ca8d8d..58b6c2e 100644
--- a/utils/build_swift/tests/expected_options.py
+++ b/utils/build_swift/tests/expected_options.py
@@ -41,14 +41,23 @@
     'benchmark': False,
     'benchmark_num_o_iterations': 3,
     'benchmark_num_onone_iterations': 3,
+    'build_android': False,
     'build_args': [],
+    'build_benchmarks': True,
+    'build_cygwin': True,
     'build_foundation': False,
+    'build_freebsd': True,
+    'build_ios': True,
+    'build_ios_device': False,
+    'build_ios_simulator': False,
     'build_jobs': 4,
     'build_libdispatch': False,
     'build_libicu': False,
+    'build_linux': True,
     'build_llbuild': False,
     'build_lldb': False,
     'build_ninja': False,
+    'build_osx': True,
     'build_playgroundlogger': False,
     'build_playgroundsupport': False,
     'build_runtime_with_host_compiler': False,
@@ -60,7 +69,13 @@
     'build_swift_static_stdlib': False,
     'build_swift_stdlib_unittest_extra': False,
     'build_swiftpm': False,
+    'build_tvos': True,
+    'build_tvos_device': False,
+    'build_tvos_simulator': False,
     'build_variant': 'Debug',
+    'build_watchos': True,
+    'build_watchos_device': False,
+    'build_watchos_simulator': False,
     'build_xctest': False,
     'clang_compiler_version': None,
     'clang_profile_instr_use': None,
@@ -116,36 +131,6 @@
     'lto_type': None,
     'show_sdks': False,
     'skip_build': False,
-    'skip_build_android': True,
-    'skip_build_benchmarks': False,
-    'skip_build_cygwin': False,
-    'skip_build_freebsd': False,
-    'skip_build_ios': False,
-    'skip_build_ios_device': True,
-    'skip_build_ios_simulator': True,
-    'skip_build_linux': False,
-    'skip_build_osx': False,
-    'skip_build_tvos': False,
-    'skip_build_tvos_device': True,
-    'skip_build_tvos_simulator': True,
-    'skip_build_watchos': False,
-    'skip_build_watchos_device': True,
-    'skip_build_watchos_simulator': True,
-    'skip_test_android_host': True,
-    'skip_test_cygwin': True,
-    'skip_test_freebsd': True,
-    'skip_test_ios': True,
-    'skip_test_ios_32bit_simulator': False,
-    'skip_test_ios_host': True,
-    'skip_test_ios_simulator': True,
-    'skip_test_linux': True,
-    'skip_test_osx': True,
-    'skip_test_tvos': True,
-    'skip_test_tvos_host': True,
-    'skip_test_tvos_simulator': True,
-    'skip_test_watchos': True,
-    'skip_test_watchos_host': True,
-    'skip_test_watchos_simulator': True,
     'stdlib_deployment_targets': [
         'macosx-x86_64',
         'iphonesimulator-i386',
@@ -168,8 +153,23 @@
     'swift_user_visible_version': '4.1',
     'symbols_package': None,
     'test': None,
+    'test_android_device': False,
+    'test_cygwin': False,
+    'test_freebsd': False,
+    'test_ios': False,
+    'test_ios_32bit_simulator': True,
+    'test_ios_device': False,
+    'test_ios_simulator': False,
+    'test_linux': False,
     'test_optimize_for_size': None,
     'test_optimized': None,
+    'test_osx': False,
+    'test_tvos': False,
+    'test_tvos_device': False,
+    'test_tvos_simulator': False,
+    'test_watchos': False,
+    'test_watchos_device': False,
+    'test_watchos_simulator': False,
     'tvos': False,
     'tvos_all': False,
     'validation_test': None,
@@ -373,43 +373,43 @@
     ToggleOption('--libicu', dest='build_libicu'),
     ToggleOption('--long-test', dest='long_test'),
     ToggleOption('--show-sdks', dest='show_sdks'),
-    ToggleOption('--skip-build-android', dest='skip_build_android'),
-    ToggleOption('--skip-build-benchmarks', dest='skip_build_benchmarks'),
-    ToggleOption('--skip-build-cygwin', dest='skip_build_cygwin'),
-    ToggleOption('--skip-build-freebsd', dest='skip_build_freebsd'),
-    ToggleOption('--skip-build-ios', dest='skip_build_ios'),
-    ToggleOption('--skip-build-ios-device', dest='skip_build_ios_device'),
+    ToggleOption('--skip-build-android', dest='build_android'),
+    ToggleOption('--skip-build-benchmarks', dest='build_benchmarks'),
+    ToggleOption('--skip-build-cygwin', dest='build_cygwin'),
+    ToggleOption('--skip-build-freebsd', dest='build_freebsd'),
+    ToggleOption('--skip-build-ios', dest='build_ios'),
+    ToggleOption('--skip-build-ios-device', dest='build_ios_device'),
     ToggleOption('--skip-build-ios-simulator',
-                 dest='skip_build_ios_simulator'),
-    ToggleOption('--skip-build-linux', dest='skip_build_linux'),
-    ToggleOption('--skip-build-osx', dest='skip_build_osx'),
-    ToggleOption('--skip-build-tvos', dest='skip_build_tvos'),
-    ToggleOption('--skip-build-tvos-device', dest='skip_build_tvos_device'),
+                 dest='build_ios_simulator'),
+    ToggleOption('--skip-build-linux', dest='build_linux'),
+    ToggleOption('--skip-build-osx', dest='build_osx'),
+    ToggleOption('--skip-build-tvos', dest='build_tvos'),
+    ToggleOption('--skip-build-tvos-device', dest='build_tvos_device'),
     ToggleOption('--skip-build-tvos-simulator',
-                 dest='skip_build_tvos_simulator'),
-    ToggleOption('--skip-build-watchos', dest='skip_build_watchos'),
+                 dest='build_tvos_simulator'),
+    ToggleOption('--skip-build-watchos', dest='build_watchos'),
     ToggleOption('--skip-build-watchos-device',
-                 dest='skip_build_watchos_device'),
+                 dest='build_watchos_device'),
     ToggleOption('--skip-build-watchos-simulator',
-                 dest='skip_build_watchos_simulator'),
-    ToggleOption('--skip-test-android-host', dest='skip_test_android_host'),
-    ToggleOption('--skip-test-cygwin', dest='skip_test_cygwin'),
-    ToggleOption('--skip-test-freebsd', dest='skip_test_freebsd'),
-    ToggleOption('--skip-test-ios', dest='skip_test_ios'),
+                 dest='build_watchos_simulator'),
+    ToggleOption('--skip-test-android-host', dest='test_android_device'),
+    ToggleOption('--skip-test-cygwin', dest='test_cygwin'),
+    ToggleOption('--skip-test-freebsd', dest='test_freebsd'),
+    ToggleOption('--skip-test-ios', dest='test_ios'),
     ToggleOption('--skip-test-ios-32bit-simulator',
-                 dest='skip_test_ios_32bit_simulator'),
-    ToggleOption('--skip-test-ios-host', dest='skip_test_ios_host'),
-    ToggleOption('--skip-test-ios-simulator', dest='skip_test_ios_simulator'),
-    ToggleOption('--skip-test-linux', dest='skip_test_linux'),
-    ToggleOption('--skip-test-osx', dest='skip_test_osx'),
-    ToggleOption('--skip-test-tvos', dest='skip_test_tvos'),
-    ToggleOption('--skip-test-tvos-host', dest='skip_test_tvos_host'),
+                 dest='test_ios_32bit_simulator'),
+    ToggleOption('--skip-test-ios-host', dest='test_ios_device'),
+    ToggleOption('--skip-test-ios-simulator', dest='test_ios_simulator'),
+    ToggleOption('--skip-test-linux', dest='test_linux'),
+    ToggleOption('--skip-test-osx', dest='test_osx'),
+    ToggleOption('--skip-test-tvos', dest='test_tvos'),
+    ToggleOption('--skip-test-tvos-host', dest='test_tvos_device'),
     ToggleOption('--skip-test-tvos-simulator',
-                 dest='skip_test_tvos_simulator'),
-    ToggleOption('--skip-test-watchos', dest='skip_test_watchos'),
-    ToggleOption('--skip-test-watchos-host', dest='skip_test_watchos_host'),
+                 dest='test_tvos_simulator'),
+    ToggleOption('--skip-test-watchos', dest='test_watchos'),
+    ToggleOption('--skip-test-watchos-host', dest='test_watchos_device'),
     ToggleOption('--skip-test-watchos-simulator',
-                 dest='skip_test_watchos_simulator'),
+                 dest='test_watchos_simulator'),
     ToggleOption('--test', dest='test'),
     ToggleOption('--test-optimize-for-size', dest='test_optimize_for_size'),
     ToggleOption('--test-optimized', dest='test_optimized'),
diff --git a/utils/build_swift/tests/test_driver_arguments.py b/utils/build_swift/tests/test_driver_arguments.py
index 8fdb3ba..f5c9b94 100644
--- a/utils/build_swift/tests/test_driver_arguments.py
+++ b/utils/build_swift/tests/test_driver_arguments.py
@@ -170,6 +170,8 @@
                 self.parse_args([option.option_string])
                 self.assertNotEmpty(output)
 
+        return test
+
     @classmethod
     def _generate_int_option_test(cls, option):
         def test(self):
@@ -416,16 +418,16 @@
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--skip-build'])
 
-            self.assertTrue(args.skip_build_benchmarks)
+            self.assertFalse(args.build_benchmarks)
 
-            self.assertTrue(args.skip_build_linux)
-            self.assertTrue(args.skip_build_android)
-            self.assertTrue(args.skip_build_freebsd)
-            self.assertTrue(args.skip_build_cygwin)
-            self.assertTrue(args.skip_build_osx)
-            self.assertTrue(args.skip_build_ios)
-            self.assertTrue(args.skip_build_tvos)
-            self.assertTrue(args.skip_build_watchos)
+            self.assertFalse(args.build_linux)
+            self.assertFalse(args.build_android)
+            self.assertFalse(args.build_freebsd)
+            self.assertFalse(args.build_cygwin)
+            self.assertFalse(args.build_osx)
+            self.assertFalse(args.build_ios)
+            self.assertFalse(args.build_tvos)
+            self.assertFalse(args.build_watchos)
 
             self.assertFalse(args.build_foundation)
             self.assertFalse(args.build_libdispatch)
@@ -440,32 +442,32 @@
     def test_implied_defaults_skip_build_ios(self):
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--skip-build-ios'])
-            self.assertTrue(args.skip_build_ios_device)
-            self.assertTrue(args.skip_build_ios_simulator)
+            self.assertFalse(args.build_ios_device)
+            self.assertFalse(args.build_ios_simulator)
 
             # Also implies that the tests should be skipped
-            self.assertTrue(args.skip_test_ios_host)
-            self.assertTrue(args.skip_test_ios_simulator)
+            self.assertFalse(args.test_ios_device)
+            self.assertFalse(args.test_ios_simulator)
 
     def test_implied_defaults_skip_build_tvos(self):
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--skip-build-tvos'])
-            self.assertTrue(args.skip_build_tvos_device)
-            self.assertTrue(args.skip_build_tvos_simulator)
+            self.assertFalse(args.build_tvos_device)
+            self.assertFalse(args.build_tvos_simulator)
 
             # Also implies that the tests should be skipped
-            self.assertTrue(args.skip_test_tvos_host)
-            self.assertTrue(args.skip_test_tvos_simulator)
+            self.assertFalse(args.test_tvos_device)
+            self.assertFalse(args.test_tvos_simulator)
 
     def test_implied_defaults_skip_build_watchos(self):
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--skip-build-watchos'])
-            self.assertTrue(args.skip_build_watchos_device)
-            self.assertTrue(args.skip_build_watchos_simulator)
+            self.assertFalse(args.build_watchos_device)
+            self.assertFalse(args.build_watchos_simulator)
 
             # Also implies that the tests should be skipped
-            self.assertTrue(args.skip_test_watchos_host)
-            self.assertTrue(args.skip_test_watchos_simulator)
+            self.assertFalse(args.test_watchos_device)
+            self.assertFalse(args.test_watchos_simulator)
 
     def test_implied_defaults_validation_test(self):
         with self.assertNotRaises(ParserError):
@@ -490,48 +492,48 @@
                 '--long-test', '0',
             ])
 
-            self.assertTrue(args.skip_test_linux)
-            self.assertTrue(args.skip_test_freebsd)
-            self.assertTrue(args.skip_test_cygwin)
-            self.assertTrue(args.skip_test_osx)
-            self.assertTrue(args.skip_test_ios)
-            self.assertTrue(args.skip_test_tvos)
-            self.assertTrue(args.skip_test_watchos)
+            self.assertFalse(args.test_linux)
+            self.assertFalse(args.test_freebsd)
+            self.assertFalse(args.test_cygwin)
+            self.assertFalse(args.test_osx)
+            self.assertFalse(args.test_ios)
+            self.assertFalse(args.test_tvos)
+            self.assertFalse(args.test_watchos)
 
     def test_implied_defaults_skip_test_ios(self):
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--skip-test-ios'])
-            self.assertTrue(args.skip_test_ios_host)
-            self.assertTrue(args.skip_test_ios_simulator)
+            self.assertFalse(args.test_ios_device)
+            self.assertFalse(args.test_ios_simulator)
 
     def test_implied_defaults_skip_test_tvos(self):
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--skip-test-tvos'])
-            self.assertTrue(args.skip_test_tvos_host)
-            self.assertTrue(args.skip_test_tvos_simulator)
+            self.assertFalse(args.test_tvos_device)
+            self.assertFalse(args.test_tvos_simulator)
 
     def test_implied_defaults_skip_test_watchos(self):
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--skip-test-watchos'])
-            self.assertTrue(args.skip_test_watchos_host)
-            self.assertTrue(args.skip_test_watchos_simulator)
+            self.assertFalse(args.test_watchos_device)
+            self.assertFalse(args.test_watchos_simulator)
 
     def test_implied_defaults_skip_build_android(self):
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--android', '0'])
-            self.assertTrue(args.skip_test_android_host)
+            self.assertFalse(args.test_android_device)
 
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--skip-build-android'])
-            self.assertTrue(args.skip_test_android_host)
+            self.assertFalse(args.test_android_device)
 
     def test_implied_defaults_host_test(self):
         with self.assertNotRaises(ParserError):
             args = self.parse_args(['--host-test', '0'])
-            self.assertTrue(args.skip_test_ios_host)
-            self.assertTrue(args.skip_test_tvos_host)
-            self.assertTrue(args.skip_test_watchos_host)
-            self.assertTrue(args.skip_test_android_host)
+            self.assertFalse(args.test_ios_device)
+            self.assertFalse(args.test_tvos_device)
+            self.assertFalse(args.test_watchos_device)
+            self.assertFalse(args.test_android_device)
 
 
 if __name__ == '__main__':
diff --git a/utils/jobstats/jobstats.py b/utils/jobstats/jobstats.py
index 947a5e8..022ada9 100644
--- a/utils/jobstats/jobstats.py
+++ b/utils/jobstats/jobstats.py
@@ -61,11 +61,22 @@
         assert(self.is_driver_job())
         return self.driver_jobs_ran() + self.driver_jobs_skipped()
 
-    def merged_with(self, other):
+    def merged_with(self, other, merge_by="sum"):
         """Return a new JobStats, holding the merger of self and other"""
         merged_stats = {}
+        ops = {"sum": lambda a, b: a + b,
+               # Because 0 is also a sentinel on counters we do a modified
+               # "nonzero-min" here. Not ideal but best we can do.
+               "min": lambda a, b: (min(a, b)
+                                    if a != 0 and b != 0
+                                    else max(a, b)),
+               "max": lambda a, b: max(a, b)}
+        op = ops[merge_by]
         for k, v in self.stats.items() + other.stats.items():
-            merged_stats[k] = v + merged_stats.get(k, 0.0)
+            if k in merged_stats:
+                merged_stats[k] = op(v, merged_stats[k])
+            else:
+                merged_stats[k] = v
         merged_kind = self.jobkind
         if other.jobkind != merged_kind:
             merged_kind = "<merged>"
@@ -160,7 +171,7 @@
 
 
 def load_stats_dir(path, select_module=[], select_stat=[],
-                   exclude_timers=False):
+                   exclude_timers=False, **kwargs):
     """Loads all stats-files found in path into a list of JobStats objects"""
     jobstats = []
     auxpat = (r"(?P<module>[^-]+)-(?P<input>[^-]+)-(?P<triple>[^-]+)" +
@@ -213,7 +224,8 @@
     return jobstats
 
 
-def merge_all_jobstats(jobstats, select_module=[], group_by_module=False):
+def merge_all_jobstats(jobstats, select_module=[], group_by_module=False,
+                       merge_by="sum", **kwargs):
     """Does a pairwise merge of the elements of list of jobs"""
     m = None
     if len(select_module) > 0:
@@ -221,15 +233,16 @@
     if group_by_module:
         def keyfunc(j):
             return j.module
+        jobstats = list(jobstats)
         jobstats.sort(key=keyfunc)
         prefixed = []
         for mod, group in itertools.groupby(jobstats, keyfunc):
-            groupmerge = merge_all_jobstats(group)
+            groupmerge = merge_all_jobstats(group, merge_by=merge_by)
             prefixed.append(groupmerge.prefixed_by(mod))
         jobstats = prefixed
     for j in jobstats:
         if m is None:
             m = j
         else:
-            m = m.merged_with(j)
+            m = m.merged_with(j, merge_by=merge_by)
     return m
diff --git a/utils/process-stats-dir.py b/utils/process-stats-dir.py
index cbef76e..c9a3425 100755
--- a/utils/process-stats-dir.py
+++ b/utils/process-stats-dir.py
@@ -36,9 +36,6 @@
 def load_paired_stats_dirs(args):
     assert(len(args.remainder) == 2)
     paired_stats = []
-    mod = args.select_module
-    stat = args.select_stat
-    xt = args.exclude_timers
     (old, new) = args.remainder
     for p in sorted(os.listdir(old)):
         full_old = os.path.join(old, p)
@@ -46,14 +43,8 @@
         if not (os.path.exists(full_old) and os.path.isdir(full_old) and
                 os.path.exists(full_new) and os.path.isdir(full_new)):
             continue
-        old_stats = load_stats_dir(full_old,
-                                   select_module=mod,
-                                   select_stat=stat,
-                                   exclude_timers=xt)
-        new_stats = load_stats_dir(full_new,
-                                   select_module=mod,
-                                   select_stat=stat,
-                                   exclude_timers=xt)
+        old_stats = load_stats_dir(full_old, **vars(args))
+        new_stats = load_stats_dir(full_new, **vars(args))
         if len(old_stats) == 0 or len(new_stats) == 0:
             continue
         paired_stats.append((p, (old_stats, new_stats)))
@@ -63,22 +54,14 @@
 def write_catapult_trace(args):
     allstats = []
     for path in args.remainder:
-        allstats += load_stats_dir(path,
-                                   select_module=args.select_module,
-                                   select_stat=args.select_stat,
-                                   exclude_timers=args.exclude_timers)
+        allstats += load_stats_dir(path, **vars(args))
     json.dump([s.to_catapult_trace_obj() for s in allstats], args.output)
 
 
 def write_lnt_values(args):
     for d in args.remainder:
-        stats = load_stats_dir(d,
-                               select_module=args.select_module,
-                               select_stat=args.select_stat,
-                               exclude_timers=args.exclude_timers)
-        merged = merge_all_jobstats(stats,
-                                    select_module=args.select_module,
-                                    group_by_module=args.group_by_module)
+        stats = load_stats_dir(d, **vars(args))
+        merged = merge_all_jobstats(stats, **vars(args))
         j = merged.to_lnt_test_obj(args)
         if args.lnt_submit is None:
             json.dump(j, args.output, indent=4)
@@ -107,16 +90,11 @@
     out = csv.DictWriter(args.output, fieldnames, dialect='excel-tab')
     out.writeheader()
 
-    sel = args.select_module
     for (name, (oldstats, newstats)) in load_paired_stats_dirs(args):
         olddriver = merge_all_jobstats((x for x in oldstats
-                                        if x.is_driver_job()),
-                                       select_module=sel,
-                                       group_by_module=args.group_by_module)
+                                        if x.is_driver_job()), **vars(args))
         newdriver = merge_all_jobstats((x for x in newstats
-                                        if x.is_driver_job()),
-                                       select_module=sel,
-                                       group_by_module=args.group_by_module)
+                                        if x.is_driver_job()), **vars(args))
         if olddriver is None or newdriver is None:
             continue
         oldpct = olddriver.incrementality_percentage()
@@ -137,10 +115,7 @@
     out.writeheader()
 
     for path in args.remainder:
-        stats = load_stats_dir(path,
-                               select_module=args.select_module,
-                               select_stat=args.select_stat,
-                               exclude_timers=args.exclude_timers)
+        stats = load_stats_dir(path, **vars(args))
         for s in stats:
             if s.is_driver_job():
                 pct = s.incrementality_percentage()
@@ -223,16 +198,12 @@
     with open(args.set_csv_baseline, "wb") as f:
         out = csv.DictWriter(f, fieldnames, dialect='excel-tab',
                              quoting=csv.QUOTE_NONNUMERIC)
-        mod = args.select_module
-        stat = args.select_stat
-        xt = args.exclude_timers
         m = merge_all_jobstats((s for d in args.remainder
-                                for s in load_stats_dir(d,
-                                                        select_module=mod,
-                                                        select_stat=stat,
-                                                        exclude_timers=xt)),
-                               select_module=mod,
-                               group_by_module=args.group_by_module)
+                                for s in load_stats_dir(d, **vars(args))),
+                               **vars(args))
+        if m is None:
+            print "no stats found"
+            return 1
         changed = 0
         newepoch = int(time.time())
         for name in sorted(m.stats.keys()):
@@ -303,16 +274,9 @@
 
 def compare_to_csv_baseline(args):
     old_stats = read_stats_dict_from_csv(args.compare_to_csv_baseline)
-    mod = args.select_module
-    stat = args.select_stat
-    xt = args.exclude_timers
     m = merge_all_jobstats((s for d in args.remainder
-                            for s in load_stats_dir(d,
-                                                    select_module=mod,
-                                                    select_stat=stat,
-                                                    exclude_timers=xt)),
-                           select_module=mod,
-                           group_by_module=args.group_by_module)
+                            for s in load_stats_dir(d, **vars(args))),
+                           **vars(args))
     old_stats = dict((k, v) for (k, (_, v)) in old_stats.items())
     new_stats = m.stats
 
@@ -325,20 +289,10 @@
         raise ValueError("Expected exactly 2 stats-dirs")
 
     (old, new) = args.remainder
-    old_stats = merge_all_jobstats(
-        load_stats_dir(old,
-                       select_module=args.select_module,
-                       select_stat=args.select_stat,
-                       exclude_timers=args.exclude_timers),
-        select_module=args.select_module,
-        group_by_module=args.group_by_module)
-    new_stats = merge_all_jobstats(
-        load_stats_dir(new,
-                       select_module=args.select_module,
-                       select_stat=args.select_stat,
-                       exclude_timers=args.exclude_timers),
-        select_module=args.select_module,
-        group_by_module=args.group_by_module)
+    old_stats = merge_all_jobstats(load_stats_dir(old, **vars(args)),
+                                   **vars(args))
+    new_stats = merge_all_jobstats(load_stats_dir(new, **vars(args)),
+                                   **vars(args))
 
     return write_comparison(args, old_stats.stats, new_stats.stats)
 
@@ -395,6 +349,10 @@
                         default=False,
                         action="store_true",
                         help="Sort comparison results in descending order")
+    parser.add_argument("--merge-by",
+                        default="sum",
+                        type=str,
+                        help="Merge identical metrics by (sum|min|max)")
     parser.add_argument("--markdown",
                         default=False,
                         action="store_true",
diff --git a/utils/swift_build_support/swift_build_support/arguments.py b/utils/swift_build_support/swift_build_support/arguments.py
index 436ebb1..d55cfbe 100644
--- a/utils/swift_build_support/swift_build_support/arguments.py
+++ b/utils/swift_build_support/swift_build_support/arguments.py
@@ -190,6 +190,7 @@
                  option_strings,
                  dest,
                  default=False,
+                 const=True,
                  metavar="BOOL",
                  help=None):
         super(_OptionalBoolAction, self).__init__(
@@ -200,10 +201,40 @@
             nargs="?",
             type=type.bool,
             help=help,
-            const=True)
+            const=const)
 
     def __call__(self, parser, namespace, values, option_string=None):
         setattr(namespace, self.dest, values)
 
 
 _register(action, 'optional_bool', _OptionalBoolAction)
+
+
+class _OptionalTrueAction(_OptionalBoolAction):
+    """Action that defaults to False when absent and to True when parsed with
+    the option to override the value by passing a bool-like value as an
+    argument.
+    """
+
+    def __init__(self, *args, **kwargs):
+        kwargs['const'] = True
+        kwargs['default'] = kwargs.pop('default', False)
+        super(_OptionalTrueAction, self).__init__(*args, **kwargs)
+
+
+_register(action, 'optional_true', _OptionalTrueAction)
+
+
+class _OptionalFalseAction(_OptionalBoolAction):
+    """Action that defaults to True when absent and to False when parsed with
+    the option to override the value by passing a bool-like value as an
+    argument.
+    """
+
+    def __init__(self, *args, **kwargs):
+        kwargs['const'] = False
+        kwargs['default'] = kwargs.pop('default', True)
+        super(_OptionalFalseAction, self).__init__(*args, **kwargs)
+
+
+_register(action, 'optional_false', _OptionalFalseAction)
diff --git a/validation-test/Evolution/test_function_change_transparent_body.swift b/validation-test/Evolution/test_function_change_transparent_body.swift
index 577d875..10f8113 100644
--- a/validation-test/Evolution/test_function_change_transparent_body.swift
+++ b/validation-test/Evolution/test_function_change_transparent_body.swift
@@ -1,6 +1,9 @@
-// RUN: %target-resilience-test
+// RUN: %target-resilience-test-wmo
 // REQUIRES: executable_test
 
+// FIXME: shouldn't need -whole-module-optimization here; we need to fix the
+// frontend to merge serialized SIL functions from different translation units
+
 import StdlibUnittest
 import function_change_transparent_body