Skip to content

[Parse/Sema/SILGen] Variables in 'case' labels with multiple patterns. #1383

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

Merged
merged 1 commit into from
Feb 25, 2016
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
5 changes: 2 additions & 3 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,8 @@ WARNING(let_on_param_is_redundant, none,
ERROR(var_pattern_in_var,none,
"'%select{var|let}0' cannot appear nested inside another 'var' or "
"'let' pattern", (unsigned))
ERROR(extra_var_in_multiple_pattern_list,none,
"%0 must be bound in every pattern", (Identifier))
ERROR(let_pattern_in_immutable_context,none,
"'let' pattern cannot appear nested in an already immutable context", ())
ERROR(inout_must_have_type,none,
Expand Down Expand Up @@ -888,9 +890,6 @@ ERROR(expected_case_colon,PointsToFirstBadToken,
ERROR(default_with_where,none,
"'default' cannot be used with a 'where' guard expression",
())
ERROR(var_binding_with_multiple_case_patterns,none,
"'case' labels with multiple patterns cannot declare variables",
())
ERROR(case_stmt_without_body,none,
"%select{'case'|'default'}0 label in a 'switch' should have at least one "
"executable statement", (bool))
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2154,6 +2154,9 @@ ERROR(fallthrough_into_case_with_var_binding,none,
ERROR(unnecessary_cast_over_optionset,none,
"unnecessary cast over raw value of %0", (Type))

ERROR(type_mismatch_multiple_pattern_list,none,
"pattern variable bound to type %0, expected type %1", (Type, Type))

//------------------------------------------------------------------------------
// Type Check Patterns
//------------------------------------------------------------------------------
Expand Down
82 changes: 65 additions & 17 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,8 @@ namespace {
static void parseGuardedPattern(Parser &P, GuardedPattern &result,
ParserStatus &status,
SmallVectorImpl<VarDecl *> &boundDecls,
GuardedPatternContext parsingContext) {
GuardedPatternContext parsingContext,
bool isFirstPattern) {
ParserResult<Pattern> patternResult;
auto setErrorResult = [&] () {
patternResult = makeParserErrorResult(new (P.Context)
Expand Down Expand Up @@ -945,14 +946,64 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result,
status |= patternResult;
result.ThePattern = patternResult.get();

// Add variable bindings from the pattern to the case scope. We have
// to do this with a full AST walk, because the freshly parsed pattern
// represents tuples and var patterns as tupleexprs and
// unresolved_pattern_expr nodes, instead of as proper pattern nodes.
patternResult.get()->forEachVariable([&](VarDecl *VD) {
if (VD->hasName()) P.addToScope(VD);
boundDecls.push_back(VD);
});
if (isFirstPattern) {
// Add variable bindings from the pattern to the case scope. We have
// to do this with a full AST walk, because the freshly parsed pattern
// represents tuples and var patterns as tupleexprs and
// unresolved_pattern_expr nodes, instead of as proper pattern nodes.
patternResult.get()->forEachVariable([&](VarDecl *VD) {
if (VD->hasName()) P.addToScope(VD);
boundDecls.push_back(VD);
});
} else {
// If boundDecls already contains variables, then we must match the
// same number and same names in this pattern as were declared in a
// previous pattern (and later we will make sure they have the same
// types).
SmallVector<VarDecl*, 4> repeatedDecls;
patternResult.get()->forEachVariable([&](VarDecl *VD) {
if (!VD->hasName())
return;

for (auto repeat : repeatedDecls)
if (repeat->getName() == VD->getName())
P.addToScope(VD); // will diagnose a duplicate declaration

bool found = false;
for (auto previous : boundDecls) {
if (previous->hasName() && previous->getName() == VD->getName()) {
found = true;
break;
}
}
if (!found) {
// Diagnose a declaration that doesn't match a previous pattern.
P.diagnose(VD->getLoc(), diag::extra_var_in_multiple_pattern_list, VD->getName());
status.setIsParseError();
}
repeatedDecls.push_back(VD);
});

for (auto previous : boundDecls) {
bool found = false;
for (auto repeat : repeatedDecls) {
if (previous->hasName() && previous->getName() == repeat->getName()) {
found = true;
break;
}
}
if (!found) {
// Diagnose a previous declaration that is missing in this pattern.
P.diagnose(previous->getLoc(), diag::extra_var_in_multiple_pattern_list, previous->getName());
status.setIsParseError();
}
}

for (auto VD : repeatedDecls) {
VD->setHasNonPatternBindingInit();
VD->setImplicit();
}
}

// Now that we have them, mark them as being initialized without a PBD.
for (auto VD : boundDecls)
Expand Down Expand Up @@ -2004,7 +2055,7 @@ ParserResult<CatchStmt> Parser::parseStmtCatch() {
ParserStatus status;
GuardedPattern pattern;
parseGuardedPattern(*this, pattern, status, boundDecls,
GuardedPatternContext::Catch);
GuardedPatternContext::Catch, /* isFirst */ true);
if (status.hasCodeCompletion()) {
return makeParserCodeCompletionResult<CatchStmt>();
}
Expand Down Expand Up @@ -2509,17 +2560,19 @@ static ParserStatus parseStmtCase(Parser &P, SourceLoc &CaseLoc,
SmallVectorImpl<VarDecl *> &BoundDecls,
SourceLoc &ColonLoc) {
ParserStatus Status;

bool isFirst = true;

CaseLoc = P.consumeToken(tok::kw_case);

do {
GuardedPattern PatternResult;
parseGuardedPattern(P, PatternResult, Status, BoundDecls,
GuardedPatternContext::Case);
GuardedPatternContext::Case, isFirst);
LabelItems.push_back(CaseLabelItem(/*IsDefault=*/false,
PatternResult.ThePattern,
PatternResult.WhereLoc,
PatternResult.Guard));
isFirst = false;
} while (P.consumeIf(tok::comma));

ColonLoc = P.Tok.getLoc();
Expand Down Expand Up @@ -2586,11 +2639,6 @@ ParserResult<CaseStmt> Parser::parseStmtCase() {

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

// Case blocks with multiple patterns cannot bind variables.
if (!BoundDecls.empty() && CaseLabelItems.size() > 1)
diagnose(BoundDecls[0]->getLoc(),
diag::var_binding_with_multiple_case_patterns);

SmallVector<ASTNode, 8> BodyItems;

SourceLoc StartOfBody = Tok.getLoc();
Expand Down
2 changes: 2 additions & 0 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction

void emitConditionalPBD(PatternBindingDecl *PBD, SILBasicBlock *FailBB);

void usingImplicitVariablesForPattern(Pattern *pattern, CaseStmt *stmt,
const llvm::function_ref<void(void)> &f);
void emitSwitchStmt(SwitchStmt *S);
void emitSwitchFallthrough(FallthroughStmt *S);

Expand Down
Loading