Merge pull request #10759 from gottesmm/guaranteed_ownership_transforming_terminators

diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp
index 8b445f6..90bce40 100644
--- a/lib/SIL/SILOwnershipVerifier.cpp
+++ b/lib/SIL/SILOwnershipVerifier.cpp
@@ -174,6 +174,15 @@
   return K1.merge(K2).hasValue();
 }
 
+/// Returns true if \p Kind is trivial or if \p Kind is compatible with \p
+/// ComparisonKind.
+static bool
+trivialOrCompatibleOwnershipKinds(ValueOwnershipKind Kind,
+                                  ValueOwnershipKind ComparisonKind) {
+  return compatibleOwnershipKinds(Kind, ValueOwnershipKind::Trivial) ||
+         compatibleOwnershipKinds(Kind, ComparisonKind);
+}
+
 static bool isValueAddressOrTrivial(SILValue V, SILModule &M) {
   return V->getType().isAddress() ||
          V.getOwnershipKind() == ValueOwnershipKind::Trivial ||
@@ -198,6 +207,8 @@
   case ValueKind::UncheckedEnumDataInst:
   case ValueKind::MarkUninitializedInst:
   case ValueKind::SelectEnumInst:
+  case ValueKind::SwitchEnumInst:
+  case ValueKind::CheckedCastBranchInst:
     return true;
   default:
     return false;
@@ -327,6 +338,12 @@
   OwnershipUseCheckerResult 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
   visitApplyArgument(ValueOwnershipKind RequiredConvention, bool ShouldCheck);
   OwnershipUseCheckerResult
@@ -420,7 +437,6 @@
     return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP),            \
             SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS};                             \
   }
-CONSTANT_OWNERSHIP_INST(Guaranteed, true, EndBorrowArgument)
 CONSTANT_OWNERSHIP_INST(Guaranteed, false, RefElementAddr)
 CONSTANT_OWNERSHIP_INST(Owned, true, AutoreleaseValue)
 CONSTANT_OWNERSHIP_INST(Owned, true, DeallocBox)
@@ -511,9 +527,7 @@
     return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP),            \
             SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS};                             \
   }
-CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, CheckedCastBranch)
 CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, CheckedCastValueBranch)
-CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, SwitchEnum)
 CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, InitExistentialOpaque)
 CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, DeinitExistentialOpaque)
 #undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
@@ -656,6 +670,17 @@
 #undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
 
 OwnershipUseCheckerResult
+OwnershipCompatibilityUseChecker::visitEndBorrowArgumentInst(EndBorrowArgumentInst *I) {
+  // If we are currently checking an end_borrow_argument as a subobject, then we
+  // treat this as just a use.
+  if (isCheckingSubObject())
+    return {true, false};
+
+  // Otherwise, we must be checking an actual argument. Make sure it is guaranteed!
+  return {true, compatibleWithOwnership(ValueOwnershipKind::Guaranteed)};
+}
+
+OwnershipUseCheckerResult
 OwnershipCompatibilityUseChecker::visitSelectEnumInst(SelectEnumInst *I) {
   if (getValue() == I->getEnumOperand()) {
     return {true, false};
@@ -725,6 +750,59 @@
 }
 
 OwnershipUseCheckerResult
+OwnershipCompatibilityUseChecker::visitSwitchEnumInst(SwitchEnumInst *SEI) {
+  return visitTransformingTerminatorInst(SEI);
+}
+
+OwnershipUseCheckerResult
+OwnershipCompatibilityUseChecker::visitCheckedCastBranchInst(
+    CheckedCastBranchInst *SEI) {
+  return visitTransformingTerminatorInst(SEI);
+}
+
+OwnershipUseCheckerResult
+OwnershipCompatibilityUseChecker::visitTransformingTerminatorInst(
+    TermInst *TI) {
+  // If our operand was trivial, return early.
+  if (compatibleWithOwnership(ValueOwnershipKind::Trivial))
+    return {true, 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)
+      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))
+      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';
+    });
+  }
+
+  // Finally, if everything lines up, emit that we match and are a lifetime
+  // ending point if we are owned.
+  return {true, compatibleWithOwnership(ValueOwnershipKind::Owned)};
+}
+
+OwnershipUseCheckerResult
 OwnershipCompatibilityUseChecker::visitReturnInst(ReturnInst *RI) {
   SILModule &M = RI->getModule();
   bool IsTrivial = RI->getOperand()->getType().isTrivial(M);
@@ -1454,8 +1532,8 @@
   // 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.
-  bool IsGuaranteed =
-      Value.getOwnershipKind() == ValueOwnershipKind::Guaranteed;
+  auto OwnershipKind = Value.getOwnershipKind();
+  bool IsGuaranteed = OwnershipKind == ValueOwnershipKind::Guaranteed;
 
   if (IsGuaranteed && isOwnershipForwardingValue(Value))
     return;
@@ -1512,19 +1590,63 @@
 
     // At this point, we know that we must have a forwarded subobject. Since the
     // base type is guaranteed, we know that the subobject is either guaranteed
-    // or trivial. The trivial case is not interesting for ARC verification, so
-    // if the user has a trivial ownership kind, continue.
-    if (SILValue(User).getOwnershipKind() == ValueOwnershipKind::Trivial) {
+    // 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 User->getUses() to the
+    // worklist.
+    auto *TI = dyn_cast<TermInst>(User);
+    if (!TI) {
+      if (SILValue(User).getOwnershipKind() == ValueOwnershipKind::Trivial) {
+        continue;
+      }
+
+      // Now, we /must/ have a guaranteed subobject, so lets assert that the
+      // user
+      // is actually guaranteed and add the subobject's users to our worklist.
+      assert(SILValue(User).getOwnershipKind() ==
+                 ValueOwnershipKind::Guaranteed &&
+             "Our value is guaranteed and this is a forwarding instruction. "
+             "Should have guaranteed ownership as well.");
+      std::copy(User->use_begin(), User->use_end(), std::back_inserter(Users));
       continue;
     }
 
-    // Now, we /must/ have a guaranteed subobject, so lets assert that the user
-    // is actually guaranteed and add the subobject's users to our worklist.
-    assert(SILValue(User).getOwnershipKind() ==
-               ValueOwnershipKind::Guaranteed &&
-           "Our value is guaranteed and this is a forwarding instruction. "
-           "Should have guaranteed ownership as well.");
-    std::copy(User->use_begin(), User->use_end(), std::back_inserter(Users));
+    // Otherwise if we have a terminator, add any as uses any
+    // end_borrow_argument to ensure that the subscope is completely enclsed
+    // within the super scope. all of the arguments to the work list. We require
+    // all of our arguments to be either trivial or guaranteed.
+    for (auto &Succ : TI->getSuccessors()) {
+      auto *BB = Succ.getBB();
+
+      // If we do not have any arguments, then continue.
+      if (BB->args_empty())
+        continue;
+
+      // Otherwise, make sure that all arguments are trivial or guaranteed. If
+      // we fail, emit an error.
+      //
+      // TODO: We could ignore this error and emit a more specific error on the
+      // actual terminator.
+      for (auto *BBArg : BB->getArguments()) {
+        // *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.
+        if (!trivialOrCompatibleOwnershipKinds(BBArg->getOwnershipKind(),
+                                               OwnershipKind)) {
+          // This is where the error would go.
+          continue;
+        }
+
+        // If we have a trivial value, just continue.
+        if (BBArg->getOwnershipKind() == ValueOwnershipKind::Trivial)
+          continue;
+
+        // Otherwise, 
+        std::copy(BBArg->use_begin(), BBArg->use_end(), std::back_inserter(Users));
+      }
+    }
   }
 }
 
diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp
index 5002239..d59e239 100644
--- a/lib/SILGen/SILGenPattern.cpp
+++ b/lib/SILGen/SILGenPattern.cpp
@@ -35,6 +35,14 @@
 using namespace swift;
 using namespace Lowering;
 
+//===----------------------------------------------------------------------===//
+//                             Pattern Utilities
+//===----------------------------------------------------------------------===//
+
+// TODO: These routines should probably be refactored into their own file since
+// they have nothing to do with the implementation of SILGenPattern
+// specifically.
+
 /// Shallow-dump a pattern node one level deep for debug purposes.
 static void dumpPattern(const Pattern *p, llvm::raw_ostream &os) {
   if (!p) {
@@ -290,6 +298,10 @@
   llvm_unreachable("Unhandled PatternKind in switch.");
 }
 
+//===----------------------------------------------------------------------===//
+//                           SILGenPattern Emission
+//===----------------------------------------------------------------------===//
+
 namespace {
 
 /// A row which we intend to specialize.
@@ -467,6 +479,10 @@
 
 /// A handle to a row in a clause matrix. Does not own memory; use of the
 /// ClauseRow must be dominated by its originating ClauseMatrix.
+///
+/// TODO: This should be refactored into a more general formulation that uses a
+/// child template pattern to inject our logic. This will then allow us to
+/// inject "mock" objects in a unittest file.
 class ClauseRow {
   friend class ClauseMatrix;
   
@@ -872,7 +888,7 @@
       assert(operand.getFinalConsumption() !=
                  CastConsumptionKind::TakeOnSuccess &&
              "When compiling with sil ownership take on success is disabled");
-      // No unforwarding is needed, we always copy.
+      // No unforwarding is needed, we always borrow/copy.
       return false;
     }
 
@@ -1378,12 +1394,14 @@
 getManagedSubobject(SILGenFunction &SGF, SILValue value,
                     const TypeLowering &valueTL,
                     CastConsumptionKind consumption) {
-  if (consumption != CastConsumptionKind::CopyOnSuccess) {
-    return {SGF.emitManagedRValueWithCleanup(value, valueTL),
-             consumption};
-  } else {
+  if (consumption == CastConsumptionKind::CopyOnSuccess) {
     return {ManagedValue::forUnmanaged(value), consumption};
   }
+
+  assert((!SGF.F.getModule().getOptions().EnableSILOwnership ||
+          consumption != CastConsumptionKind::TakeOnSuccess) &&
+         "TakeOnSuccess should never be used when sil ownership is enabled");
+  return {SGF.emitManagedRValueWithCleanup(value, valueTL), consumption};
 }
 
 static ConsumableManagedValue
diff --git a/test/SIL/ownership-verifier/over_consume.sil b/test/SIL/ownership-verifier/over_consume.sil
index 31100ba..f802f76 100644
--- a/test/SIL/ownership-verifier/over_consume.sil
+++ b/test/SIL/ownership-verifier/over_consume.sil
@@ -22,6 +22,8 @@
 case none
 }
 
+class SuperKlass {}
+
 ///////////
 // Tests //
 ///////////
@@ -172,3 +174,253 @@
   %9999 = tuple()
   return %9999 : $()
 }
+
+// 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
+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
+
+bb1(%1 : @owned $Builtin.NativeObject):
+  destroy_value %1 : $Builtin.NativeObject
+  br bb3
+
+bb2:
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+// 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
+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
+
+bb1(%1 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %1 : $Builtin.NativeObject
+  br bb3
+
+bb2:
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+
+// CHECK-LABEL: Function: 'switch_enum_guaranteed_arg_outlives_original_value'
+// CHECK: Found use after free?!
+// CHECK: Value:   %1 = begin_borrow %0 : $Optional<Builtin.NativeObject>
+// CHECK: Consuming User:   end_borrow %1 from %0 : $Optional<Builtin.NativeObject>, $Optional<Builtin.NativeObject>
+// CHECK: Non Consuming User:   end_borrow_argument %3 : $Builtin.NativeObject
+// CHECK: Block: bb1
+sil @switch_enum_guaranteed_arg_outlives_original_value : $@convention(thin) (@owned Optional<Builtin.NativeObject>) -> () {
+bb0(%0 : @owned $Optional<Builtin.NativeObject>):
+  %1 = begin_borrow %0 : $Optional<Builtin.NativeObject>
+  switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
+
+bb1(%2 : @guaranteed $Builtin.NativeObject):
+  end_borrow %1 from %0 : $Optional<Builtin.NativeObject>, $Optional<Builtin.NativeObject>
+  end_borrow_argument %2 : $Builtin.NativeObject
+  br bb3
+
+bb2:
+  end_borrow %1 from %0 : $Optional<Builtin.NativeObject>, $Optional<Builtin.NativeObject>
+  br bb3
+
+bb3:
+  destroy_value %0 : $Optional<Builtin.NativeObject>
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+// 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
+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
+
+bb1(%1 : @owned $SuperKlass):
+  destroy_value %1 : $SuperKlass
+  br bb3
+
+bb2(%2 : @owned $Builtin.NativeObject):
+  destroy_value %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+// 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
+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
+
+bb1(%1 : @guaranteed $SuperKlass):
+  end_borrow_argument %1 : $SuperKlass
+  br bb3
+
+bb2(%2 : @owned $Builtin.NativeObject):
+  destroy_value %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+// 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
+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
+
+bb1(%1 : @owned $SuperKlass):
+  destroy_value %1 : $SuperKlass
+  br bb3
+
+bb2(%2 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+// 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
+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
+
+bb1(%1 : @guaranteed $SuperKlass):
+  end_borrow_argument %1 : $SuperKlass
+  br bb3
+
+bb2(%2 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+
+// 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
+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
+
+bb1(%1 : @guaranteed $SuperKlass):
+  end_borrow_argument %1 : $SuperKlass
+  br bb3
+
+bb2(%2 : @owned $Builtin.NativeObject):
+  destroy_value %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+// 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
+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
+
+bb1(%1 : @owned $SuperKlass):
+  destroy_value %1 : $SuperKlass
+  br bb3
+
+bb2(%2 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %2 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+// CHECK-LABEL: Function: 'checked_cast_br_guaranteed_arg_outlives_original_value'
+// CHECK: Found use after free?!
+// CHECK: Value:   %1 = begin_borrow %0 : $Builtin.NativeObject
+// CHECK: Consuming User:   end_borrow %1 from %0 : $Builtin.NativeObject, $Builtin.NativeObject
+// CHECK: Non Consuming User:   end_borrow_argument %7 : $Builtin.NativeObject
+// CHECK: Block: bb2
+sil @checked_cast_br_guaranteed_arg_outlives_original_value : $@convention(thin) (@owned Builtin.NativeObject) -> () {
+bb0(%0 : @owned $Builtin.NativeObject):
+  %1 = begin_borrow %0 : $Builtin.NativeObject
+  checked_cast_br %1 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
+
+bb1(%2 : @guaranteed $SuperKlass):
+  end_borrow_argument %2 : $SuperKlass
+  end_borrow %1 from %0 : $Builtin.NativeObject, $Builtin.NativeObject
+  br bb3
+
+bb2(%3 : @guaranteed $Builtin.NativeObject):
+  end_borrow %1 from %0 : $Builtin.NativeObject, $Builtin.NativeObject
+  end_borrow_argument %3 : $Builtin.NativeObject
+  br bb3
+
+bb3:
+  destroy_value %0 : $Builtin.NativeObject
+  %9999 = tuple()
+  return %9999 : $()
+}
diff --git a/test/SIL/ownership-verifier/use_verifier.sil b/test/SIL/ownership-verifier/use_verifier.sil
index e5b1249..3837437 100644
--- a/test/SIL/ownership-verifier/use_verifier.sil
+++ b/test/SIL/ownership-verifier/use_verifier.sil
@@ -385,6 +385,85 @@
   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):
+  %1 = enum $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1, %0 : $Builtin.NativeObject
+  switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
+
+bb1(%2 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %2 : $Builtin.NativeObject
+  br bb3
+
+bb2:
+  br bb3
+
+bb3:
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+sil @switch_enum_guaranteed_beginborrow_test_1a : $@convention(thin) (@owned Builtin.NativeObject) -> () {
+bb0(%0 : @owned $Builtin.NativeObject):
+  %1 = begin_borrow %0 : $Builtin.NativeObject
+  %2 = enum $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1, %1 : $Builtin.NativeObject
+  switch_enum %2 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
+
+bb1(%3 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %3 : $Builtin.NativeObject
+  end_borrow %1 from %0 : $Builtin.NativeObject, $Builtin.NativeObject
+  br bb3
+
+bb2:
+  end_borrow %1 from %0 : $Builtin.NativeObject, $Builtin.NativeObject
+  br bb3
+
+bb3:
+  destroy_value %0 : $Builtin.NativeObject
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+sil @switch_enum_guaranteed_beginborrow_test_1b : $@convention(thin) (@owned Builtin.NativeObject) -> () {
+bb0(%0 : @owned $Builtin.NativeObject):
+  %1 = begin_borrow %0 : $Builtin.NativeObject
+  %2 = enum $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1, %1 : $Builtin.NativeObject
+  switch_enum %2 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
+
+bb1(%3 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %3 : $Builtin.NativeObject
+  br bb3
+
+bb2:
+  br bb3
+
+bb3:
+  end_borrow %1 from %0 : $Builtin.NativeObject, $Builtin.NativeObject
+  destroy_value %0 : $Builtin.NativeObject
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+sil @switch_enum_guaranteed_beginborrow_test_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
+bb0(%0 : @owned $Builtin.NativeObject):
+  %1 = enum $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1, %0 : $Builtin.NativeObject
+  %2 = begin_borrow %1 : $Optional<Builtin.NativeObject>
+  switch_enum %2 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
+
+bb1(%3 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %3 : $Builtin.NativeObject
+  br bb3
+
+bb2:
+  br bb3
+
+bb3:
+  end_borrow %2 from %1 : $Optional<Builtin.NativeObject>, $Optional<Builtin.NativeObject>
+  destroy_value %1 : $Optional<Builtin.NativeObject>
+  %9999 = tuple()
+  return %9999 : $()
+}
+
 // Make sure that we can properly handle a switch enum case where the input
 // argument is an enum, but the final argument is trivial.
 sil @switch_enum_no_payload_test : $@convention(thin) () -> () {
@@ -553,8 +632,8 @@
 }
 
 // We check first for objects and then for metatypes.
-sil @checked_cast_br_test : $@convention(thin) (@owned Builtin.NativeObject) -> () {
-bb0(%0 : @owned $Builtin.NativeObject):
+sil @checked_cast_br_test : $@convention(thin) (@owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
+bb0(%0 : @owned $Builtin.NativeObject, %6 : @guaranteed $Builtin.NativeObject):
   checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2
 
 bb1(%1 : @owned $SuperKlass):
@@ -576,6 +655,17 @@
   br bb6
 
 bb6:
+  checked_cast_br %6 : $Builtin.NativeObject to $SuperKlass, bb7, bb8
+
+bb7(%7 : @guaranteed $SuperKlass):
+  end_borrow_argument %7 : $SuperKlass
+  br bb9
+
+bb8(%8 : @guaranteed $Builtin.NativeObject):
+  end_borrow_argument %8 : $Builtin.NativeObject
+  br bb9
+
+bb9:
   %9999 = tuple()
   return %9999 : $()
 }