Skip to content

Commit 9b2fa08

Browse files
committed
SILGen: Use [unsafe] access markers for move-only storage when exclusivity enforcement is disabled.
The move-only checker relies on access markers to understand access scopes, so eliding them entirely leads to miscompiles. We can emit `begin_access [unsafe]` to semantically delimit exclusivity scopes while still doing no runtime checking. Fixes rdar://147546262.
1 parent fd35333 commit 9b2fa08

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

lib/SILGen/SILGenLValue.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,9 @@ static LValueTypeData getPhysicalStorageTypeData(TypeExpansionContext context,
245245
}
246246

247247
static bool shouldUseUnsafeEnforcement(VarDecl *var) {
248-
if (var->isDebuggerVar())
248+
if (var->isDebuggerVar()) {
249249
return true;
250+
}
250251

251252
return false;
252253
}
@@ -277,6 +278,22 @@ SILGenFunction::getDynamicEnforcement(VarDecl *var) {
277278
} else if (hasExclusivityAttr(var, ExclusivityAttr::Checked)) {
278279
return SILAccessEnforcement::Dynamic;
279280
}
281+
282+
// Access markers are especially load-bearing for the move-only checker,
283+
// so we also emit `begin_access` markers for cases where storage
284+
// is move-only.
285+
// TODO: It seems useful to do this for all unchecked declarations, since
286+
// access scopes are useful semantic information.
287+
if (var->getTypeInContext()->isNoncopyable()) {
288+
return SILAccessEnforcement::Unsafe;
289+
}
290+
if (auto param = dyn_cast<ParamDecl>(var)) {
291+
if (param->getSpecifier() == ParamSpecifier::Borrowing
292+
|| param->getSpecifier() == ParamSpecifier::Consuming) {
293+
return SILAccessEnforcement::Unsafe;
294+
}
295+
}
296+
280297
return std::nullopt;
281298
}
282299

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-frontend -enforce-exclusivity=unchecked -emit-sil %s | %FileCheck %s
2+
3+
struct Foo: ~Copyable {
4+
var x: Any
5+
}
6+
7+
final class Bar {
8+
init() { fatalError() }
9+
10+
// Ensure that noncopyable bindings still get [unsafe] exclusivity markers
11+
// and get checked properly by the move-only checker.
12+
13+
// Bar.foo.setter:
14+
// CHECK-LABEL: sil {{.*}} @$s{{.*}}3BarC3foo{{.*}}vs
15+
// CHECK: [[FIELD:%.*]] = ref_element_addr {{.*}}, #Bar.foo
16+
// CHECK: [[FIELD_ACCESS:%.*]] = begin_access [modify] [unsafe] [[FIELD]]
17+
// CHECK-NEXT: destroy_addr [[FIELD_ACCESS]]
18+
// CHECK-NEXT: copy_addr [take] {{.*}} to [init] [[FIELD_ACCESS]]
19+
// CHECK-NEXT: end_access [[FIELD_ACCESS]]
20+
// CHECK-NOT: [[FIELD]]
21+
// CHECK-NOT: [[FIELD_ACCESS]]
22+
// CHECK: } // end sil {{.*}} '$s{{.*}}3BarC3foo{{.*}}vs'
23+
24+
var foo: Foo
25+
}

0 commit comments

Comments
 (0)