Skip to content

Added parsing tests for nested cases in switch statements. #1

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

Open
wants to merge 4 commits into
base: vg-into-the-unknown
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5557,6 +5557,10 @@ WARNING(non_exhaustive_switch_unknown_only,none,
"switch covers known cases, but %0 may have additional unknown values"
"%select{|, possibly added in future versions}1", (Type, bool))

WARNING(unknown_pattern_for_non_enum,none,
"unknown pattern is not applicable to non-enum type %0", (Type))
NOTE(non_enum_drop_unknown,none, "remove '@unknown'", ())

ERROR(override_nsobject_hashvalue_error,none,
"'NSObject.hashValue' is not overridable; "
"did you mean to override 'NSObject.hash'?", ())
Expand Down
11 changes: 8 additions & 3 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1164,12 +1164,17 @@ class ObjectLiteralExpr final
/// DiscardAssignmentExpr - A '_' in the left-hand side of an assignment, which
/// discards the corresponding tuple element on the right-hand side.
class DiscardAssignmentExpr : public Expr {
/// HACK: Location for @unknown _ in conditional clauses in switch statements.
/// @unknown isn't permitted in assignment contexts.
SourceLoc UnknownLoc;
SourceLoc Loc;

public:
DiscardAssignmentExpr(SourceLoc Loc, bool Implicit)
: Expr(ExprKind::DiscardAssignment, Implicit), Loc(Loc) {}

DiscardAssignmentExpr(SourceLoc UnknownLoc, SourceLoc Loc, bool Implicit)
: Expr(ExprKind::DiscardAssignment, Implicit), UnknownLoc(UnknownLoc),
Loc(Loc) {}

SourceLoc getUnknownLoc() const { return UnknownLoc; }
SourceRange getSourceRange() const { return Loc; }
SourceLoc getLoc() const { return Loc; }

Expand Down
18 changes: 13 additions & 5 deletions include/swift/AST/Pattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ class alignas(8) Pattern {

bool isNeverDefaultInitializable() const;

/// Return true if this pattern (or a subpattern) contains @unknown _.
bool hasUnknownAnyPattern() const;

/// Mark all vardecls in this pattern as having an owning statement for
/// the pattern.
void markOwnedByStatement(Stmt *S) {
Expand Down Expand Up @@ -369,22 +372,27 @@ class NamedPattern : public Pattern {
};

/// A pattern which matches an arbitrary value of a type, but does not
/// bind a name to it. This is spelled "_".
/// bind a name to it. This is spelled "_" or "@unknown _".
class AnyPattern : public Pattern {
SourceLoc UnknownLoc;
SourceLoc Loc;

public:
explicit AnyPattern(SourceLoc Loc)
: Pattern(PatternKind::Any), Loc(Loc) { }
explicit AnyPattern(SourceLoc UnknownLoc, SourceLoc Loc)
: Pattern(PatternKind::Any), UnknownLoc(UnknownLoc), Loc(Loc) {}

static AnyPattern *createImplicit(ASTContext &Context) {
auto *AP = new (Context) AnyPattern(SourceLoc());
auto *AP = new (Context) AnyPattern(SourceLoc(), SourceLoc());
AP->setImplicit();
return AP;
}

SourceLoc getUnknownLoc() const { return UnknownLoc; }
bool isUnknown() const { return UnknownLoc.isValid(); }
SourceLoc getLoc() const { return Loc; }
SourceRange getSourceRange() const { return Loc; }
SourceRange getSourceRange() const {
return isUnknown() ? SourceRange(UnknownLoc, Loc) : SourceRange(Loc);
}

static bool classof(const Pattern *P) {
return P->getKind() == PatternKind::Any;
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ namespace {
}
void visitAnyPattern(AnyPattern *P) {
printCommon(P, "pattern_any");
if (P->getUnknownLoc().isValid())
OS << " @unknown";
PrintWithColorRAII(OS, ParenthesisColor) << ')';
}
void visitTypedPattern(TypedPattern *P) {
Expand Down Expand Up @@ -2030,6 +2032,8 @@ class PrintExpr : public ExprVisitor<PrintExpr> {

void visitDiscardAssignmentExpr(DiscardAssignmentExpr *E) {
printCommon(E, "discard_assignment_expr");
if (E->getUnknownLoc().isValid())
OS << " @unknown";
PrintWithColorRAII(OS, ParenthesisColor) << ')';
}

Expand Down
24 changes: 24 additions & 0 deletions lib/AST/Pattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,30 @@ case PatternKind::ID: foundRefutablePattern = true; break;
return foundRefutablePattern;
}

bool Pattern::hasUnknownAnyPattern() const {
bool foundUnknownAnyPattern = false;
const_cast<Pattern *>(this)->forEachNode([&](Pattern *Node) {
switch (Node->getKind()) {
case PatternKind::Any:
foundUnknownAnyPattern |= cast<AnyPattern>(Node)->isUnknown();
return;
case PatternKind::Named:
case PatternKind::Expr:
case PatternKind::Bool:
case PatternKind::Is:
case PatternKind::Paren:
case PatternKind::Typed:
case PatternKind::Binding:
case PatternKind::Tuple:
case PatternKind::EnumElement:
case PatternKind::OptionalSome:
return;
}
llvm_unreachable("Unhandled PatternKind!");
});
return foundUnknownAnyPattern;
}

/// Standard allocator for Patterns.
void *Pattern::operator new(size_t numBytes, const ASTContext &C) {
return C.Allocate(numBytes, alignof(Pattern));
Expand Down
16 changes: 13 additions & 3 deletions lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1421,7 +1421,7 @@ ParserResult<Expr> Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) {
/// '.' identifier
///
/// expr-discard:
/// '_'
/// @unknown? '_' (@unknown can only appear in patterns)
///
/// expr-primary:
/// expr-literal
Expand Down Expand Up @@ -1456,6 +1456,16 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
// Objective-C programmers habitually type @"foo", so recover gracefully
// with a fixit. If this isn't @"foo", just handle it like an unknown
// input.
if (peekToken().isContextualKeyword("unknown")) {
auto UnknownLoc = consumeToken();
consumeToken();
if (!Tok.is(tok::kw__))
goto UnknownCharacter;
// Similar to the tok::kw__ case.
ExprContext.setCreateSyntax(SyntaxKind::DiscardAssignmentExpr);
return makeParserResult(new (Context) DiscardAssignmentExpr(
UnknownLoc, consumeToken(), /*Implicit=*/false));
}
if (peekToken().isNot(tok::string_literal))
goto UnknownCharacter;

Expand Down Expand Up @@ -1558,8 +1568,8 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {

case tok::kw__: // _
ExprContext.setCreateSyntax(SyntaxKind::DiscardAssignmentExpr);
return makeParserResult(
new (Context) DiscardAssignmentExpr(consumeToken(), /*Implicit=*/false));
return makeParserResult(new (Context) DiscardAssignmentExpr(
/*@unknown*/ SourceLoc(), consumeToken(), /*Implicit=*/false));

case tok::pound_selector: // expr-selector
return parseExprSelector();
Expand Down
10 changes: 6 additions & 4 deletions lib/Parse/ParsePattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ ParserResult<Pattern> Parser::parseTypedPattern() {

if (result.isNull()) {
// Recover by creating AnyPattern.
auto *AP = new (Context) AnyPattern(colonLoc);
auto *AP = new (Context) AnyPattern(/*@unknown*/ SourceLoc(), colonLoc);
if (colonLoc.isInvalid())
AP->setImplicit();
result = makeParserErrorResult(AP);
Expand Down Expand Up @@ -1044,8 +1044,9 @@ ParserResult<Pattern> Parser::parsePattern() {
return makeParserResult(NamedPattern::createImplicit(Context, VD));
}
PatternCtx.setCreateSyntax(SyntaxKind::WildcardPattern);
return makeParserResult(new (Context) AnyPattern(consumeToken(tok::kw__)));

return makeParserResult(new (Context) AnyPattern(/*@unknown*/ SourceLoc(),
consumeToken(tok::kw__)));

case tok::identifier: {
PatternCtx.setCreateSyntax(SyntaxKind::IdentifierPattern);
Identifier name;
Expand Down Expand Up @@ -1102,7 +1103,8 @@ ParserResult<Pattern> Parser::parsePattern() {
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
SourceLoc Loc = Tok.getLoc();
consumeToken();
return makeParserErrorResult(new (Context) AnyPattern(Loc));
return makeParserErrorResult(
new (Context) AnyPattern(/*@unknown*/ SourceLoc(), Loc));
}
diagnose(Tok, diag::expected_pattern);
return nullptr;
Expand Down
12 changes: 7 additions & 5 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,8 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result,
// If that didn't work, use a bogus pattern so that we can fill out
// the AST.
if (patternResult.isNull()) {
auto *AP = new (P.Context) AnyPattern(P.PreviousLoc);
auto *AP =
new (P.Context) AnyPattern(/*@unknown*/ SourceLoc(), P.PreviousLoc);
if (P.PreviousLoc.isInvalid())
AP->setImplicit();
patternResult = makeParserErrorResult(AP);
Expand Down Expand Up @@ -1551,7 +1552,7 @@ Parser::parseStmtConditionElement(SmallVectorImpl<StmtConditionElement> &result,

if (ThePattern.isNull()) {
// Recover by creating AnyPattern.
auto *AP = new (Context) AnyPattern(PreviousLoc);
auto *AP = new (Context) AnyPattern(/*@unknown*/ SourceLoc(), PreviousLoc);
if (PreviousLoc.isInvalid())
AP->setImplicit();
ThePattern = makeParserResult(AP);
Expand Down Expand Up @@ -2407,7 +2408,7 @@ parseStmtCase(Parser &P, SourceLoc &CaseLoc,
static ParserStatus
parseStmtCaseDefault(Parser &P, SourceLoc &CaseLoc,
SmallVectorImpl<CaseLabelItem> &LabelItems,
SourceLoc &ColonLoc) {
SourceLoc UnknownLoc, SourceLoc &ColonLoc) {
SyntaxParsingContext CaseContext(P.SyntaxContext,
SyntaxKind::SwitchDefaultLabel);
ParserStatus Status;
Expand All @@ -2433,7 +2434,7 @@ parseStmtCaseDefault(Parser &P, SourceLoc &CaseLoc,
P.consumeToken(tok::colon);

// Create an implicit AnyPattern to represent the default match.
auto Any = new (P.Context) AnyPattern(CaseLoc);
auto Any = new (P.Context) AnyPattern(UnknownLoc, CaseLoc);
if (CaseLoc.isInvalid())
Any->setImplicit();
LabelItems.push_back(
Expand Down Expand Up @@ -2525,7 +2526,8 @@ ParserResult<CaseStmt> Parser::parseStmtCase(bool IsActive) {
Status |= ::parseStmtCase(*this, CaseLoc, CaseLabelItems, BoundDecls,
ColonLoc, CaseBodyDecls);
} else if (Tok.is(tok::kw_default)) {
Status |= parseStmtCaseDefault(*this, CaseLoc, CaseLabelItems, ColonLoc);
Status |= parseStmtCaseDefault(*this, CaseLoc, CaseLabelItems,
UnknownAttrLoc, ColonLoc);
} else {
llvm_unreachable("isAtStartOfSwitchCase() lied.");
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
if (E->isImplicit()) {
return AnyPattern::createImplicit(Context);
}
return new (Context) AnyPattern(E->getLoc());
return new (Context) AnyPattern(E->getUnknownLoc(), E->getLoc());
}

// Cast expressions 'x as T' get resolved to checked cast patterns.
Expand Down
Loading