Merge pull request #19651 from compnerd/SKWin

SourceKit: handle Windows codepaths better
diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h
index 27d5e74..0d3a663 100644
--- a/include/swift/SIL/ApplySite.h
+++ b/include/swift/SIL/ApplySite.h
@@ -1,4 +1,4 @@
-//===--- ApplySite.h ------------------------------------------------------===//
+//===--- ApplySite.h -------------------------------------*- mode: c++ -*--===//
 //
 // This source file is part of the Swift.org open source project
 //
@@ -459,6 +459,18 @@
     return getArguments().slice(getNumIndirectSILResults());
   }
 
+  /// Returns true if \p op is the callee operand of this apply site
+  /// and not an argument operand.
+  bool isCalleeOperand(const Operand &op) const {
+    return op.getOperandNumber() < getOperandIndexOfFirstArgument();
+  }
+
+  /// Returns true if \p op is an operand that passes an indirect
+  /// result argument to the apply site.
+  bool isIndirectResultOperand(const Operand &op) const {
+    return getCalleeArgIndex(op) < getNumIndirectSILResults();
+  }
+
   static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); }
 
   static bool classof(const SILInstruction *inst) {
diff --git a/include/swift/SIL/OwnershipUtils.h b/include/swift/SIL/OwnershipUtils.h
index 8fe9aed..10aac33 100644
--- a/include/swift/SIL/OwnershipUtils.h
+++ b/include/swift/SIL/OwnershipUtils.h
@@ -64,26 +64,26 @@
 /// so types/etc do not leak.
 struct OwnershipChecker {
   /// The list of regular users from the last run of the checker.
-  SmallVector<SILInstruction *, 16> RegularUsers;
+  SmallVector<SILInstruction *, 16> regularUsers;
 
   /// The list of regular users from the last run of the checker.
-  SmallVector<SILInstruction *, 16> LifetimeEndingUsers;
+  SmallVector<SILInstruction *, 16> lifetimeEndingUsers;
 
   /// The live blocks for the SILValue we processed. This can be used to
   /// determine if a block is in the "live" region of our SILInstruction.
-  SmallPtrSet<SILBasicBlock *, 32> LiveBlocks;
+  SmallPtrSet<SILBasicBlock *, 32> liveBlocks;
 
   /// The list of implicit regular users from the last run of the checker.
   ///
   /// This is used to encode end of scope like instructions.
-  SmallVector<SILInstruction *, 4> ImplicitRegularUsers;
+  SmallVector<SILInstruction *, 4> endScopeRegularUsers;
 
   /// The module that we are in.
-  SILModule &Mod;
+  SILModule &mod;
 
   /// A cache of dead-end basic blocks that we use to determine if we can
   /// ignore "leaks".
-  DeadEndBlocks &DEBlocks;
+  DeadEndBlocks &deadEndBlocks;
 
   bool checkValue(SILValue Value);
 };
@@ -98,7 +98,7 @@
                             ArrayRef<BranchPropagatedUser> consumingUses,
                             ArrayRef<BranchPropagatedUser> nonConsumingUses,
                             SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
-                            DeadEndBlocks &deBlocks,
+                            DeadEndBlocks &deadEndBlocks,
                             ownership::ErrorBehaviorKind errorBehavior);
 
 } // namespace swift
diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h
index 01cea12..46b2349 100644
--- a/include/swift/SIL/SILModule.h
+++ b/include/swift/SIL/SILModule.h
@@ -378,6 +378,8 @@
     return wholeModule;
   }
 
+  bool isStdlibModule() const;
+
   /// Returns true if it is the optimized OnoneSupport module.
   bool isOptimizedOnoneSupportModule() const;
 
diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h
index 322c561..4172f54 100644
--- a/include/swift/SIL/SILValue.h
+++ b/include/swift/SIL/SILValue.h
@@ -55,6 +55,33 @@
   return llvm::hash_value(size_t(K));
 }
 
+/// What constraint does the given use of an SSA value put on the lifetime of
+/// the given SSA value.
+///
+/// There are two possible constraints: MustBeLive and
+/// MustBeInvalidated. MustBeLive means that the SSA value must be able to be
+/// used in a valid way at the given use point. MustBeInvalidated means that any
+/// use of given SSA value after this instruction on any path through this
+/// instruction.
+enum class UseLifetimeConstraint {
+  /// This use requires the SSA value to be live after the given instruction's
+  /// execution.
+  MustBeLive,
+
+  /// This use invalidates the given SSA value.
+  ///
+  /// This means that the given SSA value can not have any uses that are
+  /// reachable from this instruction. When a value has owned semantics this
+  /// means the SSA value is destroyed at this point. When a value has
+  /// guaranteed (i.e. shared borrow) semantics this means that the program
+  /// has left the scope of the borrowed SSA value and said value can not be
+  /// used.
+  MustBeInvalidated,
+};
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
+                              UseLifetimeConstraint constraint);
+
 /// A value representing the specific ownership semantics that a SILValue may
 /// have.
 struct ValueOwnershipKind {
@@ -64,27 +91,23 @@
     /// with Trivial ownership kind can be used. Other side effects (e.g. Memory
     /// dependencies) must still be respected. A SILValue with Trivial ownership
     /// kind must be of Trivial SILType (i.e. SILType::isTrivial(SILModule &)
-    /// must
-    /// return true).
+    /// must return true).
     ///
     /// Some examples of SIL types with Trivial ownership are: Builtin.Int32,
     /// Builtin.RawPointer, aggregates containing all trivial types.
     Trivial,
 
     /// A SILValue with `Unowned` ownership kind is an independent value that
-    /// has
-    /// a lifetime that is only guaranteed to last until the next program
-    /// visible
-    /// side-effect. To maintain the lifetime of an unowned value, it must be
-    /// converted to an owned representation via a copy_value.
+    /// has a lifetime that is only guaranteed to last until the next program
+    /// visible side-effect. To maintain the lifetime of an unowned value, it
+    /// must be converted to an owned representation via a copy_value.
     ///
     /// Unowned ownership kind occurs mainly along method/function boundaries in
     /// between Swift and Objective-C code.
     Unowned,
 
     /// A SILValue with `Owned` ownership kind is an independent value that has
-    /// an
-    /// ownership independent of any other ownership imbued within it. The
+    /// an ownership independent of any other ownership imbued within it. The
     /// SILValue must be paired with a consuming operation that ends the SSA
     /// value's lifetime exactly once along all paths through the program.
     Owned,
@@ -105,10 +128,9 @@
     Guaranteed,
 
     /// A SILValue with undefined ownership. It can pair with /Any/ ownership
-    /// kinds . This means that it could take on /any/ ownership semantics. This
+    /// kinds. This means that it could take on /any/ ownership semantics. This
     /// is meant only to model SILUndef and to express certain situations where
-    /// we
-    /// use unqualified ownership. Expected to tighten over time.
+    /// we use unqualified ownership. Expected to tighten over time.
     Any,
 
     LastValueOwnershipKind = Any,
@@ -151,6 +173,23 @@
   ValueOwnershipKind getProjectedOwnershipKind(SILModule &M,
                                                SILType Proj) const;
 
+  /// Return the lifetime constraint semantics for this
+  /// ValueOwnershipKind when forwarding ownership.
+  ///
+  /// This is MustBeInvalidated for Owned and MustBeLive for all other ownership
+  /// kinds.
+  UseLifetimeConstraint getForwardingLifetimeConstraint() const {
+    switch (Value) {
+    case ValueOwnershipKind::Trivial:
+    case ValueOwnershipKind::Any:
+    case ValueOwnershipKind::Guaranteed:
+    case ValueOwnershipKind::Unowned:
+      return UseLifetimeConstraint::MustBeLive;
+    case ValueOwnershipKind::Owned:
+      return UseLifetimeConstraint::MustBeInvalidated;
+    }
+  }
+
   /// Returns true if \p Other can be merged successfully with this, implying
   /// that the two ownership kinds are "compatibile".
   ///
@@ -347,6 +386,149 @@
                        DeadEndBlocks *DEBlocks = nullptr) const;
 };
 
+/// A map from a ValueOwnershipKind that an operand can accept to a
+/// UseLifetimeConstraint that describes the effect that the operand's use has
+/// on the underlying value. If a ValueOwnershipKind is not in this map then
+/// matching an operand with the value results in an ill formed program.
+///
+/// So for instance, a map could specify that if a value is used as an owned
+/// parameter, then the use implies that the original value is destroyed at that
+/// point. In contrast, if the value is used as a guaranteed parameter, then the
+/// liveness constraint just requires that the value remains alive at the use
+/// point.
+struct OperandOwnershipKindMap {
+  // One bit for if a value exists and if the value exists, what the
+  // ownership constraint is. These are stored as pairs.
+  //
+  // NOTE: We are burning 1 bit per unset value. But this is without
+  // matter since we are always going to need less bits than 64, so we
+  // should always have a small case SmallBitVector, so there is no
+  // difference in size.
+  static constexpr unsigned NUM_DATA_BITS =
+      2 * (unsigned(ValueOwnershipKind::LastValueOwnershipKind) + 1);
+
+  /// A bit vector representing our "map". Given a ValueOwnershipKind k, if the
+  /// operand can accept k, the unsigned(k)*2 bit will be set to true. Assuming
+  /// that bit is set, the unsigned(k)*2+1 bit is set to the use lifetime
+  /// constraint provided by the value.
+  SmallBitVector data;
+
+  OperandOwnershipKindMap() : data(NUM_DATA_BITS) {}
+  OperandOwnershipKindMap(ValueOwnershipKind kind,
+                          UseLifetimeConstraint constraint)
+      : data(NUM_DATA_BITS) {
+    add(kind, constraint);
+  }
+
+  /// Return the OperandOwnershipKindMap that tests for compatibility with
+  /// ValueOwnershipKind kind. This means that it will accept a element whose
+  /// ownership is ValueOwnershipKind::Any.
+  static OperandOwnershipKindMap
+  compatibilityMap(ValueOwnershipKind kind, UseLifetimeConstraint constraint) {
+    OperandOwnershipKindMap set;
+    set.addCompatibilityConstraint(kind, constraint);
+    return set;
+  }
+
+  /// Return a map that is compatible with any and all ValueOwnershipKinds
+  /// except for \p kind.
+  static OperandOwnershipKindMap
+  compatibleWithAllExcept(ValueOwnershipKind kind) {
+    OperandOwnershipKindMap map;
+    unsigned index = 0;
+    unsigned end = unsigned(ValueOwnershipKind::LastValueOwnershipKind) + 1;
+    for (; index != end; ++index) {
+      if (ValueOwnershipKind(index) == kind) {
+        continue;
+      }
+      map.add(ValueOwnershipKind(index), UseLifetimeConstraint::MustBeLive);
+    }
+    return map;
+  }
+
+  /// Create a map that has compatibility constraints for each of the
+  /// ValueOwnershipKind, UseLifetimeConstraints in \p args.
+  static OperandOwnershipKindMap
+  compatibilityMap(std::initializer_list<
+                   std::pair<ValueOwnershipKind, UseLifetimeConstraint>>
+                       args) {
+    OperandOwnershipKindMap map;
+    for (auto &p : args) {
+      map.addCompatibilityConstraint(p.first, p.second);
+    }
+    return map;
+  }
+
+  /// Return a map that states that an operand can take any ownership with each
+  /// ownership having a must be live constraint.
+  static OperandOwnershipKindMap allLive() {
+    OperandOwnershipKindMap map;
+    unsigned index = 0;
+    unsigned end = unsigned(ValueOwnershipKind::LastValueOwnershipKind) + 1;
+    while (index != end) {
+      map.add(ValueOwnershipKind(index), UseLifetimeConstraint::MustBeLive);
+      ++index;
+    }
+    return map;
+  }
+
+  /// Specify that the operand associated with this set can accept a value with
+  /// ValueOwnershipKind \p kind. The value provided by the operand will have a
+  /// new ownership enforced constraint defined by \p constraint.
+  void add(ValueOwnershipKind kind, UseLifetimeConstraint constraint) {
+    unsigned index = unsigned(kind);
+    unsigned kindOffset = index * 2;
+    unsigned constraintOffset = index * 2 + 1;
+
+    // If we have already put this kind into the map, we require the constraint
+    // offset to be the same, i.e. we only allow for a kind to be added twice if
+    // the constraint is idempotent. We assert otherwise.
+    assert((!data[kindOffset] || UseLifetimeConstraint(bool(
+                                     data[constraintOffset])) == constraint) &&
+           "Adding kind twice to the map with different constraints?!");
+    data[kindOffset] = true;
+    data[constraintOffset] = bool(constraint);
+  }
+
+  void addCompatibilityConstraint(ValueOwnershipKind kind,
+                                  UseLifetimeConstraint constraint) {
+    add(ValueOwnershipKind::Any, UseLifetimeConstraint::MustBeLive);
+    add(kind, constraint);
+  }
+
+  bool canAcceptKind(ValueOwnershipKind kind) const {
+    unsigned index = unsigned(kind);
+    unsigned kindOffset = index * 2;
+    return data[kindOffset];
+  }
+
+  UseLifetimeConstraint getLifetimeConstraint(ValueOwnershipKind kind) const;
+
+  void print(llvm::raw_ostream &os) const;
+  LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in a debugger");
+};
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
+                                     OperandOwnershipKindMap map) {
+  map.print(os);
+  return os;
+}
+
+// Out of line to work around lack of forward declaration for operator <<.
+inline UseLifetimeConstraint
+OperandOwnershipKindMap::getLifetimeConstraint(ValueOwnershipKind kind) const {
+#ifndef NDEBUG
+  if (!canAcceptKind(kind)) {
+    llvm::errs() << "Can not lookup lifetime constraint: " << kind
+                 << ". Not in map!\n"
+                 << *this;
+    llvm_unreachable("standard error assertion");
+  }
+#endif
+  unsigned constraintOffset = unsigned(kind) * 2 + 1;
+  return UseLifetimeConstraint(data[constraintOffset]);
+}
+
 /// A formal SIL reference to a value, suitable for use as a stored
 /// operand.
 class Operand {
@@ -413,10 +595,20 @@
   SILInstruction *getUser() { return Owner; }
   const SILInstruction *getUser() const { return Owner; }
 
-  /// getOperandNumber - Return which operand this is in the operand list of the
-  /// using instruction.
+  /// Return which operand this is in the operand list of the using instruction.
   unsigned getOperandNumber() const;
 
+  /// Return the static map of ValueOwnershipKinds that this operand can
+  /// potentially have to the UseLifetimeConstraint associated with that
+  /// ownership kind
+  ///
+  /// NOTE: This is implemented in OperandOwnershipKindMapClassifier.cpp.
+  ///
+  /// NOTE: The default argument isSubValue is a temporary staging flag that
+  /// will be removed once borrow scoping is checked by the normal verifier.
+  OperandOwnershipKindMap
+  getOwnershipKindMap(bool isForwardingSubValue = false) const;
+
 private:
   void removeFromCurrent() {
     if (!Back) return;
diff --git a/include/swift/SILOptimizer/Utils/Local.h b/include/swift/SILOptimizer/Utils/Local.h
index 16c10b1..0224dd4 100644
--- a/include/swift/SILOptimizer/Utils/Local.h
+++ b/include/swift/SILOptimizer/Utils/Local.h
@@ -246,8 +246,8 @@
   /// In this case, if \p mode is AllowToModifyCFG, those critical edges are
   /// split, otherwise nothing is done and the returned \p Fr is not valid.
   ///
-  /// If \p DEBlocks is provided, all dead-end blocks are ignored. This prevents
-  /// unreachable-blocks to be included in the frontier.
+  /// If \p deadEndBlocks is provided, all dead-end blocks are ignored. This
+  /// prevents unreachable-blocks to be included in the frontier.
   bool computeFrontier(Frontier &Fr, Mode mode,
                        DeadEndBlocks *DEBlocks = nullptr);
 
diff --git a/lib/SIL/SILModule.cpp b/lib/SIL/SILModule.cpp
index 62ac387..ad2870c 100644
--- a/lib/SIL/SILModule.cpp
+++ b/lib/SIL/SILModule.cpp
@@ -660,6 +660,10 @@
   OptRecordRawStream = std::move(RawStream);
 }
 
+bool SILModule::isStdlibModule() const {
+  return TheSwiftModule->isStdlibModule();
+}
+
 SILProperty *SILProperty::create(SILModule &M,
                                  bool Serialized,
                                  AbstractStorageDecl *Decl,
diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp
index 15ef4bc..6d6a157 100644
--- a/lib/SIL/SILOwnershipVerifier.cpp
+++ b/lib/SIL/SILOwnershipVerifier.cpp
@@ -12,7 +12,6 @@
 
 #define DEBUG_TYPE "sil-ownership-verifier"
 
-#include "UseOwnershipRequirement.h"
 #include "swift/AST/ASTContext.h"
 #include "swift/AST/AnyFunctionRef.h"
 #include "swift/AST/Decl.h"
@@ -137,66 +136,51 @@
 }
 
 //===----------------------------------------------------------------------===//
-//                      OwnershipCompatibilityUseChecker
+//                      OperandOwnershipKindClassifier
 //===----------------------------------------------------------------------===//
 
 namespace {
 
-struct OwnershipUseCheckerResult {
-  bool HasCompatibleOwnership;
-  bool ShouldCheckForDataflowViolations;
-
-  OwnershipUseCheckerResult(bool HasCompatibleOwnership,
-                            UseLifetimeConstraint OwnershipRequirement)
-      : HasCompatibleOwnership(HasCompatibleOwnership),
-        ShouldCheckForDataflowViolations(bool(OwnershipRequirement)) {}
-};
-
-class OwnershipCompatibilityUseChecker
-    : public SILInstructionVisitor<OwnershipCompatibilityUseChecker,
-                                   OwnershipUseCheckerResult> {
+class OperandOwnershipKindClassifier
+    : public SILInstructionVisitor<OperandOwnershipKindClassifier,
+                                   OperandOwnershipKindMap> {
 public:
+  using Map = OperandOwnershipKindMap;
+
 private:
-  LLVM_ATTRIBUTE_UNUSED
-  SILModule &Mod;
+  LLVM_ATTRIBUTE_UNUSED SILModule &mod;
 
-  const Operand &Op;
-  SILValue BaseValue;
-  ErrorBehaviorKind ErrorBehavior;
+  const Operand &op;
+  ErrorBehaviorKind errorBehavior;
+  bool checkingSubObject;
 
 public:
-  /// Create a new OwnershipCompatibilityUseChecker.
+  /// Create a new OperandOwnershipKindClassifier.
   ///
   /// In most cases, one should only pass in \p Op and \p BaseValue will be set
   /// to Op.get(). In cases where one is trying to verify subobjects, Op.get()
   /// should be the subobject and Value should be the parent object. An example
   /// of where one would want to do this is in the case of value projections
   /// like struct_extract.
-  OwnershipCompatibilityUseChecker(SILModule &M, const Operand &Op,
-                                   SILValue BaseValue,
-                                   ErrorBehaviorKind ErrorBehavior)
-      : Mod(M), Op(Op), BaseValue(BaseValue), ErrorBehavior(ErrorBehavior) {
-    assert((BaseValue == Op.get() ||
-            BaseValue.getOwnershipKind() == ValueOwnershipKind::Guaranteed) &&
-           "Guaranteed values are the only values allowed to have subobject");
-    // We only support subobjects on objects.
-    assert((BaseValue->getType().isObject() || !isCheckingSubObject()) &&
-           "Checking a subobject, but do not have an object base value?!");
-  }
+  OperandOwnershipKindClassifier(SILModule &mod, const Operand &op,
+                                 ErrorBehaviorKind errorBehavior,
+                                 bool checkingSubObject)
+      : mod(mod), op(op), errorBehavior(errorBehavior),
+        checkingSubObject(checkingSubObject) {}
 
-  bool isCheckingSubObject() const { return Op.get() != BaseValue; }
+  bool isCheckingSubObject() const { return checkingSubObject; }
 
-  SILValue getValue() const { return Op.get(); }
+  SILValue getValue() const { return op.get(); }
 
   ValueOwnershipKind getOwnershipKind() const {
-    assert(getValue().getOwnershipKind() == Op.get().getOwnershipKind() &&
+    assert(getValue().getOwnershipKind() == op.get().getOwnershipKind() &&
            "Expected ownership kind of parent value and operand");
     return getValue().getOwnershipKind();
   }
 
-  unsigned getOperandIndex() const { return Op.getOperandNumber(); }
+  unsigned getOperandIndex() const { return op.getOperandNumber(); }
 
-  SILType getType() const { return Op.get()->getType(); }
+  SILType getType() const { return op.get()->getType(); }
 
   bool compatibleWithOwnership(ValueOwnershipKind Kind) const {
     return getOwnershipKind().isCompatibleWith(Kind);
@@ -213,73 +197,26 @@
       getOwnershipKind() == ValueOwnershipKind::Any;
   }
 
-  /// Depending on our initialization, either return false or call Func and
-  /// throw an error.
-  bool handleError(llvm::function_ref<void()> &&MessagePrinterFunc) const {
-    if (ErrorBehavior.shouldPrintMessage()) {
-      MessagePrinterFunc();
-    }
-
-    if (ErrorBehavior.shouldReturnFalse()) {
-      return false;
-    }
-
-    assert(ErrorBehavior.shouldAssert() && "At this point, we should assert");
-    llvm_unreachable("triggering standard assertion failure routine");
-  }
-
-  OwnershipUseCheckerResult visitForwardingInst(SILInstruction *I,
-                                                ArrayRef<Operand> Ops);
-  OwnershipUseCheckerResult visitForwardingInst(SILInstruction *I) {
+  OperandOwnershipKindMap visitForwardingInst(SILInstruction *I,
+                                              ArrayRef<Operand> Ops);
+  OperandOwnershipKindMap visitForwardingInst(SILInstruction *I) {
     return visitForwardingInst(I, I->getAllOperands());
   }
 
-  /// Visit a terminator instance that performs a transform like
-  /// operation. E.x.: switch_enum, checked_cast_br. This does not include br or
-  /// cond_br.
-  OwnershipUseCheckerResult visitTransformingTerminatorInst(TermInst *TI);
-
-  OwnershipUseCheckerResult
-  visitEnumArgument(EnumDecl *E, ValueOwnershipKind RequiredConvention);
-  OwnershipUseCheckerResult
+  OperandOwnershipKindMap
+  visitEnumArgument(ValueOwnershipKind RequiredConvention);
+  OperandOwnershipKindMap
   visitApplyParameter(ValueOwnershipKind RequiredConvention,
                       UseLifetimeConstraint Requirement);
-  OwnershipUseCheckerResult
-  visitFullApply(FullApplySite apply);
+  OperandOwnershipKindMap visitFullApply(FullApplySite apply);
 
-  /// Check if \p User as compatible ownership with the SILValue that we are
-  /// checking.
-  ///
-  /// \returns true if the user is a use that must be checked for dataflow
-  /// violations.
-  bool check(SILInstruction *User) {
-    auto Result = visit(User);
-    if (!Result.HasCompatibleOwnership) {
-      return handleError([&]() {
-        llvm::errs() << "Function: '" << User->getFunction()->getName() << "'\n"
-                     << "Have operand with incompatible ownership?!\n"
-                     << "Value: " << *getValue() << "BaseValue: " << *BaseValue
-                     << "User: " << *User << "Conv: " << getOwnershipKind()
-                     << "\n\n";
-      });
-    }
-
-    assert((!Result.ShouldCheckForDataflowViolations ||
-            !isAddressOrTrivialType()) &&
-           "Address or trivial types should never be checked for dataflow "
-           "violations");
-
-    return Result.ShouldCheckForDataflowViolations;
-  }
-
-  OwnershipUseCheckerResult visitCallee(CanSILFunctionType SubstCalleeType);
-  OwnershipUseCheckerResult
+  OperandOwnershipKindMap visitCallee(CanSILFunctionType SubstCalleeType);
+  OperandOwnershipKindMap
   checkTerminatorArgumentMatchesDestBB(SILBasicBlock *DestBB, unsigned OpIndex);
 
 // Create declarations for all instructions, so we get a warning at compile
 // time if any instructions do not have an implementation.
-#define INST(Id, Parent) \
-  OwnershipUseCheckerResult visit##Id(Id *);
+#define INST(Id, Parent) OperandOwnershipKindMap visit##Id(Id *);
 #include "swift/SIL/SILNodes.def"
 };
 
@@ -288,9 +225,9 @@
 /// Implementation for instructions without operands. These should never be
 /// visited.
 #define NO_OPERAND_INST(INST)                                                  \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) {     \
-    assert(I->getNumOperands() == 0 &&                                         \
+  OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst(   \
+      INST##Inst *i) {                                                         \
+    assert(i->getNumOperands() == 0 &&                                         \
            "Expected instruction without operands?!");                         \
     llvm_unreachable("Instruction without operand can not be compatible with " \
                      "any def's OwnershipValueKind");                          \
@@ -320,16 +257,12 @@
 
 /// Instructions whose arguments are always compatible with one convention.
 #define CONSTANT_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, INST)      \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) {     \
-    assert(I->getNumOperands() && "Expected to have non-zero operands");       \
-    if (ValueOwnershipKind::OWNERSHIP == ValueOwnershipKind::Trivial) {        \
-      assert(isAddressOrTrivialType() &&                                       \
-             "Trivial ownership requires a trivial type or an address");       \
-    }                                                                          \
-                                                                               \
-    return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP),            \
-            UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT};                   \
+  OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst(   \
+      INST##Inst *i) {                                                         \
+    assert(i->getNumOperands() && "Expected to have non-zero operands");       \
+    return Map::compatibilityMap(                                              \
+        ValueOwnershipKind::OWNERSHIP,                                         \
+        UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT);                       \
   }
 CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, IsEscapingClosure)
 CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, RefElementAddr)
@@ -413,19 +346,13 @@
 /// Instructions whose arguments are always compatible with one convention.
 #define CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, \
                                            INST)                               \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) {     \
-    assert(I->getNumOperands() && "Expected to have non-zero operands");       \
-    if (ValueOwnershipKind::OWNERSHIP == ValueOwnershipKind::Trivial) {        \
-      assert(isAddressOrTrivialType() &&                                       \
-             "Trivial ownership requires a trivial type or an address");       \
-    }                                                                          \
-                                                                               \
-    if (compatibleWithOwnership(ValueOwnershipKind::Trivial)) {                \
-      return {true, UseLifetimeConstraint::MustBeLive};                        \
-    }                                                                          \
-    return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP),            \
-            UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT};                   \
+  OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst(   \
+      INST##Inst *i) {                                                         \
+    assert(i->getNumOperands() && "Expected to have non-zero operands");       \
+    return Map::compatibilityMap(                                              \
+        {{ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive},     \
+         {ValueOwnershipKind::OWNERSHIP,                                       \
+          UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT}});                   \
   }
 CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated,
                                    CheckedCastValueBranch)
@@ -438,9 +365,9 @@
 #undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
 
 #define ACCEPTS_ANY_OWNERSHIP_INST(INST)                                       \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) {     \
-    return {true, UseLifetimeConstraint::MustBeLive};                          \
+  OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst(   \
+      INST##Inst *I) {                                                         \
+    return Map::allLive();                                                     \
   }
 ACCEPTS_ANY_OWNERSHIP_INST(BeginBorrow)
 ACCEPTS_ANY_OWNERSHIP_INST(CopyValue)
@@ -460,15 +387,14 @@
 // Trivial if trivial typed, otherwise must accept owned?
 #define ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE(USE_LIFETIME_CONSTRAINT,  \
                                                      INST)                     \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) {     \
+  OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst(   \
+      INST##Inst *I) {                                                         \
     assert(I->getNumOperands() && "Expected to have non-zero operands");       \
     if (getType().is<AnyMetatypeType>()) {                                     \
-      return {true, UseLifetimeConstraint::MustBeLive};                        \
+      return Map::compatibilityMap(ValueOwnershipKind::Trivial,                \
+                                   UseLifetimeConstraint::MustBeLive);         \
     }                                                                          \
-    bool compatible = hasExactOwnership(ValueOwnershipKind::Any) ||            \
-                      !compatibleWithOwnership(ValueOwnershipKind::Trivial);   \
-    return {compatible, UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT};       \
+    return Map::compatibleWithAllExcept(ValueOwnershipKind::Trivial);          \
   }
 ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE(MustBeLive, ClassMethod)
 ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE(MustBeLive, ObjCMethod)
@@ -478,12 +404,10 @@
 
 // Trivial if trivial typed, otherwise must accept owned?
 #define ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(USE_LIFETIME_CONSTRAINT, INST)        \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) {     \
+  OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst(   \
+      INST##Inst *I) {                                                         \
     assert(I->getNumOperands() && "Expected to have non-zero operands");       \
-    bool compatible = hasExactOwnership(ValueOwnershipKind::Any) ||            \
-                      !compatibleWithOwnership(ValueOwnershipKind::Trivial);   \
-    return {compatible, UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT};       \
+    return Map::compatibleWithAllExcept(ValueOwnershipKind::Trivial);          \
   }
 ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, BridgeObjectToWord)
 ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, ClassifyBridgeObject)
@@ -506,53 +430,47 @@
 #include "swift/AST/ReferenceStorage.def"
 #undef ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitForwardingInst(SILInstruction *I, ArrayRef<Operand> Ops) {
-  assert(I->getNumOperands() && "Expected to have non-zero operands");
-  assert(isOwnershipForwardingInst(I) &&
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitForwardingInst(SILInstruction *i,
+                                                    ArrayRef<Operand> ops) {
+  assert(i->getNumOperands() && "Expected to have non-zero operands");
+  assert(isOwnershipForwardingInst(i) &&
          "Expected to have an ownership forwarding inst");
 
-  // Find the first index where we have a trivial value.
-  auto Iter = find_if(Ops, [&I](const Operand &Op) -> bool {
-    if (I->isTypeDependentOperand(Op))
+  // Find the first index where we have a non-trivial value.
+  auto iter = find_if(ops, [&i](const Operand &op) -> bool {
+    if (i->isTypeDependentOperand(op))
       return false;
-    return Op.get().getOwnershipKind() != ValueOwnershipKind::Trivial;
+    return op.get().getOwnershipKind() != ValueOwnershipKind::Trivial;
   });
 
-  // All trivial.
-  if (Iter == Ops.end()) {
-    return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-            UseLifetimeConstraint::MustBeLive};
+  // If we do not find a non-trivial value, then we know for sure that we have a
+  // trivial value.
+  if (iter == ops.end()) {
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
   }
 
-  unsigned Index = std::distance(Ops.begin(), Iter);
-  ValueOwnershipKind Base = Ops[Index].get().getOwnershipKind();
+  // Ok, we have at least a single non-trivial value. Grab the type of the
+  // operand and see if it is trivial or non-trivial. If the type of the operand
+  // is trivial, then return that we accept trivial here. Otherwise, return the
+  // base ownership kind.
+  if (getType().isTrivial(mod))
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
 
-  for (const Operand &Op : Ops.slice(Index + 1)) {
-    if (I->isTypeDependentOperand(Op))
-      continue;
-    auto OpKind = Op.get().getOwnershipKind();
-    if (OpKind.merge(ValueOwnershipKind::Trivial))
-      continue;
-
-    auto MergedValue = Base.merge(OpKind.Value);
-    if (!MergedValue.hasValue()) {
-      return {false, UseLifetimeConstraint::MustBeInvalidated};
-    }
-    Base = MergedValue.getValue();
-  }
-
-  // We only need to treat a forwarded instruction as a lifetime ending use of
-  // it is owned.
-  auto lifetimeConstraint = hasExactOwnership(ValueOwnershipKind::Owned)
-                                ? UseLifetimeConstraint::MustBeInvalidated
-                                : UseLifetimeConstraint::MustBeLive;
-  return {true, lifetimeConstraint};
+  // Otherwise, return the value ownership kind and forwarding lifetime
+  // constraint of the first non-trivial operand. This will ensure that all
+  // non-trivial operands have the same ownership kind.
+  unsigned index = std::distance(ops.begin(), iter);
+  ValueOwnershipKind kind = ops[index].get().getOwnershipKind();
+  auto lifetimeConstraint = kind.getForwardingLifetimeConstraint();
+  return Map::compatibilityMap(kind, lifetimeConstraint);
 }
 
 #define FORWARD_ANY_OWNERSHIP_INST(INST)                                       \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) {     \
+  OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst(   \
+      INST##Inst *I) {                                                         \
     return visitForwardingInst(I);                                             \
   }
 FORWARD_ANY_OWNERSHIP_INST(Tuple)
@@ -575,189 +493,246 @@
 // An instruction that forwards a constant ownership or trivial ownership.
 #define FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(                            \
     OWNERSHIP, USE_LIFETIME_CONSTRAINT, INST)                                  \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) {     \
+  OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst(   \
+      INST##Inst *I) {                                                         \
     assert(I->getNumOperands() && "Expected to have non-zero operands");       \
     assert(isGuaranteedForwardingInst(I) &&                                    \
            "Expected an ownership forwarding inst");                           \
-    if (ValueOwnershipKind::OWNERSHIP != ValueOwnershipKind::Trivial &&        \
-        hasExactOwnership(ValueOwnershipKind::Trivial)) {                      \
-      assert(isAddressOrTrivialType() &&                                       \
-             "Trivial ownership requires a trivial type or an address");       \
-      return {true, UseLifetimeConstraint::MustBeLive};                        \
-    }                                                                          \
-    if (ValueOwnershipKind::OWNERSHIP == ValueOwnershipKind::Trivial) {        \
-      assert(isAddressOrTrivialType() &&                                       \
-             "Trivial ownership requires a trivial type or an address");       \
-    }                                                                          \
-                                                                               \
-    return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP),            \
-            UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT};                   \
+    OperandOwnershipKindMap map;                                               \
+    map.add(ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive);   \
+    map.addCompatibilityConstraint(                                            \
+        ValueOwnershipKind::OWNERSHIP,                                         \
+        UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT);                       \
+    return map;                                                                \
   }
 FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, MustBeLive, TupleExtract)
 FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, MustBeLive,
                                            StructExtract)
 #undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitDeallocPartialRefInst(
-    DeallocPartialRefInst *I) {
-  if (getValue() == I->getInstance()) {
-    return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-            UseLifetimeConstraint::MustBeInvalidated};
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitDeallocPartialRefInst(
+    DeallocPartialRefInst *i) {
+  if (getValue() == i->getInstance()) {
+    return Map::compatibilityMap(ValueOwnershipKind::Owned,
+                                 UseLifetimeConstraint::MustBeInvalidated);
   }
 
-  return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-          UseLifetimeConstraint::MustBeLive};
+  return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                               UseLifetimeConstraint::MustBeLive);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitSelectEnumInst(SelectEnumInst *I) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitSelectEnumInst(SelectEnumInst *I) {
   if (getValue() == I->getEnumOperand()) {
-    return {true, UseLifetimeConstraint::MustBeLive};
+    return Map::allLive();
   }
 
   return visitForwardingInst(I, I->getAllOperands().drop_front());
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitAllocRefInst(AllocRefInst *I) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitAllocRefInst(AllocRefInst *I) {
   assert(I->getNumOperands() != 0
          && "If we reach this point, we must have a tail operand");
-  return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-          UseLifetimeConstraint::MustBeLive};
+  return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                               UseLifetimeConstraint::MustBeLive);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitAllocRefDynamicInst(
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitAllocRefDynamicInst(
     AllocRefDynamicInst *I) {
   assert(I->getNumOperands() != 0 &&
          "If we reach this point, we must have a tail operand");
-  return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-          UseLifetimeConstraint::MustBeLive};
+  return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                               UseLifetimeConstraint::MustBeLive);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::checkTerminatorArgumentMatchesDestBB(
-    SILBasicBlock *DestBB, unsigned OpIndex) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::checkTerminatorArgumentMatchesDestBB(
+    SILBasicBlock *destBB, unsigned opIndex) {
   // Grab the ownership kind of the destination block.
-  ValueOwnershipKind DestBlockArgOwnershipKind =
-      DestBB->getArgument(OpIndex)->getOwnershipKind();
+  ValueOwnershipKind destBlockArgOwnershipKind =
+      destBB->getArgument(opIndex)->getOwnershipKind();
 
   // Then if we do not have an enum, make sure that the conventions match.
-  EnumDecl *E = getType().getEnumOrBoundGenericEnum();
-  if (!E) {
-    bool matches = compatibleWithOwnership(DestBlockArgOwnershipKind);
-    auto lifetimeConstraint = hasExactOwnership(ValueOwnershipKind::Owned)
-                                  ? UseLifetimeConstraint::MustBeInvalidated
-                                  : UseLifetimeConstraint::MustBeLive;
-    return {matches, lifetimeConstraint};
+  if (!getType().getEnumOrBoundGenericEnum()) {
+    auto lifetimeConstraint =
+        destBlockArgOwnershipKind.getForwardingLifetimeConstraint();
+    return Map::compatibilityMap(destBlockArgOwnershipKind, lifetimeConstraint);
   }
 
-  return visitEnumArgument(E, DestBlockArgOwnershipKind);
+  // Otherwise, we need to properly handle the sum type nature of enum
+  // arguments.
+  return visitEnumArgument(destBlockArgOwnershipKind);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitBranchInst(BranchInst *BI) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitBranchInst(BranchInst *BI) {
   return checkTerminatorArgumentMatchesDestBB(BI->getDestBB(),
                                               getOperandIndex());
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitCondBranchInst(CondBranchInst *CBI) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitCondBranchInst(CondBranchInst *cbi) {
   // If our conditional branch is the condition, it is trivial. Check that the
   // ownership kind is trivial.
-  if (CBI->isConditionOperandIndex(getOperandIndex()))
-    return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-            UseLifetimeConstraint::MustBeLive};
+  if (cbi->isConditionOperandIndex(getOperandIndex()))
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
 
-  // Otherwise, make sure that our operand matches the
-  if (CBI->isTrueOperandIndex(getOperandIndex())) {
-    unsigned TrueOffset = 1;
-    return checkTerminatorArgumentMatchesDestBB(CBI->getTrueBB(),
-                                                getOperandIndex() - TrueOffset);
+  // Otherwise, make sure that our operand matches the ownership of the relevant
+  // argument.
+  //
+  // TODO: Use more updated APIs here to get the operands/etc.
+  if (cbi->isTrueOperandIndex(getOperandIndex())) {
+    unsigned trueOffset = 1;
+    return checkTerminatorArgumentMatchesDestBB(cbi->getTrueBB(),
+                                                getOperandIndex() - trueOffset);
   }
 
-  assert(CBI->isFalseOperandIndex(getOperandIndex()) &&
+  assert(cbi->isFalseOperandIndex(getOperandIndex()) &&
          "If an operand is not the condition index or a true operand index, it "
          "must be a false operand index");
-  unsigned FalseOffset = 1 + CBI->getTrueOperands().size();
-  return checkTerminatorArgumentMatchesDestBB(CBI->getFalseBB(),
-                                              getOperandIndex() - FalseOffset);
+  unsigned falseOffset = 1 + cbi->getTrueOperands().size();
+  return checkTerminatorArgumentMatchesDestBB(cbi->getFalseBB(),
+                                              getOperandIndex() - falseOffset);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitSwitchEnumInst(SwitchEnumInst *SEI) {
-  return visitTransformingTerminatorInst(SEI);
-}
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitSwitchEnumInst(SwitchEnumInst *sei) {
+  auto opTy = sei->getOperand()->getType();
+  auto &mod = sei->getModule();
+  // If our passed in type is trivial, we shouldn't have any non-trivial
+  // successors. Just bail early returning trivial.
+  if (opTy.isTrivial(mod))
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitCheckedCastBranchInst(
-    CheckedCastBranchInst *SEI) {
-  return visitTransformingTerminatorInst(SEI);
-}
+  // Otherwise, go through the cases of the enum. If we have any cases with
+  // trivial payload or no payload cases, add trivial as a base ownership kind
+  // we can accept.
+  OperandOwnershipKindMap map;
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitTransformingTerminatorInst(
-    TermInst *TI) {
-  // If our operand was trivial, return early.
-  if (compatibleWithOwnership(ValueOwnershipKind::Trivial))
-    return {true, UseLifetimeConstraint::MustBeLive};
+  bool foundNonTrivialCase = false;
 
-  // Then we need to go through all of our destinations and make sure that if
-  // they have a payload, the payload's convention matches our
-  // convention.
-  //
-  // *NOTE* we assume that all of our types line up and are checked by the
-  // normal verifier.
-  for (auto *Succ : TI->getParent()->getSuccessorBlocks()) {
-    // This must be a no-payload case... continue.
-    if (Succ->args_size() == 0)
+  auto *enumDecl = opTy.getEnumOrBoundGenericEnum();
+  assert(enumDecl);
+  for (auto *eltDecl : enumDecl->getAllElements()) {
+    // If we have a no-payload case add that we support trivial and continue.
+    if (!eltDecl->hasAssociatedValues()) {
+      map.addCompatibilityConstraint(ValueOwnershipKind::Trivial,
+                                     UseLifetimeConstraint::MustBeLive);
       continue;
+    }
 
-    // If we have a trivial value or a value with ownership kind that matches
-    // the switch_enum, then continue.
-    auto OwnershipKind = Succ->getArgument(0)->getOwnershipKind();
-    if (OwnershipKind == ValueOwnershipKind::Trivial ||
-        compatibleWithOwnership(OwnershipKind))
+    // If we have a completely trivial payload case, then add that we support
+    // trivial and continue.
+    if (opTy.getEnumElementType(eltDecl, mod).isTrivial(mod)) {
+      map.addCompatibilityConstraint(ValueOwnershipKind::Trivial,
+                                     UseLifetimeConstraint::MustBeLive);
       continue;
+    }
 
-    // Otherwise, emit an error.
-    handleError([&]() {
-      llvm::errs()
-          << "Function: '" << Succ->getParent()->getName() << "'\n"
-          << "Error! Argument ownership kind does not match terminator!\n"
-          << "Terminator: " << *TI << "Argument: " << *Succ->getArgument(0)
-          << "Expected convention: " << getOwnershipKind() << ".\n"
-          << "Actual convention:   " << OwnershipKind << '\n'
-          << '\n';
-    });
+    // Otherwise, we have a non-trivial case. Set foundNonTrivialCase to
+    // true. We will need to check the arguments of the switch_enum's successors
+    // for the ownership kind that we can accept.
+    foundNonTrivialCase = true;
   }
 
-  // Finally, if everything lines up, emit that we match and are a lifetime
-  // ending point if we are owned.
-  auto lifetimeConstraint = hasExactOwnership(ValueOwnershipKind::Owned)
-                                ? UseLifetimeConstraint::MustBeInvalidated
-                                : UseLifetimeConstraint::MustBeLive;
-  return {true, lifetimeConstraint};
+  // If we didn't find a non-trivial case, return the map we have constructed so
+  // far.
+  if (!foundNonTrivialCase)
+    return map;
+
+  // Otherwise, we want to find the ownership constraint of our successor
+  // arguments.
+  Optional<ValueOwnershipKind> nonTrivialKind;
+  for (auto argArray : sei->getSuccessorBlockArguments()) {
+    if (argArray.empty())
+      continue;
+    SILValue arg = argArray[getOperandIndex()];
+    if (arg->getType().isTrivial(mod))
+      continue;
+
+    // If we haven't found a non-trivial kind yet, stash the kind we find.
+    if (!nonTrivialKind) {
+      nonTrivialKind = arg.getOwnershipKind();
+      continue;
+    }
+
+    // Otherwise if we /do/ have a non trivial kind and the argument's ownership
+    // kind is compatible, merge in case the first value we saw had Any
+    // ownership.
+    auto newKind = nonTrivialKind->merge(arg.getOwnershipKind());
+    if (newKind) {
+      nonTrivialKind = newKind;
+      continue;
+    }
+
+    // Otherwise, we have inconsistent ownership in between our successors. To
+    // be sure that we error, return an empty map.
+    return Map();
+  }
+
+  // We should never have an enum with a non-trivial case where we do not have
+  // at least one successor with a proper ownership qualifier since we either
+  // switch over the entire enum implying we visit that case, or we go through
+  // the default which will have our enum type as its type and thus some form of
+  // non-trivial ownership. So it is correct to use the optional here without
+  // checking.
+  auto lifetimeConstraint = nonTrivialKind->getForwardingLifetimeConstraint();
+  map.addCompatibilityConstraint(*nonTrivialKind, lifetimeConstraint);
+
+  return map;
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitReturnInst(ReturnInst *RI) {
-  SILModule &M = RI->getModule();
-  bool IsTrivial = RI->getOperand()->getType().isTrivial(M);
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitCheckedCastBranchInst(
+    CheckedCastBranchInst *ccbi) {
+  Optional<OperandOwnershipKindMap> map;
+  for (auto argArray : ccbi->getSuccessorBlockArguments()) {
+    assert(!argArray.empty());
+
+    auto argOwnershipKind = argArray[getOperandIndex()]->getOwnershipKind();
+    // If we do not have a map yet, initialize it and continue.
+    if (!map) {
+      auto lifetimeConstraint =
+          argOwnershipKind.getForwardingLifetimeConstraint();
+      map = Map::compatibilityMap(argOwnershipKind, lifetimeConstraint);
+      continue;
+    }
+
+    // Otherwise, make sure that we can accept the rest of our
+    // arguments. If not, we return an empty ownership kind to make
+    // sure that we flag everything as an error.
+    if (map->canAcceptKind(argOwnershipKind)) {
+      continue;
+    }
+
+    return OperandOwnershipKindMap();
+  }
+
+  return map.getValue();
+}
+
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitReturnInst(ReturnInst *RI) {
+  bool IsTrivial = RI->getOperand()->getType().isTrivial(mod);
   SILFunctionConventions fnConv = RI->getFunction()->getConventions();
   auto Results = fnConv.getDirectSILResults();
+  // FIXME: Shouldn't we return an empty OperandOwnershipKindMap here if we do
+  // not have any results?
   if (Results.empty() || IsTrivial) {
-    return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-            UseLifetimeConstraint::MustBeLive};
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
   }
 
   CanGenericSignature Sig = fnConv.funcTy->getGenericSignature();
 
   // Find the first index where we have a trivial value.
-  auto Iter = find_if(Results, [&M, &Sig](const SILResultInfo &Info) -> bool {
-    return Info.getOwnershipKind(M, Sig) != ValueOwnershipKind::Trivial;
+  auto Iter = find_if(Results, [this, &Sig](const SILResultInfo &Info) -> bool {
+    return Info.getOwnershipKind(mod, Sig) != ValueOwnershipKind::Trivial;
   });
 
   // If we have all trivial, then we must be trivial. Why wasn't our original
@@ -766,115 +741,111 @@
   if (Iter == Results.end())
     llvm_unreachable("Should have already checked a trivial type?!");
 
-  ValueOwnershipKind Base = Iter->getOwnershipKind(M, Sig);
+  ValueOwnershipKind Base = Iter->getOwnershipKind(mod, Sig);
 
   for (const SILResultInfo &ResultInfo :
        SILFunctionConventions::DirectSILResultRange(std::next(Iter),
                                                     Results.end())) {
-    auto RKind = ResultInfo.getOwnershipKind(M, Sig);
+    auto RKind = ResultInfo.getOwnershipKind(mod, Sig);
     // Ignore trivial types.
     if (RKind.merge(ValueOwnershipKind::Trivial))
       continue;
 
     auto MergedValue = Base.merge(RKind);
     // If we fail to merge all types in, bail. We can not come up with a proper
-    // result type.
-    if (!MergedValue.hasValue()) {
-      return {false, UseLifetimeConstraint::MustBeLive};
-    }
+    // result type. We assert here since this is a hard error in the normal
+    // SILVerifier since the return type of the function would not match its
+    // terminator.
+    assert(MergedValue.hasValue() &&
+           "Failed to merge all types in on a return?!");
     // In case Base is Any.
     Base = MergedValue.getValue();
   }
 
-  if (auto *E = getType().getEnumOrBoundGenericEnum()) {
-    return visitEnumArgument(E, Base);
+  if (getType().getEnumOrBoundGenericEnum()) {
+    return visitEnumArgument(Base);
   }
 
-  return {compatibleWithOwnership(Base),
-          UseLifetimeConstraint::MustBeInvalidated};
+  return Map::compatibilityMap(Base, UseLifetimeConstraint::MustBeInvalidated);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitEndBorrowInst(EndBorrowInst *I) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitEndBorrowInst(EndBorrowInst *I) {
   // If we are checking a subobject, make sure that we are from a guaranteed
   // basic block argument.
   if (isCheckingSubObject()) {
-    auto *phiArg = cast<SILPhiArgument>(Op.get());
+    auto *phiArg = cast<SILPhiArgument>(op.get());
     (void)phiArg;
-    assert(phiArg->getOwnershipKind() == ValueOwnershipKind::Guaranteed &&
-           "Expected an end_borrow paired with an argument.");
-    return {true, UseLifetimeConstraint::MustBeLive};
+    return Map::compatibilityMap(ValueOwnershipKind::Guaranteed,
+                                 UseLifetimeConstraint::MustBeLive);
   }
 
   /// An end_borrow is modeled as invalidating the guaranteed value preventing
   /// any further uses of the value.
-  return {compatibleWithOwnership(ValueOwnershipKind::Guaranteed),
-          UseLifetimeConstraint::MustBeInvalidated};
+  return Map::compatibilityMap(ValueOwnershipKind::Guaranteed,
+                               UseLifetimeConstraint::MustBeInvalidated);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitThrowInst(ThrowInst *I) {
-  return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-          UseLifetimeConstraint::MustBeInvalidated};
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitThrowInst(ThrowInst *I) {
+  return Map::compatibilityMap(ValueOwnershipKind::Owned,
+                               UseLifetimeConstraint::MustBeInvalidated);
 }
 
-#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
-OwnershipUseCheckerResult \
-OwnershipCompatibilityUseChecker::visitStore##Name##Inst(Store##Name##Inst *I){\
-  /* A store instruction implies that the value to be stored to be live, */ \
-  /* but it does not touch the strong reference count of the value. */ \
-  if (getValue() == I->getSrc()) \
-    return {true, UseLifetimeConstraint::MustBeLive}; \
-  return {compatibleWithOwnership(ValueOwnershipKind::Trivial), \
-          UseLifetimeConstraint::MustBeLive}; \
-}
+#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...)                          \
+  OperandOwnershipKindMap                                                      \
+      OperandOwnershipKindClassifier::visitStore##Name##Inst(                  \
+          Store##Name##Inst *I) {                                              \
+    /* A store instruction implies that the value to be stored to be live, */  \
+    /* but it does not touch the strong reference count of the value. We */    \
+    /* also just care about liveness for the dest. So just match everything */ \
+    /* as must be live. */                                                     \
+    return Map::allLive();                                                     \
+  }
 #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
   NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...")
 #include "swift/AST/ReferenceStorage.def"
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitStoreBorrowInst(StoreBorrowInst *I) {
-  if (getValue() == I->getSrc())
-    return {compatibleWithOwnership(ValueOwnershipKind::Guaranteed),
-            UseLifetimeConstraint::MustBeLive};
-  return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-          UseLifetimeConstraint::MustBeLive};
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitStoreBorrowInst(StoreBorrowInst *I) {
+  if (getValue() == I->getSrc()) {
+    return Map::compatibilityMap(ValueOwnershipKind::Guaranteed,
+                                 UseLifetimeConstraint::MustBeLive);
+  }
+  return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                               UseLifetimeConstraint::MustBeLive);
 }
 
 // FIXME: Why not use SILArgumentConvention here?
-OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitCallee(
-    CanSILFunctionType SubstCalleeType) {
-  ParameterConvention Conv = SubstCalleeType->getCalleeConvention();
-  switch (Conv) {
+OperandOwnershipKindMap OperandOwnershipKindClassifier::visitCallee(
+    CanSILFunctionType substCalleeType) {
+  ParameterConvention conv = substCalleeType->getCalleeConvention();
+  switch (conv) {
   case ParameterConvention::Indirect_In:
   case ParameterConvention::Indirect_In_Constant:
-    assert(!SILModuleConventions(Mod).isSILIndirect(
-        SILParameterInfo(SubstCalleeType, Conv)));
-    return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-            UseLifetimeConstraint::MustBeInvalidated};
+    assert(!SILModuleConventions(mod).isSILIndirect(
+        SILParameterInfo(substCalleeType, conv)));
+    return Map::compatibilityMap(ValueOwnershipKind::Owned,
+                                 UseLifetimeConstraint::MustBeInvalidated);
   case ParameterConvention::Indirect_In_Guaranteed:
-    assert(!SILModuleConventions(Mod).isSILIndirect(
-        SILParameterInfo(SubstCalleeType, Conv)));
-    return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-            UseLifetimeConstraint::MustBeLive};
+    assert(!SILModuleConventions(mod).isSILIndirect(
+        SILParameterInfo(substCalleeType, conv)));
+    return Map::compatibilityMap(ValueOwnershipKind::Guaranteed,
+                                 UseLifetimeConstraint::MustBeLive);
   case ParameterConvention::Indirect_Inout:
   case ParameterConvention::Indirect_InoutAliasable:
     llvm_unreachable("Illegal convention for callee");
   case ParameterConvention::Direct_Unowned:
-    if (isAddressOrTrivialType())
-      return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-              UseLifetimeConstraint::MustBeLive};
-    // We accept unowned, owned, and guaranteed in unowned positions.
-    return {true, UseLifetimeConstraint::MustBeLive};
+    return Map::allLive();
   case ParameterConvention::Direct_Owned:
-    return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-            UseLifetimeConstraint::MustBeInvalidated};
+    return Map::compatibilityMap(ValueOwnershipKind::Owned,
+                                 UseLifetimeConstraint::MustBeInvalidated);
   case ParameterConvention::Direct_Guaranteed:
-    if (SubstCalleeType->isNoEscape())
-      return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-        UseLifetimeConstraint::MustBeLive};
-    return {compatibleWithOwnership(ValueOwnershipKind::Guaranteed),
-            UseLifetimeConstraint::MustBeLive};
+    if (substCalleeType->isNoEscape())
+      return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                   UseLifetimeConstraint::MustBeLive);
+    return Map::compatibilityMap(ValueOwnershipKind::Guaranteed,
+                                 UseLifetimeConstraint::MustBeLive);
   }
 
   llvm_unreachable("Unhandled ParameterConvention in switch.");
@@ -889,117 +860,159 @@
 //
 // %val = enum $Optional<SomeClass>, #Optional.none // trivial ownership
 // apply %f(%val) : (@owned Optional<SomeClass>)    // owned argument
-OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitEnumArgument(
-    EnumDecl *E, ValueOwnershipKind RequiredKind) {
-  // If this value is already categorized as a trivial ownership kind, it is
-  // safe to pass to any argument convention.
-  if (compatibleWithOwnership(ValueOwnershipKind::Trivial)) {
-    return {true, UseLifetimeConstraint::MustBeLive};
-  }
+OperandOwnershipKindMap OperandOwnershipKindClassifier::visitEnumArgument(
+    ValueOwnershipKind requiredKind) {
+  // If this value is already categorized as a trivial ownership kind,
+  // it is safe to pass to any argument convention. This is ok since
+  // we know that the enum type must match up as checked by the
+  // ownership verifier.
+  OperandOwnershipKindMap map;
+  map.addCompatibilityConstraint(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
 
   // The operand has a non-trivial ownership kind. It must match the argument
   // convention.
-  auto ownership = getOwnershipKind();
-  UseLifetimeConstraint lifetimeConstraint;
-  if (ownership == ValueOwnershipKind::Owned) {
-    if (RequiredKind != ValueOwnershipKind::Owned) {
-      lifetimeConstraint = UseLifetimeConstraint::MustBeLive;
-    } else {
-      lifetimeConstraint = UseLifetimeConstraint::MustBeInvalidated;
-    }
+  if (requiredKind != ValueOwnershipKind::Owned) {
+    map.addCompatibilityConstraint(ValueOwnershipKind::Owned,
+                                   UseLifetimeConstraint::MustBeLive);
   } else {
-    lifetimeConstraint = UseLifetimeConstraint::MustBeLive;
+    map.addCompatibilityConstraint(ValueOwnershipKind::Owned,
+                                   UseLifetimeConstraint::MustBeInvalidated);
   }
-  return {ownership.isCompatibleWith(RequiredKind), lifetimeConstraint};
+  map.addCompatibilityConstraint(ValueOwnershipKind::Guaranteed,
+                                 UseLifetimeConstraint::MustBeLive);
+  map.addCompatibilityConstraint(ValueOwnershipKind::Unowned,
+                                 UseLifetimeConstraint::MustBeLive);
+  return map;
 }
 
 // We allow for trivial cases of enums with non-trivial cases to be passed in
 // non-trivial argument positions. This fits with modeling of a
 // SILFunctionArgument as a phi in a global program graph.
-OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitApplyParameter(
+OperandOwnershipKindMap OperandOwnershipKindClassifier::visitApplyParameter(
     ValueOwnershipKind Kind, UseLifetimeConstraint Requirement) {
+
   // Check if we have an enum. If not, then we just check against the passed in
   // convention.
-  EnumDecl *E = getType().getEnumOrBoundGenericEnum();
-  if (!E) {
-    return {compatibleWithOwnership(Kind), Requirement};
+  if (!getType().getEnumOrBoundGenericEnum()) {
+    // We allow for owned to be passed to apply parameters.
+    if (Kind != ValueOwnershipKind::Owned) {
+      return Map::compatibilityMap(
+          {{Kind, Requirement},
+           {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeLive}});
+    }
+    return Map::compatibilityMap(Kind, Requirement);
   }
-  return visitEnumArgument(E, Kind);
+
+  // Otherwise consider that we may have a payload with a trivial case
+  // that has other non-trivial cases.
+  return visitEnumArgument(Kind);
 }
 
 // Handle Apply and TryApply.
-OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::
-visitFullApply(FullApplySite apply) {
-  // If we are visiting the callee, handle it specially.
-  if (getOperandIndex() == 0)
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitFullApply(FullApplySite apply) {
+  // If we are visiting the callee operand, handle it specially.
+  if (apply.isCalleeOperand(op)) {
     return visitCallee(apply.getSubstCalleeType());
+  }
 
   // Indirect return arguments are address types.
-  if (isAddressOrTrivialType())
-    return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-            UseLifetimeConstraint::MustBeLive};
+  if (apply.isIndirectResultOperand(op)) {
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
+  }
 
-  unsigned argIndex = apply.getCalleeArgIndex(Op);
-  SILParameterInfo paramInfo =
-    apply.getSubstCalleeConv().getParamInfoForSILArg(argIndex);
+  unsigned argIndex = apply.getCalleeArgIndex(op);
+  auto conv = apply.getSubstCalleeConv();
+  SILParameterInfo paramInfo = conv.getParamInfoForSILArg(argIndex);
 
   switch (paramInfo.getConvention()) {
-  case ParameterConvention::Indirect_In:
   case ParameterConvention::Direct_Owned:
     return visitApplyParameter(ValueOwnershipKind::Owned,
                                UseLifetimeConstraint::MustBeInvalidated);
-  case ParameterConvention::Indirect_In_Constant:
   case ParameterConvention::Direct_Unowned:
-    // We accept unowned, owned, and guaranteed in unowned positions.
-    return {true, UseLifetimeConstraint::MustBeLive};
-  case ParameterConvention::Indirect_In_Guaranteed:
+    return Map::allLive();
+
+  case ParameterConvention::Indirect_In: {
+    // This expects an @trivial if we have lowered addresses and @
+    if (conv.useLoweredAddresses()) {
+      return visitApplyParameter(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
+    }
+    // TODO: Once trivial is subsumed in any, this goes away.
+    auto map = visitApplyParameter(ValueOwnershipKind::Owned,
+                                   UseLifetimeConstraint::MustBeInvalidated);
+    map.addCompatibilityConstraint(ValueOwnershipKind::Trivial,
+                                   UseLifetimeConstraint::MustBeLive);
+    return map;
+  }
+
+  case ParameterConvention::Indirect_In_Guaranteed: {
+    // This expects an @trivial if we have lowered addresses and @
+    if (conv.useLoweredAddresses()) {
+      return visitApplyParameter(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
+    }
+    // TODO: Once trivial is subsumed in any, this goes away.
+    auto map = visitApplyParameter(ValueOwnershipKind::Guaranteed,
+                                   UseLifetimeConstraint::MustBeLive);
+    map.addCompatibilityConstraint(ValueOwnershipKind::Trivial,
+                                   UseLifetimeConstraint::MustBeLive);
+    return map;
+  }
+
+  // The following conventions should take address types and thus be
+  // trivial.
+  case ParameterConvention::Indirect_In_Constant:
+  case ParameterConvention::Indirect_Inout:
+  case ParameterConvention::Indirect_InoutAliasable:
+    return visitApplyParameter(ValueOwnershipKind::Trivial,
+                               UseLifetimeConstraint::MustBeLive);
+
   case ParameterConvention::Direct_Guaranteed:
     // A +1 value may be passed to a guaranteed argument. From the caller's
     // point of view, this is just like a normal non-consuming use.
     // Direct_Guaranteed only accepts non-trivial types, but trivial types are
     // already handled above.
-    return visitApplyParameter(ValueOwnershipKind::Any,
+    return visitApplyParameter(ValueOwnershipKind::Guaranteed,
                                UseLifetimeConstraint::MustBeLive);
-  // The following conventions should take address types.
-  case ParameterConvention::Indirect_Inout:
-  case ParameterConvention::Indirect_InoutAliasable:
-    llvm_unreachable("Unexpected non-trivial parameter convention.");
   }
   llvm_unreachable("unhandled convension");
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitBeginApplyInst(BeginApplyInst *I) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitBeginApplyInst(BeginApplyInst *I) {
   return visitFullApply(I);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitApplyInst(ApplyInst *I) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitApplyInst(ApplyInst *I) {
   return visitFullApply(I);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitTryApplyInst(TryApplyInst *I) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitTryApplyInst(TryApplyInst *I) {
   return visitFullApply(I);
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitPartialApplyInst(PartialApplyInst *I) {
-  // All non-trivial types should be captured.
-  if (isAddressOrTrivialType()) {
-    return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-            UseLifetimeConstraint::MustBeLive};
-  }
-  return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-          UseLifetimeConstraint::MustBeInvalidated};
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitPartialApplyInst(PartialApplyInst *I) {
+  return Map::compatibilityMap(
+      {{ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive},
+       // All non-trivial types should be captured.
+       {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated}});
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitYieldInst(YieldInst *I) {
+// TODO: FIX THIS
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitYieldInst(YieldInst *I) {
   // Indirect return arguments are address types.
+  //
+  // TODO: Change this to check if this operand is an indirect result
   if (isAddressOrTrivialType())
-    return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-            UseLifetimeConstraint::MustBeLive};
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
 
   auto fnType = I->getFunction()->getLoweredFunctionType();
   auto yieldInfo = fnType->getYields()[getOperandIndex()];
@@ -1011,7 +1024,7 @@
   case ParameterConvention::Indirect_In_Constant:
   case ParameterConvention::Direct_Unowned:
     // We accept unowned, owned, and guaranteed in unowned positions.
-    return {true, UseLifetimeConstraint::MustBeLive};
+    return Map::allLive();
   case ParameterConvention::Indirect_In_Guaranteed:
   case ParameterConvention::Direct_Guaranteed:
     return visitApplyParameter(ValueOwnershipKind::Guaranteed,
@@ -1024,50 +1037,45 @@
   llvm_unreachable("unhandled convension");
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitAssignInst(AssignInst *I) {
-  if (getValue() == I->getSrc()) {
-    if (isAddressOrTrivialType()) {
-      return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-              UseLifetimeConstraint::MustBeLive};
-    }
-    return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-            UseLifetimeConstraint::MustBeInvalidated};
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitAssignInst(AssignInst *I) {
+  if (getValue() != I->getSrc()) {
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
   }
 
-  return {true, UseLifetimeConstraint::MustBeLive};
+  return Map::compatibilityMap({
+      {ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive},
+      {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated},
+  });
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitStoreInst(StoreInst *I) {
-  if (getValue() == I->getSrc()) {
-    if (isAddressOrTrivialType()) {
-      return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-              UseLifetimeConstraint::MustBeLive};
-    }
-    return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-            UseLifetimeConstraint::MustBeInvalidated};
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitStoreInst(StoreInst *I) {
+  if (getValue() != I->getSrc()) {
+    return Map::compatibilityMap(ValueOwnershipKind::Trivial,
+                                 UseLifetimeConstraint::MustBeLive);
   }
-  return {true, UseLifetimeConstraint::MustBeLive};
+
+  return Map::compatibilityMap({
+      {ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive},
+      {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated},
+  });
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitCopyBlockWithoutEscapingInst(
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitCopyBlockWithoutEscapingInst(
     CopyBlockWithoutEscapingInst *I) {
   // Consumes the closure parameter.
   if (getValue() == I->getClosure()) {
-    return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-            UseLifetimeConstraint::MustBeInvalidated};
+    return Map::compatibilityMap(ValueOwnershipKind::Owned,
+                                 UseLifetimeConstraint::MustBeInvalidated);
   }
-  bool compatible = hasExactOwnership(ValueOwnershipKind::Any) ||
-                    !compatibleWithOwnership(ValueOwnershipKind::Trivial);
 
-  return { compatible, UseLifetimeConstraint::MustBeLive };
+  return Map::compatibleWithAllExcept(ValueOwnershipKind::Trivial);
 }
 
-
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitMarkDependenceInst(
+OperandOwnershipKindMap OperandOwnershipKindClassifier::visitMarkDependenceInst(
     MarkDependenceInst *MDI) {
 
   // Forward ownership if the mark_dependence instruction marks a dependence
@@ -1076,24 +1084,22 @@
     if (auto ResFnTy = MDI->getType().getAs<SILFunctionType>())
       if (auto BaseFnTy = MDI->getBase()->getType().getAs<SILFunctionType>())
         if (!ResFnTy->isNoEscape() && BaseFnTy->isNoEscape())
-          return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-                  UseLifetimeConstraint::MustBeInvalidated};
+          return Map::compatibilityMap(
+              ValueOwnershipKind::Owned,
+              UseLifetimeConstraint::MustBeInvalidated);
 
   // We always treat mark dependence as a use that keeps a value alive. We will
   // be introducing a begin_dependence/end_dependence version of this later.
-  return {true, UseLifetimeConstraint::MustBeLive};
+  return Map::allLive();
 }
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitKeyPathInst(KeyPathInst *I) {
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitKeyPathInst(KeyPathInst *I) {
   // KeyPath moves the value in memory out of address operands, but the
   // ownership checker doesn't reason about that yet.
-  if (isAddressOrTrivialType()) {
-    return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
-            UseLifetimeConstraint::MustBeLive};
-  }
-  return {compatibleWithOwnership(ValueOwnershipKind::Owned),
-          UseLifetimeConstraint::MustBeInvalidated};
+  return Map::compatibilityMap(
+      {{ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive},
+       {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated}});
 }
 
 //===----------------------------------------------------------------------===//
@@ -1102,50 +1108,26 @@
 
 namespace {
 
-class OwnershipCompatibilityBuiltinUseChecker
-    : public SILBuiltinVisitor<OwnershipCompatibilityBuiltinUseChecker,
-                               OwnershipUseCheckerResult> {
+struct OperandOwnershipKindBuiltinClassifier
+    : SILBuiltinVisitor<OperandOwnershipKindBuiltinClassifier,
+                        OperandOwnershipKindMap> {
+  using Map = OperandOwnershipKindMap;
 
-  const OwnershipCompatibilityUseChecker &ParentChecker;
-
-public:
-  OwnershipCompatibilityBuiltinUseChecker(
-      OwnershipCompatibilityUseChecker &ParentChecker)
-      : ParentChecker(ParentChecker) {}
-
-  SILValue getValue() const { return ParentChecker.getValue(); }
-
-  ValueOwnershipKind getOwnershipKind() const {
-    return ParentChecker.getOwnershipKind();
-  }
-
-  unsigned getOperandIndex() const { return ParentChecker.getOperandIndex(); }
-
-  SILType getType() const { return ParentChecker.getType(); }
-
-  bool compatibleWithOwnership(ValueOwnershipKind Kind) const {
-    return ParentChecker.compatibleWithOwnership(Kind);
-  }
-
-  bool isAddressOrTrivialType() const {
-    return ParentChecker.isAddressOrTrivialType();
-  }
-
-  OwnershipUseCheckerResult visitLLVMIntrinsic(BuiltinInst *BI,
-                                               llvm::Intrinsic::ID ID) {
+  OperandOwnershipKindMap visitLLVMIntrinsic(BuiltinInst *BI,
+                                             llvm::Intrinsic::ID ID) {
     // LLVM intrinsics do not traffic in ownership, so if we have a result, it
     // must be trivial.
-    return {true, UseLifetimeConstraint::MustBeLive};
+    return {ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive};
   }
 
     // BUILTIN_TYPE_CHECKER_OPERATION does not live past the type checker.
 #define BUILTIN_TYPE_CHECKER_OPERATION(ID, NAME)
 
 #define BUILTIN(ID, NAME, ATTRS)                                               \
-  OwnershipUseCheckerResult visit##ID(BuiltinInst *BI, StringRef Attr);
+  OperandOwnershipKindMap visit##ID(BuiltinInst *BI, StringRef Attr);
 #include "swift/AST/Builtins.def"
 
-  OwnershipUseCheckerResult check(BuiltinInst *BI) { return visit(BI); }
+  OperandOwnershipKindMap check(BuiltinInst *BI) { return visit(BI); }
 };
 
 } // end anonymous namespace
@@ -1154,11 +1136,11 @@
 // @guaranteed parameters. This means that we can only have a lifetime ending
 // use with our builtins if it is owned.
 #define CONSTANT_OWNERSHIP_BUILTIN(OWNERSHIP, USE_LIFETIME_CONSTRAINT, ID)     \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityBuiltinUseChecker::visit##ID(BuiltinInst *BI,      \
-                                                         StringRef Attr) {     \
-    return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP),            \
-            UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT};                   \
+  OperandOwnershipKindMap OperandOwnershipKindBuiltinClassifier::visit##ID(    \
+      BuiltinInst *BI, StringRef Attr) {                                       \
+    return Map::compatibilityMap(                                              \
+        ValueOwnershipKind::OWNERSHIP,                                         \
+        UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT);                       \
   }
 CONSTANT_OWNERSHIP_BUILTIN(Owned, MustBeLive, ErrorInMain)
 CONSTANT_OWNERSHIP_BUILTIN(Owned, MustBeLive, UnexpectedError)
@@ -1282,17 +1264,16 @@
 // Builtins that should be lowered to SIL instructions so we should never see
 // them.
 #define BUILTIN_SIL_OPERATION(ID, NAME, CATEGORY)                              \
-  OwnershipUseCheckerResult                                                    \
-      OwnershipCompatibilityBuiltinUseChecker::visit##ID(BuiltinInst *BI,      \
-                                                         StringRef Attr) {     \
+  OperandOwnershipKindMap OperandOwnershipKindBuiltinClassifier::visit##ID(    \
+      BuiltinInst *BI, StringRef Attr) {                                       \
     llvm_unreachable("Builtin should have been lowered to SIL instruction?!"); \
   }
 #define BUILTIN(X, Y, Z)
 #include "swift/AST/Builtins.def"
 
-OwnershipUseCheckerResult
-OwnershipCompatibilityUseChecker::visitBuiltinInst(BuiltinInst *BI) {
-  return OwnershipCompatibilityBuiltinUseChecker(*this).check(BI);
+OperandOwnershipKindMap
+OperandOwnershipKindClassifier::visitBuiltinInst(BuiltinInst *BI) {
+  return OperandOwnershipKindBuiltinClassifier().check(BI);
 }
 
 //===----------------------------------------------------------------------===//
@@ -1305,28 +1286,28 @@
 // refactored into a large state object that is used by functions.
 class SILValueOwnershipChecker {
   /// The result of performing the check.
-  llvm::Optional<bool> Result;
+  llvm::Optional<bool> result;
 
   /// The module that we are in.
-  SILModule &Mod;
+  SILModule &mod;
 
   /// A cache of dead-end basic blocks that we use to determine if we can
   /// ignore "leaks".
-  DeadEndBlocks &DEBlocks;
+  DeadEndBlocks &deadEndBlocks;
 
   /// The value whose ownership we will check.
-  SILValue Value;
+  SILValue value;
 
   /// The action that the checker should perform on detecting an error.
-  ErrorBehaviorKind ErrorBehavior;
+  ErrorBehaviorKind errorBehavior;
 
   /// The list of lifetime ending users that we found. Only valid if check is
   /// successful.
-  SmallVector<BranchPropagatedUser, 16> LifetimeEndingUsers;
+  SmallVector<BranchPropagatedUser, 16> lifetimeEndingUsers;
 
   /// The list of non lifetime ending users that we found. Only valid if check
   /// is successful.
-  SmallVector<BranchPropagatedUser, 16> RegularUsers;
+  SmallVector<BranchPropagatedUser, 16> regularUsers;
 
   /// The list of implicit non lifetime ending users that we found. This
   /// consists of instructions like end_borrow that end a scoped lifetime. We
@@ -1334,19 +1315,19 @@
   /// destroyed while that sub-scope is valid.
   ///
   /// TODO: Rename to SubBorrowScopeUsers?
-  SmallVector<BranchPropagatedUser, 4> ImplicitRegularUsers;
+  SmallVector<BranchPropagatedUser, 4> implicitRegularUsers;
 
   /// The set of blocks that we have visited.
-  SmallPtrSetImpl<SILBasicBlock *> &VisitedBlocks;
+  SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks;
 
 public:
   SILValueOwnershipChecker(
-      SILModule &M, DeadEndBlocks &DEBlocks, SILValue V,
-      ErrorBehaviorKind ErrorBehavior,
-      llvm::SmallPtrSetImpl<SILBasicBlock *> &VisitedBlocks)
-      : Result(), Mod(M), DEBlocks(DEBlocks), Value(V),
-        ErrorBehavior(ErrorBehavior), VisitedBlocks(VisitedBlocks) {
-    assert(Value && "Can not initialize a checker with an empty SILValue");
+      SILModule &mod, DeadEndBlocks &deadEndBlocks, SILValue value,
+      ErrorBehaviorKind errorBehavior,
+      llvm::SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks)
+      : result(), mod(mod), deadEndBlocks(deadEndBlocks), value(value),
+        errorBehavior(errorBehavior), visitedBlocks(visitedBlocks) {
+    assert(value && "Can not initialize a checker with an empty SILValue");
   }
 
   ~SILValueOwnershipChecker() = default;
@@ -1354,21 +1335,22 @@
   SILValueOwnershipChecker(SILValueOwnershipChecker &&) = delete;
 
   bool check() {
-    if (Result.hasValue())
-      return Result.getValue();
+    if (result.hasValue())
+      return result.getValue();
 
-    LLVM_DEBUG(llvm::dbgs() << "Verifying ownership of: " << *Value);
-    Result = checkUses();
-    if (!Result.getValue())
+    LLVM_DEBUG(llvm::dbgs() << "Verifying ownership of: " << *value);
+    result = checkUses();
+    if (!result.getValue())
       return false;
 
     SmallVector<BranchPropagatedUser, 32> allRegularUsers;
-    copy(RegularUsers, std::back_inserter(allRegularUsers));
-    copy(ImplicitRegularUsers, std::back_inserter(allRegularUsers));
-    Result = valueHasLinearLifetime(Value, LifetimeEndingUsers, allRegularUsers,
-                                    VisitedBlocks, DEBlocks, ErrorBehavior);
+    copy(regularUsers, std::back_inserter(allRegularUsers));
+    copy(implicitRegularUsers, std::back_inserter(allRegularUsers));
+    result =
+        valueHasLinearLifetime(value, lifetimeEndingUsers, allRegularUsers,
+                               visitedBlocks, deadEndBlocks, errorBehavior);
 
-    return Result.getValue();
+    return result.getValue();
   }
 
   using user_array_transform =
@@ -1378,134 +1360,175 @@
   /// A function that returns a range of lifetime ending users found for the
   /// given value.
   user_array getLifetimeEndingUsers() const {
-    assert(Result.hasValue() && "Can not call until check() is called");
-    assert(Result.getValue() && "Can not call if check() returned false");
+    assert(result.hasValue() && "Can not call until check() is called");
+    assert(result.getValue() && "Can not call if check() returned false");
 
-    user_array_transform Transform(
-        [](BranchPropagatedUser User) -> SILInstruction * {
-          return User.getInst();
+    user_array_transform transform(
+        [](BranchPropagatedUser user) -> SILInstruction * {
+          return user.getInst();
         });
-    return user_array(ArrayRef<BranchPropagatedUser>(LifetimeEndingUsers),
-                      Transform);
+    return user_array(ArrayRef<BranchPropagatedUser>(lifetimeEndingUsers),
+                      transform);
   }
 
   /// A function that returns a range of regular (i.e. "non lifetime ending")
   /// users found for the given value.
   user_array getRegularUsers() const {
-    assert(Result.hasValue() && "Can not call until check() is called");
-    assert(Result.getValue() && "Can not call if check() returned false");
+    assert(result.hasValue() && "Can not call until check() is called");
+    assert(result.getValue() && "Can not call if check() returned false");
 
-    user_array_transform Transform(
-        [](BranchPropagatedUser User) -> SILInstruction * {
-          return User.getInst();
+    user_array_transform transform(
+        [](BranchPropagatedUser user) -> SILInstruction * {
+          return user.getInst();
         });
-    return user_array(ArrayRef<BranchPropagatedUser>(RegularUsers), Transform);
+    return user_array(ArrayRef<BranchPropagatedUser>(regularUsers), transform);
   }
 
 private:
   bool checkUses();
-  void gatherUsers(SmallVectorImpl<BranchPropagatedUser> &LifetimeEndingUsers,
-                   SmallVectorImpl<BranchPropagatedUser> &RegularUsers,
-                   SmallVectorImpl<BranchPropagatedUser> &ImplicitRegularUsers);
+  bool gatherUsers(SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers,
+                   SmallVectorImpl<BranchPropagatedUser> &regularUsers,
+                   SmallVectorImpl<BranchPropagatedUser> &implicitRegularUsers);
 
   bool checkValueWithoutLifetimeEndingUses();
 
-  bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *Arg);
-  bool checkYieldWithoutLifetimeEndingUses(BeginApplyResult *Yield);
+  bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *arg);
+  bool checkYieldWithoutLifetimeEndingUses(BeginApplyResult *yield);
 
   bool isGuaranteedFunctionArgWithLifetimeEndingUses(
-      SILFunctionArgument *Arg,
-      const llvm::SmallVectorImpl<BranchPropagatedUser> &LifetimeEndingUsers)
-      const;
+      SILFunctionArgument *arg,
+      const SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers) const;
   bool isSubobjectProjectionWithLifetimeEndingUses(
-      SILValue Value,
-      const llvm::SmallVectorImpl<BranchPropagatedUser> &LifetimeEndingUsers)
-      const;
+      SILValue value,
+      const SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers) const;
 
   /// Depending on our initialization, either return false or call Func and
   /// throw an error.
-  bool handleError(llvm::function_ref<void()> &&MessagePrinterFunc) const {
-    if (ErrorBehavior.shouldPrintMessage()) {
-      MessagePrinterFunc();
+  bool handleError(function_ref<void()> &&messagePrinterFunc) const {
+    if (errorBehavior.shouldPrintMessage()) {
+      messagePrinterFunc();
     }
 
-    if (ErrorBehavior.shouldReturnFalse()) {
+    if (errorBehavior.shouldReturnFalse()) {
       return false;
     }
 
-    assert(ErrorBehavior.shouldAssert() && "At this point, we should assert");
+    assert(errorBehavior.shouldAssert() && "At this point, we should assert");
     llvm_unreachable("triggering standard assertion failure routine");
   }
 };
 
 } // end anonymous namespace
 
-void SILValueOwnershipChecker::gatherUsers(
-    SmallVectorImpl<BranchPropagatedUser> &LifetimeEndingUsers,
-    SmallVectorImpl<BranchPropagatedUser> &NonLifetimeEndingUsers,
-    SmallVectorImpl<BranchPropagatedUser> &ImplicitRegularUsers) {
+bool SILValueOwnershipChecker::gatherUsers(
+    SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers,
+    SmallVectorImpl<BranchPropagatedUser> &nonLifetimeEndingUsers,
+    SmallVectorImpl<BranchPropagatedUser> &implicitRegularUsers) {
 
   // See if Value is guaranteed. If we are guaranteed and not forwarding, then
   // we need to look through subobject uses for more uses. Otherwise, if we are
   // forwarding, we do not create any lifetime ending users/non lifetime ending
   // users since we verify against our base.
-  auto OwnershipKind = Value.getOwnershipKind();
-  bool IsGuaranteed = OwnershipKind == ValueOwnershipKind::Guaranteed;
-  bool IsOwned = OwnershipKind == ValueOwnershipKind::Owned;
+  auto ownershipKind = value.getOwnershipKind();
+  bool isGuaranteed = ownershipKind == ValueOwnershipKind::Guaranteed;
+  bool isOwned = ownershipKind == ValueOwnershipKind::Owned;
 
-  if (IsGuaranteed && isGuaranteedForwardingValue(Value))
-    return;
+  if (isGuaranteed && isGuaranteedForwardingValue(value))
+    return true;
 
   // Then gather up our initial list of users.
-  SmallVector<Operand *, 8> Users;
-  std::copy(Value->use_begin(), Value->use_end(), std::back_inserter(Users));
+  SmallVector<Operand *, 8> users;
+  std::copy(value->use_begin(), value->use_end(), std::back_inserter(users));
 
-  auto addCondBranchToList = [](SmallVectorImpl<BranchPropagatedUser> &List,
-                                CondBranchInst *CBI, unsigned OperandIndex) {
-    if (CBI->isConditionOperandIndex(OperandIndex)) {
-      List.emplace_back(CBI);
+  auto addCondBranchToList = [](SmallVectorImpl<BranchPropagatedUser> &list,
+                                CondBranchInst *cbi, unsigned operandIndex) {
+    if (cbi->isConditionOperandIndex(operandIndex)) {
+      list.emplace_back(cbi);
       return;
     }
 
-    bool isTrueOperand = CBI->isTrueOperandIndex(OperandIndex);
-    List.emplace_back(CBI, isTrueOperand ? CondBranchInst::TrueIdx
+    bool isTrueOperand = cbi->isTrueOperandIndex(operandIndex);
+    list.emplace_back(cbi, isTrueOperand ? CondBranchInst::TrueIdx
                                          : CondBranchInst::FalseIdx);
   };
 
-  while (!Users.empty()) {
-    Operand *Op = Users.pop_back_val();
-    SILInstruction *User = Op->getUser();
+  bool foundError = false;
+  while (!users.empty()) {
+    Operand *op = users.pop_back_val();
+    SILInstruction *user = op->getUser();
 
     // If this op is a type dependent operand, skip it. It is not interesting
     // from an ownership perspective.
-    if (User->isTypeDependentOperand(*Op))
+    if (user->isTypeDependentOperand(*op))
       continue;
 
-    if (OwnershipCompatibilityUseChecker(Mod, *Op, Value, ErrorBehavior)
-            .check(User)) {
-      LLVM_DEBUG(llvm::dbgs() << "        Lifetime Ending User: " << *User);
-      if (auto *CBI = dyn_cast<CondBranchInst>(User)) {
-        addCondBranchToList(LifetimeEndingUsers, CBI, Op->getOperandNumber());
+    bool isGuaranteedSubValue = false;
+    if (isGuaranteed && isGuaranteedForwardingInst(op->getUser())) {
+      isGuaranteedSubValue = true;
+    }
+
+    auto opOwnershipKindMap = op->getOwnershipKindMap(isGuaranteedSubValue);
+    // If our ownership kind doesn't match, track that we found an error, emit
+    // an error message optionally and then continue.
+    if (!opOwnershipKindMap.canAcceptKind(ownershipKind)) {
+      foundError = true;
+
+      // If we did not support /any/ ownership kind, it means that we found a
+      // conflicting answer so the kind map that was returned is the empty
+      // map. Put out a more specific error here.
+      if (!opOwnershipKindMap.data.any()) {
+        handleError([&]() {
+          llvm::errs() << "Function: '" << user->getFunction()->getName()
+                       << "'\n"
+                       << "Ill-formed SIL! Unable to compute ownership kind "
+                          "map for user?!\n"
+                       << "For terminator users, check that successors have "
+                          "compatible ownership kinds.\n"
+                       << "Value: " << op->get() << "User: " << *user
+                       << "Operand Number: " << op->getOperandNumber() << '\n'
+                       << "Conv: " << ownershipKind << "\n\n";
+        });
+        continue;
+      }
+
+      handleError([&]() {
+        llvm::errs() << "Function: '" << user->getFunction()->getName() << "'\n"
+                     << "Have operand with incompatible ownership?!\n"
+                     << "Value: " << op->get() << "User: " << *user
+                     << "Operand Number: " << op->getOperandNumber() << '\n'
+                     << "Conv: " << ownershipKind << '\n'
+                     << "OwnershipMap:\n"
+                     << opOwnershipKindMap << '\n';
+      });
+      continue;
+    }
+
+    auto lifetimeConstraint =
+        opOwnershipKindMap.getLifetimeConstraint(ownershipKind);
+    if (lifetimeConstraint == UseLifetimeConstraint::MustBeInvalidated) {
+      LLVM_DEBUG(llvm::dbgs() << "        Lifetime Ending User: " << *user);
+      if (auto *cbi = dyn_cast<CondBranchInst>(user)) {
+        addCondBranchToList(lifetimeEndingUsers, cbi, op->getOperandNumber());
       } else {
-        LifetimeEndingUsers.emplace_back(User);
+        lifetimeEndingUsers.emplace_back(user);
       }
     } else {
-      LLVM_DEBUG(llvm::dbgs() << "        Regular User: " << *User);
-      if (auto *CBI = dyn_cast<CondBranchInst>(User)) {
-        addCondBranchToList(NonLifetimeEndingUsers, CBI,
-                            Op->getOperandNumber());
+      LLVM_DEBUG(llvm::dbgs() << "        Regular User: " << *user);
+      if (auto *cbi = dyn_cast<CondBranchInst>(user)) {
+        addCondBranchToList(nonLifetimeEndingUsers, cbi,
+                            op->getOperandNumber());
       } else {
-        NonLifetimeEndingUsers.emplace_back(User);
+        nonLifetimeEndingUsers.emplace_back(user);
       }
     }
 
     // If our base value is not guaranteed, we do not to try to visit
     // subobjects.
-    if (!IsGuaranteed) {
+    if (!isGuaranteed) {
       // But if we are owned, check if we have any end_borrows. We
       // need to treat these as sub-scope users. We can rely on the
       // end_borrow to prevent recursion.
-      if (IsOwned) {
+      if (isOwned) {
         // Do a check if any of our users are begin_borrows. If we find such a
         // use, then we want to include the end_borrow associated with the
         // begin_borrow in our NonLifetimeEndingUser lists.
@@ -1514,11 +1537,11 @@
         // append to NonLifetimeEndingUsers without needing to deal with
         // iterator invalidation.
         SmallVector<SILInstruction *, 4> endBorrowInsts;
-        for (unsigned i : indices(NonLifetimeEndingUsers)) {
+        for (unsigned i : indices(nonLifetimeEndingUsers)) {
           if (auto *bbi = dyn_cast<BeginBorrowInst>(
-                  NonLifetimeEndingUsers[i].getInst())) {
+                  nonLifetimeEndingUsers[i].getInst())) {
             copy(bbi->getEndBorrows(),
-                 std::back_inserter(ImplicitRegularUsers));
+                 std::back_inserter(implicitRegularUsers));
           }
         }
       }
@@ -1527,7 +1550,7 @@
 
     // If we are guaranteed, but are not a guaranteed forwarding inst,
     // just continue. This user is just treated as a normal use.
-    if (!isGuaranteedForwardingInst(User))
+    if (!isGuaranteedForwardingInst(user))
       continue;
 
     // At this point, we know that we must have a forwarded subobject. Since the
@@ -1535,8 +1558,8 @@
     // or trivial. We now split into two cases, if the user is a terminator or
     // not. If we do not have a terminator, then just add the uses of all of
     // User's results to the worklist.
-    if (User->getResults().size()) {
-      for (SILValue result : User->getResults()) {
+    if (user->getResults().size()) {
+      for (SILValue result : user->getResults()) {
         if (result.getOwnershipKind() == ValueOwnershipKind::Trivial) {
           continue;
         }
@@ -1547,27 +1570,27 @@
         assert(result.getOwnershipKind() == ValueOwnershipKind::Guaranteed &&
                "Our value is guaranteed and this is a forwarding instruction. "
                "Should have guaranteed ownership as well.");
-        copy(result->getUses(), std::back_inserter(Users));
+        copy(result->getUses(), std::back_inserter(users));
       }
 
       continue;
     }
 
-    assert(User->getResults().empty());
+    assert(user->getResults().empty());
 
-    auto *TI = dyn_cast<TermInst>(User);
-    if (!TI) {
+    auto *ti = dyn_cast<TermInst>(user);
+    if (!ti) {
       continue;
     }
 
     // Otherwise if we have a terminator, add any as uses any end_borrow to
     // ensure that the subscope is completely enclsed within the super scope. We
     // require all of our arguments to be either trivial or guaranteed.
-    for (auto &Succ : TI->getSuccessors()) {
-      auto *BB = Succ.getBB();
+    for (auto &succ : ti->getSuccessors()) {
+      auto *succBlock = succ.getBB();
 
       // If we do not have any arguments, then continue.
-      if (BB->args_empty())
+      if (succBlock->args_empty())
         continue;
 
       // Otherwise, make sure that all arguments are trivial or guaranteed. If
@@ -1575,21 +1598,21 @@
       //
       // TODO: We could ignore this error and emit a more specific error on the
       // actual terminator.
-      for (auto *BBArg : BB->getPhiArguments()) {
+      for (auto *succArg : succBlock->getPhiArguments()) {
         // *NOTE* We do not emit an error here since we want to allow for more
         // specific errors to be found during use_verification.
         //
         // TODO: Add a flag that associates the terminator instruction with
         // needing to be verified. If it isn't verified appropriately, assert
         // when the verifier is destroyed.
-        auto BBArgOwnershipKind = BBArg->getOwnershipKind();
-        if (!BBArgOwnershipKind.isTrivialOrCompatibleWith(OwnershipKind)) {
+        auto succArgOwnershipKind = succArg->getOwnershipKind();
+        if (!succArgOwnershipKind.isTrivialOrCompatibleWith(ownershipKind)) {
           // This is where the error would go.
           continue;
         }
 
         // If we have a trivial value, just continue.
-        if (BBArgOwnershipKind == ValueOwnershipKind::Trivial)
+        if (succArgOwnershipKind == ValueOwnershipKind::Trivial)
           continue;
 
         // Otherwise add all end_borrow users for this BBArg to the
@@ -1597,19 +1620,25 @@
         // completely joint post-dominated by these users, so we use
         // them to ensure that all of BBArg's uses are completely
         // enclosed within the end_borrow of this argument.
-        for (auto *op : BBArg->getUses()) {
+        for (auto *op : succArg->getUses()) {
           if (auto *ebi = dyn_cast<EndBorrowInst>(op->getUser())) {
-            ImplicitRegularUsers.push_back(ebi);
+            implicitRegularUsers.push_back(ebi);
           }
         }
       }
     }
   }
+
+  // Return true if we did not have an error and false if we did find an error.
+  //
+  // The reason why we use this extra variable is to make sure that when we are
+  // testing, we print out all mismatching pairs rather than just the first.
+  return !foundError;
 }
 
 bool SILValueOwnershipChecker::checkFunctionArgWithoutLifetimeEndingUses(
-    SILFunctionArgument *Arg) {
-  switch (Arg->getOwnershipKind()) {
+    SILFunctionArgument *arg) {
+  switch (arg->getOwnershipKind()) {
   case ValueOwnershipKind::Guaranteed:
   case ValueOwnershipKind::Unowned:
   case ValueOwnershipKind::Trivial:
@@ -1621,19 +1650,19 @@
     break;
   }
 
-  if (DEBlocks.isDeadEnd(Arg->getParent()))
+  if (deadEndBlocks.isDeadEnd(arg->getParent()))
     return true;
 
   return !handleError([&] {
-    llvm::errs() << "Function: '" << Arg->getFunction()->getName() << "'\n"
+    llvm::errs() << "Function: '" << arg->getFunction()->getName() << "'\n"
                  << "    Owned function parameter without life ending uses!\n"
-                 << "Value: " << *Arg << '\n';
+                 << "Value: " << *arg << '\n';
   });
 }
 
 bool SILValueOwnershipChecker::checkYieldWithoutLifetimeEndingUses(
-    BeginApplyResult *Yield) {
-  switch (Yield->getOwnershipKind()) {
+    BeginApplyResult *yield) {
+  switch (yield->getOwnershipKind()) {
   case ValueOwnershipKind::Guaranteed:
   case ValueOwnershipKind::Unowned:
   case ValueOwnershipKind::Trivial:
@@ -1644,25 +1673,25 @@
     break;
   }
 
-  if (DEBlocks.isDeadEnd(Yield->getParent()->getParent()))
+  if (deadEndBlocks.isDeadEnd(yield->getParent()->getParent()))
     return true;
 
   return !handleError([&] {
-    llvm::errs() << "Function: '" << Yield->getFunction()->getName() << "'\n"
+    llvm::errs() << "Function: '" << yield->getFunction()->getName() << "'\n"
                  << "    Owned yield without life ending uses!\n"
-                 << "Value: " << *Yield << '\n';
+                 << "Value: " << *yield << '\n';
   });
 }
 bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() {
   LLVM_DEBUG(llvm::dbgs() << "    No lifetime ending users?! Bailing early.\n");
-  if (auto *Arg = dyn_cast<SILFunctionArgument>(Value)) {
-    if (checkFunctionArgWithoutLifetimeEndingUses(Arg)) {
+  if (auto *arg = dyn_cast<SILFunctionArgument>(value)) {
+    if (checkFunctionArgWithoutLifetimeEndingUses(arg)) {
       return true;
     }
   }
 
-  if (auto *Yield = dyn_cast<BeginApplyResult>(Value)) {
-    if (checkYieldWithoutLifetimeEndingUses(Yield)) {
+  if (auto *yield = dyn_cast<BeginApplyResult>(value)) {
+    if (checkYieldWithoutLifetimeEndingUses(yield)) {
       return true;
     }
   }
@@ -1670,29 +1699,29 @@
   // Check if we are a guaranteed subobject. In such a case, we should never
   // have lifetime ending uses, since our lifetime is guaranteed by our
   // operand, so there is nothing further to do. So just return true.
-  if (isGuaranteedForwardingValue(Value) &&
-      Value.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
+  if (isGuaranteedForwardingValue(value) &&
+      value.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
     return true;
 
   // If we have an unowned value, then again there is nothing left to do.
-  if (Value.getOwnershipKind() == ValueOwnershipKind::Unowned)
+  if (value.getOwnershipKind() == ValueOwnershipKind::Unowned)
     return true;
 
-  if (auto *ParentBlock = Value->getParentBlock()) {
-    if (DEBlocks.isDeadEnd(ParentBlock)) {
+  if (auto *parentBlock = value->getParentBlock()) {
+    if (deadEndBlocks.isDeadEnd(parentBlock)) {
       LLVM_DEBUG(llvm::dbgs() << "    Ignoring transitively unreachable value "
                               << "without users!\n"
                               << "    Function: '"
-                              << Value->getFunction()->getName() << "'\n"
-                              << "    Value: " << *Value << '\n');
+                              << value->getFunction()->getName() << "'\n"
+                              << "    Value: " << *value << '\n');
       return true;
     }
   }
 
-  if (!isValueAddressOrTrivial(Value, Mod)) {
+  if (!isValueAddressOrTrivial(value, mod)) {
     return !handleError([&] {
-      llvm::errs() << "Function: '" << Value->getFunction()->getName() << "'\n";
-      if (Value.getOwnershipKind() == ValueOwnershipKind::Owned) {
+      llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n";
+      if (value.getOwnershipKind() == ValueOwnershipKind::Owned) {
         llvm::errs() << "Error! Found a leaked owned value that was never "
                         "consumed.\n";
       } else {
@@ -1700,7 +1729,7 @@
                         "guaranteed function args must have at least one "
                         "lifetime ending use?!\n";
       }
-      llvm::errs() << "Value: " << *Value << '\n';
+      llvm::errs() << "Value: " << *value << '\n';
     });
   }
 
@@ -1708,34 +1737,34 @@
 }
 
 bool SILValueOwnershipChecker::isGuaranteedFunctionArgWithLifetimeEndingUses(
-    SILFunctionArgument *Arg,
-    const llvm::SmallVectorImpl<BranchPropagatedUser> &LifetimeEndingUsers)
+    SILFunctionArgument *arg,
+    const llvm::SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers)
     const {
-  if (Arg->getOwnershipKind() != ValueOwnershipKind::Guaranteed)
+  if (arg->getOwnershipKind() != ValueOwnershipKind::Guaranteed)
     return true;
 
   return handleError([&] {
-    llvm::errs() << "    Function: '" << Arg->getFunction()->getName() << "'\n"
+    llvm::errs() << "    Function: '" << arg->getFunction()->getName() << "'\n"
                  << "    Guaranteed function parameter with life ending uses!\n"
-                 << "    Value: " << *Arg;
-    for (const auto &U : LifetimeEndingUsers) {
-      llvm::errs() << "    Lifetime Ending User: " << *U;
+                 << "    Value: " << *arg;
+    for (const auto &user : lifetimeEndingUsers) {
+      llvm::errs() << "    Lifetime Ending User: " << *user;
     }
     llvm::errs() << '\n';
   });
 }
 
 bool SILValueOwnershipChecker::isSubobjectProjectionWithLifetimeEndingUses(
-    SILValue Value,
-    const llvm::SmallVectorImpl<BranchPropagatedUser> &LifetimeEndingUsers)
+    SILValue value,
+    const llvm::SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers)
     const {
   return handleError([&] {
-    llvm::errs() << "    Function: '" << Value->getFunction()->getName()
+    llvm::errs() << "    Function: '" << value->getFunction()->getName()
                  << "'\n"
                  << "    Subobject projection with life ending uses!\n"
-                 << "    Value: " << *Value;
-    for (const auto &U : LifetimeEndingUsers) {
-      llvm::errs() << "    Lifetime Ending User: " << *U;
+                 << "    Value: " << *value;
+    for (const auto &user : lifetimeEndingUsers) {
+      llvm::errs() << "    Lifetime Ending User: " << *user;
     }
     llvm::errs() << '\n';
   });
@@ -1749,7 +1778,15 @@
   // 1. Verify that none of the uses are in the same block. This would be an
   // overconsume so in this case we assert.
   // 2. Verify that the uses are compatible with our ownership convention.
-  gatherUsers(LifetimeEndingUsers, RegularUsers, ImplicitRegularUsers);
+  if (!gatherUsers(lifetimeEndingUsers, regularUsers, implicitRegularUsers)) {
+    // Silently return false if this fails.
+    //
+    // If the user pass in a ErrorBehaviorKind that will assert, we
+    // will have asserted in gatherUsers(). If we get here the user
+    // asked us to optionally print out a message and indicate that
+    // the verification failed.
+    return false;
+  }
 
   // We can only have no lifetime ending uses if we have:
   //
@@ -1764,7 +1801,7 @@
   // more. Specifically, we should have /no/ lifetime ending uses of a
   // guaranteed function argument, since a guaranteed function argument should
   // outlive the current function always.
-  if (LifetimeEndingUsers.empty() && checkValueWithoutLifetimeEndingUses()) {
+  if (lifetimeEndingUsers.empty() && checkValueWithoutLifetimeEndingUses()) {
     return false;
   }
 
@@ -1773,9 +1810,9 @@
 
   // See if we have a guaranteed function address. Guaranteed function addresses
   // should never have any lifetime ending uses.
-  if (auto *Arg = dyn_cast<SILFunctionArgument>(Value)) {
-    if (!isGuaranteedFunctionArgWithLifetimeEndingUses(Arg,
-                                                       LifetimeEndingUsers)) {
+  if (auto *arg = dyn_cast<SILFunctionArgument>(value)) {
+    if (!isGuaranteedFunctionArgWithLifetimeEndingUses(arg,
+                                                       lifetimeEndingUsers)) {
       return false;
     }
   }
@@ -1783,10 +1820,10 @@
   // Check if we are an instruction that forwards forwards guaranteed
   // ownership. In such a case, we are a subobject projection. We should not
   // have any lifetime ending uses.
-  if (isGuaranteedForwardingValue(Value) &&
-      Value.getOwnershipKind() == ValueOwnershipKind::Guaranteed) {
-    if (!isSubobjectProjectionWithLifetimeEndingUses(Value,
-                                                     LifetimeEndingUsers)) {
+  if (isGuaranteedForwardingValue(value) &&
+      value.getOwnershipKind() == ValueOwnershipKind::Guaranteed) {
+    if (!isSubobjectProjectionWithLifetimeEndingUses(value,
+                                                     lifetimeEndingUsers)) {
       return false;
     }
   }
@@ -1798,6 +1835,14 @@
 //                           Top Level Entrypoints
 //===----------------------------------------------------------------------===//
 
+OperandOwnershipKindMap
+Operand::getOwnershipKindMap(bool isForwardingSubValue) const {
+  OperandOwnershipKindClassifier classifier(getUser()->getModule(), *this,
+                                            ErrorBehaviorKind::ReturnFalse,
+                                            isForwardingSubValue);
+  return classifier.visit(const_cast<SILInstruction *>(getUser()));
+}
+
 void SILInstruction::verifyOperandOwnership() const {
 #ifndef NDEBUG
   if (DisableOwnershipVerification)
@@ -1827,26 +1872,47 @@
   if (isa<TermInst>(this))
     return;
 
-  ErrorBehaviorKind ErrorBehavior;
+  ErrorBehaviorKind errorBehavior;
   if (IsSILOwnershipVerifierTestingEnabled) {
-    ErrorBehavior = ErrorBehaviorKind::PrintMessageAndReturnFalse;
+    errorBehavior = ErrorBehaviorKind::PrintMessageAndReturnFalse;
   } else {
-    ErrorBehavior = ErrorBehaviorKind::PrintMessageAndAssert;
+    errorBehavior = ErrorBehaviorKind::PrintMessageAndAssert;
   }
-  auto *Self = const_cast<SILInstruction *>(this);
-  for (const Operand &Op : getAllOperands()) {
-    if (isTypeDependentOperand(Op))
+  for (const Operand &op : getAllOperands()) {
+    // Skip type dependence operands.
+    if (isTypeDependentOperand(op))
       continue;
+    SILValue opValue = op.get();
+
     // Skip any SILUndef that we see.
-    if (isa<SILUndef>(Op.get()))
+    if (isa<SILUndef>(opValue))
       continue;
-    OwnershipCompatibilityUseChecker(getModule(), Op, Op.get(), ErrorBehavior)
-        .check(Self);
+    auto operandOwnershipKindMap = op.getOwnershipKindMap();
+    auto valueOwnershipKind = opValue.getOwnershipKind();
+    if (operandOwnershipKindMap.canAcceptKind(valueOwnershipKind))
+      continue;
+
+    if (errorBehavior.shouldPrintMessage()) {
+      llvm::errs() << "Found an operand with a value that is not compatible "
+                      "with the operand's operand ownership kind map.\n";
+      llvm::errs() << "Value: " << opValue;
+      llvm::errs() << "Value Ownership Kind: " << valueOwnershipKind << "\n";
+      llvm::errs() << "Instruction: " << *this;
+      llvm::errs() << "Operand Ownership Kind Map: " << operandOwnershipKindMap;
+    }
+
+    if (errorBehavior.shouldReturnFalse())
+      continue;
+
+    assert(errorBehavior.shouldAssert() &&
+           "At this point, we are expected to assert");
+    llvm_unreachable("triggering standard assertion failure routine");
   }
 #endif
 }
 
-void SILValue::verifyOwnership(SILModule &Mod, DeadEndBlocks *DEBlocks) const {
+void SILValue::verifyOwnership(SILModule &mod,
+                               DeadEndBlocks *deadEndBlocks) const {
 #ifndef NDEBUG
   if (DisableOwnershipVerification)
     return;
@@ -1858,62 +1924,65 @@
 
   // Since we do not have SILUndef, we now know that getFunction() should return
   // a real function. Assert in case this assumption is no longer true.
-  SILFunction *F = (*this)->getFunction();
-  assert(F && "Instructions and arguments should have a function");
+  SILFunction *f = (*this)->getFunction();
+  assert(f && "Instructions and arguments should have a function");
 
   // If the given function has unqualified ownership or we have been asked by
   // the user not to verify this function, there is nothing to verify.
-  if (!F->hasQualifiedOwnership() || !F->shouldVerifyOwnership())
+  if (!f->hasQualifiedOwnership() || !f->shouldVerifyOwnership())
     return;
 
-  ErrorBehaviorKind ErrorBehavior;
+  ErrorBehaviorKind errorBehavior;
   if (IsSILOwnershipVerifierTestingEnabled) {
-    ErrorBehavior = ErrorBehaviorKind::PrintMessageAndReturnFalse;
+    errorBehavior = ErrorBehaviorKind::PrintMessageAndReturnFalse;
   } else {
-    ErrorBehavior = ErrorBehaviorKind::PrintMessageAndAssert;
+    errorBehavior = ErrorBehaviorKind::PrintMessageAndAssert;
   }
-  llvm::SmallPtrSet<SILBasicBlock *, 32> LiveBlocks;
-  if (DEBlocks) {
-    SILValueOwnershipChecker(Mod, *DEBlocks, *this, ErrorBehavior, LiveBlocks)
+
+  llvm::SmallPtrSet<SILBasicBlock *, 32> liveBlocks;
+  if (deadEndBlocks) {
+    SILValueOwnershipChecker(mod, *deadEndBlocks, *this, errorBehavior,
+                             liveBlocks)
         .check();
   } else {
-    DeadEndBlocks DEBlocks((*this)->getFunction());
-    SILValueOwnershipChecker(Mod, DEBlocks, *this, ErrorBehavior, LiveBlocks)
+    DeadEndBlocks deadEndBlocks(f);
+    SILValueOwnershipChecker(mod, deadEndBlocks, *this, errorBehavior,
+                             liveBlocks)
         .check();
   }
 #endif
 }
 
-bool OwnershipChecker::checkValue(SILValue Value) {
-  RegularUsers.clear();
-  LifetimeEndingUsers.clear();
-  LiveBlocks.clear();
+bool OwnershipChecker::checkValue(SILValue value) {
+  regularUsers.clear();
+  lifetimeEndingUsers.clear();
+  liveBlocks.clear();
 
   // If we are SILUndef, just bail. SILUndef can pair with anything. Any uses of
   // the SILUndef will make sure that the matching checks out.
-  if (isa<SILUndef>(Value))
+  if (isa<SILUndef>(value))
     return false;
 
   // Since we do not have SILUndef, we now know that getFunction() should return
   // a real function. Assert in case this assumption is no longer true.
-  SILFunction *F = Value->getFunction();
-  assert(F && "Instructions and arguments should have a function");
+  SILFunction *f = value->getFunction();
+  assert(f && "Instructions and arguments should have a function");
 
   // If the given function has unqualified ownership, there is nothing further
   // to verify.
-  if (!F->hasQualifiedOwnership())
+  if (!f->hasQualifiedOwnership())
     return false;
 
-  ErrorBehaviorKind ErrorBehavior(ErrorBehaviorKind::ReturnFalse);
-  SILValueOwnershipChecker Checker(Mod, DEBlocks, Value, ErrorBehavior,
-                                   LiveBlocks);
-  if (!Checker.check()) {
+  ErrorBehaviorKind errorBehavior(ErrorBehaviorKind::ReturnFalse);
+  SILValueOwnershipChecker checker(mod, deadEndBlocks, value, errorBehavior,
+                                   liveBlocks);
+  if (!checker.check()) {
     return false;
   }
 
   // TODO: Make this more efficient.
-  copy(Checker.getRegularUsers(), std::back_inserter(RegularUsers));
-  copy(Checker.getLifetimeEndingUsers(),
-       std::back_inserter(LifetimeEndingUsers));
+  copy(checker.getRegularUsers(), std::back_inserter(regularUsers));
+  copy(checker.getLifetimeEndingUsers(),
+       std::back_inserter(lifetimeEndingUsers));
   return true;
 }
diff --git a/lib/SIL/SILValue.cpp b/lib/SIL/SILValue.cpp
index 42de105..a704147 100644
--- a/lib/SIL/SILValue.cpp
+++ b/lib/SIL/SILValue.cpp
@@ -286,3 +286,43 @@
   }
 }
 #endif
+
+//===----------------------------------------------------------------------===//
+//                          OperandOwnershipKindMap
+//===----------------------------------------------------------------------===//
+
+void OperandOwnershipKindMap::print(llvm::raw_ostream &os) const {
+  os << "-- OperandOwnershipKindMap --\n";
+
+  unsigned index = 0;
+  unsigned end = unsigned(ValueOwnershipKind::LastValueOwnershipKind) + 1;
+  while (index != end) {
+    auto kind = ValueOwnershipKind(index);
+    if (canAcceptKind(kind)) {
+      os << kind << ": Yes. Liveness: " << getLifetimeConstraint(kind) << "\n";
+    } else {
+      os << kind << ":  No."
+         << "\n";
+    }
+    ++index;
+  }
+}
+
+void OperandOwnershipKindMap::dump() const { print(llvm::dbgs()); }
+
+//===----------------------------------------------------------------------===//
+//                           UseLifetimeConstraint
+//===----------------------------------------------------------------------===//
+
+llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
+                                     UseLifetimeConstraint constraint) {
+  switch (constraint) {
+  case UseLifetimeConstraint::MustBeLive:
+    os << "MustBeLive";
+    break;
+  case UseLifetimeConstraint::MustBeInvalidated:
+    os << "MustBeInvalidated";
+    break;
+  }
+  return os;
+}
diff --git a/lib/SIL/UseOwnershipRequirement.h b/lib/SIL/UseOwnershipRequirement.h
deleted file mode 100644
index 44eb244..0000000
--- a/lib/SIL/UseOwnershipRequirement.h
+++ /dev/null
@@ -1,54 +0,0 @@
-//===--- UseOwnershipRequirement.h ----------------------------------------===//
-//
-// This source file is part of the Swift.org open source project
-//
-// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-///
-/// This file contains declarations that enable clients to compute the ownership
-/// requirements that a use puts on an SSA value. This is used in coordination
-/// with the OwnershipChecker in SILOwnershipVerifier.cpp and the SILValue
-/// ValueOwnershipKind visitor in ValueOwnershipKindClassifier.cpp to perform
-/// SIL level ownership verification.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef SWIFT_SIL_USEOWNERSHIPREQUIREMENT_H
-#define SWIFT_SIL_USEOWNERSHIPREQUIREMENT_H
-
-namespace swift {
-
-/// What constraint does the given use of an SSA value put on the lifetime of
-/// the given SSA value.
-///
-/// There are two possible constraints: MustBeLive and
-/// MustBeInvalidated. MustBeLive means that the SSA value must be able to be
-/// used in a valid way at the given use point. MustBeInvalidated means that any
-/// use of given SSA value after this instruction on any path through this
-/// instruction.
-enum class UseLifetimeConstraint {
-  /// This use requires the SSA value to be live after the given instruction's
-  /// execution.
-  MustBeLive,
-
-  /// This use invalidates the given SSA value.
-  ///
-  /// This means that the given SSA value can not have any uses that are
-  /// reachable from this instruction. When a value has owned semantics this
-  /// means the SSA value is destroyed at this point. When a value has
-  /// guaranteed (i.e. shared borrow) semantics this means that the program
-  /// has left the scope of the borrowed SSA value and said value can not be
-  /// used.
-  MustBeInvalidated,
-};
-
-} // end namespace swift
-
-#endif
diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp
index fc697a2..77d86c8 100644
--- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp
+++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp
@@ -66,7 +66,7 @@
     // 1. copy_value.
     // 2. begin_borrow.
     // 3. end_borrow.
-    if (!all_of(Checker.LifetimeEndingUsers, [](SILInstruction *I) -> bool {
+    if (!all_of(Checker.lifetimeEndingUsers, [](SILInstruction *I) -> bool {
           return isa<DestroyValueInst>(I);
         }))
       continue;
@@ -74,7 +74,7 @@
     // Extra copy values that we should visit recursively.
     llvm::SmallVector<CopyValueInst *, 8> NewCopyInsts;
     llvm::SmallVector<SILInstruction *, 8> NewBorrowInsts;
-    if (!all_of(Checker.RegularUsers, [&](SILInstruction *I) -> bool {
+    if (!all_of(Checker.regularUsers, [&](SILInstruction *I) -> bool {
           if (auto *CVI = dyn_cast<CopyValueInst>(I)) {
             NewCopyInsts.push_back(CVI);
             return true;
@@ -94,8 +94,8 @@
     CVI->eraseFromParent();
     ++NumEliminatedInsts;
 
-    while (!Checker.LifetimeEndingUsers.empty()) {
-      Checker.LifetimeEndingUsers.pop_back_val()->eraseFromParent();
+    while (!Checker.lifetimeEndingUsers.empty()) {
+      Checker.lifetimeEndingUsers.pop_back_val()->eraseFromParent();
       ++NumEliminatedInsts;
     }
 
@@ -158,6 +158,9 @@
   void run() override {
     bool MadeChange = false;
     SILFunction *F = getFunction();
+    if (!F->getModule().isStdlibModule()) {
+      return;
+    }
 
     DeadEndBlocks DEBlocks(F);
     OwnershipChecker Checker{{}, {}, {}, {}, F->getModule(), DEBlocks};
diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift
index cc15df4..8169357 100644
--- a/stdlib/public/core/Dictionary.swift
+++ b/stdlib/public/core/Dictionary.swift
@@ -933,6 +933,7 @@
     get {
       return _variant.lookup(key) ?? defaultValue()
     }
+    @inline(__always)
     _modify {
       let (bucket, found) = _variant.mutatingFind(key)
       let native = _variant.asNative
diff --git a/test/SIL/ownership-verifier/opaque_use_verifier.sil b/test/SIL/ownership-verifier/opaque_use_verifier.sil
index 7ab9101..273ec38 100644
--- a/test/SIL/ownership-verifier/opaque_use_verifier.sil
+++ b/test/SIL/ownership-verifier/opaque_use_verifier.sil
@@ -79,3 +79,13 @@
   %18 = tuple ()
   return %18 : $()
 }
+
+sil @passTrivialAsOpaqueValue : $@convention(thin) (Builtin.Int64) -> () {
+bb0(%0 : @trivial $Builtin.Int64):
+  %1 = function_ref @opaque_copy : $@convention(thin) <T> (@in_guaranteed T) -> @out T
+  %2 = apply %1<Builtin.Int64>(%0) : $@convention(thin) <T> (@in_guaranteed T) -> @out T
+  %3 = function_ref @opaque_arg_copy : $@convention(thin) <T> (@in T) -> @out T
+  apply %3<Builtin.Int64>(%0) : $@convention(thin) <T> (@in T) -> @out T
+  %9999 = tuple()
+  return %9999 : $()
+}
diff --git a/test/SIL/ownership-verifier/over_consume.sil b/test/SIL/ownership-verifier/over_consume.sil
index bb96892..f87f5fa 100644
--- a/test/SIL/ownership-verifier/over_consume.sil
+++ b/test/SIL/ownership-verifier/over_consume.sil
@@ -148,7 +148,6 @@
 // CHECK-LABEL: Function: 'ref_element_addr_requires_borrow'
 // CHECK: Have operand with incompatible ownership?!
 // CHECK: Value:   %0 = argument of bb0 : $RefWithInt
-// CHECK: BaseValue:    %0 = argument of bb0 : $RefWithInt
 // CHECK: User:   %1 = ref_element_addr %0 : $RefWithInt, #RefWithInt.value
 // CHECK: Conv: owned
 sil @ref_element_addr_requires_borrow : $@convention(thin) (@owned RefWithInt) -> () {
@@ -176,11 +175,17 @@
 }
 
 // CHECK-LABEL: Function: 'switch_enum_mismatching_argument_guaranteed_to_owned'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
-// CHECK: Argument:   %2 = argument of bb1 : $Builtin.NativeObject
-// CHECK: Expected convention: guaranteed.
-// CHECK: Actual convention:   owned
+// CHECK: Have operand with incompatible ownership?!
+// CHECK: Value: %0 = argument of bb0
+// CHECK: User:   switch_enum %0
+// CHECK: Conv: guaranteed
+// CHECK: OwnershipMap:
+// CHECK: -- OperandOwnershipKindMap --
+// CHECK: trivial:  Yes. Liveness: MustBeLive
+// CHECK: unowned:  No.
+// CHECK: owned: Yes. Liveness: MustBeInvalidated
+// CHECK: guaranteed:  No.
+// CHECK: any: Yes. Liveness: MustBeLive
 sil @switch_enum_mismatching_argument_guaranteed_to_owned : $@convention(thin) (@guaranteed Optional<Builtin.NativeObject>) -> () {
 bb0(%0 : @guaranteed $Optional<Builtin.NativeObject>):
   switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
@@ -198,11 +203,17 @@
 }
 
 // CHECK-LABEL: Function: 'switch_enum_mismatching_argument_owned_to_guaranteed'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
-// CHECK: Argument:   %2 = argument of bb1 : $Builtin.NativeObject
-// CHECK: Expected convention: owned.
-// CHECK: Actual convention: guaranteed
+// CHECK: Have operand with incompatible ownership?!
+// CHECK: Value: %0 = argument of bb0 : $Optional<Builtin.NativeObject>
+// CHECK: User:   switch_enum %0 : $Optional<Builtin.NativeObject>
+// CHECK: Conv: owned
+// CHECK: OwnershipMap:
+// CHECK: -- OperandOwnershipKindMap --
+// CHECK: trivial:  Yes. Liveness: MustBeLive
+// CHECK: unowned:  No.
+// CHECK: owned:  No.
+// CHECK: guaranteed: Yes. Liveness: MustBeLive
+// CHECK: any: Yes. Liveness: MustBeLive
 sil @switch_enum_mismatching_argument_owned_to_guaranteed : $@convention(thin) (@owned Optional<Builtin.NativeObject>) -> () {
 bb0(%0 : @owned $Optional<Builtin.NativeObject>):
   switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
@@ -247,18 +258,17 @@
 }
 
 // CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_guaranteed_to_owned_1'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
-// CHECK: Argument:   %2 = argument of bb1 : $SuperKlass
-// CHECK: Expected convention: guaranteed.
-// CHECK: Actual convention:   owned
-
-// CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_guaranteed_to_owned_1'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
-// CHECK: Argument:   %5 = argument of bb2 : $Builtin.NativeObject
-// CHECK: Expected convention: guaranteed.
-// CHECK: Actual convention:   owned
+// CHECK: Have operand with incompatible ownership?!
+// CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject
+// CHECK: User:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
+// CHECK: Conv: guaranteed
+// CHECK: OwnershipMap:
+// CHECK: -- OperandOwnershipKindMap --
+// CHECK: trivial:  No.
+// CHECK: unowned:  No.
+// CHECK: owned: Yes. Liveness: MustBeInvalidated
+// CHECK: guaranteed:  No.
+// CHECK: any: Yes. Liveness: MustBeLive
 sil @checked_cast_br_mismatching_argument_guaranteed_to_owned_1 : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
 bb0(%0 : @guaranteed $Builtin.NativeObject):
   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
@@ -277,11 +287,11 @@
 }
 
 // CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_guaranteed_to_owned_2'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
-// CHECK: Argument:   %5 = argument of bb2 : $Builtin.NativeObject
-// CHECK: Expected convention: guaranteed.
-// CHECK: Actual convention:   owned
+// CHECK: Ill-formed SIL! Unable to compute ownership kind map for user?!
+// CHECK: For terminator users, check that successors have compatible ownership kinds.
+// CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject
+// CHECK: User:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
+// CHECK: Conv: guaranteed
 sil @checked_cast_br_mismatching_argument_guaranteed_to_owned_2 : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
 bb0(%0 : @guaranteed $Builtin.NativeObject):
   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
@@ -300,11 +310,11 @@
 }
 
 // CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_guaranteed_to_owned_3'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
-// CHECK: Argument:   %2 = argument of bb1 : $SuperKlass
-// CHECK: Expected convention: guaranteed.
-// CHECK: Actual convention:   owned
+// CHECK: Ill-formed SIL! Unable to compute ownership kind map for user?!
+// CHECK: For terminator users, check that successors have compatible ownership kinds.
+// CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject
+// CHECK: User:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
+// CHECK: Conv: guaranteed
 sil @checked_cast_br_mismatching_argument_guaranteed_to_owned_3 : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
 bb0(%0 : @guaranteed $Builtin.NativeObject):
   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
@@ -323,17 +333,17 @@
 }
 
 // CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_owned_to_guaranteed_1'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
-// CHECK: Argument:   %2 = argument of bb1 : $SuperKlass
-// CHECK: Expected convention: owned.
-// CHECK: Actual convention:   guaranteed
-// CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_owned_to_guaranteed_1'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
-// CHECK: Argument:   %5 = argument of bb2 : $Builtin.NativeObject
-// CHECK: Expected convention: owned.
-// CHECK: Actual convention:   guaranteed
+// CHECK: Have operand with incompatible ownership?!
+// CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject
+// CHECK: User:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
+// CHECK: Conv: owned
+// CHECK: OwnershipMap:
+// CHECK: -- OperandOwnershipKindMap --
+// CHECK: trivial:  No.
+// CHECK: unowned:  No.
+// CHECK: owned:  No.
+// CHECK: guaranteed: Yes. Liveness: MustBeLive
+// CHECK: any: Yes. Liveness: MustBeLive
 sil @checked_cast_br_mismatching_argument_owned_to_guaranteed_1 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
 bb0(%0 : @owned $Builtin.NativeObject):
   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
@@ -353,11 +363,11 @@
 
 
 // CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_owned_to_guaranteed_2'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
-// CHECK: Argument:   %2 = argument of bb1 : $SuperKlass
-// CHECK: Expected convention: owned.
-// CHECK: Actual convention:   guaranteed
+// CHECK: Ill-formed SIL! Unable to compute ownership kind map for user?!
+// CHECK: For terminator users, check that successors have compatible ownership kinds.
+// CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject
+// CHECK: User:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
+// CHECK: Conv: owned
 sil @checked_cast_br_mismatching_argument_owned_to_guaranteed_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
 bb0(%0 : @owned $Builtin.NativeObject):
   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
@@ -376,11 +386,11 @@
 }
 
 // CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_owned_to_guaranteed_3'
-// CHECK: Error! Argument ownership kind does not match terminator!
-// CHECK: Terminator:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
-// CHECK: Argument:   %5 = argument of bb2 : $Builtin.NativeObject
-// CHECK: Expected convention: owned.
-// CHECK: Actual convention:   guaranteed
+// CHECK: Ill-formed SIL! Unable to compute ownership kind map for user?!
+// CHECK: For terminator users, check that successors have compatible ownership kinds.
+// CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject
+// CHECK: User:   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
+// CHECK: Conv: owned
 sil @checked_cast_br_mismatching_argument_owned_to_guaranteed_3 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
 bb0(%0 : @owned $Builtin.NativeObject):
   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
diff --git a/test/SIL/ownership-verifier/subobject_borrowing.sil b/test/SIL/ownership-verifier/subobject_borrowing.sil
index 94a3c12..54c7012 100644
--- a/test/SIL/ownership-verifier/subobject_borrowing.sil
+++ b/test/SIL/ownership-verifier/subobject_borrowing.sil
@@ -77,7 +77,6 @@
 // CHECK-LABEL: Function: 'value_subobject_with_destroy_of_subobject'
 // CHECK: Have operand with incompatible ownership?!
 // CHECK: Value:   %5 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 1
-// CHECK: BaseValue:   %1 = begin_borrow %0 : $B
 // CHECK: User:   destroy_value %5 : $Builtin.NativeObject
 // CHECK: Conv: guaranteed
 
@@ -140,7 +139,6 @@
 // CHECK-LABEL: Function: 'funcarg_subobject_with_destroy_of_subobject'
 // CHECK: Have operand with incompatible ownership?!
 // CHECK: Value:   %4 = tuple_extract %3 : $(Builtin.NativeObject, Builtin.NativeObject), 1
-// CHECK: BaseValue:   %0 = argument of bb0 : $B
 // CHECK: User:   destroy_value %4 : $Builtin.NativeObject
 // CHECK: Conv: guaranteed
 sil @funcarg_subobject_with_destroy_of_subobject : $@convention(thin) (@guaranteed B) -> () {
diff --git a/test/SIL/ownership-verifier/use_verifier.sil b/test/SIL/ownership-verifier/use_verifier.sil
index 6639bb1..31762c9 100644
--- a/test/SIL/ownership-verifier/use_verifier.sil
+++ b/test/SIL/ownership-verifier/use_verifier.sil
@@ -101,6 +101,9 @@
   init()
 }
 
+sil @produce_owned_optional : $@convention(thin) () -> @owned Optional<Builtin.NativeObject>
+sil @guaranteed_nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
+
 ////////////////
 // Test Cases //
 ////////////////
@@ -448,6 +451,61 @@
   return %9999 : $()
 }
 
+// Make sure that we can propagate through an argument through an enum that is
+// switched upon and whose payload is then destroyed.
+sil @switch_enum_owned_payload_test2 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
+bb0(%0 : @owned $Builtin.NativeObject):
+  %1 = enum $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1, %0 : $Builtin.NativeObject
+  switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt.1: bb2
+
+bb1:
+  br bb3
+
+bb2(%2 : @owned $Builtin.NativeObject):
+  destroy_value %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+sil @switch_enum_owned_payload_test3 : $@convention(thin) () -> () {
+bb0:
+  %1 = enum $Optional<Builtin.NativeObject>, #Optional.none!enumelt
+  switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt.1: bb2
+
+bb1:
+  br bb3
+
+bb2(%2 : @owned $Builtin.NativeObject):
+  destroy_value %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+// Make sure that we can propagate through an argument through an enum that is
+// switched upon and whose payload is then destroyed.
+sil @switch_enum_owned_payload_test4 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
+bb0(%0 : @owned $Builtin.NativeObject):
+  %1 = enum $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1, %0 : $Builtin.NativeObject
+  switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb2, case #Optional.none!enumelt: bb1
+
+bb1:
+  br bb3
+
+bb2(%2 : @owned $Builtin.NativeObject):
+  destroy_value %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
 // Make sure that we can properly handle a guaranteed switch_enum.
 sil @switch_enum_guaranteed_arg_test : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
 bb0(%0 : @guaranteed $Builtin.NativeObject):
@@ -1029,3 +1087,28 @@
   %9999 = tuple()
   return %9999 : $()
 }
+
+sil @switch_enum_apply_test : $@convention(thin) () -> @owned Builtin.NativeObject {
+bb0:
+  %0 = function_ref @produce_owned_optional : $@convention(thin) () -> @owned Optional<Builtin.NativeObject>
+  %1 = apply %0() : $@convention(thin) () -> @owned Optional<Builtin.NativeObject>
+  switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb2, case #Optional.none!enumelt: bb1
+
+bb1:
+  unreachable
+
+bb2(%2 : @owned $Builtin.NativeObject):
+  return %2 : $Builtin.NativeObject
+}
+
+// Make sure that we allow for @owned parameters to be passed as @guaranteed
+// parameters since we are performing an "instantaneous" borrow.
+sil @owned_passed_to_guaranteed_apply_parameter : $@convention(thin) (@owned Builtin.NativeObject) -> () {
+bb0k(%0 : @owned $Builtin.NativeObject):
+  %1 = function_ref @guaranteed_nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
+  apply %1(%0) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
+  destroy_value %0 : $Builtin.NativeObject
+  %9999 = tuple()
+  return %9999 : $()
+}
+
diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil
index 0e0e3d0..7e54ab9 100644
--- a/test/SILOptimizer/semantic-arc-opts.sil
+++ b/test/SILOptimizer/semantic-arc-opts.sil
@@ -1,4 +1,4 @@
-// RUN: %target-sil-opt -enable-sil-verify-all -enable-sil-ownership -semantic-arc-opts %s | %FileCheck %s
+// RUN: %target-sil-opt -module-name Swift -enable-sil-verify-all -enable-sil-ownership -semantic-arc-opts %s | %FileCheck %s
 
 sil_stage canonical