Skip to content

Commit 9cb2873

Browse files
committed
[Concurrency] Downgrade missing 'await' error to a warning for synchronous
access to isolated global and static variables that are lazily initialized. (cherry picked from commit 61b43e6)
1 parent 674d3d7 commit 9cb2873

File tree

4 files changed

+90
-16
lines changed

4 files changed

+90
-16
lines changed

lib/Sema/TypeCheckConcurrency.cpp

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

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

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

lib/Sema/TypeCheckConcurrency.h

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

553+
/// Determines if the 'let' can be read from anywhere within the given module,
554+
/// regardless of the isolation or async-ness of the context in which
555+
/// the var is read.
556+
bool isLetAccessibleAnywhere(const ModuleDecl *fromModule,
557+
VarDecl *let,
558+
ActorReferenceResult::Options &options);
559+
553560
/// Check whether given variable references to a potentially
554561
/// isolated actor.
555562
bool isPotentiallyIsolatedActor(

lib/Sema/TypeCheckEffects.cpp

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,8 @@ static void simple_display(llvm::raw_ostream &out, ConditionalEffectKind kind) {
611611
class Classification {
612612
bool IsInvalid = false; // The AST is malformed. Don't diagnose.
613613

614+
bool downgradeToWarning = false;
615+
614616
ConditionalEffectKind ThrowKind = ConditionalEffectKind::None;
615617
llvm::Optional<PotentialEffectReason> ThrowReason;
616618

@@ -703,6 +705,14 @@ class Classification {
703705
}
704706

705707
bool isInvalid() const { return IsInvalid; }
708+
709+
bool shouldDowngradeToWarning() const {
710+
return downgradeToWarning;
711+
}
712+
void setDowngradeToWarning(bool downgrade) {
713+
downgradeToWarning = downgrade;
714+
}
715+
706716
ConditionalEffectKind getConditionalKind(EffectKind kind) const {
707717
switch (kind) {
708718
case EffectKind::Throws: return ThrowKind;
@@ -2087,15 +2097,20 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
20872097

20882098
struct DiagnosticInfo {
20892099
DiagnosticInfo(Expr &failingExpr,
2090-
PotentialEffectReason reason) :
2100+
PotentialEffectReason reason,
2101+
bool downgradeToWarning) :
20912102
reason(reason),
2092-
expr(failingExpr) {}
2103+
expr(failingExpr),
2104+
downgradeToWarning(downgradeToWarning) {}
20932105

20942106
/// Reason for throwing
20952107
PotentialEffectReason reason;
20962108

20972109
/// Failing expression
20982110
Expr &expr;
2111+
2112+
/// Whether the error should be downgraded to a warning.
2113+
bool downgradeToWarning;
20992114
};
21002115

21012116
SmallVector<Expr *, 4> errorOrder;
@@ -2504,6 +2519,23 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25042519
return PotentialEffectReason::forPropertyAccess();
25052520
}
25062521

2522+
/// Whether a missing 'await' error on accessing an async var should be
2523+
/// downgraded to a warning. This is only the case for synchronous access
2524+
/// to isolated global or static 'let' variables.
2525+
bool downgradeAsyncAccessToWarning(Decl *decl) {
2526+
if (auto *var = dyn_cast<VarDecl>(decl)) {
2527+
ActorReferenceResult::Options options = llvm::None;
2528+
// The newly-diagnosed cases are invalid regardless of the module context
2529+
// of the caller, i.e. isolated static and global 'let' variables.
2530+
auto *module = var->getDeclContext()->getParentModule();
2531+
if (!isLetAccessibleAnywhere(module, var, options)) {
2532+
return options.contains(ActorReferenceResult::Flags::Preconcurrency);
2533+
}
2534+
}
2535+
2536+
return false;
2537+
}
2538+
25072539
ShouldRecurse_t checkLookup(LookupExpr *E) {
25082540
auto member = E->getMember();
25092541
if (auto getter = getEffectfulGetOnlyAccessor(member)) {
@@ -2536,10 +2568,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25362568
}
25372569

25382570
if (!effects.empty()) {
2539-
checkThrowAsyncSite(E, requiresTry,
2540-
Classification::forEffect(effects,
2541-
ConditionalEffectKind::Always,
2542-
getKindOfEffectfulProp(member)));
2571+
auto classification = Classification::forEffect(
2572+
effects, ConditionalEffectKind::Always,
2573+
getKindOfEffectfulProp(member));
2574+
classification.setDowngradeToWarning(
2575+
downgradeAsyncAccessToWarning(member.getDecl()));
2576+
2577+
checkThrowAsyncSite(E, requiresTry, classification);
25432578
}
25442579
}
25452580

@@ -2564,9 +2599,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25642599
PotentialEffectReason::forPropertyAccess()));
25652600

25662601
} else if (E->isImplicitlyAsync()) {
2602+
auto classification = Classification::forUnconditional(
2603+
EffectKind::Async, PotentialEffectReason::forPropertyAccess());
2604+
classification.setDowngradeToWarning(
2605+
downgradeAsyncAccessToWarning(E->getDecl()));
2606+
25672607
checkThrowAsyncSite(E, /*requiresTry=*/E->isImplicitlyThrows(),
2568-
Classification::forUnconditional(EffectKind::Async,
2569-
PotentialEffectReason::forPropertyAccess()));
2608+
classification);
25702609

25712610
} else if (auto decl = E->getDecl()) {
25722611
if (auto var = dyn_cast<VarDecl>(decl)) {
@@ -2701,8 +2740,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27012740
CurContext.isWithinInterpolatedString());
27022741
if (uncoveredAsync.find(anchor) == uncoveredAsync.end())
27032742
errorOrder.push_back(anchor);
2704-
uncoveredAsync[anchor].emplace_back(*expr,
2705-
classification.getAsyncReason());
2743+
uncoveredAsync[anchor].emplace_back(
2744+
*expr, classification.getAsyncReason(),
2745+
classification.shouldDowngradeToWarning());
27062746
}
27072747
}
27082748

@@ -2888,7 +2928,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
28882928
awaitInsertLoc = tryExpr->getSubExpr()->getStartLoc();
28892929
}
28902930

2931+
bool downgradeToWarning = llvm::all_of(errors,
2932+
[&](DiagnosticInfo diag) -> bool {
2933+
return diag.downgradeToWarning;
2934+
});
2935+
28912936
Ctx.Diags.diagnose(anchor->getStartLoc(), diag::async_expr_without_await)
2937+
.warnUntilSwiftVersionIf(downgradeToWarning, 6)
28922938
.fixItInsert(awaitInsertLoc, "await ")
28932939
.highlight(anchor->getSourceRange());
28942940

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)