| // RUN: %target-swift-emit-silgen -module-name without_actually_escaping %s | %FileCheck %s |
| |
| var escapeHatch: Any = 0 |
| |
| // CHECK-LABEL: sil hidden [ossa] @$s25without_actually_escaping9letEscape1fyycyyXE_tF |
| func letEscape(f: () -> ()) -> () -> () { |
| // CHECK: bb0([[ARG:%.*]] : $@noescape @callee_guaranteed () -> ()): |
| // CHECK: [[THUNK:%.*]] = function_ref @$sIg_Ieg_TR : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () |
| // TODO: Use a canary wrapper instead of just copying the nonescaping value |
| // CHECK: [[ESCAPABLE_COPY:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[ARG]]) |
| // CHECK: [[MD_ESCAPABLE_COPY:%.*]] = mark_dependence [[ESCAPABLE_COPY]] |
| // CHECK: [[BORROW_MD_ESCAPABLE_COPY:%.*]] = begin_borrow [[MD_ESCAPABLE_COPY]] |
| // CHECK: [[SUB_CLOSURE:%.*]] = function_ref @ |
| // CHECK: [[RESULT:%.*]] = apply [[SUB_CLOSURE]]([[BORROW_MD_ESCAPABLE_COPY]]) |
| // CHECK: destroy_value [[MD_ESCAPABLE_COPY]] |
| // CHECK: return [[RESULT]] |
| return withoutActuallyEscaping(f) { return $0 } |
| } |
| |
| // thunk for @callee_guaranteed () -> () |
| // The thunk must be [without_actually_escaping]. |
| // CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [without_actually_escaping] [ossa] @$sIg_Ieg_TR : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () { |
| |
| // CHECK-LABEL: sil hidden [ossa] @$s25without_actually_escaping14letEscapeThrow1fyycyycyKXE_tKF |
| // CHECK: bb0([[ARG:%.*]] : $@noescape @callee_guaranteed () -> (@owned @callee_guaranteed () -> (), @error Error)): |
| // CHECK: [[CVT:%.*]] = function_ref @$sIeg_s5Error_pIgozo_Ieg_sAA_pIegozo_TR |
| // CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CVT]]([[ARG]]) |
| // CHECK: [[MD:%.*]] = mark_dependence [[CLOSURE]] : {{.*}} on [[ARG]] |
| // CHECK: [[BORROW:%.*]] = begin_borrow [[MD]] |
| // CHECK: [[USER:%.*]] = function_ref @$s25without_actually_escaping14letEscapeThrow1fyycyycyKXE_tKFyycyycyKcKXEfU_ |
| // CHECK: try_apply [[USER]]([[BORROW]]) : {{.*}}, normal bb1, error bb2 |
| // |
| // CHECK: bb1([[RES:%.*]] : @owned $@callee_guaranteed () -> ()): |
| // CHECK: [[ESCAPED:%.*]] = is_escaping_closure [[BORROW]] |
| // CHECK: cond_fail [[ESCAPED]] : $Builtin.Int1 |
| // CHECK: end_borrow [[BORROW]] |
| // CHECK: destroy_value [[MD]] |
| // CHECK: return [[RES]] |
| // |
| // CHECK: bb2([[ERR:%.*]] : @owned $Error): |
| // CHECK: end_borrow [[BORROW]] |
| // CHECK: destroy_value [[MD]] |
| // CHECK: throw [[ERR]] : $Error |
| // CHECK: } |
| |
| func letEscapeThrow(f: () throws -> () -> ()) throws -> () -> () { |
| return try withoutActuallyEscaping(f) { return try $0() } |
| } |
| |
| // thunk for @callee_guaranteed () -> (@owned @escaping @callee_guaranteed () -> (), @error @owned Error) |
| // The thunk must be [without_actually_escaping]. |
| // CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [without_actually_escaping] [ossa] @$sIeg_s5Error_pIgozo_Ieg_sAA_pIegozo_TR : $@convention(thin) (@noescape @callee_guaranteed () -> (@owned @callee_guaranteed () -> (), @error Error)) -> (@owned @callee_guaranteed () -> (), @error Error) { |
| |
| // We used to crash on this example because we would use the wrong substitution |
| // map. |
| struct DontCrash { |
| private func firstEnv<L1>( |
| closure1: (L1) -> Bool, |
| closure2: (L1) -> Bool |
| ) { |
| withoutActuallyEscaping(closure1) { closure1 in |
| secondEnv( |
| closure1: closure1, |
| closure2: closure2 |
| ) |
| } |
| } |
| |
| private func secondEnv<L2>( |
| closure1: @escaping (L2) -> Bool, |
| closure2: (L2) -> Bool |
| ) { |
| withoutActuallyEscaping(closure2) { closure2 in |
| } |
| } |
| } |
| |
| func modifyAndPerform<T>(_ _: UnsafeMutablePointer<T>, closure: () ->()) { |
| closure() |
| } |
| |
| // Make sure that we properly handle cases where the input closure is not |
| // trivial. This means we need to copy first. |
| // CHECK-LABEL: sil hidden [ossa] @$s25without_actually_escaping0A24ActuallyEscapingConflictyyF : $@convention(thin) () -> () { |
| // CHECK: [[CLOSURE_1_FUN:%.*]] = function_ref @$s25without_actually_escaping0A24ActuallyEscapingConflictyyFyycfU_ : |
| // CHECK: [[CLOSURE_1:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_1_FUN]]( |
| // CHECK: [[BORROWED_CLOSURE_1:%.*]] = begin_borrow [[CLOSURE_1]] |
| // CHECK: [[COPY_BORROWED_CLOSURE_1:%.*]] = copy_value [[BORROWED_CLOSURE_1]] |
| // CHECK: [[THUNK_FUNC:%.*]] = function_ref @$sIeg_Ieg_TR : |
| // CHECK: [[COPY_2_BORROWED_CLOSURE_1:%.*]] = copy_value [[COPY_BORROWED_CLOSURE_1]] |
| // CHECK: [[THUNK_PA:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FUNC]]([[COPY_2_BORROWED_CLOSURE_1]]) |
| // CHECK: [[THUNK_PA_MDI:%.*]] = mark_dependence [[THUNK_PA]] : $@callee_guaranteed () -> () on [[COPY_BORROWED_CLOSURE_1]] : $@callee_guaranteed () -> () |
| // CHECK: destroy_value [[THUNK_PA_MDI]] |
| // CHECK: destroy_value [[COPY_BORROWED_CLOSURE_1]] |
| // CHECK: } // end sil function '$s25without_actually_escaping0A24ActuallyEscapingConflictyyF' |
| func withoutActuallyEscapingConflict() { |
| var localVar = 0 |
| let nestedModify = { localVar = 3 } |
| withoutActuallyEscaping(nestedModify) { |
| modifyAndPerform(&localVar, closure: $0) |
| } |
| } |