@@ -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;
@@ -1956,6 +1966,13 @@ namespace {
1956
1966
using MutableVarParent
1957
1967
= llvm::PointerUnion<InOutExpr *, LoadExpr *, AssignExpr *>;
1958
1968
1969
+ ApplyExpr *getImmediateApply () const {
1970
+ if (applyStack.empty ())
1971
+ return nullptr ;
1972
+
1973
+ return applyStack.back ().dyn_cast <ApplyExpr *>();
1974
+ }
1975
+
1959
1976
const PatternBindingDecl *getTopPatternBindingDecl () const {
1960
1977
return patternBindingStack.empty () ? nullptr : patternBindingStack.back ();
1961
1978
}
@@ -2312,7 +2329,7 @@ namespace {
2312
2329
2313
2330
const auto End = applyStack.rend ();
2314
2331
for (auto I = applyStack.rbegin (); I != End; ++I)
2315
- if (auto call = dyn_cast<CallExpr>(*I )) {
2332
+ if (auto call = dyn_cast<CallExpr>(I-> dyn_cast <ApplyExpr *>() )) {
2316
2333
if (setAsync) {
2317
2334
call->setImplicitlyAsync (*setAsync);
2318
2335
}
@@ -2366,6 +2383,11 @@ namespace {
2366
2383
}
2367
2384
2368
2385
PreWalkResult<Expr *> walkToExprPre (Expr *expr) override {
2386
+ // Skip expressions that didn't make it to solution application
2387
+ // because the constraint system diagnosed an error.
2388
+ if (!expr->getType ())
2389
+ return Action::SkipChildren (expr);
2390
+
2369
2391
if (auto *openExistential = dyn_cast<OpenExistentialExpr>(expr)) {
2370
2392
opaqueValues.push_back ({
2371
2393
openExistential->getOpaqueValue (),
@@ -2400,6 +2422,7 @@ namespace {
2400
2422
recordMutableVarParent (load, load->getSubExpr ());
2401
2423
2402
2424
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
2425
+ applyStack.push_back (lookup);
2403
2426
checkReference (lookup->getBase (), lookup->getMember (), lookup->getLoc (),
2404
2427
/* partialApply*/ llvm::None, lookup);
2405
2428
return Action::Continue (expr);
@@ -2442,7 +2465,7 @@ namespace {
2442
2465
// Self applications are checked as part of the outer call.
2443
2466
// However, we look for inout issues here.
2444
2467
if (applyStack.size () >= 2 ) {
2445
- ApplyExpr * outerCall = applyStack[applyStack.size () - 2 ];
2468
+ auto outerCall = applyStack[applyStack.size () - 2 ];
2446
2469
if (isAsyncCall (outerCall)) {
2447
2470
// This call is a partial application within an async call.
2448
2471
// If the partial application take a value inout, it is bad.
@@ -2500,17 +2523,21 @@ namespace {
2500
2523
}
2501
2524
2502
2525
if (auto *apply = dyn_cast<ApplyExpr>(expr)) {
2503
- assert (applyStack.back () == apply);
2526
+ assert (applyStack.back (). get <ApplyExpr *>() == apply);
2504
2527
applyStack.pop_back ();
2505
2528
}
2506
2529
2507
2530
// Clear out the mutable local variable parent map on the way out.
2508
- if (auto *declRefExpr = dyn_cast<DeclRefExpr>(expr))
2531
+ if (auto *declRefExpr = dyn_cast<DeclRefExpr>(expr)) {
2509
2532
mutableLocalVarParent.erase (declRefExpr);
2510
- else if (auto *lookupExpr = dyn_cast<LookupExpr>(expr))
2533
+ } else if (auto *lookupExpr = dyn_cast<LookupExpr>(expr)) {
2511
2534
mutableLocalVarParent.erase (lookupExpr);
2512
- else if (auto *inoutExpr = dyn_cast<InOutExpr>(expr))
2535
+
2536
+ assert (applyStack.back ().dyn_cast <LookupExpr *>() == lookupExpr);
2537
+ applyStack.pop_back ();
2538
+ } else if (auto *inoutExpr = dyn_cast<InOutExpr>(expr)) {
2513
2539
mutableLocalVarParent.erase (inoutExpr);
2540
+ }
2514
2541
2515
2542
// Remove the tracked capture contexts.
2516
2543
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
@@ -2725,28 +2752,31 @@ namespace {
2725
2752
// / Diagnose an inout argument passed into an async call
2726
2753
// /
2727
2754
// / \returns true if we diagnosed the entity, \c false otherwise.
2728
- bool diagnoseInOutArg (const ApplyExpr *call, const InOutExpr *arg,
2729
- bool isPartialApply) {
2755
+ bool diagnoseInOutArg (
2756
+ llvm::PointerUnion<ApplyExpr *, LookupExpr *> call,
2757
+ const InOutExpr *arg,
2758
+ bool isPartialApply) {
2730
2759
// check that the call is actually async
2731
2760
if (!isAsyncCall (call))
2732
2761
return false ;
2733
2762
2734
2763
bool result = false ;
2735
- auto checkDiagnostic = [this , call, isPartialApply, &result](
2764
+ auto diagnoseIsolatedInoutState = [this , call, isPartialApply, &result](
2736
2765
ConcreteDeclRef declRef, SourceLoc argLoc) {
2737
2766
auto decl = declRef.getDecl ();
2738
2767
auto isolation = getActorIsolationForReference (decl, getDeclContext ());
2739
2768
if (!isolation.isActorIsolated ())
2740
2769
return ;
2741
2770
2742
2771
if (isPartialApply) {
2772
+ auto *apply = call.get <ApplyExpr *>();
2743
2773
// The partially applied InoutArg is a property of actor. This
2744
2774
// can really only happen when the property is a struct with a
2745
2775
// mutating async method.
2746
- if (auto partialApply = dyn_cast<ApplyExpr>(call ->getFn ())) {
2776
+ if (auto partialApply = dyn_cast<ApplyExpr>(apply ->getFn ())) {
2747
2777
if (auto declRef = dyn_cast<DeclRefExpr>(partialApply->getFn ())) {
2748
2778
ValueDecl *fnDecl = declRef->getDecl ();
2749
- ctx.Diags .diagnose (call ->getLoc (),
2779
+ ctx.Diags .diagnose (apply ->getLoc (),
2750
2780
diag::actor_isolated_mutating_func,
2751
2781
fnDecl->getName (), decl);
2752
2782
result = true ;
@@ -2755,29 +2785,36 @@ namespace {
2755
2785
}
2756
2786
}
2757
2787
2788
+ bool isImplicitlyAsync;
2789
+ if (auto *apply = call.dyn_cast <ApplyExpr *>()) {
2790
+ isImplicitlyAsync = apply->isImplicitlyAsync ().has_value ();
2791
+ } else {
2792
+ auto *lookup = call.get <LookupExpr *>();
2793
+ isImplicitlyAsync = lookup->isImplicitlyAsync ().has_value ();
2794
+ }
2795
+
2758
2796
ctx.Diags .diagnose (argLoc, diag::actor_isolated_inout_state,
2759
- decl, call-> isImplicitlyAsync (). has_value () );
2797
+ decl, isImplicitlyAsync);
2760
2798
decl->diagnose (diag::kind_declared_here, decl->getDescriptiveKind ());
2761
2799
result = true ;
2762
2800
return ;
2763
2801
};
2764
- auto expressionWalker = [baseArg = arg->getSubExpr (),
2765
- checkDiagnostic](Expr *expr) -> Expr * {
2766
- if (isa<InOutExpr>(expr))
2767
- return nullptr ; // AST walker will hit this again
2802
+
2803
+ auto findIsolatedState = [&](Expr *expr) -> Expr * {
2768
2804
if (LookupExpr *lookup = dyn_cast<LookupExpr>(expr)) {
2769
2805
if (isa<DeclRefExpr>(lookup->getBase ())) {
2770
- checkDiagnostic (lookup->getMember ().getDecl (), baseArg->getLoc ());
2806
+ diagnoseIsolatedInoutState (lookup->getMember ().getDecl (),
2807
+ expr->getLoc ());
2771
2808
return nullptr ; // Diagnosed. Don't keep walking
2772
2809
}
2773
2810
}
2774
2811
if (DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(expr)) {
2775
- checkDiagnostic (declRef->getDecl (), baseArg ->getLoc ());
2812
+ diagnoseIsolatedInoutState (declRef->getDecl (), expr ->getLoc ());
2776
2813
return nullptr ; // Diagnosed. Don't keep walking
2777
2814
}
2778
2815
return expr;
2779
2816
};
2780
- arg->getSubExpr ()->forEachChildExpr (expressionWalker );
2817
+ arg->getSubExpr ()->forEachChildExpr (findIsolatedState );
2781
2818
return result;
2782
2819
}
2783
2820
@@ -3284,9 +3321,9 @@ namespace {
3284
3321
3285
3322
// If this declaration is a callee from the enclosing application,
3286
3323
// it's already been checked via the call.
3287
- if (!applyStack. empty ()) {
3324
+ if (auto *apply = getImmediateApply ()) {
3288
3325
auto immediateCallee =
3289
- applyStack. back () ->getCalledValue (/* skipFunctionConversions=*/ true );
3326
+ apply ->getCalledValue (/* skipFunctionConversions=*/ true );
3290
3327
if (decl == immediateCallee)
3291
3328
return false ;
3292
3329
}
0 commit comments