1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
// RUN: %target-swift-frontend -enforce-exclusivity=checked -Onone -emit-sil -parse-as-library %s -Xllvm -debug-only=access-enforcement-selection 2>&1 | %FileCheck %s
// REQUIRES: asserts
// This is a source-level test because it helps bring up the entire -Onone pipeline with the access markers.
public func takesInout(_ i: inout Int) {
i = 42
}
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection10takesInoutyySizF
// CHECK: Static Access: %{{.*}} = begin_access [modify] [static] %{{.*}} : $*Int
// Helper taking a basic, no-escape closure.
func takeClosure(_: ()->Int) {}
// Helper taking an escaping closure.
func takeClosureAndInout(_: inout Int, _: @escaping ()->Int) {}
// Helper taking an escaping closure.
func takeEscapingClosure(_: @escaping ()->Int) {}
// Generate an alloc_stack that escapes into a closure.
public func captureStack() -> Int {
// Use a `var` so `x` isn't treated as a literal.
var x = 3
takeClosure { return x }
return x
}
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection12captureStackSiyF
// Dynamic access for `return x`. Since the closure is non-escaping, using
// dynamic enforcement here is more conservative than it needs to be -- static
// is sufficient here.
// CHECK: Static Access: %{{.*}} = begin_access [read] [static] %{{.*}} : $*Int
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection12captureStackSiyFSiyXEfU_
// CHECK: Static Access: %{{.*}} = begin_access [read] [static] %{{.*}} : $*Int
// Generate an alloc_stack that does not escape into a closure.
public func nocaptureStack() -> Int {
var x = 3
takeClosure { return 5 }
return x
}
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection14nocaptureStackSiyF
// Static access for `return x`.
// CHECK: Static Access: %{{.*}} = begin_access [read] [static] %{{.*}} : $*Int
//
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection14nocaptureStackSiyFSiyXEfU_
// Generate an alloc_stack that escapes into a closure while an access is
// in progress.
public func captureStackWithInoutInProgress() -> Int {
// Use a `var` so `x` isn't treated as a literal.
var x = 3
takeClosureAndInout(&x) { return x }
return x
}
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection31captureStackWithInoutInProgressSiyF
// Access for `return x`.
// CHECK-DAG: Dynamic Access: %{{.*}} = begin_access [read] [dynamic] %{{.*}} : $*Int
// Access for `&x`.
// CHECK-DAG: Dynamic Access: %{{.*}} = begin_access [modify] [dynamic] %{{.*}} : $*Int
//
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection31captureStackWithInoutInProgressSiyF
// CHECK: Dynamic Access: %{{.*}} = begin_access [read] [dynamic] %{{.*}} : $*Int
// Generate an alloc_box that escapes into a closure.
// FIXME: `x` is eventually promoted to an alloc_stack even though it has dynamic enforcement.
// We should ensure that alloc_stack variables are statically enforced.
public func captureBox() -> Int {
var x = 3
takeEscapingClosure { x = 4; return x }
return x
}
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection10captureBoxSiyF
// Dynamic access for `return x`.
// CHECK: Dynamic Access: %{{.*}} = begin_access [read] [dynamic] %{{.*}} : $*Int
// CHECK-LABEL: $s28access_enforcement_selection10captureBoxSiyFSiycfU_
// Generate a closure in which the @inout_aliasing argument
// escapes to an @inout function `bar`.
public func recaptureStack() -> Int {
var x = 3
takeClosure { takesInout(&x); return x }
return x
}
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection14recaptureStackSiyF
//
// Static access for `return x`.
// CHECK: Static Access: %{{.*}} = begin_access [read] [static] %{{.*}} : $*Int
// CHECK-LABEL: Access Enforcement Selection in $s28access_enforcement_selection14recaptureStackSiyFSiyXEfU_
//
// The first [modify] access inside the closure is static. It enforces the
// @inout argument.
// CHECK: Static Access: %{{.*}} = begin_access [modify] [static] %{{.*}} : $*Int
//
// The second [read] access is static. Same as `captureStack` above.
//
// CHECK: Static Access: %{{.*}} = begin_access [read] [static] %{{.*}} : $*Int
// -----------------------------------------------------------------------------
// Inout access in a recursive non-escaping closure currently has
// static enforcement.
public protocol Apply {
func apply(_ body: (Apply) -> ())
}
// CHECK-LABEL: sil private @$s28access_enforcement_selection20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF : $@convention(thin) (@in_guaranteed any Apply, @inout_aliasable Int) -> () {
// CHECK: bb0(%0 : $*any Apply, %1 : @closureCapture $*Int):
// CHECK: begin_access [modify] [static] %1 : $*Int
// CHECK-LABEL: } // end sil function '$s28access_enforcement_selection20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF'
public func testRecursiveClosure(a: Apply, x: inout Int) {
func localFunc(b: Apply) {
x += 1
let closure = { (c: Apply) in
c.apply(localFunc)
}
b.apply(closure)
}
a.apply(localFunc)
}
// CHECK-LABEL: sil private @$s28access_enforcement_selection25testRecursiveLocalCapture1iys5Int64Vz_tF6local2L_yyF : $@convention(thin) (@inout_aliasable Int64) -> () {
// CHECK: begin_access [modify] [static] %0 : $*Int64
// CHECK-LABEL: } // end sil function '$s28access_enforcement_selection25testRecursiveLocalCapture1iys5Int64Vz_tF6local2L_yyF'
func testRecursiveLocalCapture(i: inout Int64) {
func local1() {
if i < 1 {
local2()
}
}
func local2() {
i = i + 1
local1()
}
local1()
}
|