[ast][silgen] Wire up the case body var decls and use them in SILGenPattern emission to fix the evil fallthrough bug.
rdar://47467128
diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h
index badf942..1a521c4 100644
--- a/include/swift/Parse/Parser.h
+++ b/include/swift/Parse/Parser.h
@@ -689,11 +689,11 @@
swift::ScopeInfo &getScopeInfo() { return State->getScopeInfo(); }
/// Add the given Decl to the current scope.
- void addToScope(ValueDecl *D) {
+ void addToScope(ValueDecl *D, bool diagnoseRedefinitions = true) {
if (Context.LangOpts.EnableASTScopeLookup)
return;
- getScopeInfo().addToScope(D, *this);
+ getScopeInfo().addToScope(D, *this, diagnoseRedefinitions);
}
ValueDecl *lookupInScope(DeclName Name) {
diff --git a/include/swift/Parse/Scope.h b/include/swift/Parse/Scope.h
index 14175d5..2fb49bf 100644
--- a/include/swift/Parse/Scope.h
+++ b/include/swift/Parse/Scope.h
@@ -52,7 +52,8 @@
/// addToScope - Register the specified decl as being in the current lexical
/// scope.
- void addToScope(ValueDecl *D, Parser &TheParser);
+ void addToScope(ValueDecl *D, Parser &TheParser,
+ bool diagnoseRedefinitions = true);
bool isInactiveConfigBlock() const;
diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp
index cbf8067..6a5c740 100644
--- a/lib/AST/ASTVerifier.cpp
+++ b/lib/AST/ASTVerifier.cpp
@@ -2500,6 +2500,20 @@
}
}
+ if (auto *caseStmt =
+ dyn_cast_or_null<CaseStmt>(var->getRecursiveParentPatternStmt())) {
+ // In a type checked AST, a case stmt that is a recursive parent pattern
+ // stmt of a var decl, must have bound decls. This is because we
+ // guarantee that all case label items bind corresponding patterns and
+ // the case body var decls of a case stmt are created from the var decls
+ // of the first case label items.
+ if (!caseStmt->hasBoundDecls()) {
+ Out << "parent CaseStmt of VarDecl does not have any case body "
+ "decls?!\n";
+ abort();
+ }
+ }
+
verifyCheckedBase(var);
}
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 73e951f..991a0cf 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -1294,14 +1294,6 @@
return nullptr;
}
-static bool patternContainsVarDeclBinding(const Pattern *P, const VarDecl *VD) {
- bool Result = false;
- P->forEachVariable([&](VarDecl *FoundVD) {
- Result |= FoundVD == VD;
- });
- return Result;
-}
-
unsigned PatternBindingDecl::getPatternEntryIndexForVarDecl(const VarDecl *VD) const {
assert(VD && "Cannot find a null VarDecl");
diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp
index a11c888..01980d1 100644
--- a/lib/AST/NameLookup.cpp
+++ b/lib/AST/NameLookup.cpp
@@ -2347,8 +2347,14 @@
}
}
}
- if (!inPatterns && !items.empty())
- checkPattern(items[0].getPattern(), DeclVisibilityKind::LocalVariable);
+
+ if (!inPatterns && !items.empty()) {
+ if (auto caseBodyVars = S->getCaseBodyVariables()) {
+ for (auto *vd : *caseBodyVars) {
+ checkValueDecl(vd, DeclVisibilityKind::LocalVariable);
+ }
+ }
+ }
visit(S->getBody());
}
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index 54c54c9..c8a6ef6 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -2476,6 +2476,20 @@
assert(!CaseLabelItems.empty() && "did not parse any labels?!");
+ // Add a scope so that the parser can find our body bound decls if it emits
+ // optimized accesses.
+ Optional<Scope> BodyScope;
+ if (CaseBodyDecls) {
+ BodyScope.emplace(this, ScopeKind::CaseVars);
+ for (auto *v : *CaseBodyDecls) {
+ setLocalDiscriminator(v);
+ // If we had any bad redefinitions, we already diagnosed them against the
+ // first case label item.
+ if (v->hasName())
+ addToScope(v, false /*diagnoseRedefinitions*/);
+ }
+ }
+
SmallVector<ASTNode, 8> BodyItems;
SourceLoc StartOfBody = Tok.getLoc();
diff --git a/lib/Parse/Scope.cpp b/lib/Parse/Scope.cpp
index d3db84e..ad8400e 100644
--- a/lib/Parse/Scope.cpp
+++ b/lib/Parse/Scope.cpp
@@ -104,7 +104,8 @@
/// addToScope - Register the specified decl as being in the current lexical
/// scope.
-void ScopeInfo::addToScope(ValueDecl *D, Parser &TheParser) {
+void ScopeInfo::addToScope(ValueDecl *D, Parser &TheParser,
+ bool diagnoseRedefinitions) {
if (!CurScope->isResolvable())
return;
@@ -121,9 +122,13 @@
// If this is in a resolvable scope, diagnose redefinitions. Later
// phases will handle scopes like module-scope, etc.
- if (CurScope->getDepth() >= ResolvableDepth)
- return TheParser.diagnoseRedefinition(PrevDecl, D);
-
+ if (CurScope->getDepth() >= ResolvableDepth) {
+ if (diagnoseRedefinitions) {
+ return TheParser.diagnoseRedefinition(PrevDecl, D);
+ }
+ return;
+ }
+
// If this is at top-level scope, validate that the members of the overload
// set all agree.
diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp
index 15b8aae..aa7870c 100644
--- a/lib/SILGen/SILGenPattern.cpp
+++ b/lib/SILGen/SILGenPattern.cpp
@@ -445,6 +445,12 @@
void emitCaseBody(CaseStmt *caseBlock);
+ SILValue getAddressOnlyTemporary(VarDecl *decl) {
+ auto found = Temporaries.find(decl);
+ assert(found != Temporaries.end());
+ return found->second;
+ }
+
private:
void emitWildcardDispatch(ClauseMatrix &matrix, ArgArray args, unsigned row,
const FailureHandler &failure);
@@ -2326,16 +2332,12 @@
auto *block = SGF.createBasicBlock();
result.first->second.first = block;
- // If we do not have any bound decls, we do not need to setup any phi
- // arguments for the shared case block. Just bail early.
- if (!caseBlock->hasBoundDecls()) {
+ // Add args for any pattern variables
+ auto caseBodyVars = caseBlock->getCaseBodyVariables();
+ if (!caseBodyVars)
return;
- }
- auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
- SmallVector<VarDecl *, 4> patternVarDecls;
- pattern->collectVariables(patternVarDecls);
- for (auto *vd : patternVarDecls) {
+ for (auto *vd : *caseBodyVars) {
if (!vd->hasName())
continue;
@@ -2363,39 +2365,18 @@
for (auto &entry : SharedCases) {
CaseStmt *caseBlock = entry.first;
- // If we do not have any bound decls, we can bail early.
- if (!caseBlock->hasBoundDecls()) {
+ auto caseBodyVars = caseBlock->getCaseBodyVariables();
+ if (!caseBodyVars)
continue;
- }
- // If we have a shared case with bound decls, then the 0th pattern has the
- // order of variables that are the incoming BB arguments. Setup the VarLocs
- // to point to the incoming args and setup initialization so any args needing
- // cleanup will get that as well.
- auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
- SmallVector<VarDecl *, 4> patternVarDecls;
- pattern->collectVariables(patternVarDecls);
- for (auto *vd : patternVarDecls) {
+ // If we have a shared case with bound decls, setup the arguments for the
+ // shared block by emitting the temporary allocation used for the arguments
+ // of the shared block.
+ for (auto *vd : *caseBodyVars) {
if (!vd->hasName())
continue;
SILType ty = SGF.getLoweredType(vd->getType());
- if (ty.isNull()) {
- // If we're making the shared block on behalf of a previous case's
- // fallthrough, caseBlock's VarDecl's won't be in the SGF yet, so
- // determine phi types by using current vars of the same name.
- for (auto var : SGF.VarLocs) {
- auto varDecl = dyn_cast<VarDecl>(var.getFirst());
- if (!varDecl || !varDecl->hasName() ||
- varDecl->getName() != vd->getName())
- continue;
- ty = var.getSecond().value->getType();
- if (var.getSecond().box) {
- ty = ty.getObjectType();
- }
- }
- }
-
if (!ty.isAddressOnly(SGF.F.getModule()))
continue;
assert(!Temporaries[vd]);
@@ -2455,23 +2436,19 @@
assert(SGF.getCleanupsDepth() == PatternMatchStmtDepth);
SWIFT_DEFER { assert(SGF.getCleanupsDepth() == PatternMatchStmtDepth); };
- // If we do not have any bound decls, just emit the case body since we do
- // not need to setup any var locs.
- if (!caseBlock->hasBoundDecls()) {
+ auto caseBodyVars = caseBlock->getCaseBodyVariables();
+ if (!caseBodyVars) {
emitCaseBody(caseBlock);
continue;
}
- // If we have a shared case with bound decls, then the 0th pattern has the
- // order of variables that are the incoming BB arguments. Setup the VarLocs
- // to point to the incoming args and setup initialization so any args
- // needing cleanup will get that as well.
+ // If we have a shared case with bound decls, then the case stmt pattern has
+ // the order of variables that are the incoming BB arguments. Setup the
+ // VarLocs to point to the incoming args and setup initialization so any
+ // args needing Cleanup will get that as well.
Scope scope(SGF.Cleanups, CleanupLocation(caseBlock));
- auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
- SmallVector<VarDecl *, 4> patternVarDecls;
- pattern->collectVariables(patternVarDecls);
unsigned argIndex = 0;
- for (auto *vd : patternVarDecls) {
+ for (auto *vd : *caseBodyVars) {
if (!vd->hasName())
continue;
@@ -2622,10 +2599,33 @@
// Certain case statements can be entered along multiple paths, either because
// they have multiple labels or because of fallthrough. When we need multiple
- // entrance path, we factor the paths with a shared block. If we don't have a
- // fallthrough or a multi-pattern 'case', we can just emit the body inline and
- // save some dead blocks. Emit the statement here and bail early.
+ // entrance path, we factor the paths with a shared block.
+ //
+ // If we don't have a fallthrough or a multi-pattern 'case', we can emit the
+ // body inline. Emit the statement here and bail early.
if (!row.hasFallthroughTo() && caseBlock->getCaseLabelItems().size() == 1) {
+ // If we have case body vars, set them up to point at the matching var
+ // decls.
+ if (auto caseBodyVars = caseBlock->getCaseBodyVariables()) {
+ // Since we know that we only have one case label item, grab its pattern
+ // vars and use that to update expected with the right SILValue.
+ //
+ // TODO: Do we need a copy here?
+ SmallVector<VarDecl *, 4> patternVars;
+ row.getCasePattern()->collectVariables(patternVars);
+ for (auto *expected : *caseBodyVars) {
+ if (!expected->hasName())
+ continue;
+ for (auto *vd : patternVars) {
+ if (!vd->hasName() || vd->getName() != expected->getName()) {
+ continue;
+ }
+
+ // Ok, we found a match. Update the VarLocs for the case block.
+ SGF.VarLocs[expected] = SGF.VarLocs[vd];
+ }
+ }
+ }
emission.emitCaseBody(caseBlock);
return;
}
@@ -2639,7 +2639,10 @@
// If we do not have any bound decls, we do not need to setup any
// variables. Just jump to the shared destination.
- if (!caseBlock->hasBoundDecls()) {
+ auto caseBodyVars = caseBlock->getCaseBodyVariables();
+ if (!caseBodyVars) {
+ // Don't emit anything yet, we emit it at the cleanup level of the switch
+ // statement.
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock);
SGF.Cleanups.emitBranchAndCleanups(sharedDest, caseBlock);
return;
@@ -2650,18 +2653,14 @@
// +1 along to the shared case block dest. (The cleanups still happen, as they
// are threaded through here messily, but the explicit retains here counteract
// them, and then the retain/release pair gets optimized out.)
- ArrayRef<CaseLabelItem> labelItems = caseBlock->getCaseLabelItems();
SmallVector<SILValue, 4> args;
- SmallVector<VarDecl *, 4> expectedVarOrder;
- SmallVector<VarDecl *, 4> vars;
- labelItems[0].getPattern()->collectVariables(expectedVarOrder);
- row.getCasePattern()->collectVariables(vars);
-
SILModule &M = SGF.F.getModule();
- for (auto expected : expectedVarOrder) {
+ SmallVector<VarDecl *, 4> patternVars;
+ row.getCasePattern()->collectVariables(patternVars);
+ for (auto *expected : *caseBodyVars) {
if (!expected->hasName())
continue;
- for (auto *var : vars) {
+ for (auto *var : patternVars) {
if (!var->hasName() || var->getName() != expected->getName())
continue;
@@ -2690,6 +2689,7 @@
}
}
+ // Now that we have initialized our arguments, branch to the shared dest.
SGF.Cleanups.emitBranchAndCleanups(sharedDest, caseBlock, args);
}
@@ -2837,45 +2837,46 @@
void SILGenFunction::emitSwitchFallthrough(FallthroughStmt *S) {
assert(!SwitchStack.empty() && "fallthrough outside of switch?!");
PatternMatchContext *context = SwitchStack.back();
-
- // Get the destination block.
- CaseStmt *caseStmt = S->getFallthroughDest();
- JumpDest sharedDest =
- context->Emission.getSharedCaseBlockDest(caseStmt);
- if (!caseStmt->hasBoundDecls()) {
+ // Get the destination block.
+ CaseStmt *destCaseStmt = S->getFallthroughDest();
+ JumpDest sharedDest = context->Emission.getSharedCaseBlockDest(destCaseStmt);
+
+ // If our destination case doesn't have any bound decls, there is no rebinding
+ // to do. Just jump to the shared dest.
+ auto destCaseBodyVars = destCaseStmt->getCaseBodyVariables();
+ if (!destCaseBodyVars) {
Cleanups.emitBranchAndCleanups(sharedDest, S);
return;
}
// Generate branch args to pass along current vars to fallthrough case.
SILModule &M = F.getModule();
- ArrayRef<CaseLabelItem> labelItems = caseStmt->getCaseLabelItems();
SmallVector<SILValue, 4> args;
- SmallVector<VarDecl *, 4> expectedVarOrder;
- labelItems[0].getPattern()->collectVariables(expectedVarOrder);
+ CaseStmt *fallthroughSourceStmt = S->getFallthroughSource();
- for (auto *expected : expectedVarOrder) {
+ for (auto *expected : *destCaseBodyVars) {
if (!expected->hasName())
continue;
- for (auto var : VarLocs) {
- auto varDecl = dyn_cast<VarDecl>(var.getFirst());
- if (!varDecl || !varDecl->hasName() ||
- varDecl->getName() != expected->getName()) {
+
+ // The type checker enforces that if our destination case has variables then
+ // our fallthrough source must as well.
+ for (auto *var : *fallthroughSourceStmt->getCaseBodyVariables()) {
+ if (!var->hasName() || var->getName() != expected->getName()) {
continue;
}
- SILValue value = var.getSecond().value;
+ auto varLoc = VarLocs[var];
+ SILValue value = varLoc.value;
if (value->getType().isAddressOnly(M)) {
context->Emission.emitAddressOnlyInitialization(expected, value);
break;
}
- if (var.getSecond().box) {
- auto &lowering = getTypeLowering(value->getType());
- auto argValue = lowering.emitLoad(B, CurrentSILLoc, value,
- LoadOwnershipQualifier::Copy);
+ if (varLoc.box) {
+ SILValue argValue = B.emitLoadValueOperation(
+ CurrentSILLoc, value, LoadOwnershipQualifier::Copy);
args.push_back(argValue);
break;
}
diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp
index 47648e1..f2a3d4c 100644
--- a/lib/Sema/MiscDiagnostics.cpp
+++ b/lib/Sema/MiscDiagnostics.cpp
@@ -2152,6 +2152,7 @@
//===----------------------------------------------------------------------===//
namespace {
+
class VarDeclUsageChecker : public ASTWalker {
DiagnosticEngine &Diags;
// Keep track of some information about a variable.
@@ -2204,9 +2205,12 @@
VarDeclUsageChecker(DiagnosticEngine &Diags) : Diags(Diags) {}
- VarDeclUsageChecker(TypeChecker &TC, VarDecl *VD) : Diags(TC.Diags) {
+ VarDeclUsageChecker(TypeChecker &tc, VarDecl *vd) : Diags(tc.Diags) {
// Track a specific VarDecl
- VarDecls[VD] = 0;
+ VarDecls[vd] = 0;
+ if (auto *childVd = vd->getCorrespondingCaseBodyVariable().getPtrOrNull()) {
+ VarDecls[childVd] = 0;
+ }
}
void suppressDiagnostics() {
@@ -2288,16 +2292,30 @@
handleIfConfig(ICD);
// If this is a VarDecl, then add it to our list of things to track.
- if (auto *vd = dyn_cast<VarDecl>(D))
+ if (auto *vd = dyn_cast<VarDecl>(D)) {
if (shouldTrackVarDecl(vd)) {
- unsigned defaultFlags = 0;
- // If this VarDecl is nested inside of a CaptureListExpr, remember that
- // fact for better diagnostics.
- auto parentAsExpr = Parent.getAsExpr();
- if (parentAsExpr && isa<CaptureListExpr>(parentAsExpr))
- defaultFlags = RK_CaptureList;
+ // Inline constructor.
+ auto defaultFlags = [&]() -> unsigned {
+ // If this VarDecl is nested inside of a CaptureListExpr, remember
+ // that fact for better diagnostics.
+ auto parentAsExpr = Parent.getAsExpr();
+ if (parentAsExpr && isa<CaptureListExpr>(parentAsExpr))
+ return RK_CaptureList;
+ // Otherwise, return none.
+ return 0;
+ }();
+
+ if (!vd->isImplicit()) {
+ if (auto *childVd =
+ vd->getCorrespondingCaseBodyVariable().getPtrOrNull()) {
+ // Child vars are never in capture lists.
+ assert(defaultFlags == 0);
+ VarDecls[childVd] |= 0;
+ }
+ }
VarDecls[vd] |= defaultFlags;
}
+ }
if (auto *afd = dyn_cast<AbstractFunctionDecl>(D)) {
// If this is a nested function with a capture list, mark any captured
@@ -2393,11 +2411,20 @@
});
}
}
-
+
+ // Make sure that we setup our case body variables.
+ if (auto *caseStmt = dyn_cast<CaseStmt>(S)) {
+ if (auto caseBoundDecls = caseStmt->getCaseBodyVariables()) {
+ for (auto *vd : *caseBoundDecls) {
+ VarDecls[vd] |= 0;
+ }
+ }
+ }
+
return { true, S };
}
-
};
+
} // end anonymous namespace
@@ -2409,19 +2436,24 @@
// lets let the bigger issues get resolved first.
if (sawError)
return;
-
- for (auto elt : VarDecls) {
- auto *var = elt.first;
- unsigned access = elt.second;
- if (auto *CS = dyn_cast_or_null<CaseStmt>(var->getRecursiveParentPatternStmt())) {
+ for (auto p : VarDecls) {
+ VarDecl *var;
+ unsigned access;
+ std::tie(var, access) = p;
+
+ if (auto *caseStmt =
+ dyn_cast_or_null<CaseStmt>(var->getRecursiveParentPatternStmt())) {
// Only diagnose VarDecls from the first CaseLabelItem in CaseStmts, as
// the remaining items must match it anyway.
- auto CaseItems = CS->getCaseLabelItems();
- assert(!CaseItems.empty() &&
+ auto caseItems = caseStmt->getCaseLabelItems();
+ assert(!caseItems.empty() &&
"If we have any case stmt var decls, we should have a case item");
- if (!CaseItems.front().getPattern()->containsVarDecl(var))
+ if (!caseItems.front().getPattern()->containsVarDecl(var))
continue;
+
+ auto *childVar = var->getCorrespondingCaseBodyVariable().get();
+ access |= VarDecls[childVar];
}
// If this is a 'let' value, any stores to it are actually initializations,
diff --git a/test/SILGen/switch.swift b/test/SILGen/switch.swift
index 74bdb5d..d245937 100644
--- a/test/SILGen/switch.swift
+++ b/test/SILGen/switch.swift
@@ -1,5 +1,9 @@
// RUN: %target-swift-emit-silgen -module-name switch %s | %FileCheck %s
+//////////////////
+// Declarations //
+//////////////////
+
func markUsed<T>(_ t: T) {}
// TODO: Implement tuple equality in the library.
@@ -25,6 +29,43 @@
func f() {}
func g() {}
+func a(_ k: Klass) {}
+func b(_ k: Klass) {}
+func c(_ k: Klass) {}
+func d(_ k: Klass) {}
+func e(_ k: Klass) {}
+func f(_ k: Klass) {}
+func g(_ k: Klass) {}
+
+class Klass {
+ var isTrue: Bool { return true }
+ var isFalse: Bool { return false }
+}
+
+enum TrivialSingleCaseEnum {
+case a
+}
+
+enum NonTrivialSingleCaseEnum {
+case a(Klass)
+}
+
+enum MultipleNonTrivialCaseEnum {
+case a(Klass)
+case b(Klass)
+case c(Klass)
+}
+
+enum MultipleAddressOnlyCaseEnum<T : BinaryInteger> {
+case a(T)
+case b(T)
+case c(T)
+}
+
+///////////
+// Tests //
+///////////
+
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test1yyF
func test1() {
switch foo() {
@@ -1091,15 +1132,6 @@
// Make sure that we properly can handle address only tuples with loadable
// subtypes.
-class Klass {}
-
-enum TrivialSingleCaseEnum {
-case a
-}
-
-enum NonTrivialSingleCaseEnum {
-case a(Klass)
-}
// CHECK-LABEL: sil hidden [ossa] @$s6switch33address_only_with_trivial_subtypeyyAA21TrivialSingleCaseEnumO_yptF : $@convention(thin) (TrivialSingleCaseEnum, @in_guaranteed Any) -> () {
// CHECK: [[MEM:%.*]] = alloc_stack $(TrivialSingleCaseEnum, Any)
@@ -1303,3 +1335,168 @@
break
}
}
+
+////////////////////////////////////////////////
+// Fallthrough Multiple Case Label Item Tests //
+////////////////////////////////////////////////
+
+// CHECK-LABEL: sil hidden [ossa] @$s6switch28addressOnlyFallthroughCalleeyyAA015MultipleAddressC8CaseEnumOyxGSzRzlF : $@convention(thin) <T where T : BinaryInteger> (@in_guaranteed MultipleAddressOnlyCaseEnum<T>) -> () {
+// CHECK: bb0([[ARG:%.*]] :
+// CHECK: [[AB_PHI:%.*]] = alloc_stack $T, let, name "x"
+// CHECK: [[ABB_PHI:%.*]] = alloc_stack $T, let, name "x"
+// CHECK: [[ABBC_PHI:%.*]] = alloc_stack $T, let, name "x"
+// CHECK: [[SWITCH_ENUM_ARG:%.*]] = alloc_stack $MultipleAddressOnlyCaseEnum<T>
+// CHECK: copy_addr [[ARG]] to [initialization] [[SWITCH_ENUM_ARG]]
+// CHECK: switch_enum_addr [[SWITCH_ENUM_ARG]] : $*MultipleAddressOnlyCaseEnum<T>, case #MultipleAddressOnlyCaseEnum.a!enumelt.1: [[BB_A:bb[0-9]+]], case #MultipleAddressOnlyCaseEnum.b!enumelt.1: [[BB_B:bb[0-9]+]], case #MultipleAddressOnlyCaseEnum.c!enumelt.1: [[BB_C:bb[0-9]+]]
+//
+// CHECK: [[BB_A]]:
+// CHECK: [[SWITCH_ENUM_ARG_PROJ:%.*]] = unchecked_take_enum_data_addr [[SWITCH_ENUM_ARG]]
+// CHECK: [[CASE_BODY_VAR_A:%.*]] = alloc_stack $T, let, name "x"
+// CHECK: copy_addr [take] [[SWITCH_ENUM_ARG_PROJ]] to [initialization] [[CASE_BODY_VAR_A]]
+// CHECK: copy_addr [[CASE_BODY_VAR_A]] to [initialization] [[AB_PHI]]
+// CHECK: destroy_addr [[CASE_BODY_VAR_A]]
+// CHECK: br [[BB_AB:bb[0-9]+]]
+//
+// CHECK: [[BB_B]]:
+// CHECK: [[SWITCH_ENUM_ARG_PROJ:%.*]] = unchecked_take_enum_data_addr [[SWITCH_ENUM_ARG]]
+// CHECK: [[CASE_BODY_VAR_B:%.*]] = alloc_stack $T, let, name "x"
+// CHECK: copy_addr [[SWITCH_ENUM_ARG_PROJ]] to [initialization] [[CASE_BODY_VAR_B]]
+// CHECK: [[FUNC_CMP:%.*]] = function_ref @$sSzsE2eeoiySbx_qd__tSzRd__lFZ :
+// CHECK: [[GUARD_RESULT:%.*]] = apply [[FUNC_CMP]]<T, Int>([[CASE_BODY_VAR_B]], {{%.*}}, {{%.*}})
+// CHECK: [[GUARD_RESULT_EXT:%.*]] = struct_extract [[GUARD_RESULT]]
+// CHECK: cond_br [[GUARD_RESULT_EXT]], [[BB_B_GUARD_SUCC:bb[0-9]+]], [[BB_B_GUARD_FAIL:bb[0-9]+]]
+//
+// CHECK: [[BB_B_GUARD_SUCC]]:
+// CHECK: copy_addr [[CASE_BODY_VAR_B]] to [initialization] [[AB_PHI]]
+// CHECK: destroy_addr [[CASE_BODY_VAR_B]]
+// CHECK: destroy_addr [[SWITCH_ENUM_ARG_PROJ]]
+// CHECK: br [[BB_AB]]
+//
+// CHECK: [[BB_AB]]:
+// CHECK: copy_addr [[AB_PHI]] to [initialization] [[ABB_PHI]]
+// CHECK: destroy_addr [[AB_PHI]]
+// CHECK: br [[BB_AB_CONT:bb[0-9]+]]
+//
+// CHECK: [[BB_AB_CONT]]:
+// CHECK: copy_addr [[ABB_PHI]] to [initialization] [[ABBC_PHI]]
+// CHECK: destroy_addr [[ABB_PHI]]
+// CHECK: br [[BB_FINAL_CONT:bb[0-9]+]]
+//
+// CHECK: [[BB_B_GUARD_FAIL]]:
+// CHECK: destroy_addr [[CASE_BODY_VAR_B]]
+// CHECK: [[CASE_BODY_VAR_B_2:%.*]] = alloc_stack $T, let, name "x"
+// CHECK: copy_addr [take] [[SWITCH_ENUM_ARG_PROJ]] to [initialization] [[CASE_BODY_VAR_B_2]]
+// CHECK: copy_addr [[CASE_BODY_VAR_B_2]] to [initialization] [[ABB_PHI]]
+// CHECK: br [[BB_AB_CONT]]
+//
+// CHECK: [[BB_C]]:
+// CHECK: [[SWITCH_ENUM_ARG_PROJ:%.*]] = unchecked_take_enum_data_addr [[SWITCH_ENUM_ARG]]
+// CHECK: [[CASE_BODY_VAR_C:%.*]] = alloc_stack $T, let, name "x"
+// CHECK: copy_addr [take] [[SWITCH_ENUM_ARG_PROJ]] to [initialization] [[CASE_BODY_VAR_C]]
+// CHECK: copy_addr [[CASE_BODY_VAR_C]] to [initialization] [[ABBC_PHI]]
+// CHECK: destroy_addr [[CASE_BODY_VAR_C]]
+// CHECK: br [[BB_FINAL_CONT]]
+//
+// CHECK: [[BB_FINAL_CONT]]:
+// CHECK: destroy_addr [[ABBC_PHI]]
+// CHECK: return
+// CHECK: } // end sil function '$s6switch28addressOnlyFallthroughCalleeyyAA015MultipleAddressC8CaseEnumOyxGSzRzlF'
+func addressOnlyFallthroughCallee<T : BinaryInteger>(_ e : MultipleAddressOnlyCaseEnum<T>) {
+ switch e {
+ case .a(let x): fallthrough
+ case .b(let x) where x == 2: fallthrough
+ case .b(let x): fallthrough
+ case .c(let x):
+ print(x)
+ }
+}
+
+func addressOnlyFallthroughCaller() {
+ var myFoo : MultipleAddressOnlyCaseEnum = MultipleAddressOnlyCaseEnum.a(10)
+ addressOnlyFallthroughCallee(myFoo)
+}
+
+// CHECK-LABEL: sil hidden [ossa] @$s6switch35nonTrivialLoadableFallthroughCalleeyyAA011MultipleNonC8CaseEnumOF : $@convention(thin) (@guaranteed MultipleNonTrivialCaseEnum) -> () {
+// CHECK: bb0([[ARG:%.*]] : @guaranteed $MultipleNonTrivialCaseEnum):
+// CHECK: switch_enum [[ARG]] : $MultipleNonTrivialCaseEnum, case #MultipleNonTrivialCaseEnum.a!enumelt.1: [[BB_A:bb[0-9]+]], case #MultipleNonTrivialCaseEnum.b!enumelt.1: [[BB_B:bb[0-9]+]], case #MultipleNonTrivialCaseEnum.c!enumelt.1: [[BB_C:bb[0-9]+]]
+//
+// CHECK: [[BB_A]]([[BB_A_ARG:%.*]] : @guaranteed
+// CHECK: [[BB_A_ARG_COPY:%.*]] = copy_value [[BB_A_ARG]]
+// CHECK: [[BB_A_ARG_COPY_BORROW:%.*]] = begin_borrow [[BB_A_ARG_COPY]]
+// CHECK: apply {{%.*}}([[BB_A_ARG_COPY_BORROW]])
+// CHECK: [[RESULT:%.*]] = copy_value [[BB_A_ARG_COPY]]
+// CHECK: br [[BB_AB:bb[0-9]+]]([[RESULT]] :
+//
+// CHECK: [[BB_B]]([[BB_B_ARG:%.*]] : @guaranteed
+// CHECK: [[BB_B_ARG_COPY:%.*]] = copy_value [[BB_B_ARG]]
+// CHECK: [[RESULT:%.*]] = copy_value [[BB_B_ARG_COPY]]
+// CHECK: br [[BB_AB:bb[0-9]+]]([[RESULT]] :
+//
+// CHECK: [[BB_AB:bb[0-9]+]]([[BB_AB_PHI:%.*]] : @owned
+// CHECK: [[BB_AB_PHI_BORROW:%.*]] = begin_borrow [[BB_AB_PHI]]
+// CHECK: apply {{%.*}}([[BB_AB_PHI_BORROW]])
+// CHECK: [[RESULT:%.*]] = copy_value [[BB_AB_PHI]]
+// CHECK: br [[BB_ABC:bb[0-9]+]]([[RESULT]] :
+//
+// CHECK: [[BB_C]]([[BB_C_ARG:%.*]] : @guaranteed
+// CHECK: [[BB_C_COPY:%.*]] = copy_value [[BB_C_ARG]]
+// CHECK: [[RESULT:%.*]] = copy_value [[BB_C_COPY]]
+// CHECK: br [[BB_ABC]]([[RESULT]] :
+//
+// CHECK: [[BB_ABC]]([[BB_ABC_ARG:%.*]] : @owned
+// CHECK: [[BB_ABC_ARG_BORROW:%.*]] = begin_borrow [[BB_ABC_ARG]]
+// CHECK: apply {{%.*}}([[BB_ABC_ARG_BORROW]])
+// CHECK: return
+// CHECK: } // end sil function '$s6switch35nonTrivialLoadableFallthroughCalleeyyAA011MultipleNonC8CaseEnumOF'
+func nonTrivialLoadableFallthroughCallee(_ e : MultipleNonTrivialCaseEnum) {
+ switch e {
+ case .a(let x):
+ a(x)
+ fallthrough
+ case .b(let x):
+ b(x)
+ fallthrough
+ case .c(let x):
+ c(x)
+ }
+}
+
+// Just make sure that we do not crash on this.
+func nonTrivialLoadableFallthroughCalleeGuards(_ e : MultipleNonTrivialCaseEnum) {
+ switch e {
+ case .a(let x) where x.isFalse:
+ a(x)
+ fallthrough
+ case .a(let x) where x.isTrue:
+ a(x)
+ fallthrough
+ case .b(let x) where x.isTrue:
+ b(x)
+ fallthrough
+ case .b(let x) where x.isFalse:
+ b(x)
+ fallthrough
+ case .c(let x) where x.isTrue:
+ c(x)
+ fallthrough
+ case .c(let x) where x.isFalse:
+ c(x)
+ break
+ default:
+ d()
+ }
+}
+
+func nonTrivialLoadableFallthroughCallee2(_ e : MultipleNonTrivialCaseEnum) {
+ switch e {
+ case .a(let x):
+ a(x)
+ fallthrough
+ case .b(let x):
+ b(x)
+ break
+ default:
+ break
+ }
+}
+
diff --git a/test/SourceKit/CursorInfo/cursor_vardecl_across_fallthrough.swift b/test/SourceKit/CursorInfo/cursor_vardecl_across_fallthrough.swift
index 020f19f..2e348ab 100644
--- a/test/SourceKit/CursorInfo/cursor_vardecl_across_fallthrough.swift
+++ b/test/SourceKit/CursorInfo/cursor_vardecl_across_fallthrough.swift
@@ -31,7 +31,7 @@
// CHECK2DECL: source.lang.swift.decl.var.local (16:20-16:21)
// CHECK2DECL2: source.lang.swift.decl.var.local (16:42-16:43)
-// CHECK2REF: source.lang.swift.ref.var.local (16:20-16:21)
+// CHECK2REF: source.lang.swift.ref.var.local (16:42-16:43)
// CHECKX: x
// CHECKX: s:33cursor_vardecl_across_fallthrough1xL_Sivp
@@ -50,7 +50,7 @@
// CHECK4DECL: source.lang.swift.decl.var.local (16:27-16:28)
// CHECK4DECL2: source.lang.swift.decl.var.local (16:49-16:50)
-// CHECK4REF: source.lang.swift.ref.var.local (16:27-16:28)
+// CHECK4REF: source.lang.swift.ref.var.local (16:49-16:50)
// CHECKY: y
// CHECKY: s:33cursor_vardecl_across_fallthrough1yL_SSvp