Skip to content

Commit 1b27c48

Browse files
committed
Switch non-member actor isolation checking to the new implementation
1 parent eef2704 commit 1b27c48

12 files changed

+105
-194
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4543,7 +4543,7 @@ ERROR(override_implicit_unowned_executor,none,
45434543
ERROR(actor_isolated_non_self_reference,none,
45444544
"%5 %0 %1 can not be "
45454545
"%select{referenced|mutated|used 'inout'}2 "
4546-
"%select{on a different actor instance|"
4546+
"%select{from outside the actor|on a different actor instance|"
45474547
"on a non-isolated actor instance|"
45484548
"from a Sendable function|from a Sendable closure|"
45494549
"from an 'async let' initializer|from global actor %4|"
@@ -4565,18 +4565,6 @@ ERROR(actor_isolated_inout_state,none,
45654565
ERROR(actor_isolated_mutating_func,none,
45664566
"cannot call mutating async function %0 on actor-isolated %1 %2",
45674567
(DeclName, DescriptiveDeclKind, DeclName))
4568-
ERROR(global_actor_from_instance_actor_context,none,
4569-
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4570-
" from actor %3 %select{|in a synchronous context}5",
4571-
(DescriptiveDeclKind, DeclName, Type, DeclName, unsigned, bool))
4572-
ERROR(global_actor_from_other_global_actor_context,none,
4573-
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4574-
" from different global actor %3 %select{|in a synchronous context}5",
4575-
(DescriptiveDeclKind, DeclName, Type, Type, unsigned, bool))
4576-
ERROR(global_actor_from_nonactor_context,none,
4577-
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4578-
" from %select{this|a non-isolated}3%select{| synchronous}5 context",
4579-
(DescriptiveDeclKind, DeclName, Type, bool, unsigned, bool))
45804568
ERROR(actor_isolated_call,none,
45814569
"call to %0 function in a synchronous %1 context",
45824570
(ActorIsolation, ActorIsolation))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 84 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,7 +1447,7 @@ static bool memberAccessHasSpecialPermissionInSwift5(DeclContext const *refCxt,
14471447
member->getDescriptiveKind(),
14481448
member->getName(),
14491449
useKindInt,
1450-
baseActor.kind,
1450+
baseActor.kind + 1,
14511451
baseActor.globalActor,
14521452
getActorIsolation(const_cast<ValueDecl *>(member)))
14531453
.warnUntilSwiftVersion(6);
@@ -1881,16 +1881,23 @@ namespace {
18811881
recordMutableVarParent(load, load->getSubExpr());
18821882

18831883
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
1884-
checkMemberReference(lookup->getBase(), lookup->getMember(),
1884+
checkReference(lookup->getBase(), lookup->getMember(),
18851885
lookup->getLoc(),
18861886
/*partialApply*/None,
18871887
lookup);
18881888
return { true, expr };
18891889
}
18901890

18911891
if (auto declRef = dyn_cast<DeclRefExpr>(expr)) {
1892-
checkNonMemberReference(
1893-
declRef->getDeclRef(), declRef->getLoc(), declRef);
1892+
auto valueRef = declRef->getDeclRef();
1893+
auto value = valueRef.getDecl();
1894+
auto loc = declRef->getLoc();
1895+
1896+
//FIXME: Should this be subsumed in reference checking?
1897+
if (value->isLocalCapture())
1898+
checkLocalCapture(valueRef, loc, declRef);
1899+
else
1900+
checkReference(nullptr, valueRef, loc, None, declRef);
18941901
return { true, expr };
18951902
}
18961903

@@ -1907,7 +1914,7 @@ namespace {
19071914
if (auto memberRef = findMemberReference(partialApply->fn)) {
19081915
// NOTE: partially-applied thunks are never annotated as
19091916
// implicitly async, regardless of whether they are escaping.
1910-
checkMemberReference(
1917+
checkReference(
19111918
partialApply->base, memberRef->first, memberRef->second,
19121919
partialApply);
19131920

@@ -1926,7 +1933,7 @@ namespace {
19261933
if (auto call = dyn_cast<SelfApplyExpr>(expr)) {
19271934
Expr *fn = call->getFn()->getValueProvidingExpr();
19281935
if (auto memberRef = findMemberReference(fn)) {
1929-
checkMemberReference(
1936+
checkReference(
19301937
call->getBase(), memberRef->first, memberRef->second,
19311938
/*partialApply=*/None, call);
19321939

@@ -2215,7 +2222,15 @@ namespace {
22152222
if (!var || var->isLet())
22162223
return false;
22172224

2218-
if (!var->getDeclContext()->isModuleScopeContext() && !var->isStatic())
2225+
if (!var->getDeclContext()->isModuleScopeContext() &&
2226+
!(var->getDeclContext()->isTypeContext() && !var->isInstanceMember()))
2227+
return false;
2228+
2229+
if (!var->hasStorage())
2230+
return false;
2231+
2232+
// If it's actor-isolated, it's already been dealt with.
2233+
if (getActorIsolation(value).isActorIsolated())
22192234
return false;
22202235

22212236
ctx.Diags.diagnose(
@@ -2604,97 +2619,6 @@ namespace {
26042619
return false;
26052620
}
26062621

2607-
/// Check a reference to an entity within a global actor.
2608-
bool checkGlobalActorReference(
2609-
ConcreteDeclRef valueRef, SourceLoc loc, Type globalActor,
2610-
bool isCrossActor,
2611-
Expr *context) {
2612-
ValueDecl *value = valueRef.getDecl();
2613-
auto declContext = const_cast<DeclContext *>(getDeclContext());
2614-
2615-
// Check whether we are within the same isolation context, in which
2616-
// case there is nothing further to check,
2617-
auto contextIsolation = getInnermostIsolatedContext(declContext);
2618-
if (contextIsolation.isGlobalActor() &&
2619-
contextIsolation.getGlobalActor()->isEqual(globalActor)) {
2620-
return false;
2621-
}
2622-
2623-
// A cross-actor access requires types to be concurrent-safe.
2624-
if (isCrossActor) {
2625-
return diagnoseNonSendableTypesInReference(
2626-
valueRef, getDeclContext(), loc,
2627-
SendableCheckReason::CrossActor);
2628-
}
2629-
2630-
// Call is implicitly asynchronous.
2631-
auto result = tryMarkImplicitlyAsync(
2632-
loc, valueRef, context,
2633-
ImplicitActorHopTarget::forGlobalActor(globalActor),
2634-
/*FIXME if we get global distributed actors*/false);
2635-
if (result == AsyncMarkingResult::FoundAsync)
2636-
return false;
2637-
2638-
// Diagnose failures.
2639-
switch (contextIsolation) {
2640-
case ActorIsolation::ActorInstance: {
2641-
auto useKind = static_cast<unsigned>(
2642-
kindOfUsage(value, context).getValueOr(VarRefUseEnv::Read));
2643-
2644-
ctx.Diags.diagnose(loc, diag::global_actor_from_instance_actor_context,
2645-
value->getDescriptiveKind(), value->getName(),
2646-
globalActor, contextIsolation.getActor()->getName(),
2647-
useKind, result == AsyncMarkingResult::SyncContext);
2648-
noteIsolatedActorMember(value, context);
2649-
return true;
2650-
}
2651-
2652-
case ActorIsolation::GlobalActor:
2653-
case ActorIsolation::GlobalActorUnsafe: {
2654-
auto useKind = static_cast<unsigned>(
2655-
kindOfUsage(value, context).getValueOr(VarRefUseEnv::Read));
2656-
2657-
// Otherwise, this is a problematic global actor decl reference.
2658-
ctx.Diags.diagnose(
2659-
loc, diag::global_actor_from_other_global_actor_context,
2660-
value->getDescriptiveKind(), value->getName(), globalActor,
2661-
contextIsolation.getGlobalActor(), useKind,
2662-
result == AsyncMarkingResult::SyncContext);
2663-
noteIsolatedActorMember(value, context);
2664-
return true;
2665-
}
2666-
2667-
case ActorIsolation::Independent: {
2668-
auto useKind = static_cast<unsigned>(
2669-
kindOfUsage(value, context).getValueOr(VarRefUseEnv::Read));
2670-
2671-
ctx.Diags.diagnose(loc, diag::global_actor_from_nonactor_context,
2672-
value->getDescriptiveKind(), value->getName(),
2673-
globalActor,
2674-
/*actorIndependent=*/true, useKind,
2675-
result == AsyncMarkingResult::SyncContext);
2676-
noteIsolatedActorMember(value, context);
2677-
return true;
2678-
}
2679-
2680-
case ActorIsolation::Unspecified: {
2681-
// Diagnose the reference.
2682-
auto useKind = static_cast<unsigned>(
2683-
kindOfUsage(value, context).getValueOr(VarRefUseEnv::Read));
2684-
ctx.Diags.diagnose(
2685-
loc, diag::global_actor_from_nonactor_context,
2686-
value->getDescriptiveKind(), value->getName(), globalActor,
2687-
/*actorIndependent=*/false, useKind,
2688-
result == AsyncMarkingResult::SyncContext);
2689-
noteGlobalActorOnContext(declContext, globalActor);
2690-
noteIsolatedActorMember(value, context);
2691-
2692-
return true;
2693-
} // end Unspecified case
2694-
} // end switch
2695-
llvm_unreachable("unhandled actor isolation kind!");
2696-
}
2697-
26982622
/// Find the innermost context in which this declaration was explicitly
26992623
/// captured.
27002624
const DeclContext *findCapturedDeclContext(ValueDecl *value) {
@@ -2874,65 +2798,38 @@ namespace {
28742798
return diagnosed;
28752799
}
28762800

2877-
/// Check a reference to a local or global.
2878-
bool checkNonMemberReference(
2879-
ConcreteDeclRef valueRef, SourceLoc loc, DeclRefExpr *declRefExpr) {
2880-
if (!valueRef)
2881-
return false;
2882-
2883-
auto value = valueRef.getDecl();
2884-
2885-
if (value->isLocalCapture())
2886-
return checkLocalCapture(valueRef, loc, declRefExpr);
2887-
2888-
switch (auto isolation =
2889-
ActorIsolationRestriction::forDeclaration(
2890-
valueRef, getDeclContext())) {
2891-
case ActorIsolationRestriction::Unrestricted:
2892-
return false;
2893-
2894-
case ActorIsolationRestriction::CrossActorSelf:
2895-
case ActorIsolationRestriction::ActorSelf:
2896-
llvm_unreachable("non-member reference into an actor");
2897-
2898-
case ActorIsolationRestriction::GlobalActorUnsafe:
2899-
// Only complain if we're in code that's adopted concurrency features.
2900-
if (!shouldDiagnoseExistingDataRaces(getDeclContext()))
2901-
return false;
2902-
2903-
LLVM_FALLTHROUGH;
2904-
2905-
case ActorIsolationRestriction::GlobalActor:
2906-
return checkGlobalActorReference(
2907-
valueRef, loc, isolation.getGlobalActor(), isolation.isCrossActor,
2908-
declRefExpr);
2909-
2910-
case ActorIsolationRestriction::Unsafe:
2911-
return diagnoseReferenceToUnsafeGlobal(value, loc);
2912-
}
2913-
llvm_unreachable("unhandled actor isolation kind!");
2914-
}
2915-
2916-
/// Check a reference with the given base expression to the given member.
2917-
/// Returns true iff the member reference refers to actor-isolated state
2918-
/// in an invalid or unsafe way such that a diagnostic was emitted.
2919-
bool checkMemberReference(
2920-
Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,
2801+
/// Check a reference to the given declaration.
2802+
///
2803+
/// \param base For a reference to a member, the base expression. May be
2804+
/// nullptr for non-member referenced.
2805+
///
2806+
/// \returns true if the reference is invalid, in which case a diagnostic
2807+
/// has already been emitted.
2808+
bool checkReference(
2809+
Expr *base, ConcreteDeclRef declRef, SourceLoc loc,
29212810
Optional<PartialApplyThunkInfo> partialApply = None,
29222811
Expr *context = nullptr) {
2923-
if (!base || !memberRef)
2812+
if (!declRef)
29242813
return false;
29252814

2926-
auto member = memberRef.getDecl();
2927-
auto isolatedActor = getIsolatedActor(base);
2815+
auto decl = declRef.getDecl();
2816+
Optional<ReferencedActor> isolatedActor;
2817+
if (base)
2818+
isolatedActor.emplace(getIsolatedActor(base));
29282819
auto result = ActorReferenceResult::forReference(
2929-
memberRef, memberLoc, getDeclContext(),
2930-
kindOfUsage(member, context), isolatedActor);
2820+
declRef, loc, getDeclContext(),
2821+
kindOfUsage(decl, context), isolatedActor);
29312822
switch (result) {
29322823
case ActorReferenceResult::SameConcurrencyDomain:
2824+
if (diagnoseReferenceToUnsafeGlobal(decl, loc))
2825+
return true;
2826+
29332827
return false;
29342828

29352829
case ActorReferenceResult::ExitsActorToNonisolated:
2830+
if (diagnoseReferenceToUnsafeGlobal(decl, loc))
2831+
return true;
2832+
29362833
// FIXME: SE-0338 would trigger Sendable checks here.
29372834
return false;
29382835

@@ -2949,24 +2846,24 @@ namespace {
29492846

29502847
// A call to a global-actor-isolated function is diagnosed elsewhere.
29512848
if (!partialApply && result.isolation.isGlobalActor() &&
2952-
isa<AbstractFunctionDecl>(member))
2849+
isa<AbstractFunctionDecl>(decl))
29532850
return false;
29542851

29552852
// An escaping partial application of something that is part of
29562853
// the actor's isolated state is never permitted.
2957-
if (partialApply && partialApply->isEscaping && !isAsyncDecl(memberRef)) {
2854+
if (partialApply && partialApply->isEscaping && !isAsyncDecl(declRef)) {
29582855
ctx.Diags.diagnose(
2959-
memberLoc, diag::actor_isolated_partial_apply,
2960-
member->getDescriptiveKind(),
2961-
member->getName());
2856+
loc, diag::actor_isolated_partial_apply,
2857+
decl->getDescriptiveKind(),
2858+
decl->getName());
29622859
return true;
29632860
}
29642861

29652862
// If we do not need any async/throws/distributed checks, just perform
29662863
// Sendable checking and we're done.
29672864
if (!result.options) {
29682865
return diagnoseNonSendableTypesInReference(
2969-
memberRef, getDeclContext(), memberLoc,
2866+
declRef, getDeclContext(), loc,
29702867
SendableCheckReason::CrossActor);
29712868
}
29722869

@@ -2980,7 +2877,7 @@ namespace {
29802877
result.isolation.getGlobalActor())
29812878
: ImplicitActorHopTarget::forInstanceSelf();
29822879
auto implicitAsyncResult = tryMarkImplicitlyAsync(
2983-
memberLoc, memberRef, context, target, isDistributed);
2880+
loc, declRef, context, target, isDistributed);
29842881
switch (implicitAsyncResult) {
29852882
case AsyncMarkingResult::FoundAsync:
29862883
// Success! We're done.
@@ -2995,18 +2892,44 @@ namespace {
29952892
case AsyncMarkingResult::NotFound:
29962893
// Complain about access outside of the isolation domain.
29972894
auto useKind = static_cast<unsigned>(
2998-
kindOfUsage(member, context).getValueOr(VarRefUseEnv::Read));
2895+
kindOfUsage(decl, context).getValueOr(VarRefUseEnv::Read));
2896+
2897+
ReferencedActor::Kind refKind;
2898+
Type refGlobalActor;
2899+
if (isolatedActor) {
2900+
refKind = isolatedActor->kind;
2901+
refGlobalActor = isolatedActor->globalActor;
2902+
} else {
2903+
auto contextIsolation = getInnermostIsolatedContext(getDeclContext());
2904+
switch (contextIsolation) {
2905+
case ActorIsolation::ActorInstance:
2906+
refKind = ReferencedActor::Isolated;
2907+
break;
2908+
2909+
case ActorIsolation::GlobalActor:
2910+
case ActorIsolation::GlobalActorUnsafe:
2911+
refGlobalActor = contextIsolation.getGlobalActor();
2912+
refKind = isMainActor(refGlobalActor)
2913+
? ReferencedActor::MainActor
2914+
: ReferencedActor::GlobalActor;
2915+
break;
2916+
2917+
case ActorIsolation::Unspecified:
2918+
case ActorIsolation::Independent:
2919+
refKind = ReferencedActor::NonIsolatedContext;
2920+
break;
2921+
}
2922+
}
29992923

30002924
ctx.Diags.diagnose(
3001-
memberLoc, diag::actor_isolated_non_self_reference,
3002-
member->getDescriptiveKind(),
3003-
member->getName(),
2925+
loc, diag::actor_isolated_non_self_reference,
2926+
decl->getDescriptiveKind(),
2927+
decl->getName(),
30042928
useKind,
3005-
isolatedActor.kind,
3006-
isolatedActor.globalActor,
2929+
refKind + 1, refGlobalActor,
30072930
result.isolation);
30082931

3009-
noteIsolatedActorMember(member, context);
2932+
noteIsolatedActorMember(decl, context);
30102933

30112934
if (result.isolation.isGlobalActor()) {
30122935
noteGlobalActorOnContext(

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,11 @@ func blender(_ peeler : () -> Void) {
278278
var money = await dollarsInBananaStand
279279
money -= 1200
280280

281-
dollarsInBananaStand = money // expected-error{{var 'dollarsInBananaStand' isolated to global actor 'BananaActor' can not be mutated from different global actor 'OrangeActor'}}
281+
dollarsInBananaStand = money // expected-error{{global actor 'BananaActor'-isolated var 'dollarsInBananaStand' can not be mutated from global actor 'OrangeActor'}}
282282

283283
// FIXME: these two errors seem a bit redundant.
284284
// expected-error@+2 {{actor-isolated var 'dollarsInBananaStand' cannot be passed 'inout' to implicitly 'async' function call}}
285-
// expected-error@+1 {{var 'dollarsInBananaStand' isolated to global actor 'BananaActor' can not be used 'inout' from different global actor 'OrangeActor'}}
285+
// expected-error@+1 {{global actor 'BananaActor'-isolated var 'dollarsInBananaStand' can not be used 'inout' from global actor 'OrangeActor'}}
286286
await takeInout(&dollarsInBananaStand)
287287

288288
_ = wisk
@@ -459,7 +459,7 @@ func tryEffPropsFromSync() {
459459
_ = effPropA // expected-error{{'async' property access in a function that does not support concurrency}}
460460

461461
// expected-error@+1 {{property access can throw, but it is not marked with 'try' and the error is not handled}}
462-
_ = effPropT // expected-error{{var 'effPropT' isolated to global actor 'BananaActor' can not be referenced from this synchronous context}}
462+
_ = effPropT // expected-error{{global actor 'BananaActor'-isolated var 'effPropT' can not be referenced from a non-isolated context}}
463463
// NOTE: that we don't complain about async access on `effPropT` because it's not declared async, and we're not in an async context!
464464

465465
// expected-error@+1 {{property access can throw, but it is not marked with 'try' and the error is not handled}}

0 commit comments

Comments
 (0)