Skip to content

Commit 3ade6c5

Browse files
committed
[Sema]: improve noasync diagnostics in defer statement bodies
updates noasync diagnostics in TypeCheckAvailability.cpp to diagnose defer bodies as if they had the same `isAsyncContext()` value as their nearest non-defer parent scope. resolves: #73614 (cherry picked from commit e68c36b)
1 parent db12dff commit 3ade6c5

File tree

2 files changed

+115
-7
lines changed

2 files changed

+115
-7
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,18 @@ static bool shouldAllowReferenceToUnavailableInSwiftDeclaration(
385385
return false;
386386
}
387387

388+
// Utility function to help determine if noasync diagnostics are still
389+
// appropriate even if a `DeclContext` returns `false` from `isAsyncContext()`.
390+
static bool shouldTreatDeclContextAsAsyncForDiagnostics(const DeclContext *DC) {
391+
if (auto *D = DC->getAsDecl())
392+
if (auto *FD = dyn_cast<FuncDecl>(D))
393+
if (FD->isDeferBody())
394+
// If this is a defer body, we should delegate to its parent.
395+
return shouldTreatDeclContextAsAsyncForDiagnostics(DC->getParent());
396+
397+
return DC->isAsyncContext();
398+
}
399+
388400
namespace {
389401

390402
/// A class to walk the AST to build the type refinement context hierarchy.
@@ -3767,17 +3779,20 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
37673779
static bool
37683780
diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
37693781
const Expr *call, const ExportContext &Where) {
3770-
// If we are in a synchronous context, don't check it
3771-
if (!Where.getDeclContext()->isAsyncContext())
3782+
// If we are not in an (effective) async context, don't check it
3783+
if (!shouldTreatDeclContextAsAsyncForDiagnostics(Where.getDeclContext()))
37723784
return false;
37733785

37743786
ASTContext &ctx = Where.getDeclContext()->getASTContext();
37753787

3776-
if (const AbstractFunctionDecl *afd = dyn_cast<AbstractFunctionDecl>(D)) {
3777-
if (const AbstractFunctionDecl *asyncAlt = afd->getAsyncAlternative()) {
3778-
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
3779-
ctx.Diags.diagnose(diagLoc, diag::warn_use_async_alternative);
3780-
asyncAlt->diagnose(diag::decl_declared_here, asyncAlt);
3788+
// Only suggest async alternatives if the DeclContext is truly async
3789+
if (Where.getDeclContext()->isAsyncContext()) {
3790+
if (const AbstractFunctionDecl *afd = dyn_cast<AbstractFunctionDecl>(D)) {
3791+
if (const AbstractFunctionDecl *asyncAlt = afd->getAsyncAlternative()) {
3792+
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
3793+
ctx.Diags.diagnose(diagLoc, diag::warn_use_async_alternative);
3794+
asyncAlt->diagnose(diag::decl_declared_here, asyncAlt);
3795+
}
37813796
}
37823797
}
37833798

test/attr/attr_availability_noasync.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,96 @@ class TestClass {
6262
@available(*, noasync)
6363
deinit { }
6464
}
65+
66+
@available(SwiftStdlib 5.5, *)
67+
func test_defers_sync() {
68+
defer {
69+
defer { basicNoAsync() }
70+
basicNoAsync()
71+
}
72+
73+
func local_sync_func() {
74+
defer { basicNoAsync() }
75+
_ = ()
76+
}
77+
78+
func local_async_func() async {
79+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
80+
defer { basicNoAsync() }
81+
_ = ()
82+
}
83+
84+
let local_sync_closure = { () -> Void in
85+
defer { basicNoAsync() }
86+
_ = ()
87+
}
88+
_ = local_sync_closure
89+
90+
// local async closure
91+
let local_async_closure = { () async -> Void in
92+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
93+
defer { basicNoAsync() }
94+
_ = ()
95+
}
96+
_ = local_async_closure
97+
98+
var local_sync_var: Void {
99+
defer { basicNoAsync() }
100+
return ()
101+
}
102+
103+
var local_async_var: Void {
104+
get async {
105+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
106+
defer { basicNoAsync() }
107+
return ()
108+
}
109+
}
110+
}
111+
112+
@available(SwiftStdlib 5.5, *)
113+
func test_defer_async() async {
114+
defer {
115+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
116+
defer { basicNoAsync() }
117+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
118+
basicNoAsync()
119+
}
120+
121+
func local_sync_func() {
122+
defer { basicNoAsync() }
123+
_ = ()
124+
}
125+
126+
func local_async_func() async {
127+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
128+
defer { basicNoAsync() }
129+
_ = ()
130+
}
131+
132+
let local_sync_closure = { () -> Void in
133+
defer { basicNoAsync() }
134+
_ = ()
135+
}
136+
_ = local_sync_closure
137+
138+
let local_async_closure = { () async -> Void in
139+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
140+
defer { basicNoAsync() }
141+
_ = ()
142+
}
143+
_ = local_async_closure
144+
145+
var local_sync_var: Void {
146+
defer { basicNoAsync() }
147+
return ()
148+
}
149+
150+
var local_async_var: Void {
151+
get async {
152+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
153+
defer { basicNoAsync() }
154+
return ()
155+
}
156+
}
157+
}

0 commit comments

Comments
 (0)