Skip to content

Commit d27a021

Browse files
Merge pull request #81633 from AnthonyLatsis/acer-campestri-2
Sema: Fix an issue with `NonisolatedNonsendingByDefault` migration fo…
2 parents a6901d8 + 2d7e040 commit d27a021

File tree

2 files changed

+170
-60
lines changed

2 files changed

+170
-60
lines changed

lib/Sema/NonisolatedNonsendingByDefaultMigration.cpp

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/AST/Decl.h"
2222
#include "swift/AST/DiagnosticsSema.h"
2323
#include "swift/AST/Expr.h"
24+
#include "swift/AST/ParameterList.h"
2425
#include "swift/AST/TypeRepr.h"
2526
#include "swift/Basic/Assertions.h"
2627
#include "swift/Basic/Feature.h"
@@ -171,19 +172,81 @@ void NonisolatedNonsendingByDefaultMigrationTarget::diagnose() const {
171172
featureName, functionDecl)
172173
.fixItInsertAttribute(
173174
decl->getAttributeInsertionLoc(/*forModifier=*/false), &attr);
174-
} else if (closure) {
175-
ctx.Diags
176-
.diagnose(closure->getLoc(),
177-
diag::attr_execution_nonisolated_behavior_will_change_closure,
178-
featureName)
179-
.fixItAddAttribute(&attr, closure);
180-
} else {
175+
} else if (functionRepr) {
181176
ctx.Diags
182177
.diagnose(
183178
functionRepr->getStartLoc(),
184179
diag::attr_execution_nonisolated_behavior_will_change_typerepr,
185180
featureName)
186181
.fixItInsertAttribute(functionRepr->getStartLoc(), &attr);
182+
} else {
183+
auto diag = ctx.Diags.diagnose(
184+
closure->getLoc(),
185+
diag::attr_execution_nonisolated_behavior_will_change_closure,
186+
featureName);
187+
diag.fixItAddAttribute(&attr, closure);
188+
189+
// The following cases fail to compile together with `@concurrent` in
190+
// Swift 5 or Swift 6 mode due to parser and type checker behaviors:
191+
// 1. - Explicit parameter list
192+
// - Explicit result type
193+
// - No explicit `async` effect
194+
// 2. - Explicit parenthesized parameter list
195+
// - No capture list
196+
// - No explicit result type
197+
// - No explicit effect
198+
//
199+
// Work around these issues by adding inferred effects together with the
200+
// attribute.
201+
202+
// If there's an explicit `async` effect, we're good.
203+
if (closure->getAsyncLoc().isValid()) {
204+
return;
205+
}
206+
207+
auto *params = closure->getParameters();
208+
// FIXME: We need a better way to distinguish an implicit parameter list.
209+
bool hasExplicitParenthesizedParamList =
210+
params->getLParenLoc().isValid() &&
211+
params->getLParenLoc() != closure->getStartLoc();
212+
213+
// If the parameter list is implicit, we're good.
214+
if (!hasExplicitParenthesizedParamList) {
215+
if (params->size() == 0) {
216+
return;
217+
} else if ((*params)[0]->isImplicit()) {
218+
return;
219+
}
220+
}
221+
222+
// At this point we must proceed if there is an explicit result type.
223+
// If there is both no explicit result type and the second case does not
224+
// apply for any other reason, we're good.
225+
if (!closure->hasExplicitResultType() &&
226+
(!hasExplicitParenthesizedParamList ||
227+
closure->getBracketRange().isValid() ||
228+
closure->getThrowsLoc().isValid())) {
229+
return;
230+
}
231+
232+
// Compute the insertion location.
233+
SourceLoc effectsInsertionLoc = closure->getThrowsLoc();
234+
if (effectsInsertionLoc.isInvalid() && closure->hasExplicitResultType()) {
235+
effectsInsertionLoc = closure->getArrowLoc();
236+
}
237+
238+
if (effectsInsertionLoc.isInvalid()) {
239+
effectsInsertionLoc = closure->getInLoc();
240+
}
241+
242+
ASSERT(effectsInsertionLoc);
243+
244+
std::string fixIt = "async ";
245+
if (closure->getThrowsLoc().isInvalid() && closure->isBodyThrowing()) {
246+
fixIt += "throws ";
247+
}
248+
249+
diag.fixItInsert(effectsInsertionLoc, fixIt);
187250
}
188251
}
189252

test/Concurrency/attr_execution/adoption_mode.swift

Lines changed: 100 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,15 @@ do {
240240

241241
// MARK: Closures
242242
do {
243+
@concurrent
244+
func asyncOnly(_: Int, _: Int) async {}
245+
@concurrent
246+
@Sendable
247+
func asyncThrows(_: Int, _: Int) async throws {}
248+
249+
nonisolated
250+
func test(_: @concurrent (Int, Int) async throws -> Void) {}
251+
243252
nonisolated
244253
func nonisolatedF() {
245254
let x = 0
@@ -249,59 +258,97 @@ do {
249258
let _ = { @concurrent () async -> Void in }
250259
let _ = { @MainActor () async -> Void in }
251260

252-
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{15-15=@concurrent }}{{none}}
253-
let _ = { () async -> Void in }
254-
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{15-15=@concurrent }}{{none}}
255-
let _ = { [x] () async -> Void in _ = x }
256-
257-
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{14-14= @concurrent in }}{{none}}
258-
let _ = {await globalAsyncF()}
259-
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{14-14=@concurrent }}{{none}}
260-
let _ = {[x] in await globalAsyncF(); _ = x}
261-
262-
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{14-14= @concurrent in }}{{none}}
263-
let _ = {
264-
await globalAsyncF()
265-
}
266-
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{14-14= @concurrent in }}{{none}}
267-
let _ = {
268-
await globalAsyncF()
269-
func takesInts(_: Int...) {}
270-
takesInts($0, $1, $2)
271-
}
272-
273-
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{25-25=@concurrent }}{{none}}
274-
let _ = { @Sendable in
275-
await globalAsyncF()
276-
}
277-
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{25-25=@concurrent }}{{none}}
278-
let _ = { @Sendable [x] in
279-
_ = x
280-
await globalAsyncF()
281-
}
282-
283-
// expected-warning@+2:18 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use '@concurrent' to preserve behavior}}{{18-18=@concurrent }}{{none}}
284-
// expected-warning@+1:45 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{47-47=@concurrent }}{{none}}
285-
var closure: (Int, Int) async -> Void = { a, b in
286-
await globalAsyncF()
287-
}
288-
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{17-17=@concurrent }}{{none}}
289-
closure = { [x] a, b in _ = x }
290-
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{+1:7-7=@concurrent }}{{none}}
291-
closure = {
292-
a, b async in ()
293-
}
294-
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{+1:7-7=@concurrent }}{{none}}
295-
closure = {
296-
[x] a, b async in _ = x
297-
}
298-
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{17-17=@concurrent }}{{none}}
299-
closure = { (a, b) in () }
300-
301-
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{17-17=@concurrent }}{{none}}
302-
closure = { [x] (a, b) in _ = x }
303-
304-
let _ = closure
261+
// Make sure all of these case compile with the fix-its applied.
262+
//
263+
// Cases where we add inferred effects to ensure it compiles with '@concurrent'.
264+
265+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{34-34=async throws }}{{none}}
266+
test { (a: consuming Int, b) in try await asyncThrows(a, b) }
267+
test { @concurrent (a: consuming Int, b) async throws in try await asyncThrows(a, b) }
268+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{29-29=async throws }}{{none}}
269+
test { (a: Int, b: Int) in try await asyncThrows(a, b) }
270+
test { @concurrent (a: Int, b: Int) async throws in try await asyncThrows(a, b) }
271+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{22-22=@concurrent }}{{39-39=async throws }}{{none}}
272+
test { @Sendable (a: Int, b: Int) in try await asyncThrows(a, b) }
273+
test { @Sendable @concurrent (a: Int, b: Int) async throws in try await asyncThrows(a, b) }
274+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{24-24=async throws }}{{none}}
275+
test { (a: Int, b) in try await asyncThrows(a, b) }
276+
test { @concurrent (a: Int, b) async throws in try await asyncThrows(a, b) }
277+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{19-19=async throws }}{{none}}
278+
test { (a, b) in try await asyncThrows(a, b) }
279+
test { @concurrent (a, b) async throws in try await asyncThrows(a, b) }
280+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{19-19=async }}{{none}}
281+
test { (a, b) throws -> Void in try await asyncThrows(a, b) }
282+
test { @concurrent (a, b) async throws -> Void in try await asyncThrows(a, b) }
283+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{23-23=async }}{{none}}
284+
test { [x] (a, b) throws -> Void in try await asyncThrows(a, b + x) }
285+
test { @concurrent [x] (a, b) async throws -> Void in try await asyncThrows(a, b + x) }
286+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{19-19=async throws }}{{none}}
287+
test { (a, b) -> Void in try await asyncThrows(a, b) }
288+
test { @concurrent (a, b) async throws -> Void in try await asyncThrows(a, b) }
289+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{23-23=async throws }}{{none}}
290+
test { [x] (a, b) -> Void in try await asyncThrows(a, b + x) }
291+
test { @concurrent [x] (a, b) async throws -> Void in try await asyncThrows(a, b + x) }
292+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{17-17=async throws }}{{none}}
293+
test { a, b -> Void in try await asyncThrows(a, b) }
294+
test { @concurrent a, b async throws -> Void in try await asyncThrows(a, b) }
295+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{17-17=async throws }}{{none}}
296+
test { _, _ -> Void in try await asyncThrows(1, 2) }
297+
test { @concurrent _, _ async throws -> Void in try await asyncThrows(1, 2) }
298+
299+
// Cases that will compile with '@concurrent' as is.
300+
301+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
302+
test { (a: Int, b: Int) throws in try await asyncThrows(a, b) }
303+
test { @concurrent (a: Int, b: Int) throws in try await asyncThrows(a, b) }
304+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
305+
test { (a, b) throws in try await asyncThrows(a, b) }
306+
test { @concurrent (a, b) throws in try await asyncThrows(a, b) }
307+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
308+
test { (_, _) throws in try await asyncThrows(1, 2) }
309+
test { @concurrent (_, _) throws in try await asyncThrows(1, 2) }
310+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
311+
test { (a, b) async throws in try await asyncThrows(a, b) }
312+
test { @concurrent (a, b) async throws in try await asyncThrows(a, b) }
313+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
314+
test { (a, b) async in await asyncOnly(a, b) }
315+
test { @concurrent (a, b) async in await asyncOnly(a, b) }
316+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
317+
test { a, b async -> Void in await asyncOnly(a, b) }
318+
test { @concurrent a, b async -> Void in await asyncOnly(a, b) }
319+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
320+
test { a, b in try await asyncThrows(a, b) }
321+
test { @concurrent a, b in try await asyncThrows(a, b) }
322+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
323+
test { a, b throws in try await asyncThrows(a, b) }
324+
test { @concurrent a, b throws in try await asyncThrows(a, b) }
325+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
326+
test { a, b async in await asyncOnly(a, b) }
327+
test { @concurrent a, b async in await asyncOnly(a, b) }
328+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{11-11= @concurrent in }}{{none}}
329+
test { try await asyncThrows($0, $1) }
330+
test { @concurrent in try await asyncThrows($0, $1) }
331+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
332+
test { [x] in try await asyncThrows($0, $1 + x) }
333+
test { @concurrent [x] in try await asyncThrows($0, $1 + x) }
334+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{22-22=@concurrent }}{{none}}
335+
test { @Sendable [x] in try await asyncThrows($0, $1 + x) }
336+
test { @Sendable @concurrent [x] in try await asyncThrows($0, $1 + x) }
337+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
338+
test { [x] a, b in try await asyncThrows(a, b + x) }
339+
test { @concurrent [x] a, b in try await asyncThrows(a, b + x) }
340+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
341+
test { [x] a, b async in await asyncOnly(a, b + x) }
342+
test { @concurrent [x] a, b async in await asyncOnly(a, b + x) }
343+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
344+
test { [x] a, b throws in try await asyncThrows(a, b + x) }
345+
test { @concurrent [x] a, b throws in try await asyncThrows(a, b + x) }
346+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
347+
test { [x] (a, b) in try await asyncThrows(a, b + x) }
348+
test { @concurrent [x] (a, b) in try await asyncThrows(a, b + x) }
349+
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use '@concurrent' to preserve behavior}}{{12-12=@concurrent }}{{none}}
350+
test { [x] (a: Int, _: Int) in try await asyncThrows(a, x) }
351+
test { @concurrent [x] (a: Int, _: Int) in try await asyncThrows(a, x) }
305352
}
306353

307354
@MainActor

0 commit comments

Comments
 (0)