@@ -556,20 +556,30 @@ findReference(Expr *expr) {
556
556
// /
557
557
// / Note that this must be called after the implicitlyAsync flag has been set,
558
558
// / or implicitly async calls will not return the correct value.
559
- static bool isAsyncCall (const ApplyExpr *call) {
560
- if (call->isImplicitlyAsync ())
559
+ static bool isAsyncCall (
560
+ llvm::PointerUnion<ApplyExpr *, LookupExpr *> call) {
561
+
562
+ if (auto *apply = call.dyn_cast <ApplyExpr *>()) {
563
+ if (apply->isImplicitlyAsync ())
564
+ return true ;
565
+
566
+ // Effectively the same as doing a
567
+ // `cast_or_null<FunctionType>(call->getFn()->getType())`, check the
568
+ // result of that and then checking `isAsync` if it's defined.
569
+ Type funcTypeType = apply->getFn ()->getType ();
570
+ if (!funcTypeType)
571
+ return false ;
572
+ AnyFunctionType *funcType = funcTypeType->getAs <AnyFunctionType>();
573
+ if (!funcType)
574
+ return false ;
575
+ return funcType->isAsync ();
576
+ }
577
+
578
+ auto *lookup = call.get <LookupExpr *>();
579
+ if (lookup->isImplicitlyAsync ())
561
580
return true ;
562
581
563
- // Effectively the same as doing a
564
- // `cast_or_null<FunctionType>(call->getFn()->getType())`, check the
565
- // result of that and then checking `isAsync` if it's defined.
566
- Type funcTypeType = call->getFn ()->getType ();
567
- if (!funcTypeType)
568
- return false ;
569
- AnyFunctionType *funcType = funcTypeType->getAs <AnyFunctionType>();
570
- if (!funcType)
571
- return false ;
572
- return funcType->isAsync ();
582
+ return isAsyncDecl (lookup->getDecl ());
573
583
}
574
584
575
585
// / Determine whether we should diagnose data races within the current context.
@@ -1932,7 +1942,7 @@ namespace {
1932
1942
class ActorIsolationChecker : public ASTWalker {
1933
1943
ASTContext &ctx;
1934
1944
SmallVector<const DeclContext *, 4 > contextStack;
1935
- SmallVector<ApplyExpr* , 4 > applyStack;
1945
+ SmallVector<llvm::PointerUnion< ApplyExpr *, LookupExpr *> , 4 > applyStack;
1936
1946
SmallVector<std::pair<OpaqueValueExpr *, Expr *>, 4 > opaqueValues;
1937
1947
SmallVector<const PatternBindingDecl *, 2 > patternBindingStack;
1938
1948
llvm::function_ref<Type(Expr *)> getType;
@@ -1950,6 +1960,13 @@ namespace {
1950
1960
using MutableVarParent
1951
1961
= llvm::PointerUnion<InOutExpr *, LoadExpr *, AssignExpr *>;
1952
1962
1963
+ ApplyExpr *getImmediateApply () const {
1964
+ if (applyStack.empty ())
1965
+ return nullptr ;
1966
+
1967
+ return applyStack.back ().dyn_cast <ApplyExpr *>();
1968
+ }
1969
+
1953
1970
const PatternBindingDecl *getTopPatternBindingDecl () const {
1954
1971
return patternBindingStack.empty () ? nullptr : patternBindingStack.back ();
1955
1972
}
@@ -2207,7 +2224,7 @@ namespace {
2207
2224
2208
2225
const auto End = applyStack.rend ();
2209
2226
for (auto I = applyStack.rbegin (); I != End; ++I)
2210
- if (auto call = dyn_cast<CallExpr>(*I )) {
2227
+ if (auto call = dyn_cast<CallExpr>(I-> dyn_cast <ApplyExpr *>() )) {
2211
2228
if (setAsync) {
2212
2229
call->setImplicitlyAsync (*setAsync);
2213
2230
}
@@ -2261,6 +2278,11 @@ namespace {
2261
2278
}
2262
2279
2263
2280
PreWalkResult<Expr *> walkToExprPre (Expr *expr) override {
2281
+ // Skip expressions that didn't make it to solution application
2282
+ // because the constraint system diagnosed an error.
2283
+ if (!expr->getType ())
2284
+ return Action::SkipChildren (expr);
2285
+
2264
2286
if (auto *openExistential = dyn_cast<OpenExistentialExpr>(expr)) {
2265
2287
opaqueValues.push_back ({
2266
2288
openExistential->getOpaqueValue (),
@@ -2295,6 +2317,7 @@ namespace {
2295
2317
recordMutableVarParent (load, load->getSubExpr ());
2296
2318
2297
2319
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
2320
+ applyStack.push_back (lookup);
2298
2321
checkReference (lookup->getBase (), lookup->getMember (), lookup->getLoc (),
2299
2322
/* partialApply*/ llvm::None, lookup);
2300
2323
return Action::Continue (expr);
@@ -2337,7 +2360,7 @@ namespace {
2337
2360
// Self applications are checked as part of the outer call.
2338
2361
// However, we look for inout issues here.
2339
2362
if (applyStack.size () >= 2 ) {
2340
- ApplyExpr * outerCall = applyStack[applyStack.size () - 2 ];
2363
+ auto outerCall = applyStack[applyStack.size () - 2 ];
2341
2364
if (isAsyncCall (outerCall)) {
2342
2365
// This call is a partial application within an async call.
2343
2366
// If the partial application take a value inout, it is bad.
@@ -2391,17 +2414,21 @@ namespace {
2391
2414
}
2392
2415
2393
2416
if (auto *apply = dyn_cast<ApplyExpr>(expr)) {
2394
- assert (applyStack.back () == apply);
2417
+ assert (applyStack.back (). get <ApplyExpr *>() == apply);
2395
2418
applyStack.pop_back ();
2396
2419
}
2397
2420
2398
2421
// Clear out the mutable local variable parent map on the way out.
2399
- if (auto *declRefExpr = dyn_cast<DeclRefExpr>(expr))
2422
+ if (auto *declRefExpr = dyn_cast<DeclRefExpr>(expr)) {
2400
2423
mutableLocalVarParent.erase (declRefExpr);
2401
- else if (auto *lookupExpr = dyn_cast<LookupExpr>(expr))
2424
+ } else if (auto *lookupExpr = dyn_cast<LookupExpr>(expr)) {
2402
2425
mutableLocalVarParent.erase (lookupExpr);
2403
- else if (auto *inoutExpr = dyn_cast<InOutExpr>(expr))
2426
+
2427
+ assert (applyStack.back ().dyn_cast <LookupExpr *>() == lookupExpr);
2428
+ applyStack.pop_back ();
2429
+ } else if (auto *inoutExpr = dyn_cast<InOutExpr>(expr)) {
2404
2430
mutableLocalVarParent.erase (inoutExpr);
2431
+ }
2405
2432
2406
2433
// Remove the tracked capture contexts.
2407
2434
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
@@ -2616,28 +2643,31 @@ namespace {
2616
2643
// / Diagnose an inout argument passed into an async call
2617
2644
// /
2618
2645
// / \returns true if we diagnosed the entity, \c false otherwise.
2619
- bool diagnoseInOutArg (const ApplyExpr *call, const InOutExpr *arg,
2620
- bool isPartialApply) {
2646
+ bool diagnoseInOutArg (
2647
+ llvm::PointerUnion<ApplyExpr *, LookupExpr *> call,
2648
+ const InOutExpr *arg,
2649
+ bool isPartialApply) {
2621
2650
// check that the call is actually async
2622
2651
if (!isAsyncCall (call))
2623
2652
return false ;
2624
2653
2625
2654
bool result = false ;
2626
- auto checkDiagnostic = [this , call, isPartialApply, &result](
2655
+ auto diagnoseIsolatedInoutState = [this , call, isPartialApply, &result](
2627
2656
ConcreteDeclRef declRef, SourceLoc argLoc) {
2628
2657
auto decl = declRef.getDecl ();
2629
2658
auto isolation = getActorIsolationForReference (decl, getDeclContext ());
2630
2659
if (!isolation.isActorIsolated ())
2631
2660
return ;
2632
2661
2633
2662
if (isPartialApply) {
2663
+ auto *apply = call.get <ApplyExpr *>();
2634
2664
// The partially applied InoutArg is a property of actor. This
2635
2665
// can really only happen when the property is a struct with a
2636
2666
// mutating async method.
2637
- if (auto partialApply = dyn_cast<ApplyExpr>(call ->getFn ())) {
2667
+ if (auto partialApply = dyn_cast<ApplyExpr>(apply ->getFn ())) {
2638
2668
if (auto declRef = dyn_cast<DeclRefExpr>(partialApply->getFn ())) {
2639
2669
ValueDecl *fnDecl = declRef->getDecl ();
2640
- ctx.Diags .diagnose (call ->getLoc (),
2670
+ ctx.Diags .diagnose (apply ->getLoc (),
2641
2671
diag::actor_isolated_mutating_func,
2642
2672
fnDecl->getName (), decl);
2643
2673
result = true ;
@@ -2646,29 +2676,36 @@ namespace {
2646
2676
}
2647
2677
}
2648
2678
2679
+ bool isImplicitlyAsync;
2680
+ if (auto *apply = call.dyn_cast <ApplyExpr *>()) {
2681
+ isImplicitlyAsync = apply->isImplicitlyAsync ().has_value ();
2682
+ } else {
2683
+ auto *lookup = call.get <LookupExpr *>();
2684
+ isImplicitlyAsync = lookup->isImplicitlyAsync ().has_value ();
2685
+ }
2686
+
2649
2687
ctx.Diags .diagnose (argLoc, diag::actor_isolated_inout_state,
2650
- decl, call-> isImplicitlyAsync (). has_value () );
2688
+ decl, isImplicitlyAsync);
2651
2689
decl->diagnose (diag::kind_declared_here, decl->getDescriptiveKind ());
2652
2690
result = true ;
2653
2691
return ;
2654
2692
};
2655
- auto expressionWalker = [baseArg = arg->getSubExpr (),
2656
- checkDiagnostic](Expr *expr) -> Expr * {
2657
- if (isa<InOutExpr>(expr))
2658
- return nullptr ; // AST walker will hit this again
2693
+
2694
+ auto findIsolatedState = [&](Expr *expr) -> Expr * {
2659
2695
if (LookupExpr *lookup = dyn_cast<LookupExpr>(expr)) {
2660
2696
if (isa<DeclRefExpr>(lookup->getBase ())) {
2661
- checkDiagnostic (lookup->getMember ().getDecl (), baseArg->getLoc ());
2697
+ diagnoseIsolatedInoutState (lookup->getMember ().getDecl (),
2698
+ expr->getLoc ());
2662
2699
return nullptr ; // Diagnosed. Don't keep walking
2663
2700
}
2664
2701
}
2665
2702
if (DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(expr)) {
2666
- checkDiagnostic (declRef->getDecl (), baseArg ->getLoc ());
2703
+ diagnoseIsolatedInoutState (declRef->getDecl (), expr ->getLoc ());
2667
2704
return nullptr ; // Diagnosed. Don't keep walking
2668
2705
}
2669
2706
return expr;
2670
2707
};
2671
- arg->getSubExpr ()->forEachChildExpr (expressionWalker );
2708
+ arg->getSubExpr ()->forEachChildExpr (findIsolatedState );
2672
2709
return result;
2673
2710
}
2674
2711
@@ -3172,9 +3209,9 @@ namespace {
3172
3209
3173
3210
// If this declaration is a callee from the enclosing application,
3174
3211
// it's already been checked via the call.
3175
- if (!applyStack. empty ()) {
3212
+ if (auto *apply = getImmediateApply ()) {
3176
3213
auto immediateCallee =
3177
- applyStack. back () ->getCalledValue (/* skipFunctionConversions=*/ true );
3214
+ apply ->getCalledValue (/* skipFunctionConversions=*/ true );
3178
3215
if (decl == immediateCallee)
3179
3216
return false ;
3180
3217
}
0 commit comments