@@ -1272,6 +1272,10 @@ namespace {
1272
1272
// It is within a nonisolated context.
1273
1273
NonIsolatedContext,
1274
1274
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
1275
1279
};
1276
1280
1277
1281
VarDecl * const actor;
@@ -1351,7 +1355,7 @@ namespace {
1351
1355
// /
1352
1356
// / @returns None if the context expression is either an InOutExpr,
1353
1357
// / 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 {
1355
1359
// we need a use for lookup.
1356
1360
if (!use)
1357
1361
return None;
@@ -1751,6 +1755,16 @@ namespace {
1751
1755
auto var = getReferencedParamOrCapture (expr);
1752
1756
bool isPotentiallyIsolated = isPotentiallyIsolatedActor (var);
1753
1757
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
+
1754
1768
// Walk the scopes between the variable reference and the variable
1755
1769
// declaration to determine whether it is still isolated.
1756
1770
auto dc = const_cast <DeclContext *>(getDeclContext ());
@@ -1774,7 +1788,7 @@ namespace {
1774
1788
return ReferencedActor (var, isPotentiallyIsolated, ReferencedActor::SendableClosure);
1775
1789
}
1776
1790
1777
- return ReferencedActor (var, isPotentiallyIsolated, ReferencedActor::NonIsolatedContext );
1791
+ return ReferencedActor (var, isPotentiallyIsolated, specificNonIsoClosureKind (dc) );
1778
1792
1779
1793
case ClosureActorIsolation::ActorInstance:
1780
1794
// If the closure is isolated to the same variable, we're all set.
@@ -1786,7 +1800,7 @@ namespace {
1786
1800
return ReferencedActor (var, isPotentiallyIsolated, ReferencedActor::Isolated);
1787
1801
}
1788
1802
1789
- return ReferencedActor (var, isPotentiallyIsolated, ReferencedActor::NonIsolatedContext );
1803
+ return ReferencedActor (var, isPotentiallyIsolated, specificNonIsoClosureKind (dc) );
1790
1804
1791
1805
case ClosureActorIsolation::GlobalActor:
1792
1806
return ReferencedActor::forGlobalActor (
@@ -1939,7 +1953,7 @@ namespace {
1939
1953
1940
1954
// / Note that the given actor member is isolated.
1941
1955
// / @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) {
1943
1957
// detect if it is a distributed actor, to provide better isolation notes
1944
1958
1945
1959
auto nominal = decl->getDeclContext ()->getSelfNominalTypeDecl ();
@@ -2693,40 +2707,124 @@ namespace {
2693
2707
return false ;
2694
2708
}
2695
2709
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) {
2700
2759
// no need for this in Swift 6+
2701
2760
if (refCxt->getASTContext ().isSwiftVersionAtLeast (6 ))
2702
2761
return false ;
2703
2762
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.
2706
2792
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);
2722
2794
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))
2728
2809
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 ;
2730
2828
}
2731
2829
2732
2830
return false ;
@@ -2743,10 +2841,11 @@ namespace {
2743
2841
// /
2744
2842
// / \returns true iff the member access is permitted in Sema because it will
2745
2843
// / be verified later by flow-isolation.
2746
- static bool checkedByFlowIsolation (DeclContext const *refCxt,
2844
+ bool checkedByFlowIsolation (DeclContext const *refCxt,
2747
2845
ReferencedActor &baseActor,
2748
2846
ValueDecl const *member,
2749
- SourceLoc memberLoc) {
2847
+ SourceLoc memberLoc,
2848
+ Expr *exprCxt) {
2750
2849
2751
2850
// base of member reference must be `self`
2752
2851
if (!baseActor.isActorSelf ())
@@ -2774,7 +2873,8 @@ namespace {
2774
2873
break ;
2775
2874
}
2776
2875
2777
- if (memberAccessWasAllowedInSwift5 (refCxt, member, memberLoc))
2876
+ if (memberAccessHasSpecialPermissionInSwift5 (refCxt, baseActor, member,
2877
+ memberLoc, exprCxt))
2778
2878
return true ; // then permit it now.
2779
2879
2780
2880
if (!usesFlowSensitiveIsolation (fnDecl))
@@ -2888,7 +2988,7 @@ namespace {
2888
2988
// access an isolated member on `self`. If that case applies, then we
2889
2989
// can skip checking.
2890
2990
if (checkedByFlowIsolation (getDeclContext (), isolatedActor,
2891
- member, memberLoc))
2991
+ member, memberLoc, context ))
2892
2992
return false ;
2893
2993
2894
2994
// An escaping partial application of something that is part of
0 commit comments