Skip to content

Commit bfff4ba

Browse files
authored
Merge pull request swiftlang#41946 from kavon/warn-actorisolated-in-nonisolated
Downgrade more errors into warnings for actor inits.
2 parents 398de94 + 9604304 commit bfff4ba

File tree

4 files changed

+189
-63
lines changed

4 files changed

+189
-63
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4517,17 +4517,13 @@ NOTE(note_distributed_actor_system_conformance_missing_adhoc_requirement,none,
45174517
ERROR(override_implicit_unowned_executor,none,
45184518
"cannot override an actor's 'unownedExecutor' property that wasn't "
45194519
"explicitly defined", ())
4520-
ERROR(actor_isolated_from_decl,none,
4521-
"actor-isolated %0 %1 can not be referenced from a non-isolated "
4522-
"%select{deinit|autoclosure|closure}2",
4523-
(DescriptiveDeclKind, DeclName, unsigned))
45244520
ERROR(actor_isolated_non_self_reference,none,
45254521
"actor-isolated %0 %1 can not be "
45264522
"%select{referenced|mutated|used 'inout'}2 "
45274523
"%select{on a non-isolated actor instance|"
45284524
"from a Sendable function|from a Sendable closure|"
45294525
"from an 'async let' initializer|from global actor %4|"
4530-
"from the main actor|from a non-isolated context}3",
4526+
"from the main actor|from a non-isolated context|from a non-isolated autoclosure}3",
45314527
(DescriptiveDeclKind, DeclName, unsigned, unsigned, Type))
45324528
ERROR(distributed_actor_isolated_non_self_reference,none,
45334529
"distributed actor-isolated %0 %1 can not be accessed from a "

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 135 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,10 @@ namespace {
12721272
// It is within a nonisolated context.
12731273
NonIsolatedContext,
12741274

1275+
// It is within a nonisolated autoclosure argument. This is primarily here
1276+
// to aid in giving specific diagnostics, because autoclosures are not
1277+
// always easy for programmers to notice.
1278+
NonIsolatedAutoclosure
12751279
};
12761280

12771281
VarDecl * const actor;
@@ -1351,7 +1355,7 @@ namespace {
13511355
///
13521356
/// @returns None if the context expression is either an InOutExpr,
13531357
/// not tracked, or if the decl is not a property or subscript
1354-
Optional<VarRefUseEnv> kindOfUsage(ValueDecl *decl, Expr *use) const {
1358+
Optional<VarRefUseEnv> kindOfUsage(ValueDecl const* decl, Expr *use) const {
13551359
// we need a use for lookup.
13561360
if (!use)
13571361
return None;
@@ -1751,6 +1755,16 @@ namespace {
17511755
auto var = getReferencedParamOrCapture(expr);
17521756
bool isPotentiallyIsolated = isPotentiallyIsolatedActor(var);
17531757

1758+
// helps aid in giving more informative diagnostics for autoclosure args.
1759+
auto specificNonIsoClosureKind =
1760+
[](DeclContext const* dc) -> ReferencedActor::Kind {
1761+
if (auto autoClos = dyn_cast<AutoClosureExpr>(dc))
1762+
if (autoClos->getThunkKind() == AutoClosureExpr::Kind::None)
1763+
return ReferencedActor::NonIsolatedAutoclosure;
1764+
1765+
return ReferencedActor::NonIsolatedContext;
1766+
};
1767+
17541768
// Walk the scopes between the variable reference and the variable
17551769
// declaration to determine whether it is still isolated.
17561770
auto dc = const_cast<DeclContext *>(getDeclContext());
@@ -1774,7 +1788,7 @@ namespace {
17741788
return ReferencedActor(var, isPotentiallyIsolated, ReferencedActor::SendableClosure);
17751789
}
17761790

1777-
return ReferencedActor(var, isPotentiallyIsolated, ReferencedActor::NonIsolatedContext);
1791+
return ReferencedActor(var, isPotentiallyIsolated, specificNonIsoClosureKind(dc));
17781792

17791793
case ClosureActorIsolation::ActorInstance:
17801794
// If the closure is isolated to the same variable, we're all set.
@@ -1786,7 +1800,7 @@ namespace {
17861800
return ReferencedActor(var, isPotentiallyIsolated, ReferencedActor::Isolated);
17871801
}
17881802

1789-
return ReferencedActor(var, isPotentiallyIsolated, ReferencedActor::NonIsolatedContext);
1803+
return ReferencedActor(var, isPotentiallyIsolated, specificNonIsoClosureKind(dc));
17901804

17911805
case ClosureActorIsolation::GlobalActor:
17921806
return ReferencedActor::forGlobalActor(
@@ -1939,7 +1953,7 @@ namespace {
19391953

19401954
/// Note that the given actor member is isolated.
19411955
/// @param context is allowed to be null if no context is appropriate.
1942-
void noteIsolatedActorMember(ValueDecl *decl, Expr *context) {
1956+
void noteIsolatedActorMember(ValueDecl const* decl, Expr *context) {
19431957
// detect if it is a distributed actor, to provide better isolation notes
19441958

19451959
auto nominal = decl->getDeclContext()->getSelfNominalTypeDecl();
@@ -2693,40 +2707,124 @@ namespace {
26932707
return false;
26942708
}
26952709

2696-
/// an ad-hoc check specific to member isolation checking.
2697-
static bool memberAccessWasAllowedInSwift5(DeclContext const *refCxt,
2698-
ValueDecl const *member,
2699-
SourceLoc memberLoc) {
2710+
/// Based on the former escaping-use restriction, which was replaced by
2711+
/// flow-isolation. We need this to support backwards compatability in the
2712+
/// type-checker for programs prior to Swift 6.
2713+
/// \param fn either a constructor or destructor of an actor.
2714+
static bool wasLegacyEscapingUseRestriction(AbstractFunctionDecl *fn) {
2715+
assert(fn->getDeclContext()->getSelfClassDecl()->isAnyActor());
2716+
assert(isa<ConstructorDecl>(fn) || isa<DestructorDecl>(fn));
2717+
2718+
// according to today's isolation, determine whether it use to have the
2719+
// escaping-use restriction
2720+
switch (getActorIsolation(fn).getKind()) {
2721+
case ActorIsolation::Independent:
2722+
case ActorIsolation::GlobalActor:
2723+
case ActorIsolation::GlobalActorUnsafe:
2724+
// convenience inits did not have the restriction.
2725+
if (auto *ctor = dyn_cast<ConstructorDecl>(fn))
2726+
if (ctor->isConvenienceInit())
2727+
return false;
2728+
2729+
break; // goto basic case
2730+
2731+
case ActorIsolation::ActorInstance:
2732+
// none of these had the restriction affect them.
2733+
assert(fn->hasAsync());
2734+
return false;
2735+
2736+
case ActorIsolation::Unspecified:
2737+
// this is basically just objc-marked inits.
2738+
break;
2739+
};
2740+
2741+
return !(fn->hasAsync()); // basic case: not async = had restriction.
2742+
}
2743+
2744+
/// An ad-hoc check specific to member isolation checking. assumed to be
2745+
/// queried when a self-member is being accessed in a context which is not
2746+
/// isolated to self. The "special permission" is part of a backwards
2747+
/// compatability with actor inits and deinits that maintains the
2748+
/// permissive nature of the escaping-use restriction, which was only
2749+
/// staged in as a warning. See implementation for more details.
2750+
///
2751+
/// \returns true if this access in the given context should be allowed
2752+
/// in Sema, with the side-effect of emitting a warning as needed.
2753+
/// If false is returned, then the "special permission" was not granted.
2754+
bool memberAccessHasSpecialPermissionInSwift5(DeclContext const *refCxt,
2755+
ReferencedActor &baseActor,
2756+
ValueDecl const *member,
2757+
SourceLoc memberLoc,
2758+
Expr *exprCxt) {
27002759
// no need for this in Swift 6+
27012760
if (refCxt->getASTContext().isSwiftVersionAtLeast(6))
27022761
return false;
27032762

2704-
// In Swift 5, we were allowing all members to be referenced from a
2705-
// deinit, nested within a wide variety of contexts.
2763+
// must be an access to an instance member.
2764+
if (!member->isInstanceMember())
2765+
return false;
2766+
2767+
// In the history of actor initializers prior to Swift 6, self-isolated
2768+
// members could be referenced from any init or deinit, even a synchronous
2769+
// one, with no diagnostics at all.
2770+
//
2771+
// When the escaping-use restriction came into place for the release of
2772+
// 5.5, it was implemented as a warning and only applied to initializers,
2773+
// which stated that it would become an error in Swift 6.
2774+
//
2775+
// Once 5.6 was released, we also added restrictions in the deinits of
2776+
// actors, at least for accessing members other than stored properties.
2777+
//
2778+
// Later on, for 5.7 we introduced flow-isolation as part of SE-327 for
2779+
// both inits and deinits. This meant that stored property accesses now
2780+
// are only sometimes going to be problematic. This change also brought
2781+
// official changes in isolation for the inits and deinits to handle the
2782+
// the non-stored-property members. Since those isolation changes are
2783+
// currently in place, the purpose of the code below is to override the
2784+
// isolation checking, so that the now-mismatched isolation on member
2785+
// access is still permitted, but with a warning stating that it will
2786+
// be rejected in Swift 6.
2787+
//
2788+
// In the checking below, we let stored-property accesses go ignored,
2789+
// so that flow-isolation can warn about them only if needed. This helps
2790+
// prevent needless warnings on property accesses that will actually be OK
2791+
// with flow-isolation in the future.
27062792
if (auto oldFn = isActorInitOrDeInitContext(refCxt)) {
2707-
if (isa<DestructorDecl>(oldFn) && member->isInstanceMember()) {
2708-
auto &diags = refCxt->getASTContext().Diags;
2709-
2710-
// if the context in which we consider the access matches between
2711-
// old and new, and its a stored property, then skip the warning
2712-
// because it will still be allowed in Swift 6.
2713-
if (!(refCxt == oldFn && isStoredProperty(member))) {
2714-
unsigned cxtKind = 0; // deinit
2715-
2716-
// try to get a better name for this context.
2717-
if (isa<AutoClosureExpr>(refCxt)) {
2718-
cxtKind = 1;
2719-
} else if (isa<AbstractClosureExpr>(refCxt)) {
2720-
cxtKind = 2;
2721-
}
2793+
auto oldFnMut = const_cast<AbstractFunctionDecl*>(oldFn);
27222794

2723-
diags.diagnose(memberLoc, diag::actor_isolated_from_decl,
2724-
member->getDescriptiveKind(),
2725-
member->getName(),
2726-
cxtKind).warnUntilSwiftVersion(6);
2727-
}
2795+
// If function did not have the escaping-use restriction, then it gets
2796+
// no special permissions here.
2797+
if (!wasLegacyEscapingUseRestriction(oldFnMut))
2798+
return false;
2799+
2800+
// At this point, the special permission will be granted. But, we
2801+
// need to warn now about this permission being taken away in Swift 6
2802+
// for specific kinds of non-stored-property member accesses:
2803+
2804+
// If the context in which we consider the access matches between the
2805+
// old (escaping-use restriction) and new (flow-isolation) contexts,
2806+
// and it is a stored property, then permit it here without any warning.
2807+
// Later, flow-isolation pass will check and emit a warning if needed.
2808+
if (refCxt == oldFn && isStoredProperty(member))
27282809
return true;
2729-
}
2810+
2811+
2812+
// Otherwise, it's definitely going to be illegal, so warn and permit.
2813+
auto &diags = refCxt->getASTContext().Diags;
2814+
auto useKind = static_cast<unsigned>(
2815+
kindOfUsage(member, exprCxt).getValueOr(VarRefUseEnv::Read));
2816+
2817+
diags.diagnose(
2818+
memberLoc, diag::actor_isolated_non_self_reference,
2819+
member->getDescriptiveKind(),
2820+
member->getName(),
2821+
useKind,
2822+
baseActor.kind - 1,
2823+
baseActor.globalActor)
2824+
.warnUntilSwiftVersion(6);
2825+
2826+
noteIsolatedActorMember(member, exprCxt);
2827+
return true;
27302828
}
27312829

27322830
return false;
@@ -2743,10 +2841,11 @@ namespace {
27432841
///
27442842
/// \returns true iff the member access is permitted in Sema because it will
27452843
/// be verified later by flow-isolation.
2746-
static bool checkedByFlowIsolation(DeclContext const *refCxt,
2844+
bool checkedByFlowIsolation(DeclContext const *refCxt,
27472845
ReferencedActor &baseActor,
27482846
ValueDecl const *member,
2749-
SourceLoc memberLoc) {
2847+
SourceLoc memberLoc,
2848+
Expr *exprCxt) {
27502849

27512850
// base of member reference must be `self`
27522851
if (!baseActor.isActorSelf())
@@ -2774,7 +2873,8 @@ namespace {
27742873
break;
27752874
}
27762875

2777-
if (memberAccessWasAllowedInSwift5(refCxt, member, memberLoc))
2876+
if (memberAccessHasSpecialPermissionInSwift5(refCxt, baseActor, member,
2877+
memberLoc, exprCxt))
27782878
return true; // then permit it now.
27792879

27802880
if (!usesFlowSensitiveIsolation(fnDecl))
@@ -2888,7 +2988,7 @@ namespace {
28882988
// access an isolated member on `self`. If that case applies, then we
28892989
// can skip checking.
28902990
if (checkedByFlowIsolation(getDeclContext(), isolatedActor,
2891-
member, memberLoc))
2991+
member, memberLoc, context))
28922992
return false;
28932993

28942994
// An escaping partial application of something that is part of

0 commit comments

Comments
 (0)