@@ -2204,7 +2204,7 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2204
2204
// / Models how member references will translate to accessor usage. This is
2205
2205
// / used to diagnose the availability of individual accessors that may be
2206
2206
// / called by the expression being checked.
2207
- enum class MemberAccessContext : unsigned {
2207
+ enum class MemberAccessContext : uint8_t {
2208
2208
// / The starting access context for the root of any expression tree. In this
2209
2209
// / context, a member access will call the get accessor only.
2210
2210
Default,
@@ -2225,11 +2225,48 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2225
2225
Writeback
2226
2226
};
2227
2227
2228
+ // / Models how key path references will translate to accessor usage. This is
2229
+ // / used to diagnose the availability of individual accessors that may be
2230
+ // / called by the expression being checked.
2231
+ enum class KeyPathAccessContext : uint8_t {
2232
+ // / The context does not involve a key path access.
2233
+ None,
2234
+
2235
+ // / The context is an expression that is coerced to a read-only key path.
2236
+ ReadOnlyCoercion,
2237
+
2238
+ // / The context is a key path application (`x[keyPath: \.member]`).
2239
+ Application,
2240
+ };
2241
+
2228
2242
ASTContext &Context;
2229
- MemberAccessContext AccessContext = MemberAccessContext::Default;
2230
2243
SmallVector<const Expr *, 16 > ExprStack;
2231
2244
SmallVector<bool , 4 > PreconcurrencyCalleeStack;
2232
2245
const ExportContext &Where;
2246
+ MemberAccessContext MemberAccess = MemberAccessContext::Default;
2247
+ KeyPathAccessContext KeyPathAccess = KeyPathAccessContext::None;
2248
+ bool InInOutExpr = false ;
2249
+
2250
+ // / A categorization of which accessors are used by a given storage access.
2251
+ enum class StorageAccessKind {
2252
+ Get,
2253
+ Set,
2254
+ GetSet,
2255
+ };
2256
+
2257
+ // / Returns the storage access kind as indicated by the current member access
2258
+ // / context.
2259
+ StorageAccessKind getMemberStorageAccessKind () const {
2260
+ switch (MemberAccess) {
2261
+ case MemberAccessContext::Default:
2262
+ case MemberAccessContext::Load:
2263
+ return StorageAccessKind::Get;
2264
+ case MemberAccessContext::Assignment:
2265
+ return StorageAccessKind::Set;
2266
+ case MemberAccessContext::Writeback:
2267
+ return StorageAccessKind::GetSet;
2268
+ }
2269
+ }
2233
2270
2234
2271
public:
2235
2272
explicit ExprAvailabilityWalker (const ExportContext &Where)
@@ -2238,7 +2275,7 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2238
2275
PreWalkAction walkToArgumentPre (const Argument &Arg) override {
2239
2276
// Arguments should be walked in their own member access context which
2240
2277
// starts out read-only by default.
2241
- walkInContext (Arg.getExpr (), MemberAccessContext::Default);
2278
+ walkInMemberAccessContext (Arg.getExpr (), MemberAccessContext::Default);
2242
2279
return Action::SkipChildren ();
2243
2280
}
2244
2281
@@ -2255,7 +2292,8 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2255
2292
if (auto DR = dyn_cast<DeclRefExpr>(E)) {
2256
2293
diagnoseDeclRefAvailability (DR->getDeclRef (), DR->getSourceRange (),
2257
2294
getEnclosingApplyExpr (), std::nullopt );
2258
- maybeDiagStorageAccess (DR->getDecl (), DR->getSourceRange (), DC);
2295
+ maybeDiagStorageAccess (DR->getDecl (), getMemberStorageAccessKind (),
2296
+ DR->getSourceRange (), DC);
2259
2297
}
2260
2298
if (auto MR = dyn_cast<MemberRefExpr>(E)) {
2261
2299
walkMemberRef (MR);
@@ -2274,7 +2312,9 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2274
2312
if (auto S = dyn_cast<SubscriptExpr>(E)) {
2275
2313
if (S->hasDecl ()) {
2276
2314
diagnoseDeclRefAvailability (S->getDecl (), S->getSourceRange (), S);
2277
- maybeDiagStorageAccess (S->getDecl ().getDecl (), S->getSourceRange (), DC);
2315
+ maybeDiagStorageAccess (S->getDecl ().getDecl (),
2316
+ getMemberStorageAccessKind (),
2317
+ S->getSourceRange (), DC);
2278
2318
PreconcurrencyCalleeStack.push_back (
2279
2319
hasReferenceToPreconcurrencyDecl (S));
2280
2320
}
@@ -2321,9 +2361,24 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2321
2361
FCE->getLoc (),
2322
2362
Where.getDeclContext ());
2323
2363
}
2364
+ if (auto DTBE = dyn_cast<DerivedToBaseExpr>(E)) {
2365
+ if (auto ty = DTBE->getType ()) {
2366
+ if (ty->isKeyPath ()) {
2367
+ walkInKeyPathAccessContext (DTBE->getSubExpr (),
2368
+ KeyPathAccessContext::ReadOnlyCoercion);
2369
+ return Action::SkipChildren (E);
2370
+ }
2371
+ }
2372
+ }
2324
2373
if (auto KP = dyn_cast<KeyPathExpr>(E)) {
2325
2374
maybeDiagKeyPath (KP);
2326
2375
}
2376
+ if (auto KPAE = dyn_cast<KeyPathApplicationExpr>(E)) {
2377
+ KPAE->getBase ()->walk (*this );
2378
+ walkInKeyPathAccessContext (KPAE->getKeyPath (),
2379
+ KeyPathAccessContext::Application);
2380
+ return Action::SkipChildren (E);
2381
+ }
2327
2382
if (auto A = dyn_cast<AssignExpr>(E)) {
2328
2383
// Attempting to assign to a @preconcurrency declaration should
2329
2384
// downgrade Sendable conformance mismatches to warnings.
@@ -2402,7 +2457,7 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2402
2457
}
2403
2458
2404
2459
if (auto LE = dyn_cast<LoadExpr>(E)) {
2405
- walkLoadExpr (LE);
2460
+ walkInMemberAccessContext (LE-> getSubExpr (), MemberAccessContext::Load );
2406
2461
return Action::SkipChildren (E);
2407
2462
}
2408
2463
@@ -2489,19 +2544,14 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2489
2544
// encountered walking (pre-order) is the Dest is the destination of the
2490
2545
// write. For the moment this is fine -- but future syntax might violate
2491
2546
// this assumption.
2492
- walkInContext (Dest, MemberAccessContext::Assignment);
2547
+ walkInMemberAccessContext (Dest, MemberAccessContext::Assignment);
2493
2548
2494
2549
// Check RHS in getter context
2495
2550
Expr *Source = E->getSrc ();
2496
2551
if (!Source) {
2497
2552
return ;
2498
2553
}
2499
- walkInContext (Source, MemberAccessContext::Default);
2500
- }
2501
-
2502
- // / Walk a load expression, checking for availability.
2503
- void walkLoadExpr (LoadExpr *E) {
2504
- walkInContext (E->getSubExpr (), MemberAccessContext::Load);
2554
+ walkInMemberAccessContext (Source, MemberAccessContext::Default);
2505
2555
}
2506
2556
2507
2557
// / Walk a member reference expression, checking for availability.
@@ -2517,10 +2567,10 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2517
2567
// ╰─── MemberAccessContext::Writeback
2518
2568
//
2519
2569
MemberAccessContext accessContext =
2520
- (AccessContext == MemberAccessContext::Assignment)
2570
+ (MemberAccess == MemberAccessContext::Assignment)
2521
2571
? MemberAccessContext::Writeback
2522
- : AccessContext ;
2523
- walkInContext (E->getBase (), accessContext);
2572
+ : MemberAccess ;
2573
+ walkInMemberAccessContext (E->getBase (), accessContext);
2524
2574
2525
2575
ConcreteDeclRef DR = E->getMember ();
2526
2576
// Diagnose for the member declaration itself.
@@ -2530,7 +2580,32 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2530
2580
2531
2581
// Diagnose for appropriate accessors, given the access context.
2532
2582
auto *DC = Where.getDeclContext ();
2533
- maybeDiagStorageAccess (DR.getDecl (), E->getSourceRange (), DC);
2583
+ maybeDiagStorageAccess (DR.getDecl (), getMemberStorageAccessKind (),
2584
+ E->getSourceRange (), DC);
2585
+ }
2586
+
2587
+ StorageAccessKind getStorageAccessKindForKeyPath (KeyPathExpr *KP) const {
2588
+ switch (KeyPathAccess) {
2589
+ case KeyPathAccessContext::None:
2590
+ // Use the key path's type to determine the access kind.
2591
+ if (!KP->isObjC ())
2592
+ if (auto keyPathType = KP->getKeyPathType ())
2593
+ if (keyPathType->isKeyPath ())
2594
+ return StorageAccessKind::Get;
2595
+
2596
+ return StorageAccessKind::GetSet;
2597
+
2598
+ case KeyPathAccessContext::ReadOnlyCoercion:
2599
+ // The type of this key path is being coerced to the type KeyPath<_, _> so
2600
+ // ignore the actual key path type.
2601
+ return StorageAccessKind::Get;
2602
+
2603
+ case KeyPathAccessContext::Application:
2604
+ // The key path is being applied directly to a base so treat it as if it
2605
+ // were a direct access to the member in the current member access
2606
+ // context.
2607
+ return getMemberStorageAccessKind ();
2608
+ }
2534
2609
}
2535
2610
2536
2611
// / Walk a keypath expression, checking all of its components for
@@ -2541,6 +2616,8 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2541
2616
if (KP->isObjC ())
2542
2617
flags = DeclAvailabilityFlag::ForObjCKeyPath;
2543
2618
2619
+ auto accessKind = getStorageAccessKindForKeyPath (KP);
2620
+
2544
2621
for (auto &component : KP->getComponents ()) {
2545
2622
switch (component.getKind ()) {
2546
2623
case KeyPathExpr::Component::Kind::Member:
@@ -2550,7 +2627,7 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2550
2627
auto range = component.getSourceRange ();
2551
2628
if (diagnoseDeclRefAvailability (decl, loc, nullptr , flags))
2552
2629
break ;
2553
- maybeDiagStorageAccess (decl.getDecl (), range, declContext);
2630
+ maybeDiagStorageAccess (decl.getDecl (), accessKind, range, declContext);
2554
2631
break ;
2555
2632
}
2556
2633
@@ -2575,13 +2652,15 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2575
2652
2576
2653
// / Walk an inout expression, checking for availability.
2577
2654
void walkInOutExpr (InOutExpr *E) {
2655
+ llvm::SaveAndRestore<bool > S (this ->InInOutExpr , true );
2656
+
2578
2657
// Typically an InOutExpr should begin a `Writeback` context. However,
2579
2658
// inside a LoadExpr this transition is suppressed since the entire
2580
2659
// expression is being coerced to an r-value.
2581
- auto accessContext = AccessContext != MemberAccessContext::Load
2660
+ auto accessContext = MemberAccess != MemberAccessContext::Load
2582
2661
? MemberAccessContext::Writeback
2583
- : AccessContext ;
2584
- walkInContext (E->getSubExpr (), accessContext);
2662
+ : MemberAccess ;
2663
+ walkInMemberAccessContext (E->getSubExpr (), accessContext);
2585
2664
}
2586
2665
2587
2666
// / Walk an abstract closure expression, checking for availability
@@ -2598,16 +2677,23 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2598
2677
return ;
2599
2678
}
2600
2679
2601
- // / Walk the given expression in the member access context.
2602
- void walkInContext (Expr *E, MemberAccessContext AccessContext) {
2603
- llvm::SaveAndRestore<MemberAccessContext>
2604
- C (this ->AccessContext , AccessContext);
2680
+ // / Walk the given expression in a specific member access context.
2681
+ void walkInMemberAccessContext (Expr *E, MemberAccessContext AccessContext) {
2682
+ llvm::SaveAndRestore<MemberAccessContext> C (this ->MemberAccess ,
2683
+ AccessContext);
2684
+ E->walk (*this );
2685
+ }
2686
+
2687
+ // / Walk the given expression in a specific key path access context.
2688
+ void walkInKeyPathAccessContext (Expr *E, KeyPathAccessContext AccessContext) {
2689
+ llvm::SaveAndRestore<KeyPathAccessContext> C (this ->KeyPathAccess ,
2690
+ AccessContext);
2605
2691
E->walk (*this );
2606
2692
}
2607
2693
2608
2694
// / Emit diagnostics, if necessary, for accesses to storage where
2609
2695
// / the accessor for the AccessContext is not available.
2610
- void maybeDiagStorageAccess (const ValueDecl *VD,
2696
+ void maybeDiagStorageAccess (const ValueDecl *VD, StorageAccessKind accessKind,
2611
2697
SourceRange ReferenceRange,
2612
2698
const DeclContext *ReferenceDC) const {
2613
2699
if (Context.LangOpts .DisableAvailabilityChecking )
@@ -2621,30 +2707,28 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
2621
2707
return ;
2622
2708
}
2623
2709
2710
+ DeclAvailabilityFlags flags;
2711
+ if (InInOutExpr)
2712
+ flags |= DeclAvailabilityFlag::ForInout;
2713
+
2624
2714
// Check availability of accessor functions.
2625
2715
// TODO: if we're talking about an inlineable storage declaration,
2626
2716
// this probably needs to be refined to not assume that the accesses are
2627
2717
// specifically using the getter/setter.
2628
- switch (AccessContext) {
2629
- case MemberAccessContext::Default:
2630
- case MemberAccessContext::Load:
2718
+ switch (accessKind) {
2719
+ case StorageAccessKind::Get:
2631
2720
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
2632
- ReferenceRange, ReferenceDC, std:: nullopt );
2721
+ ReferenceRange, ReferenceDC, flags );
2633
2722
break ;
2634
-
2635
- case MemberAccessContext::Assignment:
2723
+ case StorageAccessKind::Set:
2636
2724
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
2637
- ReferenceRange, ReferenceDC, std:: nullopt );
2725
+ ReferenceRange, ReferenceDC, flags );
2638
2726
break ;
2639
-
2640
- case MemberAccessContext::Writeback:
2727
+ case StorageAccessKind::GetSet:
2641
2728
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
2642
- ReferenceRange, ReferenceDC,
2643
- DeclAvailabilityFlag::ForInout);
2644
-
2729
+ ReferenceRange, ReferenceDC, flags);
2645
2730
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
2646
- ReferenceRange, ReferenceDC,
2647
- DeclAvailabilityFlag::ForInout);
2731
+ ReferenceRange, ReferenceDC, flags);
2648
2732
break ;
2649
2733
}
2650
2734
}
0 commit comments