Skip to content

Commit c0cf407

Browse files
committed
[Constraint solver] Check case variable mutability consistency.
1 parent 3887498 commit c0cf407

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

lib/Sema/TypeCheckStmt.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,22 +2071,45 @@ void swift::checkUnknownAttrRestrictions(
20712071
}
20722072

20732073
void swift::bindSwitchCasePatternVars(CaseStmt *caseStmt) {
2074-
llvm::SmallDenseMap<Identifier, VarDecl *, 4> latestVars;
2074+
llvm::SmallDenseMap<Identifier, std::pair<VarDecl *, bool>, 4> latestVars;
20752075
auto recordVar = [&](VarDecl *var) {
20762076
if (!var->hasName())
20772077
return;
20782078

20792079
// If there is an existing variable with this name, set it as the
20802080
// parent of this new variable.
20812081
auto &entry = latestVars[var->getName()];
2082-
if (entry) {
2082+
if (entry.first) {
20832083
assert(!var->getParentVarDecl() ||
2084-
var->getParentVarDecl() == entry);
2085-
var->setParentVarDecl(entry);
2084+
var->getParentVarDecl() == entry.first);
2085+
var->setParentVarDecl(entry.first);
2086+
2087+
// Check for a mutability mismatch.
2088+
if (entry.second != var->isLet()) {
2089+
// Find the original declaration.
2090+
auto initialCaseVarDecl = entry.first;
2091+
while (auto parentVar = initialCaseVarDecl->getParentVarDecl())
2092+
initialCaseVarDecl = parentVar;
2093+
2094+
auto diag = var->diagnose(diag::mutability_mismatch_multiple_pattern_list,
2095+
var->isLet(), initialCaseVarDecl->isLet());
2096+
2097+
VarPattern *foundVP = nullptr;
2098+
var->getParentPattern()->forEachNode([&](Pattern *P) {
2099+
if (auto *VP = dyn_cast<VarPattern>(P))
2100+
if (VP->getSingleVar() == var)
2101+
foundVP = VP;
2102+
});
2103+
if (foundVP)
2104+
diag.fixItReplace(foundVP->getLoc(),
2105+
initialCaseVarDecl->isLet() ? "let" : "var");
2106+
}
2107+
} else {
2108+
entry.second = var->isLet();
20862109
}
20872110

20882111
// Record this variable as the latest with this name.
2089-
entry = var;
2112+
entry.first = var;
20902113
};
20912114

20922115
// Wire up the parent var decls for each variable that occurs within

test/Constraints/function_builder_diags.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,3 +443,26 @@ func testUnknownInSwitchSwitch(e: E) {
443443
}
444444
}
445445
}
446+
447+
// Check for mutability mismatches when there are multiple case items
448+
// referring to same-named variables.
449+
enum E3 {
450+
case a(Int, String)
451+
case b(String, Int)
452+
case c(String, Int)
453+
}
454+
455+
func testCaseMutabilityMismatches(e: E3) {
456+
tuplify(true) { c in
457+
"testSwitch"
458+
switch e {
459+
case .a(let x, var y),
460+
.b(let y, // expected-error{{'let' pattern binding must match previous 'var' pattern binding}}
461+
var x), // expected-error{{'var' pattern binding must match previous 'let' pattern binding}}
462+
.c(let y, // expected-error{{'let' pattern binding must match previous 'var' pattern binding}}
463+
var x): // expected-error{{'var' pattern binding must match previous 'let' pattern binding}}
464+
x
465+
y += "a"
466+
}
467+
}
468+
}

0 commit comments

Comments
 (0)