Skip to content

Commit c8ffc30

Browse files
committed
Switch non-member actor isolation checking to the new implementation
(cherry picked from commit 1b27c48)
1 parent fd76c8c commit c8ffc30

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
@@ -4560,7 +4560,7 @@ ERROR(override_implicit_unowned_executor,none,
45604560
ERROR(actor_isolated_non_self_reference,none,
45614561
"%5 %0 %1 can not be "
45624562
"%select{referenced|mutated|used 'inout'}2 "
4563-
"%select{on a different actor instance|"
4563+
"%select{from outside the actor|on a different actor instance|"
45644564
"on a non-isolated actor instance|"
45654565
"from a Sendable function|from a Sendable closure|"
45664566
"from an 'async let' initializer|from global actor %4|"
@@ -4582,18 +4582,6 @@ ERROR(actor_isolated_inout_state,none,
45824582
ERROR(actor_isolated_mutating_func,none,
45834583
"cannot call mutating async function %0 on actor-isolated %1 %2",
45844584
(DeclName, DescriptiveDeclKind, DeclName))
4585-
ERROR(global_actor_from_instance_actor_context,none,
4586-
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4587-
" from actor %3 %select{|in a synchronous context}5",
4588-
(DescriptiveDeclKind, DeclName, Type, DeclName, unsigned, bool))
4589-
ERROR(global_actor_from_other_global_actor_context,none,
4590-
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4591-
" from different global actor %3 %select{|in a synchronous context}5",
4592-
(DescriptiveDeclKind, DeclName, Type, Type, unsigned, bool))
4593-
ERROR(global_actor_from_nonactor_context,none,
4594-
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4595-
" from %select{this|a non-isolated}3%select{| synchronous}5 context",
4596-
(DescriptiveDeclKind, DeclName, Type, bool, unsigned, bool))
45974585
ERROR(actor_isolated_call,none,
45984586
"call to %0 function in a synchronous %1 context",
45994587
(ActorIsolation, ActorIsolation))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 84 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,7 +1501,7 @@ static bool memberAccessHasSpecialPermissionInSwift5(DeclContext const *refCxt,
15011501
member->getDescriptiveKind(),
15021502
member->getName(),
15031503
useKindInt,
1504-
baseActor.kind,
1504+
baseActor.kind + 1,
15051505
baseActor.globalActor,
15061506
getActorIsolation(const_cast<ValueDecl *>(member)))
15071507
.warnUntilSwiftVersion(6);
@@ -1935,16 +1935,23 @@ namespace {
19351935
recordMutableVarParent(load, load->getSubExpr());
19361936

19371937
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
1938-
checkMemberReference(lookup->getBase(), lookup->getMember(),
1938+
checkReference(lookup->getBase(), lookup->getMember(),
19391939
lookup->getLoc(),
19401940
/*partialApply*/None,
19411941
lookup);
19421942
return { true, expr };
19431943
}
19441944

19451945
if (auto declRef = dyn_cast<DeclRefExpr>(expr)) {
1946-
checkNonMemberReference(
1947-
declRef->getDeclRef(), declRef->getLoc(), declRef);
1946+
auto valueRef = declRef->getDeclRef();
1947+
auto value = valueRef.getDecl();
1948+
auto loc = declRef->getLoc();
1949+
1950+
//FIXME: Should this be subsumed in reference checking?
1951+
if (value->isLocalCapture())
1952+
checkLocalCapture(valueRef, loc, declRef);
1953+
else
1954+
checkReference(nullptr, valueRef, loc, None, declRef);
19481955
return { true, expr };
19491956
}
19501957

@@ -1961,7 +1968,7 @@ namespace {
19611968
if (auto memberRef = findMemberReference(partialApply->fn)) {
19621969
// NOTE: partially-applied thunks are never annotated as
19631970
// implicitly async, regardless of whether they are escaping.
1964-
checkMemberReference(
1971+
checkReference(
19651972
partialApply->base, memberRef->first, memberRef->second,
19661973
partialApply);
19671974

@@ -1980,7 +1987,7 @@ namespace {
19801987
if (auto call = dyn_cast<SelfApplyExpr>(expr)) {
19811988
Expr *fn = call->getFn()->getValueProvidingExpr();
19821989
if (auto memberRef = findMemberReference(fn)) {
1983-
checkMemberReference(
1990+
checkReference(
19841991
call->getBase(), memberRef->first, memberRef->second,
19851992
/*partialApply=*/None, call);
19861993

@@ -2276,7 +2283,15 @@ namespace {
22762283
if (!var || var->isLet())
22772284
return false;
22782285

2279-
if (!var->getDeclContext()->isModuleScopeContext() && !var->isStatic())
2286+
if (!var->getDeclContext()->isModuleScopeContext() &&
2287+
!(var->getDeclContext()->isTypeContext() && !var->isInstanceMember()))
2288+
return false;
2289+
2290+
if (!var->hasStorage())
2291+
return false;
2292+
2293+
// If it's actor-isolated, it's already been dealt with.
2294+
if (getActorIsolation(value).isActorIsolated())
22802295
return false;
22812296

22822297
ctx.Diags.diagnose(
@@ -2665,97 +2680,6 @@ namespace {
26652680
return false;
26662681
}
26672682

2668-
/// Check a reference to an entity within a global actor.
2669-
bool checkGlobalActorReference(
2670-
ConcreteDeclRef valueRef, SourceLoc loc, Type globalActor,
2671-
bool isCrossActor,
2672-
Expr *context) {
2673-
ValueDecl *value = valueRef.getDecl();
2674-
auto declContext = const_cast<DeclContext *>(getDeclContext());
2675-
2676-
// Check whether we are within the same isolation context, in which
2677-
// case there is nothing further to check,
2678-
auto contextIsolation = getInnermostIsolatedContext(declContext);
2679-
if (contextIsolation.isGlobalActor() &&
2680-
contextIsolation.getGlobalActor()->isEqual(globalActor)) {
2681-
return false;
2682-
}
2683-
2684-
// A cross-actor access requires types to be concurrent-safe.
2685-
if (isCrossActor) {
2686-
return diagnoseNonSendableTypesInReference(
2687-
valueRef, getDeclContext(), loc,
2688-
SendableCheckReason::CrossActor);
2689-
}
2690-
2691-
// Call is implicitly asynchronous.
2692-
auto result = tryMarkImplicitlyAsync(
2693-
loc, valueRef, context,
2694-
ImplicitActorHopTarget::forGlobalActor(globalActor),
2695-
/*FIXME if we get global distributed actors*/false);
2696-
if (result == AsyncMarkingResult::FoundAsync)
2697-
return false;
2698-
2699-
// Diagnose failures.
2700-
switch (contextIsolation) {
2701-
case ActorIsolation::ActorInstance: {
2702-
auto useKind = static_cast<unsigned>(
2703-
kindOfUsage(value, context).getValueOr(VarRefUseEnv::Read));
2704-
2705-
ctx.Diags.diagnose(loc, diag::global_actor_from_instance_actor_context,
2706-
value->getDescriptiveKind(), value->getName(),
2707-
globalActor, contextIsolation.getActor()->getName(),
2708-
useKind, result == AsyncMarkingResult::SyncContext);
2709-
noteIsolatedActorMember(value, context);
2710-
return true;
2711-
}
2712-
2713-
case ActorIsolation::GlobalActor:
2714-
case ActorIsolation::GlobalActorUnsafe: {
2715-
auto useKind = static_cast<unsigned>(
2716-
kindOfUsage(value, context).getValueOr(VarRefUseEnv::Read));
2717-
2718-
// Otherwise, this is a problematic global actor decl reference.
2719-
ctx.Diags.diagnose(
2720-
loc, diag::global_actor_from_other_global_actor_context,
2721-
value->getDescriptiveKind(), value->getName(), globalActor,
2722-
contextIsolation.getGlobalActor(), useKind,
2723-
result == AsyncMarkingResult::SyncContext);
2724-
noteIsolatedActorMember(value, context);
2725-
return true;
2726-
}
2727-
2728-
case ActorIsolation::Independent: {
2729-
auto useKind = static_cast<unsigned>(
2730-
kindOfUsage(value, context).getValueOr(VarRefUseEnv::Read));
2731-
2732-
ctx.Diags.diagnose(loc, diag::global_actor_from_nonactor_context,
2733-
value->getDescriptiveKind(), value->getName(),
2734-
globalActor,
2735-
/*actorIndependent=*/true, useKind,
2736-
result == AsyncMarkingResult::SyncContext);
2737-
noteIsolatedActorMember(value, context);
2738-
return true;
2739-
}
2740-
2741-
case ActorIsolation::Unspecified: {
2742-
// Diagnose the reference.
2743-
auto useKind = static_cast<unsigned>(
2744-
kindOfUsage(value, context).getValueOr(VarRefUseEnv::Read));
2745-
ctx.Diags.diagnose(
2746-
loc, diag::global_actor_from_nonactor_context,
2747-
value->getDescriptiveKind(), value->getName(), globalActor,
2748-
/*actorIndependent=*/false, useKind,
2749-
result == AsyncMarkingResult::SyncContext);
2750-
noteGlobalActorOnContext(declContext, globalActor);
2751-
noteIsolatedActorMember(value, context);
2752-
2753-
return true;
2754-
} // end Unspecified case
2755-
} // end switch
2756-
llvm_unreachable("unhandled actor isolation kind!");
2757-
}
2758-
27592683
/// Find the innermost context in which this declaration was explicitly
27602684
/// captured.
27612685
const DeclContext *findCapturedDeclContext(ValueDecl *value) {
@@ -2936,65 +2860,38 @@ namespace {
29362860
return diagnosed;
29372861
}
29382862

2939-
/// Check a reference to a local or global.
2940-
bool checkNonMemberReference(
2941-
ConcreteDeclRef valueRef, SourceLoc loc, DeclRefExpr *declRefExpr) {
2942-
if (!valueRef)
2943-
return false;
2944-
2945-
auto value = valueRef.getDecl();
2946-
2947-
if (value->isLocalCapture())
2948-
return checkLocalCapture(valueRef, loc, declRefExpr);
2949-
2950-
switch (auto isolation =
2951-
ActorIsolationRestriction::forDeclaration(
2952-
valueRef, getDeclContext())) {
2953-
case ActorIsolationRestriction::Unrestricted:
2954-
return false;
2955-
2956-
case ActorIsolationRestriction::CrossActorSelf:
2957-
case ActorIsolationRestriction::ActorSelf:
2958-
llvm_unreachable("non-member reference into an actor");
2959-
2960-
case ActorIsolationRestriction::GlobalActorUnsafe:
2961-
// Only complain if we're in code that's adopted concurrency features.
2962-
if (!shouldDiagnoseExistingDataRaces(getDeclContext()))
2963-
return false;
2964-
2965-
LLVM_FALLTHROUGH;
2966-
2967-
case ActorIsolationRestriction::GlobalActor:
2968-
return checkGlobalActorReference(
2969-
valueRef, loc, isolation.getGlobalActor(), isolation.isCrossActor,
2970-
declRefExpr);
2971-
2972-
case ActorIsolationRestriction::Unsafe:
2973-
return diagnoseReferenceToUnsafeGlobal(value, loc);
2974-
}
2975-
llvm_unreachable("unhandled actor isolation kind!");
2976-
}
2977-
2978-
/// Check a reference with the given base expression to the given member.
2979-
/// Returns true iff the member reference refers to actor-isolated state
2980-
/// in an invalid or unsafe way such that a diagnostic was emitted.
2981-
bool checkMemberReference(
2982-
Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,
2863+
/// Check a reference to the given declaration.
2864+
///
2865+
/// \param base For a reference to a member, the base expression. May be
2866+
/// nullptr for non-member referenced.
2867+
///
2868+
/// \returns true if the reference is invalid, in which case a diagnostic
2869+
/// has already been emitted.
2870+
bool checkReference(
2871+
Expr *base, ConcreteDeclRef declRef, SourceLoc loc,
29832872
Optional<PartialApplyThunkInfo> partialApply = None,
29842873
Expr *context = nullptr) {
2985-
if (!base || !memberRef)
2874+
if (!declRef)
29862875
return false;
29872876

2988-
auto member = memberRef.getDecl();
2989-
auto isolatedActor = getIsolatedActor(base);
2877+
auto decl = declRef.getDecl();
2878+
Optional<ReferencedActor> isolatedActor;
2879+
if (base)
2880+
isolatedActor.emplace(getIsolatedActor(base));
29902881
auto result = ActorReferenceResult::forReference(
2991-
memberRef, memberLoc, getDeclContext(),
2992-
kindOfUsage(member, context), isolatedActor);
2882+
declRef, loc, getDeclContext(),
2883+
kindOfUsage(decl, context), isolatedActor);
29932884
switch (result) {
29942885
case ActorReferenceResult::SameConcurrencyDomain:
2886+
if (diagnoseReferenceToUnsafeGlobal(decl, loc))
2887+
return true;
2888+
29952889
return false;
29962890

29972891
case ActorReferenceResult::ExitsActorToNonisolated:
2892+
if (diagnoseReferenceToUnsafeGlobal(decl, loc))
2893+
return true;
2894+
29982895
// FIXME: SE-0338 would trigger Sendable checks here.
29992896
return false;
30002897

@@ -3011,24 +2908,24 @@ namespace {
30112908

30122909
// A call to a global-actor-isolated function is diagnosed elsewhere.
30132910
if (!partialApply && result.isolation.isGlobalActor() &&
3014-
isa<AbstractFunctionDecl>(member))
2911+
isa<AbstractFunctionDecl>(decl))
30152912
return false;
30162913

30172914
// An escaping partial application of something that is part of
30182915
// the actor's isolated state is never permitted.
3019-
if (partialApply && partialApply->isEscaping && !isAsyncDecl(memberRef)) {
2916+
if (partialApply && partialApply->isEscaping && !isAsyncDecl(declRef)) {
30202917
ctx.Diags.diagnose(
3021-
memberLoc, diag::actor_isolated_partial_apply,
3022-
member->getDescriptiveKind(),
3023-
member->getName());
2918+
loc, diag::actor_isolated_partial_apply,
2919+
decl->getDescriptiveKind(),
2920+
decl->getName());
30242921
return true;
30252922
}
30262923

30272924
// If we do not need any async/throws/distributed checks, just perform
30282925
// Sendable checking and we're done.
30292926
if (!result.options) {
30302927
return diagnoseNonSendableTypesInReference(
3031-
memberRef, getDeclContext(), memberLoc,
2928+
declRef, getDeclContext(), loc,
30322929
SendableCheckReason::CrossActor);
30332930
}
30342931

@@ -3042,7 +2939,7 @@ namespace {
30422939
result.isolation.getGlobalActor())
30432940
: ImplicitActorHopTarget::forInstanceSelf();
30442941
auto implicitAsyncResult = tryMarkImplicitlyAsync(
3045-
memberLoc, memberRef, context, target, isDistributed);
2942+
loc, declRef, context, target, isDistributed);
30462943
switch (implicitAsyncResult) {
30472944
case AsyncMarkingResult::FoundAsync:
30482945
// Success! We're done.
@@ -3057,18 +2954,44 @@ namespace {
30572954
case AsyncMarkingResult::NotFound:
30582955
// Complain about access outside of the isolation domain.
30592956
auto useKind = static_cast<unsigned>(
3060-
kindOfUsage(member, context).getValueOr(VarRefUseEnv::Read));
2957+
kindOfUsage(decl, context).getValueOr(VarRefUseEnv::Read));
2958+
2959+
ReferencedActor::Kind refKind;
2960+
Type refGlobalActor;
2961+
if (isolatedActor) {
2962+
refKind = isolatedActor->kind;
2963+
refGlobalActor = isolatedActor->globalActor;
2964+
} else {
2965+
auto contextIsolation = getInnermostIsolatedContext(getDeclContext());
2966+
switch (contextIsolation) {
2967+
case ActorIsolation::ActorInstance:
2968+
refKind = ReferencedActor::Isolated;
2969+
break;
2970+
2971+
case ActorIsolation::GlobalActor:
2972+
case ActorIsolation::GlobalActorUnsafe:
2973+
refGlobalActor = contextIsolation.getGlobalActor();
2974+
refKind = isMainActor(refGlobalActor)
2975+
? ReferencedActor::MainActor
2976+
: ReferencedActor::GlobalActor;
2977+
break;
2978+
2979+
case ActorIsolation::Unspecified:
2980+
case ActorIsolation::Independent:
2981+
refKind = ReferencedActor::NonIsolatedContext;
2982+
break;
2983+
}
2984+
}
30612985

30622986
ctx.Diags.diagnose(
3063-
memberLoc, diag::actor_isolated_non_self_reference,
3064-
member->getDescriptiveKind(),
3065-
member->getName(),
2987+
loc, diag::actor_isolated_non_self_reference,
2988+
decl->getDescriptiveKind(),
2989+
decl->getName(),
30662990
useKind,
3067-
isolatedActor.kind,
3068-
isolatedActor.globalActor,
2991+
refKind + 1, refGlobalActor,
30692992
result.isolation);
30702993

3071-
noteIsolatedActorMember(member, context);
2994+
noteIsolatedActorMember(decl, context);
30722995

30732996
if (result.isolation.isGlobalActor()) {
30742997
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)