Skip to content

Commit f03df6d

Browse files
committed
Check global closures
This patch adds checking for usage of completion handler functions with async alternatives in closures at either the global scope (for -parse-as-library) or as top-level-decls. The top-level and global scope are typechecked in the order that they are declared. This means that if the closure calls a completion handler function that is declared later, the attribute won't have been typechecked yet and thus won't have a resolved async function. This patch updates the warning emitter to typecheck and resolve the attribute if it needs to. If the thing doesn't typecheck, we don't need emit the warning at all because the attribute is bad and we tell them to fix it.
1 parent dfee59e commit f03df6d

File tree

4 files changed

+51
-10
lines changed

4 files changed

+51
-10
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3096,12 +3096,29 @@ class CompletionHandlerUsageChecker final : public ASTWalker {
30963096
CompletionHandlerUsageChecker(ASTContext &ctx) : ctx(ctx) {}
30973097

30983098
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
3099+
if (ClosureExpr *closure = dyn_cast<ClosureExpr>(expr)) {
3100+
if (closure->isBodyAsync())
3101+
return {true, closure};
3102+
else
3103+
return {true, nullptr};
3104+
}
3105+
30993106
if (ApplyExpr *call = dyn_cast<ApplyExpr>(expr)) {
31003107
if (DeclRefExpr *fnDeclExpr = dyn_cast<DeclRefExpr>(call->getFn())) {
31013108
ValueDecl *fnDecl = fnDeclExpr->getDecl();
31023109
CompletionHandlerAsyncAttr *asyncAltAttr =
31033110
fnDecl->getAttrs().getAttribute<CompletionHandlerAsyncAttr>();
3104-
if (asyncAltAttr && asyncAltAttr->AsyncFunctionDecl) {
3111+
if (asyncAltAttr) {
3112+
// Ensure that the attribute typechecks,
3113+
// this also resolves the async function decl.
3114+
if (!evaluateOrDefault(
3115+
ctx.evaluator,
3116+
TypeCheckCompletionHandlerAsyncAttrRequest{
3117+
cast<AbstractFunctionDecl>(fnDecl), asyncAltAttr},
3118+
false)) {
3119+
return {true,
3120+
nullptr}; // This didn't typecheck, don't traverse down
3121+
}
31053122
ctx.Diags.diagnose(call->getLoc(), diag::warn_use_async_alternative);
31063123
ctx.Diags.diagnose(asyncAltAttr->AsyncFunctionDecl->getLoc(),
31073124
diag::decl_declared_here,
@@ -3122,3 +3139,8 @@ void swift::checkFunctionAsyncUsage(AbstractFunctionDecl *decl) {
31223139
if (body)
31233140
body->walk(checker);
31243141
}
3142+
3143+
void swift::checkPatternBindingDeclAsyncUsage(PatternBindingDecl *decl) {
3144+
CompletionHandlerUsageChecker checker(decl->getASTContext());
3145+
decl->walk(checker);
3146+
}

lib/Sema/TypeCheckConcurrency.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ bool checkSendableConformance(
245245
/// a '@completionHandlerAsync' attribute, we emit a diagnostic suggesting the
246246
/// async call.
247247
void checkFunctionAsyncUsage(AbstractFunctionDecl *decl);
248-
248+
void checkPatternBindingDeclAsyncUsage(PatternBindingDecl *decl);
249249
} // end namespace swift
250250

251251
#endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
//===----------------------------------------------------------------------===//
1818

1919
#include "MiscDiagnostics.h"
20+
#include "TypeCheckConcurrency.h"
2021
#include "TypeChecker.h"
2122
#include "swift/AST/ASTVisitor.h"
2223
#include "swift/AST/ASTWalker.h"
@@ -35,14 +36,14 @@
3536
#include "llvm/ADT/SmallVector.h"
3637
#include "llvm/ADT/StringExtras.h"
3738
#include "llvm/Support/Allocator.h"
38-
#include "llvm/Support/raw_ostream.h"
39-
#include "llvm/Support/SaveAndRestore.h"
4039
#include "llvm/Support/Format.h"
40+
#include "llvm/Support/SaveAndRestore.h"
41+
#include "llvm/Support/raw_ostream.h"
4142
#include <iterator>
4243
#include <map>
4344
#include <memory>
44-
#include <utility>
4545
#include <tuple>
46+
#include <utility>
4647

4748
using namespace swift;
4849
using namespace constraints;
@@ -538,12 +539,14 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
538539
}
539540
}
540541
}
541-
542+
542543
if (hadError)
543544
PBD->setInvalid();
544545

545546
PBD->setInitializerChecked(patternNumber);
546-
547+
548+
checkPatternBindingDeclAsyncUsage(PBD);
549+
547550
return hadError;
548551
}
549552

test/attr/attr_completionhandlerasync.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// REQUIRES: concurrency
22

33
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency
4+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -parse-as-library
45

56
// ===================
67
// Parsing
78
// ===================
89

9-
// expected-note@+1{{'asyncFunc' declared here}}
10+
// expected-note@+1 3 {{'asyncFunc' declared here}}
1011
func asyncFunc(_ text: String) async -> Int { }
1112

1213
@completionHandlerAsync("asyncFunc(_:)", completionHandlerIndex: 1)
@@ -145,11 +146,21 @@ func typecheckFunc9(handler: @escaping () -> Void) {}
145146

146147
// Suggest using async alternative function in async context
147148

148-
149149
func asyncContext() async {
150-
// expected-warning@+1:3{{consider using asynchronous alternative}}
150+
// expected-warning@+1:3{{consider using asynchronous alternative function}}
151151
goodFunc1(value: "Hello") { _ in }
152152

153+
let _ = {
154+
// No warning or error since we're in a sync context here
155+
goodFunc1(value: "Hello") { _ in }
156+
}
157+
158+
let _ = { () async -> () in
159+
let _ = await asyncFunc("Hello World")
160+
// expected-warning@+1{{consider using asynchronous alternative function}}
161+
goodFunc1(value: "Hello") { _ in }
162+
}
163+
153164
let _ = await asyncFunc("World")
154165

155166
// This doesn't get the warning because the completionHandlerAsync failed to
@@ -160,3 +171,8 @@ func asyncContext() async {
160171
func syncContext() {
161172
goodFunc1(value: "Hello") { _ in }
162173
}
174+
175+
let asyncGlobalClosure = { () async -> () in
176+
// expected-warning@+1:3{{consider using asynchronous alternative function}}
177+
goodFunc1(value: "neat") { _ in }
178+
}

0 commit comments

Comments
 (0)