@@ -2276,7 +2276,8 @@ class VarDeclUsageChecker : public ASTWalker {
2276
2276
vdi->second |= Flag;
2277
2277
}
2278
2278
2279
- void markBaseOfAbstractStorageDeclStore (Expr *E, ConcreteDeclRef decl);
2279
+ void markBaseOfStorageUse (Expr *E, ConcreteDeclRef decl, unsigned flags);
2280
+ void markBaseOfStorageUse (Expr *E, bool isMutating);
2280
2281
2281
2282
void markStoredOrInOutExpr (Expr *E, unsigned Flags);
2282
2283
@@ -2655,29 +2656,57 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
2655
2656
}
2656
2657
}
2657
2658
2658
- // / Handle a store to "x.y" where 'base' is the expression for x and 'decl' is
2659
- // / the decl for 'y'.
2660
- void VarDeclUsageChecker::
2661
- markBaseOfAbstractStorageDeclStore (Expr *base, ConcreteDeclRef decl) {
2662
- // If the base is a class or an rvalue, then this store just loads the base.
2663
- if (base->getType ()->isAnyClassReferenceType () ||
2664
- !(base->getType ()->hasLValueType () || base->isSemanticallyInOutExpr ())) {
2659
+ // / Handle a use of "x.y" or "x[0]" where 'base' is the expression for x and
2660
+ // / 'decl' is the property or subscript.
2661
+ // /
2662
+ // / TODO: Rip this out and just rely on LValueAccessKind.
2663
+ void VarDeclUsageChecker::markBaseOfStorageUse (Expr *base, ConcreteDeclRef decl,
2664
+ unsigned flags) {
2665
+ // If the base is an rvalue, then we know that this is a non-mutating access.
2666
+ // Note that we can have mutating accesses even when the base has class or
2667
+ // metatype type due to protocols and protocol extensions.
2668
+ if (!base->getType ()->hasLValueType () &&
2669
+ !base->isSemanticallyInOutExpr ()) {
2665
2670
base->walk (*this );
2666
2671
return ;
2667
2672
}
2668
2673
2669
- // If the store is to a non-mutating member, then this is just a load, even
2670
- // if the base is an inout expr.
2671
- auto *ASD = cast<AbstractStorageDecl>(decl.getDecl ());
2672
- if (ASD->isSettable (nullptr ) && !ASD->isSetterMutating ()) {
2673
- // Sema conservatively converts the base to inout expr when it is an lvalue.
2674
- // Look through it because we know it isn't actually doing a load/store.
2675
- if (auto *ioe = dyn_cast<InOutExpr>(base))
2676
- base = ioe->getSubExpr ();
2674
+ // Compute whether this access is to a mutating member.
2675
+ auto *ASD = dyn_cast_or_null<AbstractStorageDecl>(decl.getDecl ());
2676
+ bool isMutating = false ;
2677
+ if (!ASD) {
2678
+ // If there's no abstract storage declaration (which should hopefully
2679
+ // only happen with invalid code), treat the base access as mutating if
2680
+ // the subobject is being mutated and the base type is not a class
2681
+ // or metatype.
2682
+ if (flags & RK_Written) {
2683
+ Type type = base->getType ()->getRValueType ()->getInOutObjectType ();
2684
+ if (!type->isAnyClassReferenceType () && !type->is <AnyMetatypeType>())
2685
+ isMutating = true ;
2686
+ }
2687
+ } else {
2688
+ // Otherwise, consider whether the accessors are mutating.
2689
+ if (flags & RK_Read)
2690
+ isMutating |= ASD->isGetterMutating ();
2691
+ if (flags & RK_Written)
2692
+ isMutating |= ASD->isSettable (nullptr ) && ASD->isSetterMutating ();
2693
+ }
2694
+
2695
+ markBaseOfStorageUse (base, isMutating);
2696
+ }
2697
+
2698
+ void VarDeclUsageChecker::markBaseOfStorageUse (Expr *base, bool isMutating) {
2699
+ // CSApply sometimes wraps the base in an InOutExpr just because the
2700
+ // base is an l-value; look through that so we can get more precise
2701
+ // checking.
2702
+ if (auto *ioe = dyn_cast<InOutExpr>(base))
2703
+ base = ioe->getSubExpr ();
2704
+
2705
+ if (!isMutating) {
2677
2706
base->walk (*this );
2678
2707
return ;
2679
2708
}
2680
-
2709
+
2681
2710
// Otherwise this is a read and write of the base.
2682
2711
return markStoredOrInOutExpr (base, RK_Written|RK_Read);
2683
2712
}
@@ -2712,36 +2741,33 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
2712
2741
if (auto *SE = dyn_cast<SubscriptExpr>(E)) {
2713
2742
// The index of the subscript is evaluated as an rvalue.
2714
2743
SE->getIndex ()->walk (*this );
2715
- if (SE->hasDecl ())
2716
- markBaseOfAbstractStorageDeclStore (SE->getBase (), SE->getDecl ());
2717
- else // FIXME: Should not be needed!
2718
- markStoredOrInOutExpr (SE->getBase (), RK_Written|RK_Read);
2719
-
2744
+ markBaseOfStorageUse (SE->getBase (), SE->getDecl (), Flags);
2720
2745
return ;
2721
2746
}
2722
2747
2723
2748
// Likewise for key path applications. An application of a WritableKeyPath
2724
- // reads and writes its base.
2749
+ // reads and writes its base; an application of a ReferenceWritableKeyPath
2750
+ // only reads its base; the other KeyPath types cannot be written at all.
2725
2751
if (auto *KPA = dyn_cast<KeyPathApplicationExpr>(E)) {
2726
2752
auto &C = KPA->getType ()->getASTContext ();
2727
2753
KPA->getKeyPath ()->walk (*this );
2728
- if (KPA-> getKeyPath ()-> getType ()-> getAnyNominal ()
2729
- == C. getWritableKeyPathDecl ())
2730
- markStoredOrInOutExpr (KPA-> getBase (), RK_Written|RK_Read);
2731
- if ( KPA->getKeyPath ()->getType ()->getAnyNominal ()
2732
- == C.getReferenceWritableKeyPathDecl ())
2733
- markStoredOrInOutExpr (KPA->getBase (), RK_Read );
2754
+
2755
+ bool isMutating =
2756
+ (Flags & RK_Written) &&
2757
+ KPA->getKeyPath ()->getType ()->getAnyNominal ()
2758
+ == C.getWritableKeyPathDecl ();
2759
+ markBaseOfStorageUse (KPA->getBase (), isMutating );
2734
2760
return ;
2735
2761
}
2736
2762
2737
2763
if (auto *ioe = dyn_cast<InOutExpr>(E))
2738
2764
return markStoredOrInOutExpr (ioe->getSubExpr (), RK_Written|RK_Read);
2739
2765
2740
2766
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
2741
- markBaseOfAbstractStorageDeclStore (MRE->getBase (), MRE->getMember ());
2767
+ markBaseOfStorageUse (MRE->getBase (), MRE->getMember (), Flags );
2742
2768
return ;
2743
2769
}
2744
-
2770
+
2745
2771
if (auto *TEE = dyn_cast<TupleElementExpr>(E))
2746
2772
return markStoredOrInOutExpr (TEE->getBase (), Flags);
2747
2773
@@ -2750,6 +2776,12 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
2750
2776
2751
2777
if (auto *BOE = dyn_cast<BindOptionalExpr>(E))
2752
2778
return markStoredOrInOutExpr (BOE->getSubExpr (), Flags);
2779
+
2780
+ // Bind existential expressions.
2781
+ if (auto *OEE = dyn_cast<OpenExistentialExpr>(E)) {
2782
+ OpaqueValueMap[OEE->getOpaqueValue ()] = OEE->getExistentialValue ();
2783
+ return markStoredOrInOutExpr (OEE->getSubExpr (), Flags);
2784
+ }
2753
2785
2754
2786
// If this is an OpaqueValueExpr that we've seen a mapping for, jump to the
2755
2787
// mapped value.
@@ -2788,8 +2820,16 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
2788
2820
if (auto VD = dyn_cast<VarDecl>(MRE->getMember ().getDecl ())) {
2789
2821
if (AssociatedGetter == VD && AssociatedGetterRefExpr == nullptr )
2790
2822
AssociatedGetterRefExpr = MRE;
2823
+ markBaseOfStorageUse (MRE->getBase (), MRE->getMember (), RK_Read);
2824
+ return { false , E };
2791
2825
}
2792
2826
}
2827
+ if (auto SE = dyn_cast<SubscriptExpr>(E)) {
2828
+ SE->getIndex ()->walk (*this );
2829
+ markBaseOfStorageUse (SE->getBase (), SE->getDecl (), RK_Read);
2830
+ return { false , E };
2831
+ }
2832
+
2793
2833
// If this is an AssignExpr, see if we're mutating something that we know
2794
2834
// about.
2795
2835
if (auto *assign = dyn_cast<AssignExpr>(E)) {
@@ -2810,6 +2850,13 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
2810
2850
// If we see an OpenExistentialExpr, remember the mapping for its OpaqueValue.
2811
2851
if (auto *oee = dyn_cast<OpenExistentialExpr>(E))
2812
2852
OpaqueValueMap[oee->getOpaqueValue ()] = oee->getExistentialValue ();
2853
+
2854
+ // Visit bindings.
2855
+ if (auto ove = dyn_cast<OpaqueValueExpr>(E)) {
2856
+ if (auto mapping = OpaqueValueMap.lookup (ove))
2857
+ mapping->walk (*this );
2858
+ return { false , E };
2859
+ }
2813
2860
2814
2861
// If we saw an ErrorExpr, take note of this.
2815
2862
if (isa<ErrorExpr>(E))
0 commit comments