Skip to content

Commit b0c1160

Browse files
authored
Merge pull request swiftlang#83420 from xedin/rdar-157061896
[Concurrency] Downgrade errors when isolated member is referenced from a preconcurrency context
2 parents 1367aab + b046efc commit b0c1160

9 files changed

+96
-29
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "swift/Strings.h"
4141

4242
using namespace swift;
43+
using namespace swift::version;
4344

4445
static ActorIsolation getOverriddenIsolationFor(const ValueDecl *value);
4546

@@ -567,7 +568,7 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule, VarDecl *var,
567568
if (var->isGlobalStorage() && var->isLazilyInitializedGlobal()) {
568569
// Compiler versions <= 5.9 accepted this code, so downgrade to a
569570
// warning prior to Swift 6.
570-
options = ActorReferenceResult::Flags::Preconcurrency;
571+
options = ActorReferenceResult::Flags::CompatibilityDowngrade;
571572
return false;
572573
}
573574

@@ -588,7 +589,7 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule, VarDecl *var,
588589
// so downgrade async access errors in the effects checker to
589590
// warnings prior to Swift 6.
590591
if (accessWithinModule)
591-
options = ActorReferenceResult::Flags::Preconcurrency;
592+
options = ActorReferenceResult::Flags::CompatibilityDowngrade;
592593

593594
return false;
594595
}
@@ -4242,11 +4243,10 @@ namespace {
42424243

42434244
applyErrors[key].push_back(mismatch);
42444245
} else {
4245-
ctx.Diags.diagnose(
4246-
apply->getLoc(),
4247-
diagnostic.getID(),
4248-
diagnostic.getArgs())
4249-
.warnUntilSwiftVersionIf(preconcurrency, 6);
4246+
ctx.Diags
4247+
.diagnose(apply->getLoc(), diagnostic.getID(),
4248+
diagnostic.getArgs())
4249+
.limitBehaviorIf(preconcurrency, DiagnosticBehavior::Warning);
42504250

42514251
if (calleeDecl) {
42524252
auto calleeIsolation = getInferredActorIsolation(calleeDecl);
@@ -4590,9 +4590,10 @@ namespace {
45904590
break;
45914591
}
45924592

4593-
bool downgrade = isolation.isGlobalActor() ||
4594-
options.contains(
4595-
ActorReferenceResult::Flags::Preconcurrency);
4593+
bool downgrade =
4594+
isolation.isGlobalActor() ||
4595+
options.contains(
4596+
ActorReferenceResult::Flags::CompatibilityDowngrade);
45964597

45974598
ctx.Diags.diagnose(
45984599
component.getLoc(), diag::actor_isolated_keypath_component,
@@ -4782,6 +4783,10 @@ namespace {
47824783
// Does the reference originate from a @preconcurrency context?
47834784
bool preconcurrencyContext =
47844785
result.options.contains(ActorReferenceResult::Flags::Preconcurrency);
4786+
bool shouldDowngradeToWarning =
4787+
preconcurrencyContext ||
4788+
result.options.contains(
4789+
ActorReferenceResult::Flags::CompatibilityDowngrade);
47854790

47864791
Type derivedConformanceType;
47874792
DeclName requirementName;
@@ -4816,14 +4821,21 @@ namespace {
48164821
refErrors.insert(std::make_pair(keyPair, list));
48174822
}
48184823
} else {
4819-
ctx.Diags.diagnose(
4820-
loc, diag::actor_isolated_non_self_reference,
4821-
decl, useKind,
4822-
refKind + 1, refGlobalActor,
4823-
result.isolation)
4824-
.warnUntilSwiftVersionIf(preconcurrencyContext, 6);
4825-
maybeNoteMutatingMethodSuggestion(ctx, decl, loc, getDeclContext(), result.isolation,
4826-
kindOfUsage(decl, context).value_or(VarRefUseEnv::Read));
4824+
{
4825+
auto diagnostic = ctx.Diags.diagnose(
4826+
loc, diag::actor_isolated_non_self_reference, decl, useKind,
4827+
refKind + 1, refGlobalActor, result.isolation);
4828+
4829+
// For compatibility downgrades - the error is downgraded until
4830+
// Swift 6, for preconcurrency - always.
4831+
if (shouldDowngradeToWarning)
4832+
diagnostic.limitBehaviorWithPreconcurrency(
4833+
DiagnosticBehavior::Warning, preconcurrencyContext);
4834+
}
4835+
4836+
maybeNoteMutatingMethodSuggestion(
4837+
ctx, decl, loc, getDeclContext(), result.isolation,
4838+
kindOfUsage(decl, context).value_or(VarRefUseEnv::Read));
48274839

48284840
if (derivedConformanceType) {
48294841
auto *decl = dyn_cast<ValueDecl>(getDeclContext()->getAsDecl());
@@ -8319,7 +8331,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
83198331
// Treat the decl isolation as 'preconcurrency' to downgrade violations
83208332
// to warnings, because violating Sendable here is accepted by the
83218333
// Swift 5.9 compiler.
8322-
options |= Flags::Preconcurrency;
8334+
options |= Flags::CompatibilityDowngrade;
83238335
return forEntersActor(declIsolation, options);
83248336
}
83258337
}
@@ -8358,8 +8370,10 @@ ActorReferenceResult ActorReferenceResult::forReference(
83588370
// This is a cross-actor reference.
83598371

83608372
// Note if the reference originates from a @preconcurrency-isolated context.
8361-
if (contextIsolation.preconcurrency() || declIsolation.preconcurrency())
8373+
if (contextIsolation.preconcurrency() || declIsolation.preconcurrency()) {
83628374
options |= Flags::Preconcurrency;
8375+
options |= Flags::CompatibilityDowngrade;
8376+
}
83638377

83648378
// If the declaration isn't asynchronous, promote to async.
83658379
if (!decl->isAsync())

lib/Sema/TypeCheckConcurrency.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,20 @@ struct ActorReferenceResult {
208208
/// potentially from a different node, so it must be marked 'distributed'.
209209
Distributed = 1 << 2,
210210

211-
/// The declaration is being accessed from a @preconcurrency context.
211+
/// The declaration is marked as `@preconcurrency` or being accessed
212+
/// from a @preconcurrency context.
212213
Preconcurrency = 1 << 3,
213214

214215
/// Only arguments cross an isolation boundary, e.g. because they
215216
/// escape into an actor in a nonisolated actor initializer.
216217
OnlyArgsCrossIsolation = 1 << 4,
218+
219+
/// The reference to the declaration is invalid but has to be downgraded
220+
/// to a warning because it was accepted by the older compilers or because
221+
/// the declaration predates concurrency and is marked as such.
222+
///
223+
/// NOTE: This flag is set for `Preconcurrency` declarations.
224+
CompatibilityDowngrade = 1 << 5,
217225
};
218226

219227
using Options = OptionSet<Flags>;

lib/Sema/TypeCheckEffects.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,8 @@ class ApplyClassifier {
14881488
module = var->getDeclContext()->getParentModule();
14891489
}
14901490
if (!isLetAccessibleAnywhere(module, var, options)) {
1491-
return options.contains(ActorReferenceResult::Flags::Preconcurrency);
1491+
return options.contains(
1492+
ActorReferenceResult::Flags::CompatibilityDowngrade);
14921493
}
14931494
}
14941495

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3588,7 +3588,7 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement,
35883588
isLetAccessibleAnywhere(
35893589
witness->getDeclContext()->getParentModule(),
35903590
var, options);
3591-
if (options.contains(ActorReferenceResult::Flags::Preconcurrency)) {
3591+
if (options.contains(ActorReferenceResult::Flags::CompatibilityDowngrade)) {
35923592
behavior = DiagnosticBehavior::Warning;
35933593
}
35943594
}

test/ClangImporter/objc_async.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ extension SomeWrapper: Sendable where T: Sendable {}
392392
func makeCall(slowServer: SlowServer) {
393393
slowServer.doSomethingSlow("churn butter") { (_ : Int) in
394394
let _ = self.isolatedThing
395-
// expected-warning@-1 {{main actor-isolated property 'isolatedThing' can not be referenced from a Sendable closure; this is an error in the Swift 6 language mode}}
395+
// expected-warning@-1 {{main actor-isolated property 'isolatedThing' can not be referenced from a Sendable closure}}
396396
}
397397
}
398398
}

test/Concurrency/assume_mainactor_typechecker_errors.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class SendableData : @unchecked Sendable {}
1515
// expected-swift5-note@-1 {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}}
1616

1717
nonisolated func getDataFromSocket() -> SendableData { SendableData() }
18-
// expected-swift5-warning@-1 {{call to main actor-isolated initializer 'init()' in a synchronous nonisolated context; this is an error in the Swift 6 language mode}}
18+
// expected-swift5-warning@-1 {{call to main actor-isolated initializer 'init()' in a synchronous nonisolated context}}
1919

2020
class Klass { // expected-swift5-note 3 {{}} expected-swift6-note 3 {{}}
2121
let s = SendableData()

test/Concurrency/objc_async_overload.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ extension Delegate {
5858
func handle(_ req: Request, with delegate: Delegate) {
5959
delegate.makeRequest1(req) {
6060
self.finish()
61-
// expected-warning@-1 {{call to main actor-isolated instance method 'finish()' in a synchronous nonisolated context; this is an error in the Swift 6 language mode}}
61+
// expected-warning@-1 {{call to main actor-isolated instance method 'finish()' in a synchronous nonisolated context}}
6262
}
6363
}
6464
}

test/Concurrency/predates_concurrency.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ do {
386386
func run() async {
387387
await test {
388388
if let value {
389-
// expected-warning@-1 {{main actor-isolated property 'value' can not be referenced from a Sendable closure; this is an error in the Swift 6 language mode}}
389+
// expected-warning@-1 {{main actor-isolated property 'value' can not be referenced from a Sendable closure}}
390390
print(value)
391391
}
392392
}

test/Concurrency/predates_concurrency_swift6.swift

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ func testElsewhere(x: X) {
6363

6464
func testCalls(x: X) {
6565
// expected-note@-1 2{{add '@MainActor' to make global function 'testCalls(x:)' part of global actor 'MainActor'}}
66-
onMainActorAlways() // expected-error{{call to main actor-isolated global function 'onMainActorAlways()' in a synchronous nonisolated context}}
66+
onMainActorAlways() // expected-warning{{call to main actor-isolated global function 'onMainActorAlways()' in a synchronous nonisolated context}}
6767

6868
let _: () -> Void = onMainActorAlways // expected-error{{converting function value of type '@MainActor @Sendable () -> ()' to '() -> Void' loses global actor 'MainActor'}}
6969

7070
let c = MyModelClass() // okay, synthesized init() is 'nonisolated'
7171

72-
c.f() // expected-error{{call to main actor-isolated instance method 'f()' in a synchronous nonisolated context}}
72+
c.f() // expected-warning{{call to main actor-isolated instance method 'f()' in a synchronous nonisolated context}}
7373
}
7474

7575
func testCallsWithAsync() async {
@@ -338,3 +338,47 @@ func testSendableMetatypeDowngrades() {
338338
acceptsSendableMetatype(t) // Ok (because P is `Sendable`
339339
}
340340
}
341+
342+
// Test that Sendable issues are downgraded to warnings in `@preconcurrency` context.
343+
do {
344+
@preconcurrency nonisolated func f(callback: @Sendable () -> Void) {}
345+
346+
@MainActor
347+
struct Env {
348+
var v: Int = 0
349+
}
350+
351+
@MainActor
352+
class IsolatedTest {
353+
var env = Env()
354+
// expected-note@-1 {{property declared here}}
355+
356+
func onMain() {}
357+
// expected-note@-1 {{calls to instance method 'onMain()' from outside of its actor context are implicitly asynchronous}}
358+
359+
func testProperty() {
360+
f {
361+
_ = self.env.v
362+
// expected-warning@-1 {{main actor-isolated property 'env' can not be referenced from a Sendable closure}}
363+
}
364+
}
365+
366+
func testMethod() {
367+
f {
368+
self.onMain()
369+
// expected-warning@-1 {{call to main actor-isolated instance method 'onMain()' in a synchronous nonisolated context}}
370+
}
371+
}
372+
}
373+
374+
class Test { // expected-note {{class 'Test' does not conform to the 'Sendable' protocol}}
375+
var env = Env()
376+
377+
func testProperty() async {
378+
f {
379+
_ = self.env.v
380+
// expected-warning@-1 {{capture of 'self' with non-Sendable type 'Test' in a '@Sendable' closure}}
381+
}
382+
}
383+
}
384+
}

0 commit comments

Comments
 (0)