@@ -3311,25 +3311,32 @@ bool swift::diagnoseExplicitUnavailability(
3311
3311
3312
3312
namespace {
3313
3313
class 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.
3316
3317
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
3329
3336
};
3330
3337
3331
3338
ASTContext &Context;
3332
- MemberAccessContext AccessContext = MemberAccessContext::Getter ;
3339
+ MemberAccessContext AccessContext = MemberAccessContext::Default ;
3333
3340
SmallVector<const Expr *, 16 > ExprStack;
3334
3341
const ExportContext &Where;
3335
3342
@@ -3346,6 +3353,13 @@ class ExprAvailabilityWalker : public ASTWalker {
3346
3353
return MacroWalking::Arguments;
3347
3354
}
3348
3355
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
+
3349
3363
PreWalkResult<Expr *> walkToExprPre (Expr *E) override {
3350
3364
auto *DC = Where.getDeclContext ();
3351
3365
@@ -3468,6 +3482,11 @@ class ExprAvailabilityWalker : public ASTWalker {
3468
3482
ME->getMacroRef (), ME->getMacroNameLoc ().getSourceRange ());
3469
3483
}
3470
3484
3485
+ if (auto LE = dyn_cast<LoadExpr>(E)) {
3486
+ walkLoadExpr (LE);
3487
+ return Action::SkipChildren (E);
3488
+ }
3489
+
3471
3490
return Action::Continue (E);
3472
3491
}
3473
3492
@@ -3524,26 +3543,6 @@ class ExprAvailabilityWalker : public ASTWalker {
3524
3543
return call;
3525
3544
}
3526
3545
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
-
3547
3546
// / Walk an assignment expression, checking for availability.
3548
3547
void walkAssignExpr (AssignExpr *E) {
3549
3548
// We take over recursive walking of assignment expressions in order to
@@ -3559,32 +3558,38 @@ class ExprAvailabilityWalker : public ASTWalker {
3559
3558
// encountered walking (pre-order) is the Dest is the destination of the
3560
3559
// write. For the moment this is fine -- but future syntax might violate
3561
3560
// this assumption.
3562
- walkInContext (E, Dest, MemberAccessContext::Setter );
3561
+ walkInContext (Dest, MemberAccessContext::Assignment );
3563
3562
3564
3563
// Check RHS in getter context
3565
3564
Expr *Source = E->getSrc ();
3566
3565
if (!Source) {
3567
3566
return ;
3568
3567
}
3569
- walkInContext (E, Source, MemberAccessContext::Getter );
3568
+ walkInContext (Source, MemberAccessContext::Default );
3570
3569
}
3571
-
3570
+
3571
+ // / Walk a load expression, checking for availability.
3572
+ void walkLoadExpr (LoadExpr *E) {
3573
+ walkInContext (E->getSubExpr (), MemberAccessContext::Load);
3574
+ }
3575
+
3572
3576
// / Walk a member reference expression, checking for availability.
3573
3577
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.
3578
3583
//
3579
3584
// someVar.x.y = 1
3580
- // │ ╰─ MemberAccessContext::Setter
3581
- // ╰─── MemberAccessContext::InOut
3585
+ // │ ╰─ MemberAccessContext::Assignment
3586
+ // ╰─── MemberAccessContext::Writeback
3582
3587
//
3583
3588
MemberAccessContext accessContext =
3584
- (AccessContext == MemberAccessContext::Setter )
3585
- ? MemberAccessContext::InOut
3589
+ (AccessContext == MemberAccessContext::Assignment )
3590
+ ? MemberAccessContext::Writeback
3586
3591
: AccessContext;
3587
- walkInContext (E, E ->getBase (), accessContext);
3592
+ walkInContext (E->getBase (), accessContext);
3588
3593
3589
3594
ConcreteDeclRef DR = E->getMember ();
3590
3595
// Diagnose for the member declaration itself.
@@ -3637,7 +3642,13 @@ class ExprAvailabilityWalker : public ASTWalker {
3637
3642
3638
3643
// / Walk an inout expression, checking for availability.
3639
3644
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);
3641
3652
}
3642
3653
3643
3654
bool shouldWalkIntoClosure (AbstractClosureExpr *closure) const {
@@ -3659,8 +3670,7 @@ class ExprAvailabilityWalker : public ASTWalker {
3659
3670
}
3660
3671
3661
3672
// / 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) {
3664
3674
llvm::SaveAndRestore<MemberAccessContext>
3665
3675
C (this ->AccessContext , AccessContext);
3666
3676
E->walk (*this );
@@ -3687,28 +3697,25 @@ class ExprAvailabilityWalker : public ASTWalker {
3687
3697
// this probably needs to be refined to not assume that the accesses are
3688
3698
// specifically using the getter/setter.
3689
3699
switch (AccessContext) {
3690
- case MemberAccessContext::Getter:
3700
+ case MemberAccessContext::Default:
3701
+ case MemberAccessContext::Load:
3691
3702
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
3692
3703
ReferenceRange, ReferenceDC, std::nullopt);
3693
3704
break ;
3694
3705
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);
3700
3709
break ;
3701
3710
3702
- case MemberAccessContext::InOut :
3711
+ case MemberAccessContext::Writeback :
3703
3712
diagAccessorAvailability (D->getOpaqueAccessor (AccessorKind::Get),
3704
3713
ReferenceRange, ReferenceDC,
3705
3714
DeclAvailabilityFlag::ForInout);
3706
3715
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);
3712
3719
break ;
3713
3720
}
3714
3721
}
0 commit comments