Skip to content

Commit 47d5037

Browse files
committed
[Exclusivity] Disable dynamic enforcement in noescape closures.
(cherry picked from commit dd31c40)
1 parent 9740549 commit 47d5037

File tree

5 files changed

+101
-87
lines changed

5 files changed

+101
-87
lines changed

lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,6 @@ void SelectEnforcement::analyzeProjection(ProjectBoxInst *projection) {
156156
if (access->getEnforcement() == SILAccessEnforcement::Unknown)
157157
Accesses.push_back(access);
158158
}
159-
160-
// FIXME: We should distinguish between escapes via arguments to escaping
161-
// closures (which must propagate to to all reachable blocks because they
162-
// could access the address at any later point in the program) and
163-
// non-escaping closures (which only force dynamic enforcement for
164-
// accesses that are in progress at the time of the escape).
165-
if (auto partialApply = dyn_cast<PartialApplyInst>(user)) {
166-
noteEscapingUse(partialApply);
167-
}
168159
}
169160
}
170161

@@ -378,19 +369,15 @@ struct AccessEnforcementSelection : SILFunctionTransform {
378369
if (auto arg = dyn_cast<SILFunctionArgument>(address)) {
379370
switch (arg->getArgumentConvention()) {
380371
case SILArgumentConvention::Indirect_Inout:
372+
case SILArgumentConvention::Indirect_InoutAliasable:
381373
// `inout` arguments are checked on the caller side, either statically
382374
// or dynamically if necessary. The @inout does not alias and cannot
383375
// escape within the callee, so static enforcement is always sufficient.
384-
setStaticEnforcement(access);
385-
break;
386-
case SILArgumentConvention::Indirect_InoutAliasable:
387-
// `inout_aliasable` are not enforced on the caller side. Dynamic
388-
// enforcement is required unless we have special knowledge of how this
389-
// closure is used at its call-site.
390376
//
391-
// TODO: optimize closures passed to call sites in which the captured
392-
// variable is not modified by any closure passed to the same call.
393-
setDynamicEnforcement(access);
377+
// FIXME: `inout_aliasable` are not currently enforced on the caller
378+
// side. Consequenctly, using static enforcement for noescape closures
379+
// may fails to diagnose certain violations.
380+
setStaticEnforcement(access);
394381
break;
395382
default:
396383
// @in/@in_guaranteed cannot be mutably accessed, mutably captured, or

test/Interpreter/enforce_exclusive_access.swift

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -101,47 +101,62 @@ ExclusiveAccessTestSuite.test("ModifyFollowedByModify") {
101101
globalX = X() // no-trap
102102
}
103103

104-
ExclusiveAccessTestSuite.test("ClosureCaptureModifyModify")
105-
.skip(.custom(
106-
{ _isFastAssertConfiguration() },
107-
reason: "this trap is not guaranteed to happen in -Ounchecked"))
108-
.crashOutputMatches("modify/modify access conflict detected on address")
109-
.code
110-
{
111-
var x = X()
112-
modifyAndPerform(&x) {
113-
expectCrashLater()
114-
x.i = 12
115-
}
116-
}
117-
118-
ExclusiveAccessTestSuite.test("ClosureCaptureReadModify")
119-
.skip(.custom(
120-
{ _isFastAssertConfiguration() },
121-
reason: "this trap is not guaranteed to happen in -Ounchecked"))
122-
.crashOutputMatches("read/modify access conflict detected on address")
123-
.code
124-
{
125-
var x = X()
126-
modifyAndPerform(&x) {
127-
expectCrashLater()
128-
_blackHole(x.i)
129-
}
130-
}
131-
132-
ExclusiveAccessTestSuite.test("ClosureCaptureModifyRead")
133-
.skip(.custom(
134-
{ _isFastAssertConfiguration() },
135-
reason: "this trap is not guaranteed to happen in -Ounchecked"))
136-
.crashOutputMatches("modify/read access conflict detected on address")
137-
.code
138-
{
139-
var x = X()
140-
readAndPerform(&x) {
141-
expectCrashLater()
142-
x.i = 12
143-
}
144-
}
104+
// FIXME: This should be a static diagnostics.
105+
// Once this radar is fixed, conirm that a it is covered by a static diagnostic
106+
// (-verify) test in exclusivity_static_diagnostics.sil.
107+
// <rdar://problem/32061282> Enforce exclusive access in noescape closures.
108+
//
109+
//ExclusiveAccessTestSuite.test("ClosureCaptureModifyModify")
110+
//.skip(.custom(
111+
// { _isFastAssertConfiguration() },
112+
// reason: "this trap is not guaranteed to happen in -Ounchecked"))
113+
// .crashOutputMatches("modify/modify access conflict detected on address")
114+
// .code
115+
//{
116+
// var x = X()
117+
// modifyAndPerform(&x) {
118+
// expectCrashLater()
119+
// x.i = 12
120+
// }
121+
//}
122+
123+
// FIXME: This should be a static diagnostics.
124+
// Once this radar is fixed, conirm that a it is covered by a static diagnostic
125+
// (-verify) test in exclusivity_static_diagnostics.sil.
126+
// <rdar://problem/32061282> Enforce exclusive access in noescape closures.
127+
//
128+
//ExclusiveAccessTestSuite.test("ClosureCaptureReadModify")
129+
//.skip(.custom(
130+
// { _isFastAssertConfiguration() },
131+
// reason: "this trap is not guaranteed to happen in -Ounchecked"))
132+
// .crashOutputMatches("read/modify access conflict detected on address")
133+
// .code
134+
//{
135+
// var x = X()
136+
// modifyAndPerform(&x) {
137+
// expectCrashLater()
138+
// _blackHole(x.i)
139+
// }
140+
//}
141+
142+
// FIXME: This should be a static diagnostics.
143+
// Once this radar is fixed, conirm that a it is covered by a static diagnostic
144+
// (-verify) test in exclusivity_static_diagnostics.sil.
145+
// <rdar://problem/32061282> Enforce exclusive access in noescape closures.
146+
//
147+
//ExclusiveAccessTestSuite.test("ClosureCaptureModifyRead")
148+
//.skip(.custom(
149+
// { _isFastAssertConfiguration() },
150+
// reason: "this trap is not guaranteed to happen in -Ounchecked"))
151+
// .crashOutputMatches("modify/read access conflict detected on address")
152+
// .code
153+
//{
154+
// var x = X()
155+
// readAndPerform(&x) {
156+
// expectCrashLater()
157+
// x.i = 12
158+
// }
159+
//}
145160

146161
ExclusiveAccessTestSuite.test("ClosureCaptureReadRead") {
147162
var x = X()

test/SILOptimizer/access_enforcement_noescape.swift

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// (Some static/dynamic enforcement selection is done in SILGen, and some is
66
// deferred. That may change over time but we want the outcome to be the same).
77
//
8-
// Each FIXME me line is a case that the current implementation misses.
8+
// Each FIXME line is a case that the current implementation misses.
99
// The model is currently being refined, so this isn't set in stone.
1010
//
1111
// TODO: Ensure that each of these cases is covered by
@@ -73,12 +73,14 @@ func readRead() {
7373

7474
// closure #1 in readRead()
7575
// CHECK-LABEL: sil private @_T027access_enforcement_noescape8readReadyyFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
76+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
7677
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
7778
// FIXME-CHECK: end_access [[ACCESS]]
7879
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape8readReadyyFyycfU_'
7980

8081
// closure #2 in readRead()
8182
// CHECK-LABEL: sil private @_T027access_enforcement_noescape8readReadyyFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
83+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
8284
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
8385
// FIXME-CHECK: end_access [[ACCESS]]
8486
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape8readReadyyFyycfU0_'
@@ -99,12 +101,14 @@ func inoutReadRead(x: inout Int) {
99101

100102
// closure #1 in inoutReadRead(x:)
101103
// CHECK-LABEL: sil private @_T027access_enforcement_noescape09inoutReadE0ySiz1x_tFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
104+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
102105
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
103106
// FIXME-CHECK: end_access [[ACCESS]]
104107
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape09inoutReadE0ySiz1x_tFyycfU_'
105108

106109
// closure #2 in inoutReadRead(x:)
107110
// CHECK-LABEL: sil private @_T027access_enforcement_noescape09inoutReadE0ySiz1x_tFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
111+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
108112
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
109113
// FIXME-CHECK: end_access [[ACCESS]]
110114
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape09inoutReadE0ySiz1x_tFyycfU0_'
@@ -136,6 +140,7 @@ func readBoxRead() {
136140

137141
// closure #2 in readBoxRead()
138142
// CHECK-LABEL: sil private @_T027access_enforcement_noescape11readBoxReadyyFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
143+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
139144
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
140145
// FIXME-CHECK: end_access [[ACCESS]]
141146
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape11readBoxReadyyFyycfU0_'
@@ -165,12 +170,14 @@ func readWrite() {
165170

166171
// closure #1 in readWrite()
167172
// CHECK-LABEL: sil private @_T027access_enforcement_noescape9readWriteyyFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
173+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
168174
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
169175
// FIXME-CHECK: end_access [[ACCESS]]
170176
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape9readWriteyyFyycfU_'
171177

172178
// closure #2 in readWrite()
173179
// CHECK-LABEL: sil private @_T027access_enforcement_noescape9readWriteyyFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
180+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
174181
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
175182
// FIXME-CHECK: end_access [[ACCESS]]
176183
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape9readWriteyyFyycfU0_'
@@ -193,12 +200,14 @@ func inoutReadWrite(x: inout Int) {
193200

194201
// closure #1 in inoutReadWrite(x:)
195202
// CHECK-LABEL: sil private @_T027access_enforcement_noescape14inoutReadWriteySiz1x_tFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
203+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
196204
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
197205
// FIXME-CHECK: end_access [[ACCESS]]
198206
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape14inoutReadWriteySiz1x_tFyycfU_'
199207

200208
// closure #2 in inoutReadWrite(x:)
201209
// CHECK-LABEL: sil private @_T027access_enforcement_noescape14inoutReadWriteySiz1x_tFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
210+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
202211
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
203212
// FIXME-CHECK: end_access [[ACCESS]]
204213
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape14inoutReadWriteySiz1x_tFyycfU0_'
@@ -236,6 +245,7 @@ func readBoxWrite() {
236245

237246
// closure #2 in readBoxWrite()
238247
// CHECK-LABEL: sil private @_T027access_enforcement_noescape12readBoxWriteyyFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
248+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
239249
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
240250
// FIXME-CHECK: end_access [[ACCESS]]
241251
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape12readBoxWriteyyFyycfU0_'
@@ -275,6 +285,7 @@ func readWriteBox() {
275285

276286
// closure #2 in readWriteBox()
277287
// CHECK-LABEL: sil private @_T027access_enforcement_noescape12readWriteBoxyyFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
288+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
278289
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
279290
// FIXME-CHECK: end_access [[ACCESS]]
280291
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape12readWriteBoxyyFyycfU0_'
@@ -305,7 +316,8 @@ func readWriteInout() {
305316

306317
// closure #1 in readWriteInout()
307318
// CHECK-LABEL: sil private @_T027access_enforcement_noescape14readWriteInoutyyFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
308-
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
319+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
320+
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
309321
// FIXME-CHECK: end_access [[ACCESS]]
310322
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape14readWriteInoutyyFyycfU_'
311323

@@ -328,7 +340,8 @@ func inoutReadWriteInout(x: inout Int) {
328340

329341
// closure #1 in inoutReadWriteInout(x:)
330342
// CHECK-LABEL: sil private @_T027access_enforcement_noescape19inoutReadWriteInoutySiz1x_tFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
331-
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
343+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [read] [dynamic]
344+
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Int
332345
// FIXME-CHECK: end_access [[ACCESS]]
333346
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape19inoutReadWriteInoutySiz1x_tFyycfU_'
334347

@@ -381,12 +394,14 @@ func writeWrite() {
381394

382395
// closure #1 in writeWrite()
383396
// CHECK-LABEL: sil private @_T027access_enforcement_noescape10writeWriteyyFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
397+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
384398
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
385399
// FIXME-CHECK: end_access [[ACCESS]]
386400
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape10writeWriteyyFyycfU_'
387401

388402
// closure #2 in writeWrite()
389403
// CHECK-LABEL: sil private @_T027access_enforcement_noescape10writeWriteyyFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
404+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
390405
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
391406
// FIXME-CHECK: end_access [[ACCESS]]
392407
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape10writeWriteyyFyycfU0_'
@@ -410,12 +425,14 @@ func inoutWriteWrite(x: inout Int) {
410425

411426
// closure #1 in inoutWriteWrite(x:)
412427
// CHECK-LABEL: sil private @_T027access_enforcement_noescape010inoutWriteE0ySiz1x_tFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
428+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
413429
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
414430
// FIXME-CHECK: end_access [[ACCESS]]
415431
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape010inoutWriteE0ySiz1x_tFyycfU_'
416432

417433
// closure #2 in inoutWriteWrite(x:)
418434
// CHECK-LABEL: sil private @_T027access_enforcement_noescape010inoutWriteE0ySiz1x_tFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
435+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
419436
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
420437
// FIXME-CHECK: end_access [[ACCESS]]
421438
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape010inoutWriteE0ySiz1x_tFyycfU0_'
@@ -450,6 +467,7 @@ func writeWriteBox() {
450467

451468
// closure #2 in writeWriteBox()
452469
// CHECK-LABEL: sil private @_T027access_enforcement_noescape13writeWriteBoxyyFyycfU0_ : $@convention(thin) (@inout_aliasable Int) -> () {
470+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
453471
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
454472
// FIXME-CHECK: end_access [[ACCESS]]
455473
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape13writeWriteBoxyyFyycfU0_'
@@ -479,6 +497,7 @@ func writeWriteInout() {
479497

480498
// closure #1 in writeWriteInout()
481499
// CHECK-LABEL: sil private @_T027access_enforcement_noescape15writeWriteInoutyyFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
500+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
482501
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
483502
// FIXME-CHECK: end_access [[ACCESS]]
484503
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape15writeWriteInoutyyFyycfU_'
@@ -502,6 +521,7 @@ func inoutWriteWriteInout(x: inout Int) {
502521

503522
// closure #1 in inoutWriteWriteInout(x:)
504523
// CHECK-LABEL: sil private @_T027access_enforcement_noescape010inoutWriteE5InoutySiz1x_tFyycfU_ : $@convention(thin) (@inout_aliasable Int) -> () {
524+
// CHECK-NOT: [[ACCESS:%.*]] = begin_access [modify] [dynamic]
505525
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] %0 : $*Int
506526
// FIXME-CHECK: end_access [[ACCESS]]
507527
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape010inoutWriteE5InoutySiz1x_tFyycfU_'
@@ -525,8 +545,8 @@ func writeBoxWriteInout() {
525545
// closure #1 in writeBoxWriteInout()
526546
// CHECK-LABEL: sil private @_T027access_enforcement_noescape18writeBoxWriteInoutyyFyycfU_ : $@convention(thin) (@owned { var Int }) -> () {
527547
// CHECK: [[ADDR:%.*]] = project_box %0 : ${ var Int }, 0
528-
// FIXME-CHECK: [[ACCESS:%.*]] = begin_access [modify] [dynamic] [[ADDR]] : $*Int
529-
// FIXME-CHECK: end_access [[ACCESS]]
548+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [dynamic] [[ADDR]] : $*Int
549+
// CHECK: end_access [[ACCESS]]
530550
// CHECK-LABEL: } // end sil function '_T027access_enforcement_noescape18writeBoxWriteInoutyyFyycfU_'
531551

532552
// Error: Cannot capture inout

test/SILOptimizer/access_enforcement_selection.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ sil hidden @markFuncEscape : $@convention(thin) () -> () {
6868
sil @takesInoutAndClosure : $@convention(thin) (@inout Builtin.Int64, @owned @callee_owned () -> ()) -> ()
6969
sil @closureCapturingByStorageAddress : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
7070

71-
// Test dynamic enforcement of box addresses that escape via closure
71+
// Test static enforcement of box addresses that escape via closure
7272
// partial_applys.
7373
// application.
7474
// CHECK-LABEL: sil hidden @escapeAsArgumentToPartialApply : $@convention(thin) () -> () {
@@ -78,7 +78,7 @@ sil @closureCapturingByStorageAddress : $@convention(thin) (@inout_aliasable Bui
7878
// CHECK: [[FUNC:%.*]] = function_ref @takesInoutAndClosure : $@convention(thin) (@inout Builtin.Int64, @owned @callee_owned () -> ()) -> ()
7979
// CHECK: [[CLOSURE:%.*]] = function_ref @closureCapturingByStorageAddress : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
8080
// CHECK: [[PA:%.*]] = partial_apply [[CLOSURE]]([[ADR]]) : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
81-
// CHECK: [[MODIFY:%.*]] = begin_access [modify] [dynamic] [[ADR]] : $*Builtin.Int64
81+
// CHECK: [[MODIFY:%.*]] = begin_access [modify] [static] [[ADR]] : $*Builtin.Int64
8282
// CHECK: %{{.*}} = apply [[FUNC]]([[MODIFY]], [[PA]]) : $@convention(thin) (@inout Builtin.Int64, @owned @callee_owned () -> ()) -> ()
8383
// CHECK: end_access [[MODIFY]] : $*Builtin.Int64
8484
// CHECK: destroy_value [[BOX]] : ${ var Builtin.Int64 }

0 commit comments

Comments
 (0)