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 : $()
}