Merge pull request #16642 from rjmccall/abi-accessibility-4.2

[4.2] Perform value operations opaquely on ABI-inaccessible types
diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h
index 349b583..c132bd5 100644
--- a/include/swift/AST/Type.h
+++ b/include/swift/AST/Type.h
@@ -419,6 +419,12 @@
       });
   }
 
+  bool findIf(llvm::function_ref<bool (CanType)> fn) const {
+    return Type::findIf([&fn](Type t) {
+      return fn(CanType(t));
+    });
+  }
+
   // Provide a few optimized accessors that are really type-class queries.
 
   /// Do values of this type have reference semantics?
diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h
index 66251e7..db50b9e 100644
--- a/include/swift/SIL/SILModule.h
+++ b/include/swift/SIL/SILModule.h
@@ -634,6 +634,14 @@
     PGOReader = std::move(IPR);
   }
 
+  /// Can value operations (copies and destroys) on the given lowered type
+  /// be performed in this module?
+  bool isTypeABIAccessible(SILType type);
+
+  /// Can type metadata for the given formal type be fetched in
+  /// the given module?
+  bool isTypeMetadataAccessible(CanType type);
+
   /// \brief Run the SIL verifier to make sure that all Functions follow
   /// invariants.
   void verify() const;
diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h
index 26990c6..22b9626 100644
--- a/include/swift/SIL/TypeLowering.h
+++ b/include/swift/SIL/TypeLowering.h
@@ -96,35 +96,120 @@
   IsNotDependent = false,
   IsDependent = true
 };
-  
+
+/// Is a lowered SIL type trivial?  That is, are copies ultimately just
+/// bit-copies, and it takes no work to destroy a value?
+enum IsTrivial_t : bool {
+  IsNotTrivial = false,
+  IsTrivial = true
+};
+
+/// Is a lowered SIL type fixed-ABI?  That is, can the current context
+/// assign it a fixed size and alignment and perform value operations on it
+/// (such as copies, destroys, constructions, and projections) without
+/// metadata?
+///
+/// Note that a fully concrete type can be non-fixed-ABI without being
+/// itself resilient if it contains a subobject which is not fixed-ABI.
+///
+/// Also note that we're only concerned with the external value ABI here:
+/// resilient class types are still fixed-ABI, indirect enum cases do not
+/// affect the fixed-ABI-ness of the enum, and so on.
+enum IsFixedABI_t : bool {
+  IsNotFixedABI = false,
+  IsFixedABI = true
+};
+
+/// Is a lowered SIL type address-only?  That is, is the current context
+/// required to keep the value in memory for some reason?
+///
+/// A type might be address-only because:
+///
+///   - it is not fixed-size (e.g. because it is a resilient type) and
+///     therefore cannot be loaded into a statically-boundable set of
+///     registers; or
+///
+///   - it is semantically bound to memory, either because its storage
+///     address is used by the language runtime to implement its semantics
+///     (as with a weak reference) or because its representation is somehow
+///     address-dependent (as with something like a relative pointer).
+///
+/// An address-only type can be fixed-layout and/or trivial.
+/// A non-fixed-layout type is always address-only.
+enum IsAddressOnly_t : bool {
+  IsNotAddressOnly = false,
+  IsAddressOnly = true
+};
+
+/// Is this type somewhat like a reference-counted type?
+enum IsReferenceCounted_t : bool {
+  IsNotReferenceCounted = false,
+  IsReferenceCounted = true
+};
+
 /// Extended type information used by SIL.
 class TypeLowering {
 public:
-  enum IsTrivial_t : bool { IsNotTrivial, IsTrivial };
-  enum IsAddressOnly_t : bool { IsNotAddressOnly, IsAddressOnly };
-  enum IsReferenceCounted_t : bool {
-    IsNotReferenceCounted,
-    IsReferenceCounted
+  class RecursiveProperties {
+    // These are chosen so that bitwise-or merges the flags properly.
+    enum : unsigned {
+      NonTrivialFlag     = 1 << 0,
+      NonFixedABIFlag    = 1 << 1,
+      AddressOnlyFlag    = 1 << 2,
+    };
+
+    uint8_t Flags;
+  public:
+    /// Construct a default RecursiveProperties, which corresponds to
+    /// a trivial, loadable, fixed-layout type.
+    constexpr RecursiveProperties() : Flags(0) {}
+
+    constexpr RecursiveProperties(IsTrivial_t isTrivial,
+                                  IsFixedABI_t isFixedABI,
+                                  IsAddressOnly_t isAddressOnly)
+      : Flags((isTrivial ? 0U : NonTrivialFlag) | 
+              (isAddressOnly ? AddressOnlyFlag : 0U) |
+              (isFixedABI ? 0U : NonFixedABIFlag)) {}
+
+    static constexpr RecursiveProperties forReference() {
+      return {IsNotTrivial, IsFixedABI, IsNotAddressOnly};
+    }
+
+    static constexpr RecursiveProperties forOpaque() {
+      return {IsNotTrivial, IsNotFixedABI, IsAddressOnly};
+    }
+
+    void addSubobject(RecursiveProperties other) {
+      Flags |= other.Flags;
+    }
+
+    IsTrivial_t isTrivial() const {
+      return IsTrivial_t((Flags & NonTrivialFlag) == 0);
+    }
+    IsFixedABI_t isFixedABI() const {
+      return IsFixedABI_t((Flags & NonFixedABIFlag) == 0);
+    }
+    IsAddressOnly_t isAddressOnly() const {
+      return IsAddressOnly_t((Flags & AddressOnlyFlag) != 0);
+    }
+
+    void setNonTrivial() { Flags |= NonTrivialFlag; }
+    void setNonFixedABI() { Flags |= NonFixedABIFlag; }
+    void setAddressOnly() { Flags |= AddressOnlyFlag; }
   };
 
 private:
   /// The SIL type of values with this Swift type.
   SILType LoweredType;
 
-  enum : unsigned {
-    IsTrivialFlag     = 0x1,
-    IsAddressOnlyFlag = 0x2,
-    IsReferenceCountedFlag = 0x4,
-  };
-  unsigned Flags;
+  RecursiveProperties Properties;
+  unsigned ReferenceCounted : 1;
 
 protected:  
-  TypeLowering(SILType type, IsTrivial_t isTrivial,
-               IsAddressOnly_t isAddressOnly,
+  TypeLowering(SILType type, RecursiveProperties properties,
                IsReferenceCounted_t isRefCounted)
-    : LoweredType(type), Flags((isTrivial ? IsTrivialFlag : 0U) | 
-                               (isAddressOnly ? IsAddressOnlyFlag : 0U) |
-                               (isRefCounted ? IsReferenceCountedFlag : 0U)) {}
+    : LoweredType(type), Properties(properties),
+      ReferenceCounted(isRefCounted) {}
 
 public:
   TypeLowering(const TypeLowering &) = delete;
@@ -143,12 +228,16 @@
   /// This is independent of whether the SIL result is address type.
   bool isFormallyReturnedIndirectly() const { return isAddressOnly(); }
 
+  RecursiveProperties getRecursiveProperties() const {
+    return Properties;
+  }
+
   /// isAddressOnly - Returns true if the type is an address-only type. A type
   /// is address-only if it is a resilient value type, or if it is a fragile
   /// value type with a resilient member. In either case, the full layout of
   /// values of the type is unavailable to the compiler.
   bool isAddressOnly() const {
-    return Flags & IsAddressOnlyFlag;
+    return Properties.isAddressOnly();
   }
   /// isLoadable - Returns true if the type is loadable, in other words, its
   /// full layout is available to the compiler. This is the inverse of
@@ -156,17 +245,24 @@
   bool isLoadable() const {
     return !isAddressOnly();
   }
+
+  /// isFixedABI - Returns true if the type has a known fixed layout.
+  /// If this is true, value operations on the type can be performed even if
+  /// the type is inaccessible.
+  bool isFixedABI() const {
+    return Properties.isFixedABI();
+  }
   
   /// Returns true if the type is trivial, meaning it is a loadable
   /// value type with no reference type members that require releasing.
   bool isTrivial() const {
-    return Flags & IsTrivialFlag;
+    return Properties.isTrivial();
   }
   
   /// Returns true if the type is a scalar reference-counted reference, which
   /// can be retained and released.
   bool isReferenceCounted() const {
-    return Flags & IsReferenceCountedFlag;
+    return ReferenceCounted;
   }
 
   /// getLoweredType - Get the type used to represent values of the Swift type
diff --git a/lib/IRGen/FixedTypeInfo.h b/lib/IRGen/FixedTypeInfo.h
index 9cabc7f..9f13336 100644
--- a/lib/IRGen/FixedTypeInfo.h
+++ b/lib/IRGen/FixedTypeInfo.h
@@ -50,8 +50,7 @@
                 Alignment align, IsPOD_t pod, IsBitwiseTakable_t bt,
                 IsFixedSize_t alwaysFixedSize,
                 SpecialTypeInfoKind stik = STIK_Fixed)
-      : TypeInfo(type, align, pod, bt, alwaysFixedSize, stik),
-        StorageSize(size), SpareBits(spareBits) {
+      : TypeInfo(type, align, pod, bt, alwaysFixedSize, IsABIAccessible, stik),        StorageSize(size), SpareBits(spareBits) {
     assert(SpareBits.size() == size.getValueInBits());
     assert(isFixedSize());
   }
@@ -61,7 +60,7 @@
                 Alignment align, IsPOD_t pod, IsBitwiseTakable_t bt,
                 IsFixedSize_t alwaysFixedSize,
                 SpecialTypeInfoKind stik = STIK_Fixed)
-      : TypeInfo(type, align, pod, bt, alwaysFixedSize, stik),
+      : TypeInfo(type, align, pod, bt, alwaysFixedSize, IsABIAccessible, stik),
         StorageSize(size), SpareBits(std::move(spareBits)) {
     assert(SpareBits.size() == size.getValueInBits());
     assert(isFixedSize());
@@ -70,6 +69,7 @@
 public:
   // This is useful for metaprogramming.
   static bool isFixed() { return true; }
+  static IsABIAccessible_t isABIAccessible() { return IsABIAccessible; }
 
   /// Whether this type is known to be empty.
   bool isKnownEmpty(ResilienceExpansion expansion) const {
diff --git a/lib/IRGen/GenArchetype.cpp b/lib/IRGen/GenArchetype.cpp
index f99a067..27f50f8 100644
--- a/lib/IRGen/GenArchetype.cpp
+++ b/lib/IRGen/GenArchetype.cpp
@@ -89,7 +89,8 @@
 class OpaqueArchetypeTypeInfo
   : public ResilientTypeInfo<OpaqueArchetypeTypeInfo>
 {
-  OpaqueArchetypeTypeInfo(llvm::Type *type) : ResilientTypeInfo(type) {}
+  OpaqueArchetypeTypeInfo(llvm::Type *type)
+    : ResilientTypeInfo(type, IsABIAccessible) {}
 
 public:
   static const OpaqueArchetypeTypeInfo *create(llvm::Type *type) {
diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp
index 6fb4ce1..1e98bb6 100644
--- a/lib/IRGen/GenEnum.cpp
+++ b/lib/IRGen/GenEnum.cpp
@@ -142,7 +142,8 @@
   return IGM.getSize(Size(uintptr_t(flags)));
 }
 
-SpareBitVector getBitVectorFromAPInt(const APInt &bits, unsigned startBit = 0) {
+static SpareBitVector
+getBitVectorFromAPInt(const APInt &bits, unsigned startBit = 0) {
   if (startBit == 0) {
     return SpareBitVector::fromAPInt(bits);
   }
@@ -152,6 +153,28 @@
   return result;
 }
 
+static IsABIAccessible_t
+areElementsABIAccessible(ArrayRef<EnumImplStrategy::Element> elts) {
+  for (auto &elt : elts) {
+    if (!elt.ti->isABIAccessible())
+      return IsNotABIAccessible;
+  }
+  return IsABIAccessible;
+}
+
+EnumImplStrategy::EnumImplStrategy(IRGenModule &IGM,
+                                   TypeInfoKind tik,
+                                   IsFixedSize_t alwaysFixedSize,
+                                   unsigned NumElements,
+                                   std::vector<Element> &&eltsWithPayload,
+                                   std::vector<Element> &&eltsWithNoPayload)
+  : ElementsWithPayload(std::move(eltsWithPayload)),
+    ElementsWithNoPayload(std::move(eltsWithNoPayload)),
+    IGM(IGM), TIK(tik), AlwaysFixedSize(alwaysFixedSize),
+    ElementsAreABIAccessible(areElementsABIAccessible(ElementsWithPayload)),
+    NumElements(NumElements) {
+}
+
 void irgen::EnumImplStrategy::initializeFromParams(IRGenFunction &IGF,
                                                    Explosion &params,
                                                    Address dest, SILType T,
@@ -465,7 +488,9 @@
     void assignWithCopy(IRGenFunction &IGF, Address dest, Address src,
                         SILType T, bool isOutlined) const override {
       if (!getSingleton()) return;
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitAssignWithCopyCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         dest = getSingletonAddress(IGF, dest);
         src = getSingletonAddress(IGF, src);
         getSingleton()->assignWithCopy(
@@ -478,7 +503,9 @@
     void assignWithTake(IRGenFunction &IGF, Address dest, Address src,
                         SILType T, bool isOutlined) const override {
       if (!getSingleton()) return;
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitAssignWithTakeCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         dest = getSingletonAddress(IGF, dest);
         src = getSingletonAddress(IGF, src);
         getSingleton()->assignWithTake(
@@ -498,7 +525,9 @@
     void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src,
                             SILType T, bool isOutlined) const override {
       if (!getSingleton()) return;
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitInitializeWithCopyCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         dest = getSingletonAddress(IGF, dest);
         src = getSingletonAddress(IGF, src);
         getSingleton()->initializeWithCopy(
@@ -511,7 +540,9 @@
     void initializeWithTake(IRGenFunction &IGF, Address dest, Address src,
                             SILType T, bool isOutlined) const override {
       if (!getSingleton()) return;
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitInitializeWithTakeCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         dest = getSingletonAddress(IGF, dest);
         src = getSingletonAddress(IGF, src);
         getSingleton()->initializeWithTake(
@@ -555,7 +586,9 @@
                  bool isOutlined) const override {
       if (getSingleton() &&
           !getSingleton()->isPOD(ResilienceExpansion::Maximal)) {
-        if (isOutlined || T.hasOpenedExistential()) {
+        if (!ElementsAreABIAccessible) {
+          emitDestroyCall(IGF, T, addr);
+        } else if (isOutlined || T.hasOpenedExistential()) {
           getSingleton()->destroy(IGF, getSingletonAddress(IGF, addr),
                                   getSingletonType(IGF.IGM, T), isOutlined);
         } else {
@@ -1478,6 +1511,8 @@
       Normal,
       /// The payload is POD, so copying is bitwise, and destruction is a noop.
       POD,
+      /// The payload type is ABI-inaccessible, so we can't recurse.
+      ABIInaccessible,
       /// The payload is a single reference-counted value, and we have
       /// a single no-payload case which uses the null extra inhabitant, so
       /// copy and destroy can pass through to retain and release entry
@@ -1595,7 +1630,9 @@
 
       // If the payload is POD, then we can use POD value semantics.
       auto &payloadTI = *ElementsWithPayload[0].ti;
-      if (payloadTI.isPOD(ResilienceExpansion::Maximal)) {
+      if (!payloadTI.isABIAccessible()) {
+        CopyDestroyKind = ABIInaccessible;
+      } else if (payloadTI.isPOD(ResilienceExpansion::Maximal)) {
         CopyDestroyKind = POD;
       // If the payload is a single refcounted pointer and we have a single
       // empty case, then the layout will be a nullable pointer, and we can
@@ -2217,6 +2254,7 @@
         return IGM.getReferenceType(Refcounting);
       case POD:
       case Normal:
+      case ABIInaccessible:
         llvm_unreachable("not a refcounted payload");
       }
 
@@ -2231,6 +2269,7 @@
         return;
       case POD:
       case Normal:
+      case ABIInaccessible:
         llvm_unreachable("not a refcounted payload");
       }
     }
@@ -2243,6 +2282,7 @@
         return;
       case POD:
       case Normal:
+      case ABIInaccessible:
         llvm_unreachable("not a refcounted payload");
       }
     }
@@ -2255,6 +2295,7 @@
         return;
       case POD:
       case Normal:
+      case ABIInaccessible:
         llvm_unreachable("not a refcounted payload");
       }
     }
@@ -2281,6 +2322,9 @@
         reexplode(IGF, src, dest);
         return;
 
+      case ABIInaccessible:
+        llvm_unreachable("ABI-inaccessible type cannot be loadable");
+
       case Normal: {
         if (!copyEnumFunction)
           copyEnumFunction = emitCopyEnumFunction(IGF.IGM, loweredType);
@@ -2315,6 +2359,9 @@
         (void)src.claim(getExplosionSize());
         return;
 
+      case ABIInaccessible:
+        llvm_unreachable("ABI-inaccessible type cannot be loadable");
+
       case Normal: {
         if (!consumeEnumFunction)
           consumeEnumFunction = emitConsumeEnumFunction(IGF.IGM, loweredType);
@@ -2345,6 +2392,9 @@
         (void)src.claim(getExplosionSize());
         return;
 
+      case ABIInaccessible:
+        llvm_unreachable("ABI-inaccessible type cannot be loadable");
+
       case Normal: {
         // Check that we have a payload.
         EnumPayload payload; llvm::Value *extraTag;
@@ -2385,11 +2435,16 @@
       if (CopyDestroyKind == POD) {
         return;
       }
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        return emitDestroyCall(IGF, T, addr);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         switch (CopyDestroyKind) {
         case POD:
           return;
 
+        case ABIInaccessible:
+          llvm_unreachable("should already have been handled");
+
         case Normal: {
           // Check that there is a payload at the address.
           llvm::BasicBlock *endBB = testEnumContainsPayload(IGF, addr, T);
@@ -2475,6 +2530,9 @@
       case POD:
         return emitPrimitiveCopy(IGF, dest, src, T);
 
+      case ABIInaccessible:
+        llvm_unreachable("shouldn't get here");
+
       case Normal: {
         llvm::BasicBlock *endBB = llvm::BasicBlock::Create(C);
 
@@ -2582,6 +2640,9 @@
       case POD:
         return emitPrimitiveCopy(IGF, dest, src, T);
 
+      case ABIInaccessible:
+        llvm_unreachable("shouldn't get here");
+
       case Normal: {
         llvm::BasicBlock *endBB = llvm::BasicBlock::Create(C);
 
@@ -2638,7 +2699,9 @@
   public:
     void assignWithCopy(IRGenFunction &IGF, Address dest, Address src,
                         SILType T, bool isOutlined) const override {
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitAssignWithCopyCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         emitIndirectAssign(IGF, dest, src, T, IsNotTake, isOutlined);
       } else {
         callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsNotTake);
@@ -2647,7 +2710,9 @@
 
     void assignWithTake(IRGenFunction &IGF, Address dest, Address src,
                         SILType T, bool isOutlined) const override {
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitAssignWithTakeCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         emitIndirectAssign(IGF, dest, src, T, IsTake, isOutlined);
       } else {
         callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsTake);
@@ -2656,7 +2721,9 @@
 
     void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src,
                             SILType T, bool isOutlined) const override {
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitInitializeWithCopyCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         emitIndirectInitialize(IGF, dest, src, T, IsNotTake, isOutlined);
       } else {
         callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsNotTake);
@@ -2665,7 +2732,9 @@
 
     void initializeWithTake(IRGenFunction &IGF, Address dest, Address src,
                             SILType T, bool isOutlined) const override {
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitInitializeWithTakeCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         emitIndirectInitialize(IGF, dest, src, T, IsTake, isOutlined);
       } else {
         callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsTake);
@@ -2932,6 +3001,8 @@
       /// The payloads are all POD, so copying is bitwise, and destruction is a
       /// noop.
       POD,
+      /// One or more of the payloads is ABI-inaccessible, so we cannot recurse.
+      ABIInaccessible,
       /// The payloads are all bitwise-takable, but have no other special
       /// shared layout.
       BitwiseTakable,
@@ -3073,7 +3144,9 @@
         }
       }
 
-      if (allPOD) {
+      if (!ElementsAreABIAccessible) {
+        CopyDestroyKind = ABIInaccessible;
+      } else if (allPOD) {
         assert(!allSingleRefcount && "pod *and* refcounted?!");
         CopyDestroyKind = POD;
       // FIXME: Memory corruption issues arise when enabling this for mixed
@@ -3168,6 +3241,7 @@
       case POD:
       case BitwiseTakable:
       case Normal:
+      case ABIInaccessible:
         llvm_unreachable("not a refcounted payload");
       }
 
@@ -3183,6 +3257,7 @@
       case POD:
       case BitwiseTakable:
       case Normal:
+      case ABIInaccessible:
         llvm_unreachable("not a refcounted payload");
       }
     }
@@ -3196,6 +3271,7 @@
       case POD:
       case BitwiseTakable:
       case Normal:
+      case ABIInaccessible:
         llvm_unreachable("not a refcounted payload");
       }
     }
@@ -3209,6 +3285,7 @@
       case POD:
       case BitwiseTakable:
       case Normal:
+      case ABIInaccessible:
         llvm_unreachable("not a refcounted payload");
       }
     }
@@ -3989,6 +4066,9 @@
         reexplode(IGF, src, dest);
         return;
 
+      case ABIInaccessible:
+        llvm_unreachable("ABI-accessible type cannot be loadable");
+
       case BitwiseTakable:
       case Normal: {
         if (!copyEnumFunction)
@@ -4031,6 +4111,9 @@
         (void)src.claim(getExplosionSize());
         return;
 
+      case ABIInaccessible:
+        llvm_unreachable("ABI-accessible type cannot be loadable");
+
       case BitwiseTakable:
       case Normal: {
         if (!consumeEnumFunction)
@@ -4064,6 +4147,9 @@
         (void)src.claim(getExplosionSize());
         return;
 
+      case ABIInaccessible:
+        llvm_unreachable("ABI-accessible type cannot be loadable");
+
       case BitwiseTakable:
       case Normal: {
         auto parts = destructureAndTagLoadableEnum(IGF, src);
@@ -4103,6 +4189,9 @@
       case POD:
         return emitPrimitiveCopy(IGF, dest, src, T);
 
+      case ABIInaccessible:
+        llvm_unreachable("shouldn't get here");
+
       case BitwiseTakable:
       case TaggedRefcounted:
       case Normal: {
@@ -4153,6 +4242,9 @@
       case POD:
         return emitPrimitiveCopy(IGF, dest, src, T);
 
+      case ABIInaccessible:
+        llvm_unreachable("shouldn't get here");
+
       case BitwiseTakable:
       case TaggedRefcounted:
         // Takes can be done by primitive copy in these case.
@@ -4277,7 +4369,9 @@
   public:
     void assignWithCopy(IRGenFunction &IGF, Address dest, Address src,
                         SILType T, bool isOutlined) const override {
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitAssignWithCopyCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         emitIndirectAssign(IGF, dest, src, T, IsNotTake, isOutlined);
       } else {
         callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsNotTake);
@@ -4286,7 +4380,9 @@
 
     void assignWithTake(IRGenFunction &IGF, Address dest, Address src,
                         SILType T, bool isOutlined) const override {
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitAssignWithTakeCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         emitIndirectAssign(IGF, dest, src, T, IsTake, isOutlined);
       } else {
         callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsTake);
@@ -4295,7 +4391,9 @@
 
     void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src,
                             SILType T, bool isOutlined) const override {
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitInitializeWithCopyCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         emitIndirectInitialize(IGF, dest, src, T, IsNotTake, isOutlined);
       } else {
         callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsNotTake);
@@ -4304,7 +4402,9 @@
 
     void initializeWithTake(IRGenFunction &IGF, Address dest, Address src,
                             SILType T, bool isOutlined) const override {
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitInitializeWithTakeCall(IGF, T, dest, src);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         emitIndirectInitialize(IGF, dest, src, T, IsTake, isOutlined);
       } else {
         callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsTake);
@@ -4332,11 +4432,16 @@
       if (CopyDestroyKind == POD) {
         return;
       }
-      if (isOutlined || T.hasOpenedExistential()) {
+      if (!ElementsAreABIAccessible) {
+        emitDestroyCall(IGF, T, addr);
+      } else if (isOutlined || T.hasOpenedExistential()) {
         switch (CopyDestroyKind) {
         case POD:
           return;
 
+        case ABIInaccessible:
+          llvm_unreachable("shouldn't get here");
+
         case BitwiseTakable:
         case Normal:
         case TaggedRefcounted: {
@@ -5395,8 +5500,9 @@
                          llvm::Type *irTy,
                          Alignment align,
                          IsPOD_t pod,
-                         IsBitwiseTakable_t bt)
-      : EnumTypeInfoBase(strategy, irTy, align, pod, bt) {}
+                         IsBitwiseTakable_t bt,
+                         IsABIAccessible_t abiAccessible)
+      : EnumTypeInfoBase(strategy, irTy, align, pod, bt, abiAccessible) {}
   };
 
   /// TypeInfo for dynamically-sized enum types.
@@ -5405,8 +5511,8 @@
   {
   public:
     ResilientEnumTypeInfo(EnumImplStrategy &strategy,
-                          llvm::Type *irTy)
-      : EnumTypeInfoBase(strategy, irTy) {}
+                          llvm::Type *irTy, IsABIAccessible_t abiAccessible)
+      : EnumTypeInfoBase(strategy, irTy, abiAccessible) {}
   };
 } // end anonymous namespace
 
@@ -5479,10 +5585,13 @@
       auto alignment = eltTI.getBestKnownAlignment();
       applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/false,
                             alignment);
+      auto enumAccessible =
+        IsABIAccessible_t(TC.IGM.getSILModule().isTypeABIAccessible(Type));
       return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy,
                              alignment,
                              eltTI.isPOD(ResilienceExpansion::Maximal),
-                             eltTI.isBitwiseTakable(ResilienceExpansion::Maximal)));
+                             eltTI.isBitwiseTakable(ResilienceExpansion::Maximal),
+                             enumAccessible));
     } else {
       auto &fixedEltTI = cast<FixedTypeInfo>(eltTI);
       auto alignment = fixedEltTI.getFixedAlignment();
@@ -5668,10 +5777,14 @@
   applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/false,
                         alignment);
   
+  auto enumAccessible =
+    IsABIAccessible_t(TC.IGM.getSILModule().isTypeABIAccessible(Type));
+
   return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy,
          alignment,
          payloadTI.isPOD(ResilienceExpansion::Maximal),
-         payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal)));
+         payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal),
+         enumAccessible));
 }
 
 TypeInfo *
@@ -5866,9 +5979,13 @@
   
   applyLayoutAttributes(TC.IGM, Type.getSwiftRValueType(), /*fixed*/false,
                         alignment);
+
+  auto enumAccessible =
+    IsABIAccessible_t(TC.IGM.getSILModule().isTypeABIAccessible(Type));
   
   return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy,
-                                                       alignment, pod, bt));
+                                                       alignment, pod, bt,
+                                                       enumAccessible));
 }
 
 TypeInfo *
@@ -5887,7 +6004,10 @@
                                                   SILType Type,
                                                   EnumDecl *theEnum,
                                                   llvm::StructType *enumTy) {
-  return registerEnumTypeInfo(new ResilientEnumTypeInfo(*this, enumTy));
+  auto abiAccessible =
+    IsABIAccessible_t(TC.IGM.getSILModule().isTypeABIAccessible(Type));
+  return registerEnumTypeInfo(
+                       new ResilientEnumTypeInfo(*this, enumTy, abiAccessible));
 }
 
 const TypeInfo *TypeConverter::convertEnumType(TypeBase *key, CanType type,
diff --git a/lib/IRGen/GenEnum.h b/lib/IRGen/GenEnum.h
index 8531f2b..c8c29aa 100644
--- a/lib/IRGen/GenEnum.h
+++ b/lib/IRGen/GenEnum.h
@@ -138,6 +138,7 @@
   const TypeInfo *TI = nullptr;
   TypeInfoKind TIK;
   IsFixedSize_t AlwaysFixedSize;
+  IsABIAccessible_t ElementsAreABIAccessible;
   unsigned NumElements;
   
   EnumImplStrategy(IRGenModule &IGM,
@@ -145,12 +146,7 @@
                    IsFixedSize_t alwaysFixedSize,
                    unsigned NumElements,
                    std::vector<Element> &&ElementsWithPayload,
-                   std::vector<Element> &&ElementsWithNoPayload)
-  : ElementsWithPayload(std::move(ElementsWithPayload)),
-    ElementsWithNoPayload(std::move(ElementsWithNoPayload)),
-    IGM(IGM), TIK(tik), AlwaysFixedSize(alwaysFixedSize),
-    NumElements(NumElements)
-  {}
+                   std::vector<Element> &&ElementsWithNoPayload);
   
   /// Save the TypeInfo created for the enum.
   TypeInfo *registerEnumTypeInfo(TypeInfo *mutableTI) {
diff --git a/lib/IRGen/GenOpaque.cpp b/lib/IRGen/GenOpaque.cpp
index a336b94..536fa32 100644
--- a/lib/IRGen/GenOpaque.cpp
+++ b/lib/IRGen/GenOpaque.cpp
@@ -388,6 +388,11 @@
   return witness;
 }
 
+static llvm::Value *emitCastToOpaquePtr(IRGenFunction &IGF,
+                                        Address object) {
+  return IGF.Builder.CreateBitCast(object.getAddress(), IGF.IGM.OpaquePtrTy);
+}
+
 llvm::Value *irgen::emitInitializeBufferWithCopyOfBufferCall(IRGenFunction &IGF,
                                                      SILType T,
                                                      Address destBuffer,
@@ -529,10 +534,8 @@
                                             Address srcObject,
                                             llvm::Value *count) {
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto dest =
-      IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy);
-  auto src =
-      IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithCopyFn(),
                          {dest, src, count, metadata});
 }
@@ -544,10 +547,8 @@
                                                    Address srcObject,
                                                    llvm::Value *count) {
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto dest =
-      IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy);
-  auto src =
-      IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeNoAliasFn(),
                          {dest, src, count, metadata});
 }
@@ -559,10 +560,8 @@
                                             Address srcObject,
                                             llvm::Value *count) {
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto dest =
-      IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy);
-  auto src =
-      IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeFrontToBackFn(),
                          {dest, src, count, metadata});
 }
@@ -574,10 +573,8 @@
                                             Address srcObject,
                                             llvm::Value *count) {
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto dest =
-      IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy);
-  auto src =
-      IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeBackToFrontFn(),
                          {dest, src, count, metadata});
 }
@@ -590,8 +587,9 @@
   llvm::Value *metadata;
   auto copyFn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                                 ValueWitness::AssignWithCopy);
-  IGF.Builder.CreateCall(copyFn,
-      {destObject.getAddress(), srcObject.getAddress(), metadata});
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
+  IGF.Builder.CreateCall(copyFn, {dest, src, metadata});
 }
 
 /// Emit a call to do an 'assignWithCopy' operation.
@@ -601,8 +599,9 @@
                                    Address srcObject) {
   auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata,
                                          ValueWitness::AssignWithCopy);
-  IGF.Builder.CreateCall(copyFn,
-      {destObject.getAddress(), srcObject.getAddress(), metadata});
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
+  IGF.Builder.CreateCall(copyFn, {dest, src, metadata});
 }
 
 /// Emit a call to do an 'arrayAssignWithCopyNoAlias' operation.
@@ -610,10 +609,8 @@
                                           Address destObject, Address srcObject,
                                           llvm::Value *count) {
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto dest =
-      IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy);
-  auto src =
-      IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyNoAliasFn(),
                          {dest, src, count, metadata});
 }
@@ -625,10 +622,8 @@
                                                    Address srcObject,
                                                    llvm::Value *count) {
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto dest =
-      IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy);
-  auto src =
-      IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyFrontToBackFn(),
                          {dest, src, count, metadata});
 }
@@ -640,10 +635,8 @@
                                                    Address srcObject,
                                                    llvm::Value *count) {
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto dest =
-      IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy);
-  auto src =
-      IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyBackToFrontFn(),
                          {dest, src, count, metadata});
 }
@@ -656,8 +649,9 @@
   llvm::Value *metadata;
   auto copyFn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                                 ValueWitness::AssignWithTake);
-  IGF.Builder.CreateCall(copyFn,
-      {destObject.getAddress(), srcObject.getAddress(), metadata});
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
+  IGF.Builder.CreateCall(copyFn, {dest, src, metadata});
 }
 
 /// Emit a call to do an 'arrayAssignWithTake' operation.
@@ -665,10 +659,8 @@
                                         Address destObject, Address srcObject,
                                         llvm::Value *count) {
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto dest =
-      IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy);
-  auto src =
-      IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithTakeFn(),
                          {dest, src, count, metadata});
 }
@@ -683,8 +675,7 @@
     return;
 
   auto metadata = IGF.emitTypeMetadataRefForLayout(T);
-  auto obj =
-      IGF.Builder.CreateBitCast(object.getAddress(), IGF.IGM.OpaquePtrTy);
+  auto obj = emitCastToOpaquePtr(IGF, object);
   IGF.Builder.CreateCall(IGF.IGM.getArrayDestroyFn(), {obj, count, metadata});
 }
 
@@ -696,9 +687,10 @@
   llvm::Value *metadata;
   auto fn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                        ValueWitness::GetExtraInhabitantIndex);
-  
+
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   llvm::CallInst *call =
-    IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata});
+    IGF.Builder.CreateCall(fn, {src, metadata});
   return call;
 }
 
@@ -711,8 +703,9 @@
   llvm::Value *metadata;
   auto fn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                           ValueWitness::StoreExtraInhabitant);
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
   llvm::CallInst *call =
-    IGF.Builder.CreateCall(fn, {destObject.getAddress(), index, metadata});
+    IGF.Builder.CreateCall(fn, {dest, index, metadata});
   return call;
 }
 
@@ -783,16 +776,15 @@
     llvm::Value *metadata;
     auto fn = IGF.emitValueWitnessFunctionRef(
         T, metadata, ValueWitness::GetEnumTagSinglePayload);
+    auto dest = emitCastToOpaquePtr(IGF, destObject);
     llvm::CallInst *call = IGF.Builder.CreateCall(
-        fn, {destObject.getAddress(), numEmptyCases, metadata});
+        fn, {dest, numEmptyCases, metadata});
     return call;
   }
   auto *metadata = IGF.emitTypeMetadataRefForLayout(T);
   auto *func = getGetEnumTagSinglePayloadTrampolineFn(IGF.IGM);
-  auto *result = IGF.Builder.CreateCall(
-      func,
-      {IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy),
-       numEmptyCases, metadata});
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto *result = IGF.Builder.CreateCall(func, {dest, numEmptyCases, metadata});
   return result;
 }
 
@@ -803,17 +795,17 @@
     llvm::Value *metadata;
     auto fn = IGF.emitValueWitnessFunctionRef(
         T, metadata, ValueWitness::StoreEnumTagSinglePayload);
+    auto dest = emitCastToOpaquePtr(IGF, destObject);
     llvm::CallInst *call = IGF.Builder.CreateCall(
-        fn, {destObject.getAddress(), whichCase, numEmptyCases, metadata});
+        fn, {dest, whichCase, numEmptyCases, metadata});
     return call;
   }
 
   auto *metadata = IGF.emitTypeMetadataRefForLayout(T);
   auto *func = getStoreEnumTagSinglePayloadTrampolineFn(IGF.IGM);
-  auto *result = IGF.Builder.CreateCall(
-      func,
-      {IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy),
-       whichCase, numEmptyCases, metadata});
+  auto dest = emitCastToOpaquePtr(IGF, destObject);
+  auto *result = IGF.Builder.CreateCall(func,
+                                    {dest, whichCase, numEmptyCases, metadata});
   return result;
 }
 
@@ -825,8 +817,9 @@
   auto fn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                             ValueWitness::GetEnumTag);
 
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
   llvm::CallInst *call =
-    IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata});
+    IGF.Builder.CreateCall(fn, {src, metadata});
   return call;
 }
 
@@ -838,7 +831,8 @@
   llvm::Value *metadata;
   auto fn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                     ValueWitness::DestructiveProjectEnumData);
-  IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata});
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
+  IGF.Builder.CreateCall(fn, {src, metadata});
 }
 
 /// Emit a call to the 'destructiveInjectEnumTag' operation.
@@ -850,7 +844,8 @@
   llvm::Value *metadata;
   auto fn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                       ValueWitness::DestructiveInjectEnumTag);
-  IGF.Builder.CreateCall(fn, {srcObject.getAddress(), tagValue, metadata});
+  auto src = emitCastToOpaquePtr(IGF, srcObject);
+  IGF.Builder.CreateCall(fn, {src, tagValue, metadata});
 }
 
 /// Load the 'size' value witness from the given table as a size_t.
@@ -950,7 +945,9 @@
   llvm::Value *metadata;
   auto fn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                             ValueWitness::InitializeWithCopy);
-  IGF.Builder.CreateCall(fn, {dest.getAddress(), src.getAddress(), metadata});
+  auto destPtr = emitCastToOpaquePtr(IGF, dest);
+  auto srcPtr = emitCastToOpaquePtr(IGF, src);
+  IGF.Builder.CreateCall(fn, {destPtr, srcPtr, metadata});
 }
 
 llvm::Value *irgen::emitInitializeWithCopyCall(IRGenFunction &IGF,
@@ -958,8 +955,10 @@
                                                Address dest, Address src) {
   auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata(
       IGF, metadata, ValueWitness::InitializeWithCopy);
+  auto destPtr = emitCastToOpaquePtr(IGF, dest);
+  auto srcPtr = emitCastToOpaquePtr(IGF, src);
   llvm::CallInst *call = IGF.Builder.CreateCall(
-      copyFn, {dest.getAddress(), src.getAddress(), metadata});
+      copyFn, {destPtr, srcPtr, metadata});
 
   return call;
 }
@@ -972,7 +971,9 @@
   llvm::Value *metadata;
   auto fn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                             ValueWitness::InitializeWithTake);
-  IGF.Builder.CreateCall(fn, {dest.getAddress(), src.getAddress(), metadata});
+  auto destPtr = emitCastToOpaquePtr(IGF, dest);
+  auto srcPtr = emitCastToOpaquePtr(IGF, src);
+  IGF.Builder.CreateCall(fn, {destPtr, srcPtr, metadata});
 }
 
 llvm::Value *irgen::emitInitializeWithTakeCall(IRGenFunction &IGF,
@@ -980,8 +981,10 @@
                                                Address dest, Address src) {
   auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata(
       IGF, metadata, ValueWitness::InitializeWithTake);
-  llvm::CallInst *call = IGF.Builder.CreateCall(
-      copyFn, {dest.getAddress(), src.getAddress(), metadata});
+  auto destPtr = emitCastToOpaquePtr(IGF, dest);
+  auto srcPtr = emitCastToOpaquePtr(IGF, src);
+  llvm::CallInst *call =
+    IGF.Builder.CreateCall(copyFn, {destPtr, srcPtr, metadata});
 
   return call;
 }
@@ -996,14 +999,16 @@
   llvm::Value *metadata;
   auto fn = IGF.emitValueWitnessFunctionRef(T, metadata,
                                             ValueWitness::Destroy);
-  IGF.Builder.CreateCall(fn, {object.getAddress(), metadata});
+  auto objectPtr = emitCastToOpaquePtr(IGF, object);
+  IGF.Builder.CreateCall(fn, {objectPtr, metadata});
 }
 
 void irgen::emitDestroyCall(IRGenFunction &IGF, llvm::Value *metadata,
                             Address object) {
   auto fn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata,
                                                        ValueWitness::Destroy);
-  IGF.Builder.CreateCall(fn, {object.getAddress(), metadata});
+  auto objectPtr = emitCastToOpaquePtr(IGF, object);
+  IGF.Builder.CreateCall(fn, {objectPtr, metadata});
 }
 
 static llvm::Constant *getAllocateValueBufferFunction(IRGenModule &IGM) {
diff --git a/lib/IRGen/GenRecord.h b/lib/IRGen/GenRecord.h
index b80a73d..ca29b14 100644
--- a/lib/IRGen/GenRecord.h
+++ b/lib/IRGen/GenRecord.h
@@ -22,6 +22,7 @@
 #include "IRGenModule.h"
 #include "Explosion.h"
 #include "GenEnum.h"
+#include "GenOpaque.h"
 #include "LoadableTypeInfo.h"
 #include "Outlining.h"
 #include "TypeInfo.h"
@@ -69,6 +70,10 @@
     return Layout.isPOD();
   }
 
+  IsABIAccessible_t isABIAccessible() const {
+    return Layout.getTypeForLayout().isABIAccessible();
+  }
+
   Address projectAddress(IRGenFunction &IGF, Address seq,
                          NonFixedOffsets offsets) const {
     return Layout.project(IGF, seq, offsets, "." + asImpl()->getFieldName());
@@ -93,6 +98,11 @@
   }
 };
 
+enum FieldsAreABIAccessible_t : bool {
+  FieldsAreNotABIAccessible = false,
+  FieldsAreABIAccessible = true,
+};
+
 /// A metaprogrammed TypeInfo implementation for record types.
 template <class Impl, class Base, class FieldImpl_,
           bool IsLoadable = std::is_base_of<LoadableTypeInfo, Base>::value>
@@ -105,13 +115,18 @@
 
 private:
   const unsigned NumFields;
+  const unsigned AreFieldsABIAccessible : 1;
 
 protected:
   const Impl &asImpl() const { return *static_cast<const Impl*>(this); }
 
   template <class... As> 
-  RecordTypeInfoImpl(ArrayRef<FieldImpl> fields, As&&...args)
-      : Base(std::forward<As>(args)...), NumFields(fields.size()) {
+  RecordTypeInfoImpl(ArrayRef<FieldImpl> fields,
+                     FieldsAreABIAccessible_t fieldsABIAccessible,
+                     As&&...args)
+      : Base(std::forward<As>(args)...),
+        NumFields(fields.size()),
+        AreFieldsABIAccessible(fieldsABIAccessible) {
     std::uninitialized_copy(fields.begin(), fields.end(),
                             this->template getTrailingObjects<FieldImpl>());
   }
@@ -138,6 +153,11 @@
 
   void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T,
                       bool isOutlined) const override {
+    // If the fields are not ABI-accessible, use the value witness table.
+    if (!AreFieldsABIAccessible) {
+      return emitAssignWithCopyCall(IGF, T, dest, src);
+    }
+
     if (isOutlined || T.hasOpenedExistential()) {
       auto offsets = asImpl().getNonFixedOffsets(IGF, T);
       for (auto &field : getFields()) {
@@ -156,6 +176,11 @@
 
   void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T,
                       bool isOutlined) const override {
+    // If the fields are not ABI-accessible, use the value witness table.
+    if (!AreFieldsABIAccessible) {
+      return emitAssignWithTakeCall(IGF, T, dest, src);
+    }
+
     if (isOutlined || T.hasOpenedExistential()) {
       auto offsets = asImpl().getNonFixedOffsets(IGF, T);
       for (auto &field : getFields()) {
@@ -181,6 +206,11 @@
           IGF, dest, src, T, isOutlined);
     }
 
+    // If the fields are not ABI-accessible, use the value witness table.
+    if (!AreFieldsABIAccessible) {
+      return emitInitializeWithCopyCall(IGF, T, dest, src);
+    }
+
     if (isOutlined || T.hasOpenedExistential()) {
       auto offsets = asImpl().getNonFixedOffsets(IGF, T);
       for (auto &field : getFields()) {
@@ -207,6 +237,11 @@
       return;
     }
 
+    // If the fields are not ABI-accessible, use the value witness table.
+    if (!AreFieldsABIAccessible) {
+      return emitInitializeWithTakeCall(IGF, T, dest, src);
+    }
+
     if (isOutlined || T.hasOpenedExistential()) {
       auto offsets = asImpl().getNonFixedOffsets(IGF, T);
       for (auto &field : getFields()) {
@@ -225,6 +260,11 @@
 
   void destroy(IRGenFunction &IGF, Address addr, SILType T,
                bool isOutlined) const override {
+    // If the fields are not ABI-accessible, use the value witness table.
+    if (!AreFieldsABIAccessible) {
+      return emitDestroyCall(IGF, T, addr);
+    }
+
     if (isOutlined || T.hasOpenedExistential()) {
       auto offsets = asImpl().getNonFixedOffsets(IGF, T);
       for (auto &field : getFields()) {
@@ -323,6 +363,9 @@
       // Ignore empty fields.
       if (field.isEmpty()) continue;
 
+      // If the field is not ABI-accessible, suppress this.
+      if (!field.isABIAccessible()) continue;
+
       // If we've already found an index, then there isn't a
       // unique non-empty field.
       if (result) return 0;
@@ -351,7 +394,8 @@
 
 protected:
   template <class... As> 
-  RecordTypeInfo(As&&...args) : super(std::forward<As>(args)...) {}
+  RecordTypeInfo(ArrayRef<FieldImpl> fields, As &&...args)
+    : super(fields, FieldsAreABIAccessible, std::forward<As>(args)...) {}
 };
 
 /// An implementation of RecordTypeInfo for loadable types. 
@@ -367,9 +411,10 @@
   using super::asImpl;
 
   template <class... As> 
-  RecordTypeInfo(ArrayRef<FieldImpl> fields, unsigned explosionSize,
+  RecordTypeInfo(ArrayRef<FieldImpl> fields,
+                 unsigned explosionSize,
                  As &&...args)
-    : super(fields, std::forward<As>(args)...),
+    : super(fields, FieldsAreABIAccessible, std::forward<As>(args)...),
       ExplosionSize(explosionSize) {}
 
 private:
@@ -518,6 +563,7 @@
     fieldTypesForLayout.reserve(astFields.size());
 
     bool loadable = true;
+    auto fieldsABIAccessible = FieldsAreABIAccessible;
 
     unsigned explosionSize = 0;
     for (unsigned i : indices(astFields)) {
@@ -527,6 +573,9 @@
       assert(fieldTI.isComplete());
       fieldTypesForLayout.push_back(&fieldTI);
 
+      if (!fieldTI.isABIAccessible())
+        fieldsABIAccessible = FieldsAreNotABIAccessible;
+
       fields.push_back(FieldImpl(asImpl()->getFieldInfo(i, astField, fieldTI)));
 
       auto loadableFieldTI = dyn_cast<LoadableTypeInfo>(&fieldTI);
@@ -550,11 +599,14 @@
     // Create the type info.
     if (loadable) {
       assert(layout.isFixedLayout());
+      assert(fieldsABIAccessible);
       return asImpl()->createLoadable(fields, std::move(layout), explosionSize);
     } else if (layout.isFixedLayout()) {
+      assert(fieldsABIAccessible);
       return asImpl()->createFixed(fields, std::move(layout));
     } else {
-      return asImpl()->createNonFixed(fields, std::move(layout));
+      return asImpl()->createNonFixed(fields, fieldsABIAccessible,
+                                      std::move(layout));
     }
   }  
 };
diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp
index 0ebd76a..2174148 100644
--- a/lib/IRGen/GenStruct.cpp
+++ b/lib/IRGen/GenStruct.cpp
@@ -494,11 +494,15 @@
                                   WitnessSizedTypeInfo<NonFixedStructTypeInfo>>
   {
   public:
-    NonFixedStructTypeInfo(ArrayRef<StructFieldInfo> fields, llvm::Type *T,
+    NonFixedStructTypeInfo(ArrayRef<StructFieldInfo> fields,
+                           FieldsAreABIAccessible_t fieldsAccessible,
+                           llvm::Type *T,
                            Alignment align,
-                           IsPOD_t isPOD, IsBitwiseTakable_t isBT)
+                           IsPOD_t isPOD, IsBitwiseTakable_t isBT,
+                           IsABIAccessible_t structAccessible)
       : StructTypeInfoBase(StructTypeInfoKind::NonFixedStructTypeInfo,
-                           fields, T, align, isPOD, isBT) {
+                           fields, fieldsAccessible,
+                           T, align, isPOD, isBT, structAccessible) {
     }
 
     // We have an indirect schema.
@@ -556,11 +560,16 @@
     }
 
     NonFixedStructTypeInfo *createNonFixed(ArrayRef<StructFieldInfo> fields,
+                                     FieldsAreABIAccessible_t fieldsAccessible,
                                            StructLayout &&layout) {
-      return NonFixedStructTypeInfo::create(fields, layout.getType(),
+      auto structAccessible = IsABIAccessible_t(
+        IGM.getSILModule().isTypeMetadataAccessible(TheStruct));
+      return NonFixedStructTypeInfo::create(fields, fieldsAccessible,
+                                            layout.getType(),
                                             layout.getAlignment(),
                                             layout.isPOD(),
-                                            layout.isBitwiseTakable());
+                                            layout.isBitwiseTakable(),
+                                            structAccessible);
     }
 
     StructFieldInfo getFieldInfo(unsigned index,
@@ -871,24 +880,29 @@
       : public ResilientTypeInfo<ResilientStructTypeInfo>
   {
   public:
-    ResilientStructTypeInfo(llvm::Type *T)
-      : ResilientTypeInfo(T) {
+    ResilientStructTypeInfo(llvm::Type *T, IsABIAccessible_t abiAccessible)
+      : ResilientTypeInfo(T, abiAccessible) {
       setSubclassKind((unsigned) StructTypeInfoKind::ResilientStructTypeInfo);
     }
   };
 } // end anonymous namespace
 
-const TypeInfo *TypeConverter::convertResilientStruct() {
+const TypeInfo *
+TypeConverter::convertResilientStruct(IsABIAccessible_t abiAccessible) {
   llvm::Type *storageType = IGM.OpaquePtrTy->getElementType();
-  return new ResilientStructTypeInfo(storageType);
+  return new ResilientStructTypeInfo(storageType, abiAccessible);
 }
 
 const TypeInfo *TypeConverter::convertStructType(TypeBase *key, CanType type,
                                                  StructDecl *D) {
   // All resilient structs have the same opaque lowering, since they are
-  // indistinguishable as values.
-  if (IGM.isResilient(D, ResilienceExpansion::Maximal))
-    return &getResilientStructTypeInfo();
+  // indistinguishable as values --- except that we have to track
+  // ABI-accessibility.
+  if (IGM.isResilient(D, ResilienceExpansion::Maximal)) {
+    auto structAccessible =
+      IsABIAccessible_t(IGM.getSILModule().isTypeMetadataAccessible(type));
+    return &getResilientStructTypeInfo(structAccessible);
+  }
 
   // Create the struct type.
   auto ty = IGM.createNominalType(type);
diff --git a/lib/IRGen/GenTuple.cpp b/lib/IRGen/GenTuple.cpp
index 1193fab..c424239 100644
--- a/lib/IRGen/GenTuple.cpp
+++ b/lib/IRGen/GenTuple.cpp
@@ -23,6 +23,7 @@
 #include "swift/AST/Types.h"
 #include "swift/AST/Decl.h"
 #include "swift/AST/Pattern.h"
+#include "swift/SIL/SILModule.h"
 #include "swift/SIL/SILType.h"
 #include "llvm/IR/DerivedTypes.h"
 
@@ -331,10 +332,14 @@
                                WitnessSizedTypeInfo<NonFixedTupleTypeInfo>>
   {
   public:
-    NonFixedTupleTypeInfo(ArrayRef<TupleFieldInfo> fields, llvm::Type *T,
+    NonFixedTupleTypeInfo(ArrayRef<TupleFieldInfo> fields,
+                          FieldsAreABIAccessible_t fieldsABIAccessible,
+                          llvm::Type *T,
                           Alignment minAlign, IsPOD_t isPOD,
-                          IsBitwiseTakable_t isBT)
-      : TupleTypeInfoBase(fields, T, minAlign, isPOD, isBT) {}
+                          IsBitwiseTakable_t isBT,
+                          IsABIAccessible_t tupleAccessible)
+      : TupleTypeInfoBase(fields, fieldsABIAccessible,
+                          T, minAlign, isPOD, isBT, tupleAccessible) {}
 
     TupleNonFixedOffsets getNonFixedOffsets(IRGenFunction &IGF,
                                             SILType T) const {
@@ -374,11 +379,16 @@
     }
 
     NonFixedTupleTypeInfo *createNonFixed(ArrayRef<TupleFieldInfo> fields,
+                                     FieldsAreABIAccessible_t fieldsAccessible,
                                           StructLayout &&layout) {
-      return NonFixedTupleTypeInfo::create(fields, layout.getType(),
+      auto tupleAccessible = IsABIAccessible_t(
+        IGM.getSILModule().isTypeABIAccessible(TheTuple));
+      return NonFixedTupleTypeInfo::create(fields, fieldsAccessible,
+                                           layout.getType(),
                                            layout.getAlignment(),
                                            layout.isPOD(),
-                                           layout.isBitwiseTakable());
+                                           layout.isBitwiseTakable(),
+                                           tupleAccessible);
     }
 
     TupleFieldInfo getFieldInfo(unsigned index,
diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp
index 84f68ca..5ff6457 100644
--- a/lib/IRGen/GenType.cpp
+++ b/lib/IRGen/GenType.cpp
@@ -1243,12 +1243,15 @@
   return *EmptyTI;
 }
 
-const TypeInfo &TypeConverter::getResilientStructTypeInfo() {
-  if (ResilientStructTI) return *ResilientStructTI;
-  ResilientStructTI = convertResilientStruct();
-  ResilientStructTI->NextConverted = FirstType;
-  FirstType = ResilientStructTI;
-  return *ResilientStructTI;
+const TypeInfo &
+TypeConverter::getResilientStructTypeInfo(IsABIAccessible_t isAccessible) {
+  auto &cache = isAccessible ? AccessibleResilientStructTI
+                             : InaccessibleResilientStructTI;
+  if (cache) return *cache;
+  cache = convertResilientStruct(isAccessible);
+  cache->NextConverted = FirstType;
+  FirstType = cache;
+  return *cache;
 }
 
 /// Get the fragile type information for the given type, which may not
diff --git a/lib/IRGen/GenType.h b/lib/IRGen/GenType.h
index 3607b74..866a8d8 100644
--- a/lib/IRGen/GenType.h
+++ b/lib/IRGen/GenType.h
@@ -82,7 +82,8 @@
   const LoadableTypeInfo *ObjCClassPtrTI = nullptr;
   const LoadableTypeInfo *EmptyTI = nullptr;
 
-  const TypeInfo *ResilientStructTI = nullptr;
+  const TypeInfo *AccessibleResilientStructTI = nullptr;
+  const TypeInfo *InaccessibleResilientStructTI = nullptr;
   
   llvm::DenseMap<std::pair<unsigned, unsigned>, const LoadableTypeInfo *>
     OpaqueStorageTypes;
@@ -123,7 +124,7 @@
   const LoadableTypeInfo *convertBuiltinNativeObject();
   const LoadableTypeInfo *convertBuiltinUnknownObject();
   const LoadableTypeInfo *convertBuiltinBridgeObject();
-  const TypeInfo *convertResilientStruct();
+  const TypeInfo *convertResilientStruct(IsABIAccessible_t abiAccessible);
   const TypeInfo *convertUnmanagedStorageType(UnmanagedStorageType *T);
   const TypeInfo *convertUnownedStorageType(UnownedStorageType *T);
   const TypeInfo *convertWeakStorageType(WeakStorageType *T);
@@ -147,7 +148,7 @@
   const LoadableTypeInfo &getObjCClassPtrTypeInfo();
   const LoadableTypeInfo &getWitnessTablePtrTypeInfo();
   const LoadableTypeInfo &getEmptyTypeInfo();
-  const TypeInfo &getResilientStructTypeInfo();
+  const TypeInfo &getResilientStructTypeInfo(IsABIAccessible_t abiAccessible);
   const ProtocolInfo &getProtocolInfo(ProtocolDecl *P);
   const LoadableTypeInfo &getOpaqueStorageTypeInfo(Size storageSize,
                                                    Alignment storageAlign);
diff --git a/lib/IRGen/IRGen.h b/lib/IRGen/IRGen.h
index a02e958..9f5b485 100644
--- a/lib/IRGen/IRGen.h
+++ b/lib/IRGen/IRGen.h
@@ -33,7 +33,7 @@
   class CanType;
   class ClusteredBitVector;
   enum ForDefinition_t : bool;
-  
+
 namespace irgen {
   using Lowering::AbstractionPattern;
   using clang::CodeGen::ConstantInitFuture;
@@ -77,6 +77,11 @@
   return (l = (l & r));
 }
 
+enum IsABIAccessible_t : bool {
+  IsNotABIAccessible = false,
+  IsABIAccessible = true  
+};
+
 /// The kind of reference counting implementation a heap object uses.
 enum class ReferenceCounting : uint8_t {
   /// The object uses native Swift reference counting.
diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp
index 9331989..c5ded19 100644
--- a/lib/IRGen/MetadataRequest.cpp
+++ b/lib/IRGen/MetadataRequest.cpp
@@ -1923,6 +1923,9 @@
   /// not to cache the result as if it were the metadata for a formal type
   /// unless the type actually cannot possibly be a formal type, e.g. because
   /// it is one of the special lowered type kinds like SILFunctionType.
+  ///
+  /// NOTE: If you modify the special cases in this, you should update
+  /// isTypeMetadataForLayoutAccessible in SIL.cpp.
   class EmitTypeMetadataRefForLayout
     : public CanTypeVisitor<EmitTypeMetadataRefForLayout, llvm::Value *,
                             DynamicMetadataRequest> {
diff --git a/lib/IRGen/NonFixedTypeInfo.h b/lib/IRGen/NonFixedTypeInfo.h
index 1a01a96..5002961 100644
--- a/lib/IRGen/NonFixedTypeInfo.h
+++ b/lib/IRGen/NonFixedTypeInfo.h
@@ -52,8 +52,8 @@
   const Impl &asImpl() const { return static_cast<const Impl &>(*this); }
 
   WitnessSizedTypeInfo(llvm::Type *type, Alignment align, IsPOD_t pod,
-                       IsBitwiseTakable_t bt)
-    : super(type, align, pod, bt, IsNotFixedSize, TypeInfo::STIK_None) {}
+                       IsBitwiseTakable_t bt, IsABIAccessible_t abi)
+    : super(type, align, pod, bt, IsNotFixedSize, abi, TypeInfo::STIK_None) {}
 
 private:
   /// Bit-cast the given pointer to the right type and assume it as an
diff --git a/lib/IRGen/ResilientTypeInfo.h b/lib/IRGen/ResilientTypeInfo.h
index 0be4944..1672755 100644
--- a/lib/IRGen/ResilientTypeInfo.h
+++ b/lib/IRGen/ResilientTypeInfo.h
@@ -42,9 +42,10 @@
 template <class Impl>
 class ResilientTypeInfo : public WitnessSizedTypeInfo<Impl> {
 protected:
-  ResilientTypeInfo(llvm::Type *type)
+  ResilientTypeInfo(llvm::Type *type, IsABIAccessible_t abiAccessible)
     : WitnessSizedTypeInfo<Impl>(type, Alignment(1),
-                                 IsNotPOD, IsNotBitwiseTakable) {}
+                                 IsNotPOD, IsNotBitwiseTakable,
+                                 abiAccessible) {}
 
 public:
   void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T,
diff --git a/lib/IRGen/TypeInfo.h b/lib/IRGen/TypeInfo.h
index 36a8d7f..4fa6c2f 100644
--- a/lib/IRGen/TypeInfo.h
+++ b/lib/IRGen/TypeInfo.h
@@ -93,10 +93,12 @@
   TypeInfo(llvm::Type *Type, Alignment A, IsPOD_t pod,
            IsBitwiseTakable_t bitwiseTakable,
            IsFixedSize_t alwaysFixedSize,
+           IsABIAccessible_t abiAccessible,
            SpecialTypeInfoKind stik)
     : NextConverted(0), StorageType(Type), nativeReturnSchema(nullptr),
       nativeParameterSchema(nullptr), StorageAlignment(A),
       POD(pod), BitwiseTakable(bitwiseTakable),
+      ABIAccessible(abiAccessible),
       AlwaysFixedSize(alwaysFixedSize), STIK(stik),
       SubclassKind(InvalidSubclassKind) {
     assert(STIK >= STIK_Fixed || !AlwaysFixedSize);
@@ -134,6 +136,9 @@
   /// Whether this type is known to be bitwise-takable.
   unsigned BitwiseTakable : 1;
 
+  /// Whether this type is ABI-accessible from this SILModule.
+  unsigned ABIAccessible : 1;
+
   /// Whether this type can be assumed to have a fixed size from all
   /// resilience domains.
   unsigned AlwaysFixedSize : 1;
@@ -161,6 +166,17 @@
   /// Whether this type is known to be empty.
   bool isKnownEmpty(ResilienceExpansion expansion) const;
 
+  /// Whether this type is known to be ABI-accessible, i.e. whether it's
+  /// actually possible to do ABI operations on it from this current SILModule.
+  /// See SILModule::isTypeABIAccessible.
+  ///
+  /// All fixed-size types are currently ABI-accessible, although this would
+  /// not be difficult to change (e.g. if we had an archetype size constraint
+  /// that didn't say anything about triviality).
+  IsABIAccessible_t isABIAccessible() const {
+    return IsABIAccessible_t(ABIAccessible);
+  }
+
   /// Whether this type is known to be POD, i.e. to not require any
   /// particular action on copy or destroy.
   IsPOD_t isPOD(ResilienceExpansion expansion) const { return IsPOD_t(POD); }
diff --git a/lib/SIL/SIL.cpp b/lib/SIL/SIL.cpp
index ecbe6ac..b91261b 100644
--- a/lib/SIL/SIL.cpp
+++ b/lib/SIL/SIL.cpp
@@ -105,3 +105,101 @@
       return (definition ? SILLinkage::Public : SILLinkage::PublicExternal);
   }
 }
+
+bool SILModule::isTypeMetadataAccessible(CanType type) {
+  assert(type->isLegalFormalType());
+
+  return !type.findIf([&](CanType type) {
+    // Note that this function returns true if the type is *illegal* to use.
+
+    // Ignore non-nominal types.
+    auto decl = type.getNominalOrBoundGenericNominal();
+    if (!decl)
+      return false;
+
+    // Check whether the declaration is inaccessible from the current context.
+    switch (getDeclLinkage(decl)) {
+
+    // Public declarations are accessible from everywhere.
+    case FormalLinkage::PublicUnique:
+    case FormalLinkage::PublicNonUnique:
+      return false;
+
+    // Hidden declarations are inaccessible from different modules.
+    case FormalLinkage::HiddenUnique:
+      return (decl->getModuleContext() != getSwiftModule());
+
+    // Private declarations are inaccessible from different files unless
+    // this is WMO and we're in the same module.
+    case FormalLinkage::Private: {
+      // The only time we don't have an associated DC is in the
+      // integrated REPL, where we also don't have a concept of other
+      // source files within the current module.
+      if (!AssociatedDeclContext)
+        return (decl->getModuleContext() != getSwiftModule());
+
+      // The associated DC should be either a SourceFile or, in WMO mode,
+      // a ModuleDecl.  In the WMO modes, IRGen will ensure that private
+      // declarations are usable throughout the module.  Therefore, in
+      // either case we just need to make sure that the declaration comes
+      // from within the associated DC.
+      auto declDC = decl->getDeclContext();
+      return !(declDC == AssociatedDeclContext ||
+               declDC->isChildContextOf(AssociatedDeclContext));
+    }
+    }
+    llvm_unreachable("bad linkage");
+  });
+}
+
+/// Answer whether IRGen's emitTypeMetadataForLayout can fetch metadata for
+/// a type, which is the necessary condition for being able to do value
+/// operations on the type using dynamic metadata.
+static bool isTypeMetadataForLayoutAccessible(SILModule &M, SILType type) {
+  // Look through types that aren't necessarily legal formal types:
+
+  //   - tuples
+  if (auto tupleType = type.getAs<TupleType>()) {
+    for (auto index : indices(tupleType.getElementTypes())) {
+      if (!isTypeMetadataForLayoutAccessible(M, type.getTupleElementType(index)))
+        return false;
+    }
+    return true;
+  }
+
+  //   - optionals
+  if (auto objType = type.getOptionalObjectType()) {
+    return isTypeMetadataForLayoutAccessible(M, objType);
+  }
+
+  //   - function types
+  if (type.is<SILFunctionType>())
+    return true;
+
+  //   - metatypes
+  if (type.is<AnyMetatypeType>())
+    return true;
+
+  // Otherwise, check that we can fetch the type metadata.
+  return M.isTypeMetadataAccessible(type.getSwiftRValueType());
+
+}
+
+/// Can we perform value operations on the given type?  We have no way
+/// of doing value operations on resilient-layout types from other modules
+/// that are ABI-private to their defining module.  But if the type is not
+/// ABI-private, we can always at least fetch its metadata and use the
+/// value witness table stored there.
+bool SILModule::isTypeABIAccessible(SILType type) {
+  // Fixed-ABI types can have value operations done without metadata.
+  if (Types.getTypeLowering(type).isFixedABI())
+    return true;
+
+  assert(!type.is<ReferenceStorageType>() &&
+         !type.is<SILFunctionType>() &&
+         !type.is<AnyMetatypeType>() &&
+         "unexpected SIL lowered-only type with non-fixed layout");
+
+  // Otherwise, we need to be able to fetch layout-metadata for the type.
+  return isTypeMetadataForLayoutAccessible(*this, type);
+}
diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp
index 662332d..c044f2b 100644
--- a/lib/SIL/SILVerifier.cpp
+++ b/lib/SIL/SILVerifier.cpp
@@ -1819,6 +1819,8 @@
             "Dest address should be lvalue");
     require(SI->getDest()->getType() == SI->getSrc()->getType(),
             "Store operand type and dest type mismatch");
+    require(F.getModule().isTypeABIAccessible(SI->getDest()->getType()),
+            "cannot directly copy type with inaccessible ABI");
   }
 
   void checkRetainValueInst(RetainValueInst *I) {
@@ -2241,6 +2243,8 @@
   void checkDestroyAddrInst(DestroyAddrInst *DI) {
     require(DI->getOperand()->getType().isAddress(),
             "Operand of destroy_addr must be address");
+    require(F.getModule().isTypeABIAccessible(DI->getOperand()->getType()),
+            "cannot directly destroy type with inaccessible ABI");
   }
 
   void checkBindMemoryInst(BindMemoryInst *BI) {
diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp
index fd15e47..eec5cd8 100644
--- a/lib/SIL/TypeLowering.cpp
+++ b/lib/SIL/TypeLowering.cpp
@@ -153,26 +153,11 @@
   llvm_unreachable("function-like captures should have been lowered away");
 }
 
-enum class LoweredTypeKind {
-  /// Trivial and loadable.
-  Trivial,
+using RecursiveProperties = TypeLowering::RecursiveProperties;
 
-  /// A reference type.
-  Reference,
-
-  /// An aggregate type that contains references (potentially recursively).
-  AggWithReference,
-
-  /// Non-trivial and not loadable.
-  AddressOnly,
-
-  /// Trivial and not loadable.
-  TrivialAddressOnly
-};
-
-static LoweredTypeKind classifyType(CanType type, SILModule &M,
-                                    CanGenericSignature sig,
-                                    ResilienceExpansion expansion);
+static RecursiveProperties
+classifyType(CanType type, SILModule &M, CanGenericSignature sig,
+             ResilienceExpansion expansion);
 
 namespace {
   /// A CRTP helper class for doing things that depends on type
@@ -190,13 +175,36 @@
 
   public:
     // The subclass should implement:
-    //   RetTy handleAddressOnly(CanType);
-    //   RetTy handleReference(CanType);
+    //   // Trivial, fixed-layout, and non-address-only.
     //   RetTy handleTrivial(CanType);
-    //   RetTy handleTrivialAddressOnly(CanType);
-    // In addition, if it does not override visitTupleType
-    // and visitAnyStructType, it should also implement:
-    //   RetTy handleAggWithReference(CanType);
+    //   // A reference type.
+    //   RetTy handleReference(CanType);
+    //   // Non-trivial and address-only.
+    //   RetTy handleAddressOnly(CanType, RecursiveProperties properties);
+    // and, if it doesn't override handleTupleType,
+    //   // An aggregate type that's non-trivial.
+    //   RetTy handleNonTrivialAggregate(CanType, IsFixedABI_t fixed);
+    //
+    // Alternatively, it can just implement:
+    //   RetTy handle(CanType, RecursiveProperties properties);
+
+    /// Handle a trivial, fixed-size, loadable type.
+    RetTy handleTrivial(CanType type) {
+      return asImpl().handle(type, RecursiveProperties());
+    }
+
+    RetTy handleReference(CanType type) {
+      return asImpl().handle(type, RecursiveProperties::forReference());
+    }
+
+    RetTy handleAddressOnly(CanType type, RecursiveProperties properties) {
+      return asImpl().handle(type, properties);
+    }
+
+    RetTy handleNonTrivialAggregate(CanType type,
+                                    RecursiveProperties properties) {
+      return asImpl().handle(type, properties);
+    }
 
 #define IMPL(TYPE, LOWERING)                            \
     RetTy visit##TYPE##Type(Can##TYPE##Type type) {     \
@@ -209,14 +217,21 @@
     IMPL(BuiltinNativeObject, Reference)
     IMPL(BuiltinBridgeObject, Reference)
     IMPL(BuiltinUnknownObject, Reference)
-    IMPL(BuiltinUnsafeValueBuffer, AddressOnly)
     IMPL(BuiltinVector, Trivial)
     IMPL(SILToken, Trivial)
     IMPL(Class, Reference)
     IMPL(BoundGenericClass, Reference)
     IMPL(AnyMetatype, Trivial)
     IMPL(Module, Trivial)
-    
+
+#undef IMPL
+
+    RetTy visitBuiltinUnsafeValueBufferType(
+                                         CanBuiltinUnsafeValueBufferType type) {
+      return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI,
+                                               IsAddressOnly});
+    }
+
     RetTy visitAnyFunctionType(CanAnyFunctionType type) {
       switch (type->getRepresentation()) {
       case AnyFunctionType::Representation::Swift:
@@ -240,8 +255,6 @@
       return asImpl().handleTrivial(type);
     }
 
-#undef IMPL
-
     RetTy visitLValueType(CanLValueType type) {
       llvm_unreachable("shouldn't get an l-value type here");
     }
@@ -268,7 +281,8 @@
           return asImpl().visit(genericSig->getConcreteType(type)
                                     ->getCanonicalType());
         } else {
-          return asImpl().handleAddressOnly(type);
+          return asImpl().handleAddressOnly(type,
+                                            RecursiveProperties::forOpaque());
         }
       }
       llvm_unreachable("should have substituted dependent type into context");
@@ -330,11 +344,15 @@
     }
 
     RetTy visitAddressOnlyUnownedStorageType(CanUnownedStorageType type) {
-      return asImpl().handleAddressOnly(type);
+      return asImpl().handleAddressOnly(type, {IsNotTrivial,
+                                               IsFixedABI,
+                                               IsAddressOnly});
     }
 
     RetTy visitWeakStorageType(CanWeakStorageType type) {
-      return asImpl().handleAddressOnly(type);
+      return asImpl().handleAddressOnly(type, {IsNotTrivial,
+                                               IsFixedABI,
+                                               IsAddressOnly});
     }
 
     RetTy visitArchetypeType(CanArchetypeType type) {
@@ -349,13 +367,14 @@
         }
 
         if (LayoutInfo->isAddressOnlyTrivial()) {
-          return asImpl().handleTrivialAddressOnly(type);
+          return asImpl().handleAddressOnly(type,
+                                     {IsTrivial, IsNotFixedABI, IsAddressOnly});
         }
 
         if (LayoutInfo->isRefCounted())
           return asImpl().handleReference(type);
       }
-      return asImpl().handleAddressOnly(type);
+      return asImpl().handleAddressOnly(type, RecursiveProperties::forOpaque());
     }
 
     RetTy visitExistentialType(CanType type) {
@@ -365,7 +384,9 @@
         llvm_unreachable("not an existential type?!");
       // Opaque existentials are address-only.
       case ExistentialRepresentation::Opaque:
-        return asImpl().handleAddressOnly(type);
+        return asImpl().handleAddressOnly(type, {IsNotTrivial,
+                                                 IsFixedABI,
+                                                 IsAddressOnly});
       // Class-constrained and boxed existentials are refcounted.
       case ExistentialRepresentation::Class:
       case ExistentialRepresentation::Boxed:
@@ -402,26 +423,11 @@
 
     // Tuples depend on their elements.
     RetTy visitTupleType(CanTupleType type) {
-      bool hasReference = false;
+      RecursiveProperties props;
       for (auto eltType : type.getElementTypes()) {
-        switch (classifyType(eltType, M, Sig, Expansion)) {
-        case LoweredTypeKind::Trivial:
-          continue;
-        case LoweredTypeKind::TrivialAddressOnly:
-          return asImpl().handleTrivialAddressOnly(type);
-        case LoweredTypeKind::AddressOnly:
-          return asImpl().handleAddressOnly(type);
-        case LoweredTypeKind::Reference:
-        case LoweredTypeKind::AggWithReference:
-          hasReference = true;
-          continue;
-        }
-        llvm_unreachable("bad type classification");
+        props.addSubobject(classifyType(eltType, M, Sig, Expansion));
       }
-
-      if (hasReference)
-        return asImpl().handleAggWithReference(type);
-      return asImpl().handleTrivial(type);
+      return asImpl().handleAggregateByProperties(type, props);
     }
 
     RetTy visitDynamicSelfType(CanDynamicSelfType type) {
@@ -430,43 +436,39 @@
     
     RetTy visitSILBlockStorageType(CanSILBlockStorageType type) {
       // Should not be loaded.
-      return asImpl().handleAddressOnly(type);
+      return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI,
+                                               IsAddressOnly});
     }
 
     RetTy visitSILBoxType(CanSILBoxType type) {
       // Should not be loaded.
       return asImpl().handleReference(type);
     }
+
+    RetTy handleAggregateByProperties(CanType type, RecursiveProperties props) {
+      if (props.isAddressOnly()) {
+        return asImpl().handleAddressOnly(type, props);
+      }
+      assert(props.isFixedABI() && "unsupported combination for now");
+      if (props.isTrivial()) {
+        return asImpl().handleTrivial(type);
+      }
+      return asImpl().handleNonTrivialAggregate(type, props);
+    }
   };
 
   class TypeClassifier :
-      public TypeClassifierBase<TypeClassifier, LoweredTypeKind> {
+      public TypeClassifierBase<TypeClassifier, RecursiveProperties> {
   public:
     TypeClassifier(SILModule &M, CanGenericSignature Sig,
                    ResilienceExpansion Expansion)
         : TypeClassifierBase(M, Sig, Expansion) {}
 
-    LoweredTypeKind handleReference(CanType type) {
-      return LoweredTypeKind::Reference;
-    }
-    LoweredTypeKind handleAggWithReference(CanType type) {
-      return LoweredTypeKind::AggWithReference;
-    }
-    LoweredTypeKind
-    handleTrivial(CanType type) {
-      return LoweredTypeKind::Trivial;
+    RecursiveProperties handle(CanType type, RecursiveProperties properties) {
+      return properties;
     }
 
-    LoweredTypeKind
-    handleTrivialAddressOnly(CanType type) {
-      return LoweredTypeKind::TrivialAddressOnly;
-    }
-
-    LoweredTypeKind handleAddressOnly(CanType type) {
-      return LoweredTypeKind::AddressOnly;
-    }
-
-    LoweredTypeKind visitAnyEnumType(CanType type, EnumDecl *D) {
+    RecursiveProperties visitAnyEnumType(CanType type, EnumDecl *D) {
       // We have to look through optionals here without grabbing the
       // type lowering because the way that optionals are reabstracted
       // can trip recursion checks if we try to build a lowered type.
@@ -480,7 +482,7 @@
       return handleClassificationFromLowering(type, lowering);
     }
 
-    LoweredTypeKind visitAnyStructType(CanType type, StructDecl *D) {
+    RecursiveProperties visitAnyStructType(CanType type, StructDecl *D) {
       // Consult the type lowering.
       type = getSubstitutedTypeForTypeLowering(type);
       auto &lowering = M.Types.getTypeLowering(type);
@@ -504,20 +506,16 @@
       return type;
     }
 
-    LoweredTypeKind handleClassificationFromLowering(CanType type,
+    RecursiveProperties handleClassificationFromLowering(CanType type,
                                            const TypeLowering &lowering) {
-      if (lowering.isAddressOnly())
-        return handleAddressOnly(type);
-      if (lowering.isTrivial())
-        return handleTrivial(type);
-      return handleAggWithReference(type);
+      return handle(type, lowering.getRecursiveProperties());
     }
   };
 } // end anonymous namespace
 
-static LoweredTypeKind classifyType(CanType type, SILModule &M,
-                                    CanGenericSignature sig,
-                                    ResilienceExpansion expansion) {
+static RecursiveProperties classifyType(CanType type, SILModule &M,
+                                        CanGenericSignature sig,
+                                        ResilienceExpansion expansion) {
   assert(!type->hasError() &&
          "Error types should not appear in type-checked AST");
 
@@ -530,8 +528,7 @@
 bool SILType::isAddressOnly(CanType type, SILModule &M,
                             CanGenericSignature sig,
                             ResilienceExpansion expansion) {
-  return classifyType(type, M, sig, expansion)
-      == LoweredTypeKind::AddressOnly;
+  return classifyType(type, M, sig, expansion).isAddressOnly();
 }
 
 namespace {
@@ -540,10 +537,9 @@
   /// opaque values are passed by value.
   class LoadableTypeLowering : public TypeLowering {
   protected:
-    LoadableTypeLowering(SILType type, IsTrivial_t isTrivial,
-                         IsAddressOnly_t isAddressOnly,
+    LoadableTypeLowering(SILType type, RecursiveProperties properties,
                          IsReferenceCounted_t isRefCounted)
-      : TypeLowering(type, isTrivial, isAddressOnly, isRefCounted) {}
+      : TypeLowering(type, properties, isRefCounted) {}
 
   public:
     void emitDestroyAddress(SILBuilder &B, SILLocation loc,
@@ -565,11 +561,11 @@
     }
   };
 
-  /// A class for trivial, loadable types.
+  /// A class for trivial, fixed-layout, loadable types.
   class TrivialTypeLowering final : public LoadableTypeLowering {
   public:
     TrivialTypeLowering(SILType type)
-      : LoadableTypeLowering(type, IsTrivial, IsNotAddressOnly,
+      : LoadableTypeLowering(type, {IsTrivial, IsFixedABI, IsNotAddressOnly},
                              IsNotReferenceCounted) {}
 
     SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc, SILValue addr,
@@ -632,9 +628,18 @@
   class NonTrivialLoadableTypeLowering : public LoadableTypeLowering {
   public:
     NonTrivialLoadableTypeLowering(SILType type,
-                                   IsAddressOnly_t isAddressOnly,
                                    IsReferenceCounted_t isRefCounted)
-      : LoadableTypeLowering(type, IsNotTrivial, isAddressOnly, isRefCounted) {}
+      : NonTrivialLoadableTypeLowering(type,
+                                   {IsNotTrivial, IsFixedABI, IsNotAddressOnly},
+                                       isRefCounted) {}
+
+    /// This constructor is necessary because of opaque-values.
+    NonTrivialLoadableTypeLowering(SILType type,
+                                   RecursiveProperties properties,
+                                   IsReferenceCounted_t isRefCounted)
+      : LoadableTypeLowering(type, properties, isRefCounted) {
+      assert(!properties.isTrivial());
+    }
 
     SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc,
                             SILValue addr, IsTake_t isTake) const override {
@@ -725,7 +730,6 @@
   public:
     LoadableAggTypeLowering(CanType type)
       : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type),
-                                       IsNotAddressOnly,
                                        IsNotReferenceCounted) {
     }
 
@@ -910,7 +914,6 @@
   public:
     LoadableEnumTypeLowering(CanType type)
       : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type),
-                                       IsNotAddressOnly,
                                        IsNotReferenceCounted) {}
 
     SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
@@ -948,10 +951,9 @@
 
   class LeafLoadableTypeLowering : public NonTrivialLoadableTypeLowering {
   public:
-    LeafLoadableTypeLowering(SILType type,
-                             IsAddressOnly_t isAddressOnly,
+    LeafLoadableTypeLowering(SILType type, RecursiveProperties properties,
                              IsReferenceCounted_t isRefCounted)
-      : NonTrivialLoadableTypeLowering(type, isAddressOnly, isRefCounted) {}
+      : NonTrivialLoadableTypeLowering(type, properties, isRefCounted) {}
 
     SILValue emitLoweredCopyValue(SILBuilder &B, SILLocation loc,
                                   SILValue value,
@@ -970,7 +972,8 @@
   class ReferenceTypeLowering : public LeafLoadableTypeLowering {
   public:
     ReferenceTypeLowering(SILType type)
-      : LeafLoadableTypeLowering(type, IsNotAddressOnly, IsReferenceCounted) {}
+      : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(),
+                                 IsReferenceCounted) {}
 
     SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
                            SILValue value) const override {
@@ -998,7 +1001,8 @@
   class LoadableUnownedTypeLowering final : public LeafLoadableTypeLowering {
   public:
     LoadableUnownedTypeLowering(SILType type)
-      : LeafLoadableTypeLowering(type, IsNotAddressOnly, IsReferenceCounted) {}
+      : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(),
+                                 IsReferenceCounted) {}
 
     SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
                            SILValue value) const override {
@@ -1022,8 +1026,8 @@
   /// A class for non-trivial, address-only types.
   class AddressOnlyTypeLowering : public TypeLowering {
   public:
-    AddressOnlyTypeLowering(SILType type)
-      : TypeLowering(type, IsNotTrivial, IsAddressOnly, IsNotReferenceCounted)
+    AddressOnlyTypeLowering(SILType type, RecursiveProperties properties)
+      : TypeLowering(type, properties, IsNotReferenceCounted)
     {}
 
     void emitCopyInto(SILBuilder &B, SILLocation loc,
@@ -1055,12 +1059,14 @@
 
     void emitDestroyAddress(SILBuilder &B, SILLocation loc,
                             SILValue addr) const override {
-      B.emitDestroyAddrAndFold(loc, addr);
+      if (!isTrivial())
+        B.emitDestroyAddrAndFold(loc, addr);
     }
 
     void emitDestroyRValue(SILBuilder &B, SILLocation loc,
                            SILValue value) const override {
-      B.emitDestroyAddrAndFold(loc, value);
+      if (!isTrivial())
+        B.emitDestroyAddrAndFold(loc, value);
     }
 
     SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
@@ -1090,7 +1096,8 @@
   class UnsafeValueBufferTypeLowering : public AddressOnlyTypeLowering {
   public:
     UnsafeValueBufferTypeLowering(SILType type)
-      : AddressOnlyTypeLowering(type) {}
+      : AddressOnlyTypeLowering(type,
+                                {IsNotTrivial, IsFixedABI, IsAddressOnly}) {}
 
     void emitCopyInto(SILBuilder &B, SILLocation loc,
                       SILValue src, SILValue dest, IsTake_t isTake,
@@ -1116,8 +1123,8 @@
   /// FIXME: When you remove an unreachable, just delete the method.
   class OpaqueValueTypeLowering : public LeafLoadableTypeLowering {
   public:
-    OpaqueValueTypeLowering(SILType type)
-      : LeafLoadableTypeLowering(type, IsAddressOnly, IsNotReferenceCounted) {}
+    OpaqueValueTypeLowering(SILType type, RecursiveProperties properties)
+      : LeafLoadableTypeLowering(type, properties, IsNotReferenceCounted) {}
 
     void emitCopyInto(SILBuilder &B, SILLocation loc,
                       SILValue src, SILValue dest, IsTake_t isTake,
@@ -1149,71 +1156,6 @@
     }
   };
 
-  /// A class for trivial, address-only types.
-  class AddressOnlyTrivialTypeLowering : public TypeLowering {
-  public:
-    AddressOnlyTrivialTypeLowering(SILType type)
-      : TypeLowering(type, IsTrivial, IsAddressOnly, IsNotReferenceCounted)
-    {}
-
-    void emitCopyInto(SILBuilder &B, SILLocation loc,
-                      SILValue src, SILValue dest, IsTake_t isTake,
-                      IsInitialization_t isInit) const override {
-      B.createCopyAddr(loc, src, dest, isTake, isInit);
-    }
-
-    SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc,
-                            SILValue addr, IsTake_t isTake) const override {
-      llvm_unreachable("calling emitLoadOfCopy on non-loadable type");
-    }
-
-    void emitStoreOfCopy(SILBuilder &B, SILLocation loc,
-                         SILValue newValue, SILValue addr,
-                         IsInitialization_t isInit) const override {
-      llvm_unreachable("calling emitStoreOfCopy on non-loadable type");
-    }
-
-    void emitStore(SILBuilder &B, SILLocation loc, SILValue value,
-                   SILValue addr, StoreOwnershipQualifier qual) const override {
-      llvm_unreachable("calling emitStore on non-loadable type");
-    }
-
-    SILValue emitLoad(SILBuilder &B, SILLocation loc, SILValue addr,
-                      LoadOwnershipQualifier qual) const override {
-      llvm_unreachable("calling emitLoad on non-loadable type");
-    }
-
-    void emitDestroyAddress(SILBuilder &B, SILLocation loc,
-                            SILValue addr) const override {
-    }
-
-    void emitDestroyRValue(SILBuilder &B, SILLocation loc,
-                           SILValue value) const override {
-    }
-
-    SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
-                           SILValue value) const override {
-      llvm_unreachable("type is not loadable!");
-    }
-
-    SILValue emitLoweredCopyValue(SILBuilder &B, SILLocation loc,
-                                  SILValue value,
-                                  TypeExpansionKind style) const override {
-      llvm_unreachable("type is not loadable!");
-    }
-
-    void emitDestroyValue(SILBuilder &B, SILLocation loc,
-                          SILValue value) const override {
-      llvm_unreachable("type is not loadable!");
-    }
-
-    void emitLoweredDestroyValue(SILBuilder &B, SILLocation loc, SILValue value,
-                                 TypeExpansionKind style) const override {
-      llvm_unreachable("type is not loadable!");
-    }
-  };
-
-
   /// Build the appropriate TypeLowering subclass for the given type,
   /// which is assumed to already have been lowered.
   class LowerType
@@ -1233,24 +1175,19 @@
       return new (TC, Dependent) TrivialTypeLowering(silType);
     }
 
-    const TypeLowering *
-    handleTrivialAddressOnly(CanType type) {
-      auto silType = SILType::getPrimitiveObjectType(type);
-      return new (TC, Dependent) AddressOnlyTrivialTypeLowering(silType);
-    }
-
     const TypeLowering *handleReference(CanType type) {
       auto silType = SILType::getPrimitiveObjectType(type);
       return new (TC, Dependent) ReferenceTypeLowering(silType);
     }
 
-    const TypeLowering *handleAddressOnly(CanType type) {
+    const TypeLowering *handleAddressOnly(CanType type, 
+                                          RecursiveProperties properties) {
       if (SILModuleConventions(M).useLoweredAddresses()) {
         auto silType = SILType::getPrimitiveAddressType(type);
-        return new (TC, Dependent) AddressOnlyTypeLowering(silType);
+        return new (TC, Dependent) AddressOnlyTypeLowering(silType, properties);
       }
       auto silType = SILType::getPrimitiveObjectType(type);
-      return new (TC, Dependent) OpaqueValueTypeLowering(silType);
+      return new (TC, Dependent) OpaqueValueTypeLowering(silType, properties);
     }
 
     const TypeLowering *
@@ -1266,19 +1203,14 @@
     }
 
     const TypeLowering *visitTupleType(CanTupleType tupleType) {
-      bool hasOnlyTrivialChildren = true;
-
+      RecursiveProperties properties;
       for (auto eltType : tupleType.getElementTypes()) {
         auto &lowering = TC.getTypeLowering(eltType);
-        if (lowering.isAddressOnly())
-          return handleAddressOnly(tupleType);
-        hasOnlyTrivialChildren &= lowering.isTrivial();
+        properties.addSubobject(lowering.getRecursiveProperties());
       }
 
-      if (hasOnlyTrivialChildren)
-        return handleTrivial(tupleType);
-
-      return new (TC, Dependent) LoadableTupleTypeLowering(tupleType);
+      return handleAggregateByProperties<LoadableTupleTypeLowering>(tupleType,
+                                                                    properties);
     }
 
     const TypeLowering *visitAnyStructType(CanType structType, StructDecl *D) {
@@ -1286,37 +1218,27 @@
       // For now, if the type does not have a fixed layout in all resilience
       // domains, we will treat it as address-only in SIL.
       if (D->isResilient(M.getSwiftModule(), Expansion))
-        return handleAddressOnly(structType);
+        return handleAddressOnly(structType,
+                                 RecursiveProperties::forOpaque());
 
       // Classify the type according to its stored properties.
-      bool trivial = true;
+      RecursiveProperties properties;
       for (auto field : D->getStoredProperties()) {
         auto substFieldType =
           structType->getTypeOfMember(D->getModuleContext(), field, nullptr);
-        switch (classifyType(substFieldType->getCanonicalType(),
-                             M, Sig, Expansion)) {
-        case LoweredTypeKind::TrivialAddressOnly:
-        case LoweredTypeKind::AddressOnly:
-          return handleAddressOnly(structType);
-        case LoweredTypeKind::AggWithReference:
-        case LoweredTypeKind::Reference:
-          trivial = false;
-          break;
-        case LoweredTypeKind::Trivial:
-          break;
-        }
+        properties.addSubobject(classifyType(substFieldType->getCanonicalType(),
+                                             M, Sig, Expansion));
       }
 
-      if (trivial)
-        return handleTrivial(structType);
-      return new (TC, Dependent) LoadableStructTypeLowering(structType);
+      return handleAggregateByProperties<LoadableStructTypeLowering>(structType,
+                                                                    properties);
     }
         
     const TypeLowering *visitAnyEnumType(CanType enumType, EnumDecl *D) {
       // For now, if the type does not have a fixed layout in all resilience
       // domains, we will treat it as address-only in SIL.
       if (D->isResilient(M.getSwiftModule(), Expansion))
-        return handleAddressOnly(enumType);
+        return handleAddressOnly(enumType, RecursiveProperties::forOpaque());
 
       // If the whole enum is indirect, we lower it as if all payload
       // cases were indirect. This means a fixed-layout indirect enum
@@ -1327,18 +1249,16 @@
         return new (TC, Dependent) LoadableEnumTypeLowering(enumType);
       }
 
-      // If any of the enum elements have address-only data, the enum is
-      // address-only.
-      bool trivial = true;
+      // Accumulate the properties of all direct payloads.
+      RecursiveProperties properties;
       for (auto elt : D->getAllElements()) {
-        // No-payload elements do not affect address-only-ness.
+        // No-payload elements do not affect any recursive properties.
         if (!elt->hasAssociatedValues())
           continue;
 
-        // Indirect elements make the type nontrivial, but don't affect
-        // address-only-ness.
+        // Indirect elements only make the type nontrivial.
         if (elt->isIndirect()) {
-          trivial = false;
+          properties.setNonTrivial();
           continue;
         }
         
@@ -1347,22 +1267,24 @@
                               elt->getArgumentInterfaceType())
           ->getCanonicalType();
         
-        switch (classifyType(substEltType, M, Sig, Expansion)) {
-        case LoweredTypeKind::TrivialAddressOnly:
-        case LoweredTypeKind::AddressOnly:
-          return handleAddressOnly(enumType);
-        case LoweredTypeKind::AggWithReference:
-        case LoweredTypeKind::Reference:
-          trivial = false;
-          break;
-        case LoweredTypeKind::Trivial:
-          break;
-        }
-        
+        properties.addSubobject(classifyType(substEltType, M, Sig, Expansion));
       }
-      if (trivial)
-        return handleTrivial(enumType);
-      return new (TC, Dependent) LoadableEnumTypeLowering(enumType);
+
+      return handleAggregateByProperties<LoadableEnumTypeLowering>(enumType,
+                                                                   properties);
+    }
+
+    template <class LoadableLoweringClass>
+    const TypeLowering *handleAggregateByProperties(CanType type,
+                                                    RecursiveProperties props) {
+      if (props.isAddressOnly()) {
+        return handleAddressOnly(type, props);
+      }
+      assert(props.isFixedABI());
+      if (props.isTrivial()) {
+        return handleTrivial(type);
+      }
+      return new (TC, Dependent) LoadableLoweringClass(type);
     }
   };
 } // end anonymous namespace
diff --git a/test/IRGen/Inputs/OtherModule.swift b/test/IRGen/Inputs/OtherModule.swift
new file mode 100644
index 0000000..ef16b8f
--- /dev/null
+++ b/test/IRGen/Inputs/OtherModule.swift
@@ -0,0 +1,24 @@
+import resilient_struct
+
+public struct First {}
+public struct Second {
+    public let resilientData: Size
+}
+
+private enum PrivateEnum {
+  case first(First?)
+  case second(Second?)
+}
+
+public struct Foo {
+    private var _property = PrivateEnum.first(nil)
+}
+
+internal enum InternalEnum {
+  case first(First?)
+  case second(Second?)
+}
+
+public struct Bar {
+    private var _property = InternalEnum.first(nil)
+}
diff --git a/test/IRGen/multi_file_resilience.swift b/test/IRGen/multi_file_resilience.swift
new file mode 100644
index 0000000..02aef2c
--- /dev/null
+++ b/test/IRGen/multi_file_resilience.swift
@@ -0,0 +1,41 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+
+// RUN: %target-swift-frontend -emit-module -enable-resilience \
+// RUN:   -emit-module-path=%t/resilient_struct.swiftmodule \
+// RUN:   -module-name=resilient_struct %S/../Inputs/resilient_struct.swift
+
+// RUN: %target-swift-frontend -module-name main -I %t -emit-ir -primary-file %s %S/Inputs/OtherModule.swift | %FileCheck %s -DINT=i%target-ptrsize
+
+// This is a single-module version of the test case in
+// multi_module_resilience.
+// rdar://39763787
+
+// CHECK-LABEL: define {{(protected )?}}swiftcc void @"$S4main7copyFoo3fooAA0C0VAE_tF"
+// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S4main3FooVMa"([[INT]] 0)
+// CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
+// CHECK: [[VWT:%.*]] = load i8**,
+//   Allocate 'copy'.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 9
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[SIZE:%.*]] = ptrtoint i8* [[T1]] to [[INT]]
+// CHECK: [[ALLOCA:%.*]] = alloca i8, [[INT]] [[SIZE]],
+// CHECK: [[COPY:%.*]] = bitcast i8* [[ALLOCA]] to [[FOO:%T4main3FooV]]*
+//   Perform 'initializeWithCopy' via the VWT instead of trying to inline it.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 2
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[COPYFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
+// CHECK: [[DEST:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque*
+// CHECK: [[SRC:%.*]] = bitcast [[FOO]]* %1 to %swift.opaque*
+// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]])
+//   Perform 'initializeWithTake' via the VWT.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
+// CHECK: [[DEST:%.*]] = bitcast [[FOO]]* %0 to %swift.opaque*
+// CHECK: [[SRC:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque*
+// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]])
+public func copyFoo(foo: Foo) -> Foo {
+  let copy = foo
+  return copy
+}
diff --git a/test/IRGen/multi_module_resilience.swift b/test/IRGen/multi_module_resilience.swift
new file mode 100644
index 0000000..cfd146a
--- /dev/null
+++ b/test/IRGen/multi_module_resilience.swift
@@ -0,0 +1,74 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+
+// RUN: %target-swift-frontend -emit-module -enable-resilience \
+// RUN:   -emit-module-path=%t/resilient_struct.swiftmodule \
+// RUN:   -module-name=resilient_struct %S/../Inputs/resilient_struct.swift
+
+// RUN: %target-swift-frontend -emit-module -I %t \
+// RUN:   -emit-module-path=%t/OtherModule.swiftmodule \
+// RUN:   -module-name=OtherModule %S/Inputs/OtherModule.swift
+
+// RUN: %target-swift-frontend -module-name main -I %t -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize
+
+// rdar://39763787
+
+import OtherModule
+
+// CHECK-LABEL: define {{(protected )?}}swiftcc void @"$S4main7copyFoo3foo11OtherModule0C0VAF_tF"
+// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S11OtherModule3FooVMa"([[INT]] 0)
+// CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
+// CHECK: [[VWT:%.*]] = load i8**,
+//   Allocate 'copy'.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 9
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[SIZE:%.*]] = ptrtoint i8* [[T1]] to [[INT]]
+// CHECK: [[ALLOCA:%.*]] = alloca i8, [[INT]] [[SIZE]],
+// CHECK: [[COPY:%.*]] = bitcast i8* [[ALLOCA]] to [[FOO:%T11OtherModule3FooV]]*
+//   Perform 'initializeWithCopy' via the VWT instead of trying to inline it.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 2
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[COPYFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
+// CHECK: [[DEST:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque*
+// CHECK: [[SRC:%.*]] = bitcast [[FOO]]* %1 to %swift.opaque*
+// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]])
+//   Perform 'initializeWithTake' via the VWT.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
+// CHECK: [[DEST:%.*]] = bitcast [[FOO]]* %0 to %swift.opaque*
+// CHECK: [[SRC:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque*
+// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]])
+public func copyFoo(foo: Foo) -> Foo {
+  let copy = foo
+  return copy
+}
+
+// CHECK-LABEL: define {{(protected )?}}swiftcc void @"$S4main7copyBar3bar11OtherModule0C0VAF_tF"
+// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S11OtherModule3BarVMa"([[INT]] 0)
+// CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
+// CHECK: [[VWT:%.*]] = load i8**,
+//   Allocate 'copy'.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 9
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[SIZE:%.*]] = ptrtoint i8* [[T1]] to [[INT]]
+// CHECK: [[ALLOCA:%.*]] = alloca i8, [[INT]] [[SIZE]],
+// CHECK: [[COPY:%.*]] = bitcast i8* [[ALLOCA]] to [[BAR:%T11OtherModule3BarV]]*
+//   Perform 'initializeWithCopy' via the VWT instead of trying to inline it.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 2
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[COPYFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
+// CHECK: [[DEST:%.*]] = bitcast [[BAR]]* [[COPY]] to %swift.opaque*
+// CHECK: [[SRC:%.*]] = bitcast [[BAR]]* %1 to %swift.opaque*
+// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]])
+//   Perform 'initializeWithTake' via the VWT.
+// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4
+// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]],
+// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
+// CHECK: [[DEST:%.*]] = bitcast [[BAR]]* %0 to %swift.opaque*
+// CHECK: [[SRC:%.*]] = bitcast [[BAR]]* [[COPY]] to %swift.opaque*
+// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]])
+public func copyBar(bar: Bar) -> Bar {
+  let copy = bar
+  return copy
+}