@@ -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,20 +3551,6 @@ class ExprAvailabilityWalker : public ASTWalker {
3532
3551
return call;
3533
3552
}
3534
3553
3535
- // / Walks up to the first enclosing LoadExpr and returns it.
3536
- const LoadExpr *getEnclosingLoadExpr () const {
3537
- assert (!ExprStack.empty () && " must be called while visiting an expression" );
3538
- ArrayRef<const Expr *> stack = ExprStack;
3539
- stack = stack.drop_back ();
3540
-
3541
- for (auto expr : llvm::reverse (stack)) {
3542
- if (auto loadExpr = dyn_cast<LoadExpr>(expr))
3543
- return loadExpr;
3544
- }
3545
-
3546
- return nullptr ;
3547
- }
3548
-
3549
3554
// / Walk an assignment expression, checking for availability.
3550
3555
void walkAssignExpr (AssignExpr *E) {
3551
3556
// We take over recursive walking of assignment expressions in order to
@@ -3561,32 +3566,38 @@ class ExprAvailabilityWalker : public ASTWalker {
3561
3566
// encountered walking (pre-order) is the Dest is the destination of the
3562
3567
// write. For the moment this is fine -- but future syntax might violate
3563
3568
// this assumption.
3564
- walkInContext (E, Dest, MemberAccessContext::Setter );
3569
+ walkInContext (Dest, MemberAccessContext::Assignment );
3565
3570
3566
3571
// Check RHS in getter context
3567
3572
Expr *Source = E->getSrc ();
3568
3573
if (!Source) {
3569
3574
return ;
3570
3575
}
3571
- walkInContext (E, Source, MemberAccessContext::Getter );
3576
+ walkInContext (Source, MemberAccessContext::Default );
3572
3577
}
3573
-
3578
+
3579
+ // / Walk a load expression, checking for availability.
3580
+ void walkLoadExpr (LoadExpr *E) {
3581
+ walkInContext (E->getSubExpr (), MemberAccessContext::Load);
3582
+ }
3583
+
3574
3584
// / Walk a member reference expression, checking for availability.
3575
3585
void walkMemberRef (MemberRefExpr *E) {
3576
- // Walk the base. If the access context is currently `Setter`, then we must
3577
- // be diagnosing the destination of an assignment. When recursing, diagnose
3578
- // any remaining member refs as if they were in an InOutExpr, since there is
3579
- // 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.
3580
3591
//
3581
3592
// someVar.x.y = 1
3582
- // │ ╰─ MemberAccessContext::Setter
3583
- // ╰─── MemberAccessContext::InOut
3593
+ // │ ╰─ MemberAccessContext::Assignment
3594
+ // ╰─── MemberAccessContext::Writeback
3584
3595
//
3585
3596
MemberAccessContext accessContext =
3586
- (AccessContext == MemberAccessContext::Setter )
3587
- ? MemberAccessContext::InOut
3597
+ (AccessContext == MemberAccessContext::Assignment )
3598
+ ? MemberAccessContext::Writeback
3588
3599
: AccessContext;
3589
- walkInContext (E, E ->getBase (), accessContext);
3600
+ walkInContext (E->getBase (), accessContext);
3590
3601
3591
3602
ConcreteDeclRef DR = E->getMember ();
3592
3603
// Diagnose for the member declaration itself.
@@ -3639,12 +3650,13 @@ class ExprAvailabilityWalker : public ASTWalker {
3639
3650
3640
3651
// / Walk an inout expression, checking for availability.
3641
3652
void walkInOutExpr (InOutExpr *E) {
3642
- // If there is a LoadExpr in the stack, then this InOutExpr is not actually
3643
- // indicative of any mutation so the access context should just be Getter.
3644
- auto accessContext = getEnclosingLoadExpr () ? MemberAccessContext::Getter
3645
- : MemberAccessContext::InOut;
3646
-
3647
- walkInContext (E, E->getSubExpr (), accessContext);
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);
3648
3660
}
3649
3661
3650
3662
bool shouldWalkIntoClosure (AbstractClosureExpr *closure) const {
@@ -3665,10 +3677,8 @@ class ExprAvailabilityWalker : public ASTWalker {
3665
3677
return ;
3666
3678
}
3667
3679
3668
-
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,17 +3705,18 @@ 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 :
3714
+ case MemberAccessContext::Assignment :
3704
3715
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Set),
3705
3716
ReferenceRange, ReferenceDC, std::nullopt);
3706
3717
break ;
3707
3718
3708
- case MemberAccessContext::InOut :
3719
+ case MemberAccessContext::Writeback :
3709
3720
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
3710
3721
ReferenceRange, ReferenceDC,
3711
3722
DeclAvailabilityFlag::ForInout);
0 commit comments