@@ -3319,25 +3319,32 @@ bool swift::diagnoseExplicitUnavailability(
3319
3319
3320
3320
namespace {
3321
3321
class ExprAvailabilityWalker : public ASTWalker {
3322
- // / Describes how the next member reference will be treated as we traverse
3323
- // / the AST.
3322
+ // / Models how member references will translate to accessor usage. This is
3323
+ // / used to diagnose the availability of individual accessors that may be
3324
+ // / called by the expression being checked.
3324
3325
enum class MemberAccessContext : unsigned {
3325
- // / The member reference is in a context where an access will call
3326
- // / the getter.
3327
- Getter,
3328
-
3329
- // / The member reference is in a context where an access will call
3330
- // / the setter.
3331
- Setter,
3332
-
3333
- // / The member reference is in a context where it will be turned into
3334
- // / an inout argument. (Once this happens, we have to conservatively assume
3335
- // / that both the getter and setter could be called.)
3336
- InOut
3326
+ // / The starting access context for the root of any expression tree. In this
3327
+ // / context, a member access will call the get accessor only.
3328
+ Default,
3329
+
3330
+ // / The access context for expressions rooted in a LoadExpr. A LoadExpr
3331
+ // / coerces l-values to r-values and thus member access inside of a LoadExpr
3332
+ // / will only invoke get accessors.
3333
+ Load,
3334
+
3335
+ // / The access context for the outermost member accessed in the expression
3336
+ // / tree on the left-hand side of an assignment. Only the set accessor will
3337
+ // / be invoked on this member.
3338
+ Assignment,
3339
+
3340
+ // / The access context for expressions in which member is being read and
3341
+ // / then written back to. For example, a writeback will occur inside of an
3342
+ // / InOutExpr. Both the get and set accessors may be called in this context.
3343
+ Writeback
3337
3344
};
3338
3345
3339
3346
ASTContext &Context;
3340
- MemberAccessContext AccessContext = MemberAccessContext::Getter ;
3347
+ MemberAccessContext AccessContext = MemberAccessContext::Default ;
3341
3348
SmallVector<const Expr *, 16 > ExprStack;
3342
3349
const ExportContext &Where;
3343
3350
@@ -3354,6 +3361,13 @@ class ExprAvailabilityWalker : public ASTWalker {
3354
3361
return MacroWalking::Arguments;
3355
3362
}
3356
3363
3364
+ PreWalkAction walkToArgumentPre (const Argument &Arg) override {
3365
+ // Arguments should be walked in their own member access context which
3366
+ // starts out read-only by default.
3367
+ walkInContext (Arg.getExpr (), MemberAccessContext::Default);
3368
+ return Action::SkipChildren ();
3369
+ }
3370
+
3357
3371
PreWalkResult<Expr *> walkToExprPre (Expr *E) override {
3358
3372
auto *DC = Where.getDeclContext ();
3359
3373
@@ -3476,6 +3490,11 @@ class ExprAvailabilityWalker : public ASTWalker {
3476
3490
ME->getMacroRef (), ME->getMacroNameLoc ().getSourceRange ());
3477
3491
}
3478
3492
3493
+ if (auto LE = dyn_cast<LoadExpr>(E)) {
3494
+ walkLoadExpr (LE);
3495
+ return Action::SkipChildren (E);
3496
+ }
3497
+
3479
3498
return Action::Continue (E);
3480
3499
}
3481
3500
@@ -3532,26 +3551,6 @@ class ExprAvailabilityWalker : public ASTWalker {
3532
3551
return call;
3533
3552
}
3534
3553
3535
- // / Walks up from a potential member reference to the first LoadExpr that would
3536
- // / make the member reference an r-value instead of an l-value.
3537
- const LoadExpr *getEnclosingLoadExpr () const {
3538
- assert (!ExprStack.empty () && " must be called while visiting an expression" );
3539
- ArrayRef<const Expr *> stack = ExprStack;
3540
- stack = stack.drop_back ();
3541
-
3542
- for (auto expr : llvm::reverse (stack)) {
3543
- // Do not search past the first enclosing ApplyExpr. Any enclosing
3544
- // LoadExpr from this point only applies to the result of the call.
3545
- if (auto applyExpr = dyn_cast<ApplyExpr>(expr))
3546
- return nullptr ;
3547
-
3548
- if (auto loadExpr = dyn_cast<LoadExpr>(expr))
3549
- return loadExpr;
3550
- }
3551
-
3552
- return nullptr ;
3553
- }
3554
-
3555
3554
// / Walk an assignment expression, checking for availability.
3556
3555
void walkAssignExpr (AssignExpr *E) {
3557
3556
// We take over recursive walking of assignment expressions in order to
@@ -3567,32 +3566,38 @@ class ExprAvailabilityWalker : public ASTWalker {
3567
3566
// encountered walking (pre-order) is the Dest is the destination of the
3568
3567
// write. For the moment this is fine -- but future syntax might violate
3569
3568
// this assumption.
3570
- walkInContext (E, Dest, MemberAccessContext::Setter );
3569
+ walkInContext (Dest, MemberAccessContext::Assignment );
3571
3570
3572
3571
// Check RHS in getter context
3573
3572
Expr *Source = E->getSrc ();
3574
3573
if (!Source) {
3575
3574
return ;
3576
3575
}
3577
- walkInContext (E, Source, MemberAccessContext::Getter );
3576
+ walkInContext (Source, MemberAccessContext::Default );
3578
3577
}
3579
-
3578
+
3579
+ // / Walk a load expression, checking for availability.
3580
+ void walkLoadExpr (LoadExpr *E) {
3581
+ walkInContext (E->getSubExpr (), MemberAccessContext::Load);
3582
+ }
3583
+
3580
3584
// / Walk a member reference expression, checking for availability.
3581
3585
void walkMemberRef (MemberRefExpr *E) {
3582
- // Walk the base. If the access context is currently `Setter`, then we must
3583
- // be diagnosing the destination of an assignment. When recursing, diagnose
3584
- // any remaining member refs as if they were in an InOutExpr, since there is
3585
- // a writeback occurring through them as a result of the assignment.
3586
+ // Walk the base. If the access context is currently `Assignment`, then we
3587
+ // must be diagnosing the destination of an assignment. When recursing,
3588
+ // diagnose any remaining member refs in a `Writeback` context, since
3589
+ // there is a writeback occurring through them as a result of the
3590
+ // assignment.
3586
3591
//
3587
3592
// someVar.x.y = 1
3588
- // │ ╰─ MemberAccessContext::Setter
3589
- // ╰─── MemberAccessContext::InOut
3593
+ // │ ╰─ MemberAccessContext::Assignment
3594
+ // ╰─── MemberAccessContext::Writeback
3590
3595
//
3591
3596
MemberAccessContext accessContext =
3592
- (AccessContext == MemberAccessContext::Setter )
3593
- ? MemberAccessContext::InOut
3597
+ (AccessContext == MemberAccessContext::Assignment )
3598
+ ? MemberAccessContext::Writeback
3594
3599
: AccessContext;
3595
- walkInContext (E, E ->getBase (), accessContext);
3600
+ walkInContext (E->getBase (), accessContext);
3596
3601
3597
3602
ConcreteDeclRef DR = E->getMember ();
3598
3603
// Diagnose for the member declaration itself.
@@ -3645,7 +3650,13 @@ class ExprAvailabilityWalker : public ASTWalker {
3645
3650
3646
3651
// / Walk an inout expression, checking for availability.
3647
3652
void walkInOutExpr (InOutExpr *E) {
3648
- walkInContext (E, E->getSubExpr (), MemberAccessContext::InOut);
3653
+ // Typically an InOutExpr should begin a `Writeback` context. However,
3654
+ // inside a LoadExpr this transition is suppressed since the entire
3655
+ // expression is being coerced to an r-value.
3656
+ auto accessContext = AccessContext != MemberAccessContext::Load
3657
+ ? MemberAccessContext::Writeback
3658
+ : AccessContext;
3659
+ walkInContext (E->getSubExpr (), accessContext);
3649
3660
}
3650
3661
3651
3662
bool shouldWalkIntoClosure (AbstractClosureExpr *closure) const {
@@ -3667,8 +3678,7 @@ class ExprAvailabilityWalker : public ASTWalker {
3667
3678
}
3668
3679
3669
3680
// / Walk the given expression in the member access context.
3670
- void walkInContext (Expr *baseExpr, Expr *E,
3671
- MemberAccessContext AccessContext) {
3681
+ void walkInContext (Expr *E, MemberAccessContext AccessContext) {
3672
3682
llvm::SaveAndRestore<MemberAccessContext>
3673
3683
C (this ->AccessContext , AccessContext);
3674
3684
E->walk (*this );
@@ -3695,28 +3705,25 @@ class ExprAvailabilityWalker : public ASTWalker {
3695
3705
// this probably needs to be refined to not assume that the accesses are
3696
3706
// specifically using the getter/setter.
3697
3707
switch (AccessContext) {
3698
- case MemberAccessContext::Getter:
3708
+ case MemberAccessContext::Default:
3709
+ case MemberAccessContext::Load:
3699
3710
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
3700
3711
ReferenceRange, ReferenceDC, std::nullopt);
3701
3712
break ;
3702
3713
3703
- case MemberAccessContext::Setter:
3704
- if (!getEnclosingLoadExpr ()) {
3705
- diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3706
- ReferenceRange, ReferenceDC, std::nullopt);
3707
- }
3714
+ case MemberAccessContext::Assignment:
3715
+ diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3716
+ ReferenceRange, ReferenceDC, std::nullopt);
3708
3717
break ;
3709
3718
3710
- case MemberAccessContext::InOut :
3719
+ case MemberAccessContext::Writeback :
3711
3720
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
3712
3721
ReferenceRange, ReferenceDC,
3713
3722
DeclAvailabilityFlag::ForInout);
3714
3723
3715
- if (!getEnclosingLoadExpr ()) {
3716
- diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3717
- ReferenceRange, ReferenceDC,
3718
- DeclAvailabilityFlag::ForInout);
3719
- }
3724
+ diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3725
+ ReferenceRange, ReferenceDC,
3726
+ DeclAvailabilityFlag::ForInout);
3720
3727
break ;
3721
3728
}
3722
3729
}
0 commit comments