Skip to content

Commit 1de83ac

Browse files
committed
Enforce safe access to unsafe global actor declarations only from "new" code.
Allow references to unsafe global actor-isolated declarations only from existing code that has not adopted concurrency features (such as async, @Concurrent closures, etc.). This allows declarations that should be isolated to a global actor to be annotated as such without breaking existing code (as if isolation was unspecified), while code that does adopt concurrency will treat the declaration as being part of that global actor.
1 parent ac19db2 commit 1de83ac

File tree

5 files changed

+77
-14
lines changed

5 files changed

+77
-14
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -554,23 +554,21 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
554554
return forActorSelf(isolation.getActor(),
555555
isAccessibleAcrossActors || isa<ConstructorDecl>(decl));
556556

557+
case ActorIsolation::GlobalActorUnsafe:
557558
case ActorIsolation::GlobalActor: {
558559
Type actorType = isolation.getGlobalActor();
559560
if (auto subs = declRef.getSubstitutions())
560561
actorType = actorType.subst(subs);
561562

562-
return forGlobalActor(actorType, isAccessibleAcrossActors);
563+
return forGlobalActor(actorType, isAccessibleAcrossActors,
564+
isolation == ActorIsolation::GlobalActorUnsafe);
563565
}
564566

565567
case ActorIsolation::Independent:
566568
case ActorIsolation::IndependentUnsafe:
567569
// Actor-independent have no restrictions on their access.
568570
return forUnrestricted();
569571

570-
case ActorIsolation::GlobalActorUnsafe:
571-
// TODO: Capture GlobalActorUnsafe so we can do more checking.
572-
return forUnrestricted();
573-
574572
case ActorIsolation::Unspecified:
575573
return isAccessibleAcrossActors ? forUnrestricted() : forUnsafe();
576574
}
@@ -1297,6 +1295,14 @@ namespace {
12971295
case ActorIsolationRestriction::Unrestricted:
12981296
case ActorIsolationRestriction::Unsafe:
12991297
break;
1298+
case ActorIsolationRestriction::GlobalActorUnsafe:
1299+
// If we're not supposed to diagnose existing data races here,
1300+
// we're done.
1301+
if (!shouldDiagnoseExistingDataRaces(getDeclContext()))
1302+
break;
1303+
1304+
LLVM_FALLTHROUGH;
1305+
13001306
case ActorIsolationRestriction::GlobalActor: {
13011307
ctx.Diags.diagnose(argLoc, diag::actor_isolated_inout_state,
13021308
decl->getDescriptiveKind(), decl->getName(),
@@ -1682,6 +1688,13 @@ namespace {
16821688
case ActorIsolationRestriction::ActorSelf:
16831689
llvm_unreachable("non-member reference into an actor");
16841690

1691+
case ActorIsolationRestriction::GlobalActorUnsafe:
1692+
// Only complain if we're in code that's adopted concurrency features.
1693+
if (!shouldDiagnoseExistingDataRaces(getDeclContext()))
1694+
return false;
1695+
1696+
LLVM_FALLTHROUGH;
1697+
16851698
case ActorIsolationRestriction::GlobalActor:
16861699
return checkGlobalActorReference(
16871700
valueRef, loc, isolation.getGlobalActor(), isolation.isCrossActor);
@@ -1852,6 +1865,13 @@ namespace {
18521865
llvm_unreachable("Unhandled actor isolation");
18531866
}
18541867

1868+
case ActorIsolationRestriction::GlobalActorUnsafe:
1869+
// Only complain if we're in code that's adopted concurrency features.
1870+
if (!shouldDiagnoseExistingDataRaces(getDeclContext()))
1871+
return false;
1872+
1873+
LLVM_FALLTHROUGH;
1874+
18551875
case ActorIsolationRestriction::GlobalActor:
18561876
return checkGlobalActorReference(
18571877
memberRef, memberLoc, isolation.getGlobalActor(),
@@ -2190,13 +2210,14 @@ ActorIsolation ActorIsolationRequest::evaluate(
21902210
ActorIndependentKind::Safe, /*IsImplicit=*/true));
21912211
break;
21922212

2193-
case ActorIsolation::GlobalActor:
2194-
case ActorIsolation::GlobalActorUnsafe: {
2213+
case ActorIsolation::GlobalActorUnsafe:
2214+
// Don't infer unsafe global actor isolation.
2215+
return ActorIsolation::forUnspecified();
2216+
2217+
case ActorIsolation::GlobalActor: {
21952218
auto typeExpr = TypeExpr::createImplicit(inferred.getGlobalActor(), ctx);
21962219
auto attr = CustomAttr::create(
21972220
ctx, SourceLoc(), typeExpr, /*implicit=*/true);
2198-
if (inferred == ActorIsolation::GlobalActorUnsafe)
2199-
attr->setArgIsUnsafe(true);
22002221
value->getAttrs().add(attr);
22012222
break;
22022223
}
@@ -2354,6 +2375,31 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) {
23542375
}
23552376
}
23562377

2378+
// If the overriding declaration uses an unsafe global actor, we can do
2379+
// anything that doesn't actively conflict with the overridden isolation.
2380+
if (isolation == ActorIsolation::GlobalActorUnsafe) {
2381+
switch (overriddenIsolation) {
2382+
case ActorIsolation::Unspecified:
2383+
return;
2384+
2385+
case ActorIsolation::ActorInstance:
2386+
case ActorIsolation::Independent:
2387+
case ActorIsolation::IndependentUnsafe:
2388+
// Diagnose below.
2389+
break;
2390+
2391+
case ActorIsolation::GlobalActor:
2392+
case ActorIsolation::GlobalActorUnsafe:
2393+
// The global actors don't match; diagnose it.
2394+
if (overriddenIsolation.getGlobalActor()->isEqual(
2395+
isolation.getGlobalActor()))
2396+
return;
2397+
2398+
// Diagnose below.
2399+
break;
2400+
}
2401+
}
2402+
23572403
// Isolation mismatch. Diagnose it.
23582404
value->diagnose(
23592405
diag::actor_isolation_override_mismatch, isolation,

lib/Sema/TypeCheckConcurrency.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ class ActorIsolationRestriction {
9090
/// permitted from other declarations with that same global actor or
9191
/// are permitted from elsewhere as a cross-actor reference.
9292
GlobalActor,
93+
94+
/// References to a declaration that is part of a global actor are
95+
/// permitted from other declarations with that same global actor or
96+
/// are permitted from elsewhere as a cross-actor reference, but
97+
/// contexts with unspecified isolation won't diagnose anything.
98+
GlobalActorUnsafe,
9399
};
94100

95101
private:
@@ -125,7 +131,7 @@ class ActorIsolationRestriction {
125131

126132
/// Retrieve the actor class that the declaration is within.
127133
Type getGlobalActor() const {
128-
assert(kind == GlobalActor);
134+
assert(kind == GlobalActor || kind == GlobalActorUnsafe);
129135
return Type(data.globalActor);
130136
}
131137

@@ -152,8 +158,9 @@ class ActorIsolationRestriction {
152158
/// Accesses to the given declaration can only be made via this particular
153159
/// global actor or is a cross-actor access.
154160
static ActorIsolationRestriction forGlobalActor(
155-
Type globalActor, bool isCrossActor) {
156-
ActorIsolationRestriction result(GlobalActor, isCrossActor);
161+
Type globalActor, bool isCrossActor, bool isUnsafe) {
162+
ActorIsolationRestriction result(
163+
isUnsafe ? GlobalActorUnsafe : GlobalActor, isCrossActor);
157164
result.data.globalActor = globalActor.getPointer();
158165
return result;
159166
}

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ static bool checkObjCActorIsolation(const ValueDecl *VD,
408408
}
409409
return true;
410410

411+
case ActorIsolationRestriction::GlobalActorUnsafe:
411412
case ActorIsolationRestriction::GlobalActor:
412413
// FIXME: Consider whether to limit @objc on global-actor-qualified
413414
// declarations.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,7 @@ bool ConformanceChecker::checkActorIsolation(
27252725
// Ensure that the witness is not actor-isolated in a manner that makes it
27262726
// unsuitable as a witness.
27272727
bool isCrossActor = false;
2728+
bool witnessIsUnsafe = false;
27282729
Type witnessGlobalActor;
27292730
switch (auto witnessRestriction =
27302731
ActorIsolationRestriction::forDeclaration(witness)) {
@@ -2762,6 +2763,10 @@ bool ConformanceChecker::checkActorIsolation(
27622763
return diagnoseNonConcurrentTypesInReference(
27632764
witness, DC, witness->getLoc(), ConcurrentReferenceKind::CrossActor);
27642765

2766+
case ActorIsolationRestriction::GlobalActorUnsafe:
2767+
witnessIsUnsafe = true;
2768+
LLVM_FALLTHROUGH;
2769+
27652770
case ActorIsolationRestriction::GlobalActor: {
27662771
// Hang on to the global actor that's used for the witness. It will need
27672772
// to match that of the requirement.
@@ -2839,6 +2844,10 @@ bool ConformanceChecker::checkActorIsolation(
28392844
if (requirement->hasClangNode())
28402845
return false;
28412846

2847+
// If the witness is "unsafe", allow the mismatch.
2848+
if (witnessIsUnsafe)
2849+
return false;
2850+
28422851
witness->diagnose(
28432852
diag::global_actor_isolated_witness, witness->getDescriptiveKind(),
28442853
witness->getName(), witnessGlobalActor, Proto->getName());

test/Concurrency/global_actor_inference.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func barSync() {
239239
// Unsafe global actors
240240
// ----------------------------------------------------------------------
241241
protocol UGA {
242-
@SomeGlobalActor(unsafe) func req()
242+
@SomeGlobalActor(unsafe) func req() // expected-note{{calls to instance method 'req()' from outside of its actor context are implicitly asynchronous}}
243243
}
244244

245245
struct StructUGA1: UGA {
@@ -252,7 +252,7 @@ struct StructUGA2: UGA {
252252

253253
@GenericGlobalActor<String>
254254
func testUGA<T: UGA>(_ value: T) {
255-
value.req()
255+
value.req() // expected-error{{instance method 'req()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'GenericGlobalActor<String>'}}
256256
}
257257

258258
class UGAClass {

0 commit comments

Comments
 (0)