@@ -2041,48 +2041,41 @@ namespace {
20412041 contextStack.push_back (dc);
20422042 }
20432043
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 ();
20842073 }
2085- llvm_unreachable (" expected a CallExpr in applyStack!" );
2074+
2075+ return ;
2076+ }
2077+
2078+ llvm_unreachable (" Node cannot be implicitly promoted" );
20862079 }
20872080
20882081 bool shouldWalkCaptureInitializerExpressions () override { return true ; }
@@ -2153,6 +2146,19 @@ namespace {
21532146 if (auto load = dyn_cast<LoadExpr>(expr))
21542147 recordMutableVarParent (load, load->getSubExpr ());
21552148
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+
21562162 if (auto lookup = dyn_cast<LookupExpr>(expr)) {
21572163 checkReference (lookup->getBase (), lookup->getMember (), lookup->getLoc (),
21582164 /* partialApply*/ llvm::None, lookup);
@@ -2207,7 +2213,14 @@ namespace {
22072213 }
22082214 } else {
22092215 // 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+ }
22112224 }
22122225 }
22132226
@@ -2591,8 +2604,6 @@ namespace {
25912604 }
25922605 }
25932606
2594- // FIXME: Subscript?
2595-
25962607 // This is either non-distributed variable, subscript, or something else.
25972608 ctx.Diags .diagnose (declLoc,
25982609 diag::distributed_actor_isolated_non_self_reference,
@@ -2697,14 +2708,11 @@ namespace {
26972708 }
26982709
26992710 // / 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 ();
27082716
27092717 // The isolation of the context we're in.
27102718 llvm::Optional<ActorIsolation> contextIsolation;
@@ -2718,17 +2726,22 @@ namespace {
27182726 return *contextIsolation;
27192727 };
27202728
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+
27212735 // Default the call options to allow promotion to async, if it will be
27222736 // warranted.
27232737 ActorReferenceResult::Options callOptions;
2724- if (!fnType-> getExtInfo (). isAsync () )
2738+ if (!isCalleeAsync )
27252739 callOptions |= ActorReferenceResult::Flags::AsyncPromotion;
27262740
27272741 // Determine from the callee whether actor isolation is unsatisfied.
27282742 llvm::Optional<ActorIsolation> unsatisfiedIsolation;
27292743 bool mayExitToNonisolated = true ;
27302744 Expr *argForIsolatedParam = nullptr ;
2731- auto calleeDecl = apply->getCalledValue (/* skipFunctionConversions=*/ true );
27322745 if (Type globalActor = fnType->getGlobalActor ()) {
27332746 // If the function type is global-actor-qualified, determine whether
27342747 // we are within that global actor already.
@@ -2739,8 +2752,35 @@ namespace {
27392752 }
27402753
27412754 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;
27422782 } else if (auto *selfApplyFn = dyn_cast<SelfApplyExpr>(
2743- apply-> getFn () ->getValueProvidingExpr ())) {
2783+ base ->getValueProvidingExpr ())) {
27442784 // If we're calling a member function, check whether the function
27452785 // itself is isolated.
27462786 auto memberFn = selfApplyFn->getFn ()->getValueProvidingExpr ();
@@ -2768,10 +2808,6 @@ namespace {
27682808 calleeDecl = memberRef->first .getDecl ();
27692809 argForIsolatedParam = selfApplyFn->getBase ();
27702810 }
2771- } else if (calleeDecl &&
2772- calleeDecl->getAttrs ()
2773- .hasAttribute <UnsafeInheritExecutorAttr>()) {
2774- return false ;
27752811 }
27762812
27772813 // Check for isolated parameters.
@@ -2780,7 +2816,6 @@ namespace {
27802816 if (!fnType->getParams ()[paramIdx].isIsolated ())
27812817 continue ;
27822818
2783- auto *args = apply->getArgs ();
27842819 if (paramIdx >= args->size ())
27852820 continue ;
27862821
@@ -2802,7 +2837,7 @@ namespace {
28022837 unsatisfiedIsolation =
28032838 ActorIsolation::forActorInstanceParameter (nominal, paramIdx);
28042839
2805- if (!fnType-> getExtInfo (). isAsync () )
2840+ if (!isCalleeAsync )
28062841 callOptions |= ActorReferenceResult::Flags::AsyncPromotion;
28072842 mayExitToNonisolated = false ;
28082843
@@ -2811,7 +2846,7 @@ namespace {
28112846
28122847 // If we're calling an async function that's nonisolated, and we're in
28132848 // an isolated context, then we're exiting the actor context.
2814- if (mayExitToNonisolated && fnType-> isAsync () &&
2849+ if (mayExitToNonisolated && isCalleeAsync &&
28152850 getContextIsolation ().isActorIsolated ())
28162851 unsatisfiedIsolation = ActorIsolation::forIndependent ();
28172852
@@ -2827,7 +2862,7 @@ namespace {
28272862 if (requiresAsync && !getDeclContext ()->isAsyncContext ()) {
28282863 if (calleeDecl) {
28292864 ctx.Diags .diagnose (
2830- apply-> getLoc () , diag::actor_isolated_call_decl,
2865+ loc , diag::actor_isolated_call_decl,
28312866 *unsatisfiedIsolation,
28322867 calleeDecl->getDescriptiveKind (), calleeDecl->getName (),
28332868 getContextIsolation ())
@@ -2837,7 +2872,7 @@ namespace {
28372872 calleeDecl->getName ());
28382873 } else {
28392874 ctx.Diags .diagnose (
2840- apply-> getLoc () , diag::actor_isolated_call, *unsatisfiedIsolation,
2875+ loc , diag::actor_isolated_call, *unsatisfiedIsolation,
28412876 getContextIsolation ())
28422877 .warnUntilSwiftVersionIf (getContextIsolation ().preconcurrency (), 6 );
28432878 }
@@ -2859,7 +2894,7 @@ namespace {
28592894 if (unsatisfiedIsolation->isDistributedActor () &&
28602895 !(calleeDecl && isa<ConstructorDecl>(calleeDecl))) {
28612896 auto distributedAccess = checkDistributedAccess (
2862- apply-> getFn ()-> getLoc () , calleeDecl, argForIsolatedParam);
2897+ loc , calleeDecl, argForIsolatedParam);
28632898 if (!distributedAccess)
28642899 return true ;
28652900
@@ -2868,8 +2903,23 @@ namespace {
28682903
28692904 // Mark as implicitly async/throws/distributed thunk as needed.
28702905 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);
28732923 }
28742924
28752925 // Check for sendability of the parameter types.
@@ -2878,9 +2928,9 @@ namespace {
28782928 const auto ¶m = params[paramIdx];
28792929
28802930 // Dig out the location of the argument.
2881- SourceLoc argLoc = apply-> getLoc () ;
2931+ SourceLoc argLoc = loc ;
28822932 Type argType;
2883- if (auto argList = apply-> getArgs () ) {
2933+ if (auto argList = args ) {
28842934 auto arg = argList->get (paramIdx);
28852935 if (arg.getStartLoc ().isValid ())
28862936 argLoc = arg.getStartLoc ();
@@ -2904,9 +2954,9 @@ namespace {
29042954
29052955 // Check for sendability of the result type.
29062956 if (diagnoseNonSendableTypes (
2907- fnType->getResult (), getDeclContext (), apply-> getLoc () ,
2957+ fnType->getResult (), getDeclContext (), loc ,
29082958 diag::non_sendable_call_result_type,
2909- apply-> isImplicitlyAsync (). has_value () ,
2959+ requiresAsync ,
29102960 *unsatisfiedIsolation))
29112961 return true ;
29122962
0 commit comments