Skip to content

Commit

Permalink
Merge pull request #23687 from gottesmm/pr-aa1beb8a78e3e8b7772bb6e273…
Browse files Browse the repository at this point in the history
…d51f214e0aa30d
  • Loading branch information
swift-ci authored Apr 4, 2019
2 parents 3b99358 + 7b0d845 commit 0205150
Show file tree
Hide file tree
Showing 14 changed files with 479 additions and 142 deletions.
25 changes: 25 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "swift/Basic/ArrayRefView.h"
#include "swift/Basic/Compiler.h"
#include "swift/Basic/InlineBitfield.h"
#include "swift/Basic/NullablePtr.h"
#include "swift/Basic/OptionalEnum.h"
#include "swift/Basic/Range.h"
#include "llvm/ADT/DenseMap.h"
Expand Down Expand Up @@ -4747,8 +4748,32 @@ class VarDecl : public AbstractStorageDecl {
/// return this. Otherwise, this VarDecl must belong to a CaseStmt's
/// CaseLabelItem. In that case, return the first case label item of the first
/// case stmt in a sequence of case stmts that fallthrough into each other.
///
/// NOTE: During type checking, we emit an error if we have a single case
/// label item with a pattern that has multiple var decls of the same
/// name. This means that during type checking and before type checking, we
/// may have a _malformed_ switch stmt var decl linked list since var decls in
/// the same case label item that have the same name will point at the same
/// canonical var decl, namely the first var decl with the name in the
/// canonical case label item's var decl list. This is ok, since we are going
/// to emit the error, but it requires us to be more careful/cautious before
/// type checking has been complete when relying on canonical var decls
/// matching up.
VarDecl *getCanonicalVarDecl() const;

/// If this is a case stmt var decl, return the var decl that corresponds to
/// this var decl in the first case label item of the case stmt. Returns
/// nullptr if this isn't a VarDecl that is part of a case stmt.
NullablePtr<VarDecl> getCorrespondingFirstCaseLabelItemVarDecl() const;

/// If this is a case stmt var decl, return the case body var decl that this
/// var decl maps to.
NullablePtr<VarDecl> getCorrespondingCaseBodyVariable() const;

/// Return true if this var decl is an implicit var decl belonging to a case
/// stmt's body.
bool isCaseBodyVariable() const;

/// True if the global stored property requires lazy initialization.
bool isLazilyInitializedGlobal() const;

Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/Pattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ class alignas(8) Pattern {
/// pattern.
void forEachVariable(llvm::function_ref<void(VarDecl *)> f) const;

/// Returns true if \p vd is in the pattern.
bool containsVarDecl(const VarDecl *inputVD) const {
bool result = false;
forEachVariable([&](VarDecl *vd) { result |= inputVD == vd; });
return result;
}

/// apply the specified function to all pattern nodes recursively in
/// this pattern. This is a pre-order traversal.
void forEachNode(llvm::function_ref<void(Pattern *)> f);
Expand Down
8 changes: 6 additions & 2 deletions include/swift/Basic/NullablePtr.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,13 @@ class NullablePtr {

explicit operator bool() const { return Ptr; }

bool operator==(NullablePtr<T> &&other) const { return other.Ptr == Ptr; }
bool operator==(const NullablePtr<T> &other) const {
return other.Ptr == Ptr;
}

bool operator!=(NullablePtr<T> &&other) const { return !(*this == other); }
bool operator!=(const NullablePtr<T> &other) const {
return !(*this == other);
}

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

Expand Down
4 changes: 2 additions & 2 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -689,11 +689,11 @@ class Parser {
swift::ScopeInfo &getScopeInfo() { return State->getScopeInfo(); }

/// Add the given Decl to the current scope.
void addToScope(ValueDecl *D) {
void addToScope(ValueDecl *D, bool diagnoseRedefinitions = true) {
if (Context.LangOpts.EnableASTScopeLookup)
return;

getScopeInfo().addToScope(D, *this);
getScopeInfo().addToScope(D, *this, diagnoseRedefinitions);
}

ValueDecl *lookupInScope(DeclName Name) {
Expand Down
3 changes: 2 additions & 1 deletion include/swift/Parse/Scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ class ScopeInfo {

/// addToScope - Register the specified decl as being in the current lexical
/// scope.
void addToScope(ValueDecl *D, Parser &TheParser);
void addToScope(ValueDecl *D, Parser &TheParser,
bool diagnoseRedefinitions = true);

bool isInactiveConfigBlock() const;

Expand Down
14 changes: 14 additions & 0 deletions lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2500,6 +2500,20 @@ class Verifier : public ASTWalker {
}
}

if (auto *caseStmt =
dyn_cast_or_null<CaseStmt>(var->getRecursiveParentPatternStmt())) {
// In a type checked AST, a case stmt that is a recursive parent pattern
// stmt of a var decl, must have bound decls. This is because we
// guarantee that all case label items bind corresponding patterns and
// the case body var decls of a case stmt are created from the var decls
// of the first case label items.
if (!caseStmt->hasBoundDecls()) {
Out << "parent CaseStmt of VarDecl does not have any case body "
"decls?!\n";
abort();
}
}

verifyCheckedBase(var);
}

Expand Down
73 changes: 54 additions & 19 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1294,27 +1294,19 @@ VarDecl *PatternBindingInitializer::getInitializedLazyVar() const {
return nullptr;
}

static bool patternContainsVarDeclBinding(const Pattern *P, const VarDecl *VD) {
bool Result = false;
P->forEachVariable([&](VarDecl *FoundVD) {
Result |= FoundVD == VD;
});
return Result;
}

unsigned PatternBindingDecl::getPatternEntryIndexForVarDecl(const VarDecl *VD) const {
assert(VD && "Cannot find a null VarDecl");

auto List = getPatternList();
if (List.size() == 1) {
assert(patternContainsVarDeclBinding(List[0].getPattern(), VD) &&
assert(List[0].getPattern()->containsVarDecl(VD) &&
"Single entry PatternBindingDecl is set up wrong");
return 0;
}

unsigned Result = 0;
for (auto entry : List) {
if (patternContainsVarDeclBinding(entry.getPattern(), VD))
if (entry.getPattern()->containsVarDecl(VD))
return Result;
++Result;
}
Expand Down Expand Up @@ -4927,12 +4919,6 @@ SourceRange VarDecl::getTypeSourceRangeForDiagnostics() const {
return SourceRange();
}

static bool isVarInPattern(const VarDecl *vd, Pattern *p) {
bool foundIt = false;
p->forEachVariable([&](VarDecl *foundFD) { foundIt |= foundFD == vd; });
return foundIt;
}

static Optional<std::pair<CaseStmt *, Pattern *>>
findParentPatternCaseStmtAndPattern(const VarDecl *inputVD) {
auto getMatchingPattern = [&](CaseStmt *cs) -> Pattern * {
Expand All @@ -4946,7 +4932,7 @@ findParentPatternCaseStmtAndPattern(const VarDecl *inputVD) {

// Then check the rest of our case label items.
for (auto &item : cs->getMutableCaseLabelItems()) {
if (isVarInPattern(inputVD, item.getPattern())) {
if (item.getPattern()->containsVarDecl(inputVD)) {
return item.getPattern();
}
}
Expand Down Expand Up @@ -5039,15 +5025,15 @@ Pattern *VarDecl::getParentPattern() const {
// In a case statement, search for the pattern that contains it. This is
// a bit silly, because you can't have something like "case x, y:" anyway.
for (auto items : cs->getCaseLabelItems()) {
if (isVarInPattern(this, items.getPattern()))
if (items.getPattern()->containsVarDecl(this))
return items.getPattern();
}
}

if (auto *LCS = dyn_cast<LabeledConditionalStmt>(stmt)) {
for (auto &elt : LCS->getCond())
if (auto pat = elt.getPatternOrNull())
if (isVarInPattern(this, pat))
if (pat->containsVarDecl(this))
return pat;
}

Expand All @@ -5066,6 +5052,55 @@ Pattern *VarDecl::getParentPattern() const {
return nullptr;
}

NullablePtr<VarDecl>
VarDecl::getCorrespondingFirstCaseLabelItemVarDecl() const {
if (!hasName())
return nullptr;

auto *caseStmt = dyn_cast_or_null<CaseStmt>(getRecursiveParentPatternStmt());
if (!caseStmt)
return nullptr;

auto *pattern = caseStmt->getCaseLabelItems().front().getPattern();
SmallVector<VarDecl *, 8> vars;
pattern->collectVariables(vars);
for (auto *vd : vars) {
if (vd->hasName() && vd->getName() == getName())
return vd;
}
return nullptr;
}

bool VarDecl::isCaseBodyVariable() const {
auto *caseStmt = dyn_cast_or_null<CaseStmt>(getRecursiveParentPatternStmt());
if (!caseStmt)
return false;
return llvm::any_of(caseStmt->getCaseBodyVariablesOrEmptyArray(),
[&](VarDecl *vd) { return vd == this; });
}

NullablePtr<VarDecl> VarDecl::getCorrespondingCaseBodyVariable() const {
// Only var decls associated with case statements can have child var decls.
auto *caseStmt = dyn_cast_or_null<CaseStmt>(getRecursiveParentPatternStmt());
if (!caseStmt)
return nullptr;

// If this var decl doesn't have a name, it can not have a corresponding case
// body variable.
if (!hasName())
return nullptr;

auto name = getName();

// A var decl associated with a case stmt implies that the case stmt has body
// var decls. So we can access the optional value here without worry.
auto caseBodyVars = *caseStmt->getCaseBodyVariables();
auto result = llvm::find_if(caseBodyVars, [&](VarDecl *caseBodyVar) {
return caseBodyVar->getName() == name;
});
return (result != caseBodyVars.end()) ? *result : nullptr;
}

bool VarDecl::isSelfParameter() const {
if (isa<ParamDecl>(this)) {
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(getDeclContext()))
Expand Down
10 changes: 8 additions & 2 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2347,8 +2347,14 @@ void FindLocalVal::visitCaseStmt(CaseStmt *S) {
}
}
}
if (!inPatterns && !items.empty())
checkPattern(items[0].getPattern(), DeclVisibilityKind::LocalVariable);

if (!inPatterns && !items.empty()) {
if (auto caseBodyVars = S->getCaseBodyVariables()) {
for (auto *vd : *caseBodyVars) {
checkValueDecl(vd, DeclVisibilityKind::LocalVariable);
}
}
}
visit(S->getBody());
}

Expand Down
14 changes: 14 additions & 0 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2476,6 +2476,20 @@ ParserResult<CaseStmt> Parser::parseStmtCase(bool IsActive) {

assert(!CaseLabelItems.empty() && "did not parse any labels?!");

// Add a scope so that the parser can find our body bound decls if it emits
// optimized accesses.
Optional<Scope> BodyScope;
if (CaseBodyDecls) {
BodyScope.emplace(this, ScopeKind::CaseVars);
for (auto *v : *CaseBodyDecls) {
setLocalDiscriminator(v);
// If we had any bad redefinitions, we already diagnosed them against the
// first case label item.
if (v->hasName())
addToScope(v, false /*diagnoseRedefinitions*/);
}
}

SmallVector<ASTNode, 8> BodyItems;

SourceLoc StartOfBody = Tok.getLoc();
Expand Down
13 changes: 9 additions & 4 deletions lib/Parse/Scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ static bool checkValidOverload(const ValueDecl *D1, const ValueDecl *D2,

/// addToScope - Register the specified decl as being in the current lexical
/// scope.
void ScopeInfo::addToScope(ValueDecl *D, Parser &TheParser) {
void ScopeInfo::addToScope(ValueDecl *D, Parser &TheParser,
bool diagnoseRedefinitions) {
if (!CurScope->isResolvable())
return;

Expand All @@ -121,9 +122,13 @@ void ScopeInfo::addToScope(ValueDecl *D, Parser &TheParser) {

// If this is in a resolvable scope, diagnose redefinitions. Later
// phases will handle scopes like module-scope, etc.
if (CurScope->getDepth() >= ResolvableDepth)
return TheParser.diagnoseRedefinition(PrevDecl, D);

if (CurScope->getDepth() >= ResolvableDepth) {
if (diagnoseRedefinitions) {
return TheParser.diagnoseRedefinition(PrevDecl, D);
}
return;
}

// If this is at top-level scope, validate that the members of the overload
// set all agree.

Expand Down
Loading

0 comments on commit 0205150

Please sign in to comment.