Skip to content

Commit 8311f2b

Browse files
committed
[ast] Add helpers for grabbing various case stmt corresponding var decls to a specific case stmt var decl.
1 parent 0c8920e commit 8311f2b

File tree

3 files changed

+80
-2
lines changed

3 files changed

+80
-2
lines changed

include/swift/AST/Decl.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "swift/Basic/ArrayRefView.h"
3737
#include "swift/Basic/Compiler.h"
3838
#include "swift/Basic/InlineBitfield.h"
39+
#include "swift/Basic/NullablePtr.h"
3940
#include "swift/Basic/OptionalEnum.h"
4041
#include "swift/Basic/Range.h"
4142
#include "llvm/ADT/DenseMap.h"
@@ -4747,8 +4748,32 @@ class VarDecl : public AbstractStorageDecl {
47474748
/// return this. Otherwise, this VarDecl must belong to a CaseStmt's
47484749
/// CaseLabelItem. In that case, return the first case label item of the first
47494750
/// case stmt in a sequence of case stmts that fallthrough into each other.
4751+
///
4752+
/// NOTE: During type checking, we emit an error if we have a single case
4753+
/// label item with a pattern that has multiple var decls of the same
4754+
/// name. This means that during type checking and before type checking, we
4755+
/// may have a _malformed_ switch stmt var decl linked list since var decls in
4756+
/// the same case label item that have the same name will point at the same
4757+
/// canonical var decl, namely the first var decl with the name in the
4758+
/// canonical case label item's var decl list. This is ok, since we are going
4759+
/// to emit the error, but it requires us to be more careful/cautious before
4760+
/// type checking has been complete when relying on canonical var decls
4761+
/// matching up.
47504762
VarDecl *getCanonicalVarDecl() const;
47514763

4764+
/// If this is a case stmt var decl, return the var decl that corresponds to
4765+
/// this var decl in the first case label item of the case stmt. Returns
4766+
/// nullptr if this isn't a VarDecl that is part of a case stmt.
4767+
NullablePtr<VarDecl> getCorrespondingFirstCaseLabelItemVarDecl() const;
4768+
4769+
/// If this is a case stmt var decl, return the case body var decl that this
4770+
/// var decl maps to.
4771+
NullablePtr<VarDecl> getCorrespondingCaseBodyVariable() const;
4772+
4773+
/// Return true if this var decl is an implicit var decl belonging to a case
4774+
/// stmt's body.
4775+
bool isCaseBodyVariable() const;
4776+
47524777
/// True if the global stored property requires lazy initialization.
47534778
bool isLazilyInitializedGlobal() const;
47544779

include/swift/Basic/NullablePtr.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,13 @@ class NullablePtr {
6161

6262
explicit operator bool() const { return Ptr; }
6363

64-
bool operator==(NullablePtr<T> &&other) const { return other.Ptr == Ptr; }
64+
bool operator==(const NullablePtr<T> &other) const {
65+
return other.Ptr == Ptr;
66+
}
6567

66-
bool operator!=(NullablePtr<T> &&other) const { return !(*this == other); }
68+
bool operator!=(const NullablePtr<T> &other) const {
69+
return !(*this == other);
70+
}
6771

6872
bool operator==(const T *other) const { return other == Ptr; }
6973

lib/AST/Decl.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5066,6 +5066,55 @@ Pattern *VarDecl::getParentPattern() const {
50665066
return nullptr;
50675067
}
50685068

5069+
NullablePtr<VarDecl>
5070+
VarDecl::getCorrespondingFirstCaseLabelItemVarDecl() const {
5071+
if (!hasName())
5072+
return nullptr;
5073+
5074+
auto *caseStmt = dyn_cast_or_null<CaseStmt>(getRecursiveParentPatternStmt());
5075+
if (!caseStmt)
5076+
return nullptr;
5077+
5078+
auto *pattern = caseStmt->getCaseLabelItems().front().getPattern();
5079+
SmallVector<VarDecl *, 8> vars;
5080+
pattern->collectVariables(vars);
5081+
for (auto *vd : vars) {
5082+
if (vd->hasName() && vd->getName() == getName())
5083+
return vd;
5084+
}
5085+
return nullptr;
5086+
}
5087+
5088+
bool VarDecl::isCaseBodyVariable() const {
5089+
auto *caseStmt = dyn_cast_or_null<CaseStmt>(getRecursiveParentPatternStmt());
5090+
if (!caseStmt)
5091+
return false;
5092+
return llvm::any_of(caseStmt->getCaseBodyVariablesOrEmptyArray(),
5093+
[&](VarDecl *vd) { return vd == this; });
5094+
}
5095+
5096+
NullablePtr<VarDecl> VarDecl::getCorrespondingCaseBodyVariable() const {
5097+
// Only var decls associated with case statements can have child var decls.
5098+
auto *caseStmt = dyn_cast_or_null<CaseStmt>(getRecursiveParentPatternStmt());
5099+
if (!caseStmt)
5100+
return nullptr;
5101+
5102+
// If this var decl doesn't have a name, it can not have a corresponding case
5103+
// body variable.
5104+
if (!hasName())
5105+
return nullptr;
5106+
5107+
auto name = getName();
5108+
5109+
// A var decl associated with a case stmt implies that the case stmt has body
5110+
// var decls. So we can access the optional value here without worry.
5111+
auto caseBodyVars = *caseStmt->getCaseBodyVariables();
5112+
auto result = llvm::find_if(caseBodyVars, [&](VarDecl *caseBodyVar) {
5113+
return caseBodyVar->getName() == name;
5114+
});
5115+
return (result != caseBodyVars.end()) ? *result : nullptr;
5116+
}
5117+
50695118
bool VarDecl::isSelfParameter() const {
50705119
if (isa<ParamDecl>(this)) {
50715120
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(getDeclContext()))

0 commit comments

Comments
 (0)