Skip to content

Commit e48c438

Browse files
authored
Merge pull request #70151 from hborla/downgrade-missing-await-error
2 parents 54a152f + 61b43e6 commit e48c438

File tree

4 files changed

+82
-10
lines changed

4 files changed

+82
-10
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,12 +538,18 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule,
538538
}
539539

540540
bool swift::isLetAccessibleAnywhere(const ModuleDecl *fromModule,
541-
VarDecl *let) {
541+
VarDecl *let,
542+
ActorReferenceResult::Options &options) {
542543
auto isolation = getActorIsolation(let);
543-
ActorReferenceResult::Options options = llvm::None;
544544
return varIsSafeAcrossActors(fromModule, let, isolation, options);
545545
}
546546

547+
bool swift::isLetAccessibleAnywhere(const ModuleDecl *fromModule,
548+
VarDecl *let) {
549+
ActorReferenceResult::Options options = llvm::None;
550+
return isLetAccessibleAnywhere(fromModule, let, options);
551+
}
552+
547553
namespace {
548554
/// Describes the important parts of a partial apply thunk.
549555
struct PartialApplyThunkInfo {

lib/Sema/TypeCheckConcurrency.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,13 @@ bool isAccessibleAcrossActors(
560560
const DeclContext *fromDC,
561561
llvm::Optional<ReferencedActor> actorInstance = llvm::None);
562562

563+
/// Determines if the 'let' can be read from anywhere within the given module,
564+
/// regardless of the isolation or async-ness of the context in which
565+
/// the var is read.
566+
bool isLetAccessibleAnywhere(const ModuleDecl *fromModule,
567+
VarDecl *let,
568+
ActorReferenceResult::Options &options);
569+
563570
/// Check whether given variable references to a potentially
564571
/// isolated actor.
565572
bool isPotentiallyIsolatedActor(

lib/Sema/TypeCheckEffects.cpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,8 @@ static bool isRethrowLikeTypedThrows(AbstractFunctionDecl *func) {
718718
class Classification {
719719
bool IsInvalid = false; // The AST is malformed. Don't diagnose.
720720

721+
bool downgradeToWarning = false;
722+
721723
// Throwing
722724
ConditionalEffectKind ThrowKind = ConditionalEffectKind::None;
723725
llvm::Optional<PotentialEffectReason> ThrowReason;
@@ -968,6 +970,13 @@ class Classification {
968970
bool isInvalid() const { return IsInvalid; }
969971
void makeInvalid() { IsInvalid = true; }
970972

973+
bool shouldDowngradeToWarning() const {
974+
return downgradeToWarning;
975+
}
976+
void setDowngradeToWarning(bool downgrade) {
977+
downgradeToWarning = downgrade;
978+
}
979+
971980
ConditionalEffectKind getConditionalKind(EffectKind kind) const {
972981
switch (kind) {
973982
case EffectKind::Throws: return ThrowKind;
@@ -1044,6 +1053,23 @@ class ApplyClassifier {
10441053
return Classification();
10451054
}
10461055

1056+
/// Whether a missing 'await' error on accessing an async var should be
1057+
/// downgraded to a warning. This is only the case for synchronous access
1058+
/// to isolated global or static 'let' variables.
1059+
bool downgradeAsyncAccessToWarning(Decl *decl) {
1060+
if (auto *var = dyn_cast<VarDecl>(decl)) {
1061+
ActorReferenceResult::Options options = llvm::None;
1062+
// The newly-diagnosed cases are invalid regardless of the module context
1063+
// of the caller, i.e. isolated static and global 'let' variables.
1064+
auto *module = var->getDeclContext()->getParentModule();
1065+
if (!isLetAccessibleAnywhere(module, var, options)) {
1066+
return options.contains(ActorReferenceResult::Flags::Preconcurrency);
1067+
}
1068+
}
1069+
1070+
return false;
1071+
}
1072+
10471073
Classification classifyLookup(LookupExpr *E) {
10481074
auto member = E->getMember();
10491075
if (!member)
@@ -1060,6 +1086,9 @@ class ApplyClassifier {
10601086
reason = PotentialEffectReason::forSubscriptAccess();
10611087
}
10621088

1089+
classification.setDowngradeToWarning(
1090+
downgradeAsyncAccessToWarning(member.getDecl()));
1091+
10631092
classification.mergeImplicitEffects(
10641093
member.getDecl()->getASTContext(),
10651094
E->isImplicitlyAsync().has_value(), E->isImplicitlyThrows(),
@@ -1086,6 +1115,9 @@ class ApplyClassifier {
10861115
declRef, ConditionalEffectKind::Always, reason);
10871116
}
10881117

1118+
classification.setDowngradeToWarning(
1119+
downgradeAsyncAccessToWarning(E->getDecl()));
1120+
10891121
classification.mergeImplicitEffects(
10901122
E->getDeclRef().getDecl()->getASTContext(),
10911123
E->isImplicitlyAsync().has_value(), E->isImplicitlyThrows(),
@@ -2548,15 +2580,20 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25482580

25492581
struct DiagnosticInfo {
25502582
DiagnosticInfo(Expr &failingExpr,
2551-
PotentialEffectReason reason) :
2583+
PotentialEffectReason reason,
2584+
bool downgradeToWarning) :
25522585
reason(reason),
2553-
expr(failingExpr) {}
2586+
expr(failingExpr),
2587+
downgradeToWarning(downgradeToWarning) {}
25542588

25552589
/// Reason for throwing
25562590
PotentialEffectReason reason;
25572591

25582592
/// Failing expression
25592593
Expr &expr;
2594+
2595+
/// Whether the error should be downgraded to a warning.
2596+
bool downgradeToWarning;
25602597
};
25612598

25622599
SmallVector<Expr *, 4> errorOrder;
@@ -3113,8 +3150,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
31133150
CurContext.isWithinInterpolatedString());
31143151
if (uncoveredAsync.find(anchor) == uncoveredAsync.end())
31153152
errorOrder.push_back(anchor);
3116-
uncoveredAsync[anchor].emplace_back(*expr,
3117-
classification.getAsyncReason());
3153+
uncoveredAsync[anchor].emplace_back(
3154+
*expr, classification.getAsyncReason(),
3155+
classification.shouldDowngradeToWarning());
31183156
}
31193157
}
31203158

@@ -3335,7 +3373,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
33353373
awaitInsertLoc = tryExpr->getSubExpr()->getStartLoc();
33363374
}
33373375

3376+
bool downgradeToWarning = llvm::all_of(errors,
3377+
[&](DiagnosticInfo diag) -> bool {
3378+
return diag.downgradeToWarning;
3379+
});
3380+
33383381
Ctx.Diags.diagnose(anchor->getStartLoc(), diag::async_expr_without_await)
3382+
.warnUntilSwiftVersionIf(downgradeToWarning, 6)
33393383
.fixItInsert(awaitInsertLoc, "await ")
33403384
.highlight(anchor->getSourceRange());
33413385

test/Concurrency/concurrent_value_checking.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
// REQUIRES: concurrency
55
// REQUIRES: asserts
66

7-
class NotConcurrent { } // expected-note 16{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
8-
// expected-complete-note @-1 11{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
7+
class NotConcurrent { } // expected-note 18{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
8+
// expected-complete-note @-1 12{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
99

1010
// ----------------------------------------------------------------------
1111
// Sendable restriction on actor operations
@@ -115,12 +115,27 @@ func globalAsync(_: NotConcurrent?) async {
115115
globalSync(nil)
116116
}
117117

118+
enum E {
119+
@SomeGlobalActor static let notSafe: NotConcurrent? = nil
120+
}
121+
118122
func globalTest() async {
119-
// expected-error@+2 {{expression is 'async' but is not marked with 'await'}}
123+
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'}}
120124
// expected-note@+1 {{property access is 'async'}}
121125
let a = globalValue // expected-warning{{non-sendable type 'NotConcurrent?' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated let 'globalValue' cannot cross actor boundary}}
122126
await globalAsync(a) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
123127
await globalSync(a) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
128+
129+
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'}}
130+
// expected-note@+1 {{property access is 'async'}}
131+
let _ = E.notSafe // expected-warning{{non-sendable type 'NotConcurrent?' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated static property 'notSafe' cannot cross actor boundary}}
132+
133+
// expected-error@+3 {{expression is 'async' but is not marked with 'await'}}
134+
// expected-note@+2 {{call is 'async'}}
135+
// expected-note@+1 {{property access is 'async'}}
136+
globalAsync(E.notSafe)
137+
// expected-complete-warning@-1 {{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
138+
// expected-warning@-2 {{non-sendable type 'NotConcurrent?' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated static property 'notSafe' cannot cross actor boundary}}
124139
}
125140

126141
struct HasSubscript {
@@ -139,7 +154,7 @@ class ClassWithGlobalActorInits { // expected-note 2{{class 'ClassWithGlobalActo
139154

140155
@MainActor
141156
func globalTestMain(nc: NotConcurrent) async {
142-
// expected-error@+2 {{expression is 'async' but is not marked with 'await'}}
157+
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'}}
143158
// expected-note@+1 {{property access is 'async'}}
144159
let a = globalValue // expected-warning{{non-sendable type 'NotConcurrent?' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated let 'globalValue' cannot cross actor boundary}}
145160
await globalAsync(a) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}

0 commit comments

Comments
 (0)