@@ -3311,25 +3311,32 @@ bool swift::diagnoseExplicitUnavailability(
33113311
33123312namespace {
33133313class ExprAvailabilityWalker : public ASTWalker {
3314- // / Describes how the next member reference will be treated as we traverse
3315- // / the AST.
3314+ // / Models how member references will translate to accessor usage. This is
3315+ // / used to diagnose the availability of individual accessors that may be
3316+ // / called by the expression being checked.
33163317 enum class MemberAccessContext : unsigned {
3317- // / The member reference is in a context where an access will call
3318- // / the getter.
3319- Getter,
3320-
3321- // / The member reference is in a context where an access will call
3322- // / the setter.
3323- Setter,
3324-
3325- // / The member reference is in a context where it will be turned into
3326- // / an inout argument. (Once this happens, we have to conservatively assume
3327- // / that both the getter and setter could be called.)
3328- InOut
3318+ // / The starting access context for the root of any expression tree. In this
3319+ // / context, a member access will call the get accessor only.
3320+ Default,
3321+
3322+ // / The access context for expressions rooted in a LoadExpr. A LoadExpr
3323+ // / coerces l-values to r-values and thus member access inside of a LoadExpr
3324+ // / will only invoke get accessors.
3325+ Load,
3326+
3327+ // / The access context for the outermost member accessed in the expression
3328+ // / tree on the left-hand side of an assignment. Only the set accessor will
3329+ // / be invoked on this member.
3330+ Assignment,
3331+
3332+ // / The access context for expressions in which member is being read and
3333+ // / then written back to. For example, a writeback will occur inside of an
3334+ // / InOutExpr. Both the get and set accessors may be called in this context.
3335+ Writeback
33293336 };
33303337
33313338 ASTContext &Context;
3332- MemberAccessContext AccessContext = MemberAccessContext::Getter ;
3339+ MemberAccessContext AccessContext = MemberAccessContext::Default ;
33333340 SmallVector<const Expr *, 16 > ExprStack;
33343341 const ExportContext &Where;
33353342
@@ -3346,6 +3353,13 @@ class ExprAvailabilityWalker : public ASTWalker {
33463353 return MacroWalking::Arguments;
33473354 }
33483355
3356+ PreWalkAction walkToArgumentPre (const Argument &Arg) override {
3357+ // Arguments should be walked in their own member access context which
3358+ // starts out read-only by default.
3359+ walkInContext (Arg.getExpr (), MemberAccessContext::Default);
3360+ return Action::SkipChildren ();
3361+ }
3362+
33493363 PreWalkResult<Expr *> walkToExprPre (Expr *E) override {
33503364 auto *DC = Where.getDeclContext ();
33513365
@@ -3468,6 +3482,11 @@ class ExprAvailabilityWalker : public ASTWalker {
34683482 ME->getMacroRef (), ME->getMacroNameLoc ().getSourceRange ());
34693483 }
34703484
3485+ if (auto LE = dyn_cast<LoadExpr>(E)) {
3486+ walkLoadExpr (LE);
3487+ return Action::SkipChildren (E);
3488+ }
3489+
34713490 return Action::Continue (E);
34723491 }
34733492
@@ -3524,26 +3543,6 @@ class ExprAvailabilityWalker : public ASTWalker {
35243543 return call;
35253544 }
35263545
3527- // / Walks up from a potential member reference to the first LoadExpr that would
3528- // / make the member reference an r-value instead of an l-value.
3529- const LoadExpr *getEnclosingLoadExpr () const {
3530- assert (!ExprStack.empty () && " must be called while visiting an expression" );
3531- ArrayRef<const Expr *> stack = ExprStack;
3532- stack = stack.drop_back ();
3533-
3534- for (auto expr : llvm::reverse (stack)) {
3535- // Do not search past the first enclosing ApplyExpr. Any enclosing
3536- // LoadExpr from this point only applies to the result of the call.
3537- if (auto applyExpr = dyn_cast<ApplyExpr>(expr))
3538- return nullptr ;
3539-
3540- if (auto loadExpr = dyn_cast<LoadExpr>(expr))
3541- return loadExpr;
3542- }
3543-
3544- return nullptr ;
3545- }
3546-
35473546 // / Walk an assignment expression, checking for availability.
35483547 void walkAssignExpr (AssignExpr *E) {
35493548 // We take over recursive walking of assignment expressions in order to
@@ -3559,32 +3558,38 @@ class ExprAvailabilityWalker : public ASTWalker {
35593558 // encountered walking (pre-order) is the Dest is the destination of the
35603559 // write. For the moment this is fine -- but future syntax might violate
35613560 // this assumption.
3562- walkInContext (E, Dest, MemberAccessContext::Setter );
3561+ walkInContext (Dest, MemberAccessContext::Assignment );
35633562
35643563 // Check RHS in getter context
35653564 Expr *Source = E->getSrc ();
35663565 if (!Source) {
35673566 return ;
35683567 }
3569- walkInContext (E, Source, MemberAccessContext::Getter );
3568+ walkInContext (Source, MemberAccessContext::Default );
35703569 }
3571-
3570+
3571+ // / Walk a load expression, checking for availability.
3572+ void walkLoadExpr (LoadExpr *E) {
3573+ walkInContext (E->getSubExpr (), MemberAccessContext::Load);
3574+ }
3575+
35723576 // / Walk a member reference expression, checking for availability.
35733577 void walkMemberRef (MemberRefExpr *E) {
3574- // Walk the base. If the access context is currently `Setter`, then we must
3575- // be diagnosing the destination of an assignment. When recursing, diagnose
3576- // any remaining member refs as if they were in an InOutExpr, since there is
3577- // a writeback occurring through them as a result of the assignment.
3578+ // Walk the base. If the access context is currently `Assignment`, then we
3579+ // must be diagnosing the destination of an assignment. When recursing,
3580+ // diagnose any remaining member refs in a `Writeback` context, since
3581+ // there is a writeback occurring through them as a result of the
3582+ // assignment.
35783583 //
35793584 // someVar.x.y = 1
3580- // │ ╰─ MemberAccessContext::Setter
3581- // ╰─── MemberAccessContext::InOut
3585+ // │ ╰─ MemberAccessContext::Assignment
3586+ // ╰─── MemberAccessContext::Writeback
35823587 //
35833588 MemberAccessContext accessContext =
3584- (AccessContext == MemberAccessContext::Setter )
3585- ? MemberAccessContext::InOut
3589+ (AccessContext == MemberAccessContext::Assignment )
3590+ ? MemberAccessContext::Writeback
35863591 : AccessContext;
3587- walkInContext (E, E ->getBase (), accessContext);
3592+ walkInContext (E->getBase (), accessContext);
35883593
35893594 ConcreteDeclRef DR = E->getMember ();
35903595 // Diagnose for the member declaration itself.
@@ -3637,7 +3642,13 @@ class ExprAvailabilityWalker : public ASTWalker {
36373642
36383643 // / Walk an inout expression, checking for availability.
36393644 void walkInOutExpr (InOutExpr *E) {
3640- walkInContext (E, E->getSubExpr (), MemberAccessContext::InOut);
3645+ // Typically an InOutExpr should begin a `Writeback` context. However,
3646+ // inside a LoadExpr this transition is suppressed since the entire
3647+ // expression is being coerced to an r-value.
3648+ auto accessContext = AccessContext != MemberAccessContext::Load
3649+ ? MemberAccessContext::Writeback
3650+ : AccessContext;
3651+ walkInContext (E->getSubExpr (), accessContext);
36413652 }
36423653
36433654 bool shouldWalkIntoClosure (AbstractClosureExpr *closure) const {
@@ -3659,8 +3670,7 @@ class ExprAvailabilityWalker : public ASTWalker {
36593670 }
36603671
36613672 // / Walk the given expression in the member access context.
3662- void walkInContext (Expr *baseExpr, Expr *E,
3663- MemberAccessContext AccessContext) {
3673+ void walkInContext (Expr *E, MemberAccessContext AccessContext) {
36643674 llvm::SaveAndRestore<MemberAccessContext>
36653675 C (this ->AccessContext , AccessContext);
36663676 E->walk (*this );
@@ -3687,28 +3697,25 @@ class ExprAvailabilityWalker : public ASTWalker {
36873697 // this probably needs to be refined to not assume that the accesses are
36883698 // specifically using the getter/setter.
36893699 switch (AccessContext) {
3690- case MemberAccessContext::Getter:
3700+ case MemberAccessContext::Default:
3701+ case MemberAccessContext::Load:
36913702 diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
36923703 ReferenceRange, ReferenceDC, std::nullopt );
36933704 break ;
36943705
3695- case MemberAccessContext::Setter:
3696- if (!getEnclosingLoadExpr ()) {
3697- diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3698- ReferenceRange, ReferenceDC, std::nullopt );
3699- }
3706+ case MemberAccessContext::Assignment:
3707+ diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3708+ ReferenceRange, ReferenceDC, std::nullopt );
37003709 break ;
37013710
3702- case MemberAccessContext::InOut :
3711+ case MemberAccessContext::Writeback :
37033712 diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
37043713 ReferenceRange, ReferenceDC,
37053714 DeclAvailabilityFlag::ForInout);
37063715
3707- if (!getEnclosingLoadExpr ()) {
3708- diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3709- ReferenceRange, ReferenceDC,
3710- DeclAvailabilityFlag::ForInout);
3711- }
3716+ diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3717+ ReferenceRange, ReferenceDC,
3718+ DeclAvailabilityFlag::ForInout);
37123719 break ;
37133720 }
37143721 }
0 commit comments