@@ -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