-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathaccess_enforcement_selection.swift
140 lines (122 loc) · 5.65 KB
/
access_enforcement_selection.swift
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 -Xllvm -sil-print-types -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()
}