Skip to content

Commit a56695b

Browse files
committed
Sema: Extend adoption mode for AsyncCallerExecution to storage declarations
1 parent 8ad2e02 commit a56695b

File tree

2 files changed

+83
-15
lines changed

2 files changed

+83
-15
lines changed

lib/Sema/AsyncCallerExecutionMigration.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,29 @@ void AsyncCallerExecutionMigrationTarget::diagnose() const {
6262
ASSERT(node);
6363
ASSERT(ctx.LangOpts.getFeatureState(feature).isEnabledForAdoption());
6464

65-
AbstractFunctionDecl *functionDecl = nullptr;
65+
ValueDecl *decl = nullptr;
6666
ClosureExpr *closure = nullptr;
6767
FunctionTypeRepr *functionRepr = nullptr;
6868

69-
if (auto *decl = node.dyn_cast<ValueDecl *>()) {
69+
if ((decl = node.dyn_cast<ValueDecl *>())) {
7070
// Diagnose only explicit nodes.
7171
if (decl->isImplicit()) {
7272
return;
7373
}
7474

75-
// Diagnose only functions.
76-
functionDecl = dyn_cast<AbstractFunctionDecl>(decl);
77-
if (!functionDecl) {
75+
// If the attribute cannot appear on this kind of declaration, we can't
76+
// diagnose it.
77+
if (!DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::Execution,
78+
decl)) {
7879
return;
7980
}
81+
82+
// For storage, make sure we have an explicit getter to diagnose.
83+
if (auto *storageDecl = dyn_cast<AbstractStorageDecl>(decl)) {
84+
if (!storageDecl->getParsedAccessor(AccessorKind::Get)) {
85+
return;
86+
}
87+
}
8088
} else if (auto *anyClosure = node.dyn_cast<AbstractClosureExpr *>()) {
8189
// Diagnose only explicit nodes.
8290
if (anyClosure->isImplicit()) {
@@ -107,8 +115,8 @@ void AsyncCallerExecutionMigrationTarget::diagnose() const {
107115
// If the intended behavior is specified explicitly, don't diagnose.
108116
{
109117
const DeclAttributes *attrs = nullptr;
110-
if (functionDecl) {
111-
attrs = &functionDecl->getAttrs();
118+
if (decl) {
119+
attrs = &decl->getAttrs();
112120
} else if (closure) {
113121
attrs = &closure->getAttrs();
114122
}
@@ -121,8 +129,8 @@ void AsyncCallerExecutionMigrationTarget::diagnose() const {
121129
// The execution behavior changes only for async functions.
122130
{
123131
bool isAsync = false;
124-
if (functionDecl) {
125-
isAsync = functionDecl->hasAsync();
132+
if (decl) {
133+
isAsync = decl->isAsync();
126134
} else if (closure) {
127135
isAsync = closure->isBodyAsync();
128136
} else {
@@ -137,14 +145,26 @@ void AsyncCallerExecutionMigrationTarget::diagnose() const {
137145
const ExecutionAttr attr(ExecutionKind::Concurrent, /*implicit=*/true);
138146

139147
const auto featureName = getFeatureName(feature);
140-
if (functionDecl) {
148+
if (decl) {
149+
// Diagnose the function, but slap the attribute on the storage declaration
150+
// instead if the function is an accessor.
151+
auto *functionDecl = dyn_cast<AbstractFunctionDecl>(decl);
152+
if (!functionDecl) {
153+
auto *storageDecl = cast<AbstractStorageDecl>(decl);
154+
155+
// This whole logic assumes that an 'async' storage declaration only has
156+
// a getter. Yell for an update if this ever changes.
157+
ASSERT(!storageDecl->getAccessor(AccessorKind::Set));
158+
159+
functionDecl = storageDecl->getParsedAccessor(AccessorKind::Get);
160+
}
161+
141162
ctx.Diags
142163
.diagnose(functionDecl->getLoc(),
143164
diag::attr_execution_nonisolated_behavior_will_change_decl,
144165
featureName, functionDecl, &attr)
145166
.fixItInsertAttribute(
146-
functionDecl->getAttributeInsertionLoc(/*forModifier=*/false),
147-
&attr);
167+
decl->getAttributeInsertionLoc(/*forModifier=*/false), &attr);
148168
} else if (closure) {
149169
ctx.Diags
150170
.diagnose(closure->getLoc(),

test/Concurrency/attr_execution/adoption_mode.swift

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution:adoption
1+
// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -swift-version 5 -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution:adoption
2+
// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution:adoption
23

34
// REQUIRES: swift_feature_ExecutionAttribute
45
// REQUIRES: swift_feature_AsyncCallerExecution
@@ -19,8 +20,8 @@ do {
1920
isolation: isolated (any Actor)? = #isolation
2021
) async {}
2122

22-
// expected-warning@+1:8 {{feature 'AsyncCallerExecution' will cause nonisolated async local function 'asyncF' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{3-3=@execution(concurrent) }}{{none}}
23-
func asyncF() async {}
23+
// expected-warning@+1:20 {{feature 'AsyncCallerExecution' will cause nonisolated async local function 'asyncF' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{3-3=@execution(concurrent) }}{{none}}
24+
nonisolated func asyncF() async {}
2425

2526
struct S {
2627
init(sync: ()) {}
@@ -36,6 +37,53 @@ do {
3637
nonisolated
3738
public func asyncF() async {}
3839
}
40+
41+
protocol P {
42+
// FIXME: Not diagnosed
43+
func asyncF() async
44+
}
45+
}
46+
47+
// MARK: Storage
48+
do {
49+
struct S {
50+
var storedVar: Int
51+
let storedLet: Int
52+
53+
var syncS: Int { get {} set {} }
54+
subscript(syncS _: Int) -> Int { get {} }
55+
56+
@execution(concurrent) var executionAsyncS: Int { get async {} }
57+
@execution(concurrent) subscript(executionAsyncS _: Int) -> Int { get async {} }
58+
59+
@MainActor var mainActorAsyncS: Int { get async {} }
60+
@MainActor subscript(mainActorAsyncS _: Int) -> Int { get async {} }
61+
62+
// expected-warning@+2:7 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for property 'asyncS' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{-1:5-5=@execution(concurrent) }}{{none}}
63+
var asyncS: Int {
64+
get async {}
65+
}
66+
// expected-warning@+2:7 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for subscript 'subscript' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{-1:5-5=@execution(concurrent) }}{{none}}
67+
subscript(asyncS _: Int) -> Int {
68+
get async throws {}
69+
}
70+
}
71+
72+
protocol P {
73+
var syncS: Int { get }
74+
subscript(syncS _: Int) -> Int { get }
75+
76+
@execution(concurrent) var executionAsyncS: Int { get async }
77+
@execution(concurrent) subscript(executionAsyncS _: Int) -> Int { get async }
78+
79+
@MainActor var mainActorAsyncS: Int { get async }
80+
@MainActor subscript(mainActorAsyncS _: Int) -> Int { get async }
81+
82+
// expected-warning@+1:23 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for property 'asyncS' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{5-5=@execution(concurrent) }}{{none}}
83+
var asyncS: Int { get async }
84+
// FIXME: Not diagnosed
85+
subscript(asyncS _: Int) -> Int { get async }
86+
}
3987
}
4088

4189
// MARK: Parameters

0 commit comments

Comments
 (0)