Skip to content

Commit e68c36b

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
1 parent c7c244e commit e68c36b

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
@@ -397,6 +397,18 @@ static bool shouldAllowReferenceToUnavailableInSwiftDeclaration(
397397
return false;
398398
}
399399

400+
// Utility function to help determine if noasync diagnostics are still
401+
// appropriate even if a `DeclContext` returns `false` from `isAsyncContext()`.
402+
static bool shouldTreatDeclContextAsAsyncForDiagnostics(const DeclContext *DC) {
403+
if (auto *D = DC->getAsDecl())
404+
if (auto *FD = dyn_cast<FuncDecl>(D))
405+
if (FD->isDeferBody())
406+
// If this is a defer body, we should delegate to its parent.
407+
return shouldTreatDeclContextAsAsyncForDiagnostics(DC->getParent());
408+
409+
return DC->isAsyncContext();
410+
}
411+
400412
namespace {
401413

402414
/// A class to walk the AST to build the type refinement context hierarchy.
@@ -3781,17 +3793,20 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
37813793
static bool
37823794
diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
37833795
const Expr *call, const ExportContext &Where) {
3784-
// If we are in a synchronous context, don't check it
3785-
if (!Where.getDeclContext()->isAsyncContext())
3796+
// If we are not in an (effective) async context, don't check it
3797+
if (!shouldTreatDeclContextAsAsyncForDiagnostics(Where.getDeclContext()))
37863798
return false;
37873799

37883800
ASTContext &ctx = Where.getDeclContext()->getASTContext();
37893801

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);
3802+
// Only suggest async alternatives if the DeclContext is truly async
3803+
if (Where.getDeclContext()->isAsyncContext()) {
3804+
if (const AbstractFunctionDecl *afd = dyn_cast<AbstractFunctionDecl>(D)) {
3805+
if (const AbstractFunctionDecl *asyncAlt = afd->getAsyncAlternative()) {
3806+
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
3807+
ctx.Diags.diagnose(diagLoc, diag::warn_use_async_alternative);
3808+
asyncAlt->diagnose(diag::decl_declared_here, asyncAlt);
3809+
}
37953810
}
37963811
}
37973812

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)