Skip to content

Commit a416650

Browse files
committed
Merge pull request #1383 from gregomni/switch-cases
[Parse/Sema/SILGen] Variables in 'case' labels with multiple patterns.
2 parents 5c6d986 + 63f810d commit a416650

File tree

10 files changed

+491
-46
lines changed

10 files changed

+491
-46
lines changed

Diff for: include/swift/AST/DiagnosticsParse.def

+2-3
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,8 @@ WARNING(let_on_param_is_redundant, none,
644644
ERROR(var_pattern_in_var,none,
645645
"'%select{var|let}0' cannot appear nested inside another 'var' or "
646646
"'let' pattern", (unsigned))
647+
ERROR(extra_var_in_multiple_pattern_list,none,
648+
"%0 must be bound in every pattern", (Identifier))
647649
ERROR(let_pattern_in_immutable_context,none,
648650
"'let' pattern cannot appear nested in an already immutable context", ())
649651
ERROR(inout_must_have_type,none,
@@ -888,9 +890,6 @@ ERROR(expected_case_colon,PointsToFirstBadToken,
888890
ERROR(default_with_where,none,
889891
"'default' cannot be used with a 'where' guard expression",
890892
())
891-
ERROR(var_binding_with_multiple_case_patterns,none,
892-
"'case' labels with multiple patterns cannot declare variables",
893-
())
894893
ERROR(case_stmt_without_body,none,
895894
"%select{'case'|'default'}0 label in a 'switch' should have at least one "
896895
"executable statement", (bool))

Diff for: include/swift/AST/DiagnosticsSema.def

+3
Original file line numberDiff line numberDiff line change
@@ -2154,6 +2154,9 @@ ERROR(fallthrough_into_case_with_var_binding,none,
21542154
ERROR(unnecessary_cast_over_optionset,none,
21552155
"unnecessary cast over raw value of %0", (Type))
21562156

2157+
ERROR(type_mismatch_multiple_pattern_list,none,
2158+
"pattern variable bound to type %0, expected type %1", (Type, Type))
2159+
21572160
//------------------------------------------------------------------------------
21582161
// Type Check Patterns
21592162
//------------------------------------------------------------------------------

Diff for: lib/Parse/ParseStmt.cpp

+65-17
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,8 @@ namespace {
858858
static void parseGuardedPattern(Parser &P, GuardedPattern &result,
859859
ParserStatus &status,
860860
SmallVectorImpl<VarDecl *> &boundDecls,
861-
GuardedPatternContext parsingContext) {
861+
GuardedPatternContext parsingContext,
862+
bool isFirstPattern) {
862863
ParserResult<Pattern> patternResult;
863864
auto setErrorResult = [&] () {
864865
patternResult = makeParserErrorResult(new (P.Context)
@@ -945,14 +946,64 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result,
945946
status |= patternResult;
946947
result.ThePattern = patternResult.get();
947948

948-
// Add variable bindings from the pattern to the case scope. We have
949-
// to do this with a full AST walk, because the freshly parsed pattern
950-
// represents tuples and var patterns as tupleexprs and
951-
// unresolved_pattern_expr nodes, instead of as proper pattern nodes.
952-
patternResult.get()->forEachVariable([&](VarDecl *VD) {
953-
if (VD->hasName()) P.addToScope(VD);
954-
boundDecls.push_back(VD);
955-
});
949+
if (isFirstPattern) {
950+
// Add variable bindings from the pattern to the case scope. We have
951+
// to do this with a full AST walk, because the freshly parsed pattern
952+
// represents tuples and var patterns as tupleexprs and
953+
// unresolved_pattern_expr nodes, instead of as proper pattern nodes.
954+
patternResult.get()->forEachVariable([&](VarDecl *VD) {
955+
if (VD->hasName()) P.addToScope(VD);
956+
boundDecls.push_back(VD);
957+
});
958+
} else {
959+
// If boundDecls already contains variables, then we must match the
960+
// same number and same names in this pattern as were declared in a
961+
// previous pattern (and later we will make sure they have the same
962+
// types).
963+
SmallVector<VarDecl*, 4> repeatedDecls;
964+
patternResult.get()->forEachVariable([&](VarDecl *VD) {
965+
if (!VD->hasName())
966+
return;
967+
968+
for (auto repeat : repeatedDecls)
969+
if (repeat->getName() == VD->getName())
970+
P.addToScope(VD); // will diagnose a duplicate declaration
971+
972+
bool found = false;
973+
for (auto previous : boundDecls) {
974+
if (previous->hasName() && previous->getName() == VD->getName()) {
975+
found = true;
976+
break;
977+
}
978+
}
979+
if (!found) {
980+
// Diagnose a declaration that doesn't match a previous pattern.
981+
P.diagnose(VD->getLoc(), diag::extra_var_in_multiple_pattern_list, VD->getName());
982+
status.setIsParseError();
983+
}
984+
repeatedDecls.push_back(VD);
985+
});
986+
987+
for (auto previous : boundDecls) {
988+
bool found = false;
989+
for (auto repeat : repeatedDecls) {
990+
if (previous->hasName() && previous->getName() == repeat->getName()) {
991+
found = true;
992+
break;
993+
}
994+
}
995+
if (!found) {
996+
// Diagnose a previous declaration that is missing in this pattern.
997+
P.diagnose(previous->getLoc(), diag::extra_var_in_multiple_pattern_list, previous->getName());
998+
status.setIsParseError();
999+
}
1000+
}
1001+
1002+
for (auto VD : repeatedDecls) {
1003+
VD->setHasNonPatternBindingInit();
1004+
VD->setImplicit();
1005+
}
1006+
}
9561007

9571008
// Now that we have them, mark them as being initialized without a PBD.
9581009
for (auto VD : boundDecls)
@@ -2004,7 +2055,7 @@ ParserResult<CatchStmt> Parser::parseStmtCatch() {
20042055
ParserStatus status;
20052056
GuardedPattern pattern;
20062057
parseGuardedPattern(*this, pattern, status, boundDecls,
2007-
GuardedPatternContext::Catch);
2058+
GuardedPatternContext::Catch, /* isFirst */ true);
20082059
if (status.hasCodeCompletion()) {
20092060
return makeParserCodeCompletionResult<CatchStmt>();
20102061
}
@@ -2509,17 +2560,19 @@ static ParserStatus parseStmtCase(Parser &P, SourceLoc &CaseLoc,
25092560
SmallVectorImpl<VarDecl *> &BoundDecls,
25102561
SourceLoc &ColonLoc) {
25112562
ParserStatus Status;
2512-
2563+
bool isFirst = true;
2564+
25132565
CaseLoc = P.consumeToken(tok::kw_case);
25142566

25152567
do {
25162568
GuardedPattern PatternResult;
25172569
parseGuardedPattern(P, PatternResult, Status, BoundDecls,
2518-
GuardedPatternContext::Case);
2570+
GuardedPatternContext::Case, isFirst);
25192571
LabelItems.push_back(CaseLabelItem(/*IsDefault=*/false,
25202572
PatternResult.ThePattern,
25212573
PatternResult.WhereLoc,
25222574
PatternResult.Guard));
2575+
isFirst = false;
25232576
} while (P.consumeIf(tok::comma));
25242577

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

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

2589-
// Case blocks with multiple patterns cannot bind variables.
2590-
if (!BoundDecls.empty() && CaseLabelItems.size() > 1)
2591-
diagnose(BoundDecls[0]->getLoc(),
2592-
diag::var_binding_with_multiple_case_patterns);
2593-
25942642
SmallVector<ASTNode, 8> BodyItems;
25952643

25962644
SourceLoc StartOfBody = Tok.getLoc();

Diff for: lib/SILGen/SILGenFunction.h

+2
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
938938

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

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

0 commit comments

Comments
 (0)