@@ -2276,7 +2276,8 @@ class VarDeclUsageChecker : public ASTWalker {
22762276 vdi->second |= Flag;
22772277 }
22782278
2279- void markBaseOfAbstractStorageDeclStore (Expr *E, ConcreteDeclRef decl);
2279+ void markBaseOfStorageUse (Expr *E, ConcreteDeclRef decl, unsigned flags);
2280+ void markBaseOfStorageUse (Expr *E, bool isMutating);
22802281
22812282 void markStoredOrInOutExpr (Expr *E, unsigned Flags);
22822283
@@ -2655,29 +2656,57 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
26552656 }
26562657}
26572658
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 ()) {
26652670 base->walk (*this );
26662671 return ;
26672672 }
26682673
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) {
26772706 base->walk (*this );
26782707 return ;
26792708 }
2680-
2709+
26812710 // Otherwise this is a read and write of the base.
26822711 return markStoredOrInOutExpr (base, RK_Written|RK_Read);
26832712}
@@ -2712,36 +2741,33 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
27122741 if (auto *SE = dyn_cast<SubscriptExpr>(E)) {
27132742 // The index of the subscript is evaluated as an rvalue.
27142743 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);
27202745 return ;
27212746 }
27222747
27232748 // 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.
27252751 if (auto *KPA = dyn_cast<KeyPathApplicationExpr>(E)) {
27262752 auto &C = KPA->getType ()->getASTContext ();
27272753 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 );
27342760 return ;
27352761 }
27362762
27372763 if (auto *ioe = dyn_cast<InOutExpr>(E))
27382764 return markStoredOrInOutExpr (ioe->getSubExpr (), RK_Written|RK_Read);
27392765
27402766 if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
2741- markBaseOfAbstractStorageDeclStore (MRE->getBase (), MRE->getMember ());
2767+ markBaseOfStorageUse (MRE->getBase (), MRE->getMember (), Flags );
27422768 return ;
27432769 }
2744-
2770+
27452771 if (auto *TEE = dyn_cast<TupleElementExpr>(E))
27462772 return markStoredOrInOutExpr (TEE->getBase (), Flags);
27472773
@@ -2750,6 +2776,12 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
27502776
27512777 if (auto *BOE = dyn_cast<BindOptionalExpr>(E))
27522778 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+ }
27532785
27542786 // If this is an OpaqueValueExpr that we've seen a mapping for, jump to the
27552787 // mapped value.
@@ -2788,8 +2820,16 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
27882820 if (auto VD = dyn_cast<VarDecl>(MRE->getMember ().getDecl ())) {
27892821 if (AssociatedGetter == VD && AssociatedGetterRefExpr == nullptr )
27902822 AssociatedGetterRefExpr = MRE;
2823+ markBaseOfStorageUse (MRE->getBase (), MRE->getMember (), RK_Read);
2824+ return { false , E };
27912825 }
27922826 }
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+
27932833 // If this is an AssignExpr, see if we're mutating something that we know
27942834 // about.
27952835 if (auto *assign = dyn_cast<AssignExpr>(E)) {
@@ -2810,6 +2850,13 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
28102850 // If we see an OpenExistentialExpr, remember the mapping for its OpaqueValue.
28112851 if (auto *oee = dyn_cast<OpenExistentialExpr>(E))
28122852 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+ }
28132860
28142861 // If we saw an ErrorExpr, take note of this.
28152862 if (isa<ErrorExpr>(E))
0 commit comments