Add support to AccessEnforcementSelection for borrowed SILValues.

Previously, this asserted on unexpected situations that the
optimizer couldn't handle.

It makes more sense now to handle these cases conservatively since we can't
catch them in early testing.

Fixes <rdar://problem/35402799> [4.1] Assertion failed: (user->getResults().empty())
diff --git a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp
index 10e581c..f92c682 100644
--- a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp
+++ b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp
@@ -442,33 +442,56 @@
 }
 
 void SelectEnforcement::updateCapture(AddressCapture capture) {
+  auto captureIfEscaped = [&](SILInstruction *user) {
+    if (hasPotentiallyEscapedAt(user))
+      dynamicCaptures.recordCapture(capture);
+  };
   llvm::SmallSetVector<SingleValueInstruction *, 8> worklist;
   auto visitUse = [&](Operand *oper) {
     auto *user = oper->getUser();
     if (FullApplySite::isa(user)) {
       // A call is considered a closure access regardless of whether it calls
       // the closure or accepts the closure as an argument.
-      if (hasPotentiallyEscapedAt(user)) {
-        dynamicCaptures.recordCapture(capture);
-      }
+      captureIfEscaped(user);
       return;
     }
-    if (auto *CFI = dyn_cast<ConvertFunctionInst>(user)) {
-      worklist.insert(CFI);
+    switch (user->getKind()) {
+    case SILInstructionKind::ConvertFunctionInst:
+    case SILInstructionKind::BeginBorrowInst:
+    case SILInstructionKind::CopyValueInst:
+    case SILInstructionKind::EnumInst:
+    case SILInstructionKind::StructInst:
+    case SILInstructionKind::TupleInst:
+    case SILInstructionKind::PartialApplyInst:
+      // Propagate the closure.
+      worklist.insert(cast<SingleValueInstruction>(user));
       return;
-    }
-    if (auto *PAI = dyn_cast<PartialApplyInst>(user)) {
-      assert(oper->get() != PAI->getCallee() && "cannot re-partially apply");
-      // The closure is capture by another closure. Transitively consider any
-      // calls to the parent closure as an access.
-      worklist.insert(PAI);
+    case SILInstructionKind::StrongRetainInst:
+    case SILInstructionKind::StrongReleaseInst:
+    case SILInstructionKind::DebugValueInst:
+    case SILInstructionKind::DestroyValueInst:
+    case SILInstructionKind::RetainValueInst:
+    case SILInstructionKind::ReleaseValueInst:
+    case SILInstructionKind::EndBorrowInst:
+      // Benign use.
       return;
+    case SILInstructionKind::TupleExtractInst:
+    case SILInstructionKind::StructExtractInst:
+    case SILInstructionKind::AssignInst:
+    case SILInstructionKind::BranchInst:
+    case SILInstructionKind::CondBranchInst:
+    case SILInstructionKind::ReturnInst:
+    case SILInstructionKind::StoreInst:
+      // These are all valid partial_apply users, however we don't expect them
+      // to occur with non-escaping closures. Handle them conservatively just in
+      // case they occur.
+      LLVM_FALLTHROUGH;
+    default:
+      DEBUG(llvm::dbgs() << "    Unrecognized partial_apply user: " << *user);
+
+      // Handle unknown uses conservatively by assuming a capture.
+      captureIfEscaped(user);
     }
-    DEBUG(llvm::dbgs() << "    Unrecognized partial_apply user: " << *user);
-    // If this user has no results, then we can safely assume it doesn't pass
-    // the closure to a call site. If it has results, then it might propagate
-    // the closure, in which case it needs to be handled above.
-    assert(user->getResults().empty());
   };
   SingleValueInstruction *PAIUser = dyn_cast<PartialApplyInst>(capture.site);
   while (true) {
diff --git a/test/SILOptimizer/access_enforcement_selection.sil b/test/SILOptimizer/access_enforcement_selection.sil
index dbf440e..ed69a81 100644
--- a/test/SILOptimizer/access_enforcement_selection.sil
+++ b/test/SILOptimizer/access_enforcement_selection.sil
@@ -180,3 +180,39 @@
   %closure = partial_apply %f(%var) : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
   unreachable
 }
+
+// Borrowed closure can be statically checked.
+//
+// CHECK-LABEL: sil @borrowedClosure : $@convention(thin) (@inout_aliasable Builtin.Int64) -> () {
+// CHECK: begin_access [read] [static]
+// CHECK-LABEL: } // end sil function 'borrowedClosure'
+sil @borrowedClosure : $@convention(thin) (@inout_aliasable Builtin.Int64) -> () {
+bb0(%0 : $*Builtin.Int64):
+  %access = begin_access [read] [unknown] %0 : $*Builtin.Int64
+  %val = load [trivial] %access : $*Builtin.Int64
+  end_access %access : $*Builtin.Int64
+  %empty = tuple ()
+  return %empty : $()
+}
+
+// Borrow an escaping closure. The closure body will be dynamically checked.
+sil @borrowClosure : $@convention(thin) () -> () {
+  %box = alloc_box ${ var Builtin.Int64 }, var, name "x"
+  %addr = project_box %box : ${ var Builtin.Int64 }, 0
+  // box escapes.
+  %copy = copy_value %box : ${ var Builtin.Int64 }
+
+  %f = function_ref @borrowedClosure : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
+  %closure = partial_apply %f(%addr) : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
+
+  // closure is borrowed.
+  %borrow = begin_borrow %closure : $@callee_owned () -> ()
+  %closure_copy = copy_value %borrow : $@callee_owned () -> ()
+  end_borrow %borrow from %closure : $@callee_owned () -> (), $@callee_owned () -> ()
+
+  destroy_value %closure_copy : $@callee_owned () -> ()
+  destroy_value %copy : ${ var Builtin.Int64 }
+  destroy_value %box : ${ var Builtin.Int64 }
+  %empty = tuple ()
+  return %empty : $()
+}