Skip to content

Commit 63f810d

Browse files
committed
Variables in 'case' labels with multiple patterns.
Parser now accepts multiple patterns in switch cases that contain variables. Every pattern must contain the same variable names, but can be in arbitrary positions. New error for variable that doesn't exist in all patterns. Sema now checks cases with multiple patterns that each occurence of a variable name is bound to the same type. New error for unexpected types. SILGen now shares basic blocks for switch cases that contain multiple patterns. That BB takes incoming arguments from each applicable pattern match emission with the specific var decls for the pattern that matched. Added tests for all three of these, and some simple IDE completion sanity tests.
1 parent b509fca commit 63f810d

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)