@@ -2041,48 +2041,41 @@ namespace {
2041
2041
contextStack.push_back (dc);
2042
2042
}
2043
2043
2044
- // / Searches the applyStack from back to front for the inner-most CallExpr
2045
- // / and marks that CallExpr as implicitly async.
2046
- // /
2047
- // / NOTE: Crashes if no CallExpr was found.
2048
- // /
2049
- // / For example, for global actor function `curryAdd`, if we have:
2050
- // / ((curryAdd 1) 2)
2051
- // / then we want to mark the inner-most CallExpr, `(curryAdd 1)`.
2052
- // /
2053
- // / The same goes for calls to member functions, such as calc.add(1, 2),
2054
- // / aka ((add calc) 1 2), looks like this:
2055
- // /
2056
- // / (call_expr
2057
- // / (dot_syntax_call_expr
2058
- // / (declref_expr add)
2059
- // / (declref_expr calc))
2060
- // / (tuple_expr
2061
- // / ...))
2062
- // /
2063
- // / and we reach up to mark the CallExpr.
2064
- void markNearestCallAsImplicitly (llvm::Optional<ActorIsolation> setAsync,
2065
- bool setThrows = false ,
2066
- bool setDistributedThunk = false ) {
2067
- assert (applyStack.size () > 0 && " not contained within an Apply?" );
2068
-
2069
- const auto End = applyStack.rend ();
2070
- for (auto I = applyStack.rbegin (); I != End; ++I)
2071
- if (auto call = dyn_cast<CallExpr>(*I)) {
2072
- if (setAsync) {
2073
- call->setImplicitlyAsync (*setAsync);
2074
- }
2075
- if (setThrows) {
2076
- call->setImplicitlyThrows (true );
2077
- }else {
2078
- call->setImplicitlyThrows (false );
2079
- }
2080
- if (setDistributedThunk) {
2081
- call->setShouldApplyDistributedThunk (true );
2082
- }
2083
- return ;
2044
+ // / Mark the given expression
2045
+ void markImplicitlyPromoted (
2046
+ Expr *expr,
2047
+ llvm::Optional<ActorIsolation> setAsync,
2048
+ bool setThrows = false ,
2049
+ bool setDistributedThunk = false ) {
2050
+ if (auto apply = dyn_cast<ApplyExpr>(expr)) {
2051
+ if (setAsync) {
2052
+ apply->setImplicitlyAsync (*setAsync);
2053
+ }
2054
+
2055
+ apply->setImplicitlyThrows (setThrows);
2056
+ if (setDistributedThunk) {
2057
+ apply->setShouldApplyDistributedThunk (true );
2058
+ }
2059
+
2060
+ return ;
2061
+ }
2062
+
2063
+ if (auto lookup = dyn_cast<LookupExpr>(expr)) {
2064
+ assert (usageEnv (lookup) == VarRefUseEnv::Read);
2065
+
2066
+ if (setAsync)
2067
+ lookup->setImplicitlyAsync (*setAsync);
2068
+ lookup->setImplicitlyThrows (setThrows);
2069
+
2070
+ if (setDistributedThunk) {
2071
+ if (auto memberRef = dyn_cast<MemberRefExpr>(lookup))
2072
+ memberRef->setAccessViaDistributedThunk ();
2084
2073
}
2085
- llvm_unreachable (" expected a CallExpr in applyStack!" );
2074
+
2075
+ return ;
2076
+ }
2077
+
2078
+ llvm_unreachable (" Node cannot be implicitly promoted" );
2086
2079
}
2087
2080
2088
2081
bool shouldWalkCaptureInitializerExpressions () override { return true ; }
@@ -2153,6 +2146,19 @@ namespace {
2153
2146
if (auto load = dyn_cast<LoadExpr>(expr))
2154
2147
recordMutableVarParent (load, load->getSubExpr ());
2155
2148
2149
+ if (auto subscript = dyn_cast<SubscriptExpr>(expr)) {
2150
+ if (ConcreteDeclRef subscriptDecl = subscript->getMember ()) {
2151
+ Type type = subscriptDecl.getDecl ()->getInterfaceType ().subst (
2152
+ subscriptDecl.getSubstitutions ());
2153
+ if (auto fnType = type->getAs <FunctionType>()) {
2154
+ (void )checkApply (
2155
+ subscript, subscriptDecl, subscript->getBase (),
2156
+ fnType, subscript->getArgs ());
2157
+ }
2158
+ }
2159
+ return Action::Continue (expr);
2160
+ }
2161
+
2156
2162
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
2157
2163
checkReference (lookup->getBase (), lookup->getMember (), lookup->getLoc (),
2158
2164
/* partialApply*/ llvm::None, lookup);
@@ -2207,7 +2213,14 @@ namespace {
2207
2213
}
2208
2214
} else {
2209
2215
// Check the call itself.
2210
- (void )checkApply (apply);
2216
+ if (auto fnExprType = getType (apply->getFn ())) {
2217
+ if (auto fnType = fnExprType->getAs <FunctionType>()) {
2218
+ (void )checkApply (
2219
+ apply,
2220
+ apply->getCalledValue (/* skipFunctionConversions=*/ true ),
2221
+ apply->getFn (), fnType, apply->getArgs ());
2222
+ }
2223
+ }
2211
2224
}
2212
2225
}
2213
2226
@@ -2591,8 +2604,6 @@ namespace {
2591
2604
}
2592
2605
}
2593
2606
2594
- // FIXME: Subscript?
2595
-
2596
2607
// This is either non-distributed variable, subscript, or something else.
2597
2608
ctx.Diags .diagnose (declLoc,
2598
2609
diag::distributed_actor_isolated_non_self_reference,
@@ -2697,14 +2708,11 @@ namespace {
2697
2708
}
2698
2709
2699
2710
// / Check actor isolation for a particular application.
2700
- bool checkApply (ApplyExpr *apply) {
2701
- auto fnExprType = getType (apply->getFn ());
2702
- if (!fnExprType)
2703
- return false ;
2704
-
2705
- auto fnType = fnExprType->getAs <FunctionType>();
2706
- if (!fnType)
2707
- return false ;
2711
+ bool checkApply (
2712
+ Expr *apply, ConcreteDeclRef callee, Expr *base,
2713
+ AnyFunctionType *fnType, ArgumentList *args
2714
+ ) {
2715
+ SourceLoc loc = apply->getLoc ();
2708
2716
2709
2717
// The isolation of the context we're in.
2710
2718
llvm::Optional<ActorIsolation> contextIsolation;
@@ -2718,17 +2726,22 @@ namespace {
2718
2726
return *contextIsolation;
2719
2727
};
2720
2728
2729
+ // Determine whether the calle itself is async.
2730
+ auto calleeDecl = callee.getDecl ();
2731
+ bool isCalleeAsync = calleeDecl
2732
+ ? isAsyncDecl (calleeDecl)
2733
+ : fnType->getExtInfo ().isAsync ();
2734
+
2721
2735
// Default the call options to allow promotion to async, if it will be
2722
2736
// warranted.
2723
2737
ActorReferenceResult::Options callOptions;
2724
- if (!fnType-> getExtInfo (). isAsync () )
2738
+ if (!isCalleeAsync )
2725
2739
callOptions |= ActorReferenceResult::Flags::AsyncPromotion;
2726
2740
2727
2741
// Determine from the callee whether actor isolation is unsatisfied.
2728
2742
llvm::Optional<ActorIsolation> unsatisfiedIsolation;
2729
2743
bool mayExitToNonisolated = true ;
2730
2744
Expr *argForIsolatedParam = nullptr ;
2731
- auto calleeDecl = apply->getCalledValue (/* skipFunctionConversions=*/ true );
2732
2745
if (Type globalActor = fnType->getGlobalActor ()) {
2733
2746
// If the function type is global-actor-qualified, determine whether
2734
2747
// we are within that global actor already.
@@ -2739,8 +2752,35 @@ namespace {
2739
2752
}
2740
2753
2741
2754
mayExitToNonisolated = false ;
2755
+ } else if (calleeDecl &&
2756
+ calleeDecl->getAttrs ()
2757
+ .hasAttribute <UnsafeInheritExecutorAttr>()) {
2758
+ // The callee always inherits the executor.
2759
+ return false ;
2760
+ } else if (calleeDecl && isa<SubscriptDecl>(calleeDecl)) {
2761
+ auto isolatedActor = getIsolatedActor (base);
2762
+ auto result = ActorReferenceResult::forReference (
2763
+ callee, base->getLoc (), getDeclContext (),
2764
+ kindOfUsage (calleeDecl, apply),
2765
+ isolatedActor, llvm::None, llvm::None, getClosureActorIsolation);
2766
+ switch (result) {
2767
+ case ActorReferenceResult::SameConcurrencyDomain:
2768
+ break ;
2769
+
2770
+ case ActorReferenceResult::ExitsActorToNonisolated:
2771
+ unsatisfiedIsolation = ActorIsolation::forIndependent ();
2772
+ break ;
2773
+
2774
+ case ActorReferenceResult::EntersActor:
2775
+ unsatisfiedIsolation = result.isolation ;
2776
+ break ;
2777
+ }
2778
+
2779
+ callOptions = result.options ;
2780
+ mayExitToNonisolated = false ;
2781
+ argForIsolatedParam = base;
2742
2782
} else if (auto *selfApplyFn = dyn_cast<SelfApplyExpr>(
2743
- apply-> getFn () ->getValueProvidingExpr ())) {
2783
+ base ->getValueProvidingExpr ())) {
2744
2784
// If we're calling a member function, check whether the function
2745
2785
// itself is isolated.
2746
2786
auto memberFn = selfApplyFn->getFn ()->getValueProvidingExpr ();
@@ -2768,10 +2808,6 @@ namespace {
2768
2808
calleeDecl = memberRef->first .getDecl ();
2769
2809
argForIsolatedParam = selfApplyFn->getBase ();
2770
2810
}
2771
- } else if (calleeDecl &&
2772
- calleeDecl->getAttrs ()
2773
- .hasAttribute <UnsafeInheritExecutorAttr>()) {
2774
- return false ;
2775
2811
}
2776
2812
2777
2813
// Check for isolated parameters.
@@ -2780,7 +2816,6 @@ namespace {
2780
2816
if (!fnType->getParams ()[paramIdx].isIsolated ())
2781
2817
continue ;
2782
2818
2783
- auto *args = apply->getArgs ();
2784
2819
if (paramIdx >= args->size ())
2785
2820
continue ;
2786
2821
@@ -2802,7 +2837,7 @@ namespace {
2802
2837
unsatisfiedIsolation =
2803
2838
ActorIsolation::forActorInstanceParameter (nominal, paramIdx);
2804
2839
2805
- if (!fnType-> getExtInfo (). isAsync () )
2840
+ if (!isCalleeAsync )
2806
2841
callOptions |= ActorReferenceResult::Flags::AsyncPromotion;
2807
2842
mayExitToNonisolated = false ;
2808
2843
@@ -2811,7 +2846,7 @@ namespace {
2811
2846
2812
2847
// If we're calling an async function that's nonisolated, and we're in
2813
2848
// an isolated context, then we're exiting the actor context.
2814
- if (mayExitToNonisolated && fnType-> isAsync () &&
2849
+ if (mayExitToNonisolated && isCalleeAsync &&
2815
2850
getContextIsolation ().isActorIsolated ())
2816
2851
unsatisfiedIsolation = ActorIsolation::forIndependent ();
2817
2852
@@ -2827,7 +2862,7 @@ namespace {
2827
2862
if (requiresAsync && !getDeclContext ()->isAsyncContext ()) {
2828
2863
if (calleeDecl) {
2829
2864
ctx.Diags .diagnose (
2830
- apply-> getLoc () , diag::actor_isolated_call_decl,
2865
+ loc , diag::actor_isolated_call_decl,
2831
2866
*unsatisfiedIsolation,
2832
2867
calleeDecl->getDescriptiveKind (), calleeDecl->getName (),
2833
2868
getContextIsolation ())
@@ -2837,7 +2872,7 @@ namespace {
2837
2872
calleeDecl->getName ());
2838
2873
} else {
2839
2874
ctx.Diags .diagnose (
2840
- apply-> getLoc () , diag::actor_isolated_call, *unsatisfiedIsolation,
2875
+ loc , diag::actor_isolated_call, *unsatisfiedIsolation,
2841
2876
getContextIsolation ())
2842
2877
.warnUntilSwiftVersionIf (getContextIsolation ().preconcurrency (), 6 );
2843
2878
}
@@ -2859,7 +2894,7 @@ namespace {
2859
2894
if (unsatisfiedIsolation->isDistributedActor () &&
2860
2895
!(calleeDecl && isa<ConstructorDecl>(calleeDecl))) {
2861
2896
auto distributedAccess = checkDistributedAccess (
2862
- apply-> getFn ()-> getLoc () , calleeDecl, argForIsolatedParam);
2897
+ loc , calleeDecl, argForIsolatedParam);
2863
2898
if (!distributedAccess)
2864
2899
return true ;
2865
2900
@@ -2868,8 +2903,23 @@ namespace {
2868
2903
2869
2904
// Mark as implicitly async/throws/distributed thunk as needed.
2870
2905
if (requiresAsync || setThrows || usesDistributedThunk) {
2871
- markNearestCallAsImplicitly (
2872
- unsatisfiedIsolation, setThrows, usesDistributedThunk);
2906
+ // For a subscript, make sure it's a read-only access.
2907
+ if (calleeDecl && isa<SubscriptDecl>(calleeDecl)) {
2908
+ auto useKind = usageEnv (cast<LookupExpr>(apply));
2909
+ if (useKind != VarRefUseEnv::Read) {
2910
+ ctx.Diags .diagnose (loc, diag::subscript_mutation_outside_actor,
2911
+ *unsatisfiedIsolation,
2912
+ calleeDecl->getDescriptiveKind (),
2913
+ calleeDecl->getName ());
2914
+ calleeDecl->diagnose (
2915
+ diag::actor_mutable_state, calleeDecl->getDescriptiveKind ());
2916
+
2917
+ return true ;
2918
+ }
2919
+ }
2920
+
2921
+ markImplicitlyPromoted (
2922
+ apply, unsatisfiedIsolation, setThrows, usesDistributedThunk);
2873
2923
}
2874
2924
2875
2925
// Check for sendability of the parameter types.
@@ -2878,9 +2928,9 @@ namespace {
2878
2928
const auto ¶m = params[paramIdx];
2879
2929
2880
2930
// Dig out the location of the argument.
2881
- SourceLoc argLoc = apply-> getLoc () ;
2931
+ SourceLoc argLoc = loc ;
2882
2932
Type argType;
2883
- if (auto argList = apply-> getArgs () ) {
2933
+ if (auto argList = args ) {
2884
2934
auto arg = argList->get (paramIdx);
2885
2935
if (arg.getStartLoc ().isValid ())
2886
2936
argLoc = arg.getStartLoc ();
@@ -2904,9 +2954,9 @@ namespace {
2904
2954
2905
2955
// Check for sendability of the result type.
2906
2956
if (diagnoseNonSendableTypes (
2907
- fnType->getResult (), getDeclContext (), apply-> getLoc () ,
2957
+ fnType->getResult (), getDeclContext (), loc ,
2908
2958
diag::non_sendable_call_result_type,
2909
- apply-> isImplicitlyAsync (). has_value () ,
2959
+ requiresAsync ,
2910
2960
*unsatisfiedIsolation))
2911
2961
return true ;
2912
2962
0 commit comments