Skip to content

Commit fd30319

Browse files
authored
Merge pull request #81549 from xedin/rdar-151421590
[CSGen] Prevent `@concurrent` on closures from skipping `throws` infe…
2 parents ee9c0cc + cda9866 commit fd30319

File tree

2 files changed

+28
-6
lines changed

2 files changed

+28
-6
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,11 +1414,6 @@ FunctionType::ExtInfo ClosureEffectsRequest::evaluate(
14141414
bool async = expr->getAsyncLoc().isValid();
14151415
bool sendable = expr->getAttrs().hasAttribute<SendableAttr>();
14161416

1417-
// `@concurrent` attribute is only valid on asynchronous function types.
1418-
if (expr->getAttrs().hasAttribute<ConcurrentAttr>()) {
1419-
async = true;
1420-
}
1421-
14221417
if (throws || async) {
14231418
return ASTExtInfoBuilder()
14241419
.withThrows(throws, /*FIXME:*/Type())
@@ -1432,11 +1427,17 @@ FunctionType::ExtInfo ClosureEffectsRequest::evaluate(
14321427
if (!body)
14331428
return ASTExtInfoBuilder().withSendable(sendable).build();
14341429

1430+
// `@concurrent` attribute is only valid on asynchronous function types.
1431+
bool asyncFromAttr = false;
1432+
if (expr->getAttrs().hasAttribute<ConcurrentAttr>()) {
1433+
asyncFromAttr = true;
1434+
}
1435+
14351436
auto throwFinder = FindInnerThrows(expr);
14361437
body->walk(throwFinder);
14371438
return ASTExtInfoBuilder()
14381439
.withThrows(throwFinder.foundThrow(), /*FIXME:*/Type())
1439-
.withAsync(bool(findAsyncNode(expr)))
1440+
.withAsync(asyncFromAttr || bool(findAsyncNode(expr)))
14401441
.withSendable(sendable)
14411442
.build();
14421443
}

test/attr/execution_behavior_attrs.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,24 @@ _ = { @MainActor @concurrent in
137137
_ = { @concurrent () -> Int in
138138
// expected-error@-1 {{@concurrent on non-async closure}}
139139
}
140+
141+
// Make sure that explicit use of `@concurrent` doesn't interfere with inference of `throws` from the body.
142+
do {
143+
func acceptsThrowing(_ body: () async throws -> Void) async {
144+
}
145+
146+
struct Invocation {
147+
func throwingFn() async throws {
148+
}
149+
}
150+
151+
func test(invocation: Invocation) async {
152+
await acceptsThrowing({ @concurrent in try await invocation.throwingFn() }) // Ok
153+
await acceptsThrowing({ @concurrent [invocation] in try await invocation.throwingFn() }) // Ok
154+
155+
await acceptsThrowing({ @concurrent in // Ok
156+
_ = 42
157+
try await invocation.throwingFn()
158+
})
159+
}
160+
}

0 commit comments

Comments
 (0)