Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.1] AST: Avoid creating duplicate AvailabilityScopes under SequenceExprs #78719

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions include/swift/AST/ASTWalker.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ enum class QualifiedIdentTypeReprWalkingScheme {
ASTOrderRecursive
};

/// Specifies the behavior for walking SequenceExprs.
enum class SequenceWalking {
/// Walk into every element of the sequence, regardless of what state the
/// sequence is in.
Default,

/// If the sequence has been folded by type checking, only walk into the
/// elements that represent the operator nodes. This will ensure that the walk
/// does not visit the same AST nodes twice when it encounters a sequence that
/// has already been folded but hasn't been removed from the AST.
OnlyWalkFirstOperatorWhenFolded
};

/// An abstract class used to traverse an AST.
class ASTWalker {
public:
Expand Down Expand Up @@ -616,6 +629,13 @@ class ASTWalker {
return MacroWalking::ArgumentsAndExpansion;
}

/// This method configures how the walker should walk into SequenceExprs.
/// Needing to customize this behavior should be rare, as sequence expressions
/// are only encountered in un-typechecked ASTs.
virtual SequenceWalking getSequenceWalkingBehavior() const {
return SequenceWalking::Default;
}

/// This method determines whether the given declaration should be
/// considered to be in a macro expansion context. It can be configured
/// by subclasses.
Expand Down
12 changes: 10 additions & 2 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,10 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
IsObjC : 1
);

SWIFT_INLINE_BITFIELD_FULL(SequenceExpr, Expr, 32,
SWIFT_INLINE_BITFIELD_FULL(SequenceExpr, Expr, 32+1,
: NumPadBits,
NumElements : 32
NumElements : 32,
IsFolded: 1
);

SWIFT_INLINE_BITFIELD(OpaqueValueExpr, Expr, 1,
Expand Down Expand Up @@ -3987,6 +3988,13 @@ class SequenceExpr final : public Expr,
getElements()[i] = e;
}

bool isFolded() const {
return static_cast<bool>(Bits.SequenceExpr.IsFolded);
}
void setFolded(bool folded) {
Bits.SequenceExpr.IsFolded = static_cast<unsigned>(folded);
}

// Implement isa/cast/dyncast/etc.
static bool classof(const Expr *E) {
return E->getKind() == ExprKind::Sequence;
Expand Down
10 changes: 10 additions & 0 deletions lib/AST/ASTWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,16 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
}

Expr *visitSequenceExpr(SequenceExpr *E) {
if (Walker.getSequenceWalkingBehavior() ==
SequenceWalking::OnlyWalkFirstOperatorWhenFolded &&
E->isFolded()) {
if (E->getNumElements() > 1) {
if (Expr *Elt = doIt(E->getElement(1)))
E->setElement(1, Elt);
}
return E;
}

for (unsigned i = 0, e = E->getNumElements(); i != e; ++i)
if (Expr *Elt = doIt(E->getElement(i)))
E->setElement(i, Elt);
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/PreCheckTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,7 @@ class PreCheckTarget final : public ASTWalker {
if (!result)
return Action::Stop();
// Already walked.
seqExpr->setFolded(true);
return Action::SkipNode(result);
}

Expand Down
9 changes: 9 additions & 0 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,15 @@ class AvailabilityScopeBuilder : private ASTWalker {
return MacroWalking::Arguments;
}

SequenceWalking getSequenceWalkingBehavior() const override {
// Since availability scopes may be built at arbitrary times, the builder
// may encounter ASTs where SequenceExprs still exist and have not been
// folded, or it may encounter folded SequenceExprs that have not been
// removed from the AST. When folded exprs are encountered, its important
// to avoid walking into the same AST nodes twice.
return SequenceWalking::OnlyWalkFirstOperatorWhenFolded;
}

/// Check whether this declaration is within a macro expansion buffer that
/// will have its own availability scope that will be lazily expanded.
bool isDeclInMacroExpansion(Decl *decl) const override {
Expand Down
16 changes: 16 additions & 0 deletions test/Sema/availability_scopes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,22 @@ func testStringInterpolation() {
"""
}

// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=result
// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=unusedA
// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=unusedB

func testSequenceExprs(b: Bool, x: Int?) {
let result = b
? x.map {
let unusedA: Int
return $0
}
: x.map {
let unusedB: Int
return $0
}
}

// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOS()
// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=x

Expand Down