Skip to content

Commit 47d69a3

Browse files
authored
Merge pull request swiftlang#74799 from tshortli/accessor-availability-6.0
[6.0] Sema: Fix accessor availability checking regressions caused by swiftlang#72412
2 parents d64795c + e7ef347 commit 47d69a3

File tree

2 files changed

+422
-109
lines changed

2 files changed

+422
-109
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3319,25 +3319,32 @@ bool swift::diagnoseExplicitUnavailability(
33193319

33203320
namespace {
33213321
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.
33243325
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
33373344
};
33383345

33393346
ASTContext &Context;
3340-
MemberAccessContext AccessContext = MemberAccessContext::Getter;
3347+
MemberAccessContext AccessContext = MemberAccessContext::Default;
33413348
SmallVector<const Expr *, 16> ExprStack;
33423349
const ExportContext &Where;
33433350

@@ -3354,6 +3361,13 @@ class ExprAvailabilityWalker : public ASTWalker {
33543361
return MacroWalking::Arguments;
33553362
}
33563363

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+
33573371
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
33583372
auto *DC = Where.getDeclContext();
33593373

@@ -3476,6 +3490,11 @@ class ExprAvailabilityWalker : public ASTWalker {
34763490
ME->getMacroRef(), ME->getMacroNameLoc().getSourceRange());
34773491
}
34783492

3493+
if (auto LE = dyn_cast<LoadExpr>(E)) {
3494+
walkLoadExpr(LE);
3495+
return Action::SkipChildren(E);
3496+
}
3497+
34793498
return Action::Continue(E);
34803499
}
34813500

@@ -3532,20 +3551,6 @@ class ExprAvailabilityWalker : public ASTWalker {
35323551
return call;
35333552
}
35343553

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-
35493554
/// Walk an assignment expression, checking for availability.
35503555
void walkAssignExpr(AssignExpr *E) {
35513556
// We take over recursive walking of assignment expressions in order to
@@ -3561,32 +3566,38 @@ class ExprAvailabilityWalker : public ASTWalker {
35613566
// encountered walking (pre-order) is the Dest is the destination of the
35623567
// write. For the moment this is fine -- but future syntax might violate
35633568
// this assumption.
3564-
walkInContext(E, Dest, MemberAccessContext::Setter);
3569+
walkInContext(Dest, MemberAccessContext::Assignment);
35653570

35663571
// Check RHS in getter context
35673572
Expr *Source = E->getSrc();
35683573
if (!Source) {
35693574
return;
35703575
}
3571-
walkInContext(E, Source, MemberAccessContext::Getter);
3576+
walkInContext(Source, MemberAccessContext::Default);
35723577
}
3573-
3578+
3579+
/// Walk a load expression, checking for availability.
3580+
void walkLoadExpr(LoadExpr *E) {
3581+
walkInContext(E->getSubExpr(), MemberAccessContext::Load);
3582+
}
3583+
35743584
/// Walk a member reference expression, checking for availability.
35753585
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.
35803591
//
35813592
// someVar.x.y = 1
3582-
// │ ╰─ MemberAccessContext::Setter
3583-
// ╰─── MemberAccessContext::InOut
3593+
// │ ╰─ MemberAccessContext::Assignment
3594+
// ╰─── MemberAccessContext::Writeback
35843595
//
35853596
MemberAccessContext accessContext =
3586-
(AccessContext == MemberAccessContext::Setter)
3587-
? MemberAccessContext::InOut
3597+
(AccessContext == MemberAccessContext::Assignment)
3598+
? MemberAccessContext::Writeback
35883599
: AccessContext;
3589-
walkInContext(E, E->getBase(), accessContext);
3600+
walkInContext(E->getBase(), accessContext);
35903601

35913602
ConcreteDeclRef DR = E->getMember();
35923603
// Diagnose for the member declaration itself.
@@ -3639,12 +3650,13 @@ class ExprAvailabilityWalker : public ASTWalker {
36393650

36403651
/// Walk an inout expression, checking for availability.
36413652
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);
36483660
}
36493661

36503662
bool shouldWalkIntoClosure(AbstractClosureExpr *closure) const {
@@ -3665,10 +3677,8 @@ class ExprAvailabilityWalker : public ASTWalker {
36653677
return;
36663678
}
36673679

3668-
36693680
/// 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) {
36723682
llvm::SaveAndRestore<MemberAccessContext>
36733683
C(this->AccessContext, AccessContext);
36743684
E->walk(*this);
@@ -3695,17 +3705,18 @@ class ExprAvailabilityWalker : public ASTWalker {
36953705
// this probably needs to be refined to not assume that the accesses are
36963706
// specifically using the getter/setter.
36973707
switch (AccessContext) {
3698-
case MemberAccessContext::Getter:
3708+
case MemberAccessContext::Default:
3709+
case MemberAccessContext::Load:
36993710
diagAccessorAvailability(D->getOpaqueAccessor(AccessorKind::Get),
37003711
ReferenceRange, ReferenceDC, std::nullopt);
37013712
break;
37023713

3703-
case MemberAccessContext::Setter:
3714+
case MemberAccessContext::Assignment:
37043715
diagAccessorAvailability(D->getOpaqueAccessor(AccessorKind::Set),
37053716
ReferenceRange, ReferenceDC, std::nullopt);
37063717
break;
37073718

3708-
case MemberAccessContext::InOut:
3719+
case MemberAccessContext::Writeback:
37093720
diagAccessorAvailability(D->getOpaqueAccessor(AccessorKind::Get),
37103721
ReferenceRange, ReferenceDC,
37113722
DeclAvailabilityFlag::ForInout);

0 commit comments

Comments
 (0)