mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Support for fallthrough into cases with pattern variables.
This commit is contained in:
@@ -2889,14 +2889,16 @@ ERROR(fallthrough_outside_switch,none,
|
|||||||
ERROR(fallthrough_from_last_case,none,
|
ERROR(fallthrough_from_last_case,none,
|
||||||
"'fallthrough' without a following 'case' or 'default' block", ())
|
"'fallthrough' without a following 'case' or 'default' block", ())
|
||||||
ERROR(fallthrough_into_case_with_var_binding,none,
|
ERROR(fallthrough_into_case_with_var_binding,none,
|
||||||
"'fallthrough' cannot transfer control to a case label that declares variables",
|
"'fallthrough' from a case which doesn't bind variable %0",
|
||||||
())
|
(Identifier))
|
||||||
|
|
||||||
ERROR(unnecessary_cast_over_optionset,none,
|
ERROR(unnecessary_cast_over_optionset,none,
|
||||||
"unnecessary cast over raw value of %0", (Type))
|
"unnecessary cast over raw value of %0", (Type))
|
||||||
|
|
||||||
ERROR(type_mismatch_multiple_pattern_list,none,
|
ERROR(type_mismatch_multiple_pattern_list,none,
|
||||||
"pattern variable bound to type %0, expected type %1", (Type, Type))
|
"pattern variable bound to type %0, expected type %1", (Type, Type))
|
||||||
|
ERROR(type_mismatch_fallthrough_pattern_list,none,
|
||||||
|
"pattern variable bound to type %0, fallthrough case bound to type %1", (Type, Type))
|
||||||
|
|
||||||
WARNING(where_on_one_item, none,
|
WARNING(where_on_one_item, none,
|
||||||
"'where' only applies to the second pattern match in this case", ())
|
"'where' only applies to the second pattern match in this case", ())
|
||||||
|
|||||||
@@ -1052,18 +1052,29 @@ public:
|
|||||||
/// FallthroughStmt - The keyword "fallthrough".
|
/// FallthroughStmt - The keyword "fallthrough".
|
||||||
class FallthroughStmt : public Stmt {
|
class FallthroughStmt : public Stmt {
|
||||||
SourceLoc Loc;
|
SourceLoc Loc;
|
||||||
|
CaseStmt *FallthroughSource;
|
||||||
CaseStmt *FallthroughDest;
|
CaseStmt *FallthroughDest;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FallthroughStmt(SourceLoc Loc, Optional<bool> implicit = None)
|
FallthroughStmt(SourceLoc Loc, Optional<bool> implicit = None)
|
||||||
: Stmt(StmtKind::Fallthrough, getDefaultImplicitFlag(implicit, Loc)),
|
: Stmt(StmtKind::Fallthrough, getDefaultImplicitFlag(implicit, Loc)),
|
||||||
Loc(Loc), FallthroughDest(nullptr)
|
Loc(Loc), FallthroughSource(nullptr), FallthroughDest(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SourceLoc getLoc() const { return Loc; }
|
SourceLoc getLoc() const { return Loc; }
|
||||||
|
|
||||||
SourceRange getSourceRange() const { return Loc; }
|
SourceRange getSourceRange() const { return Loc; }
|
||||||
|
|
||||||
|
/// Get the CaseStmt block from which the fallthrough transfers control.
|
||||||
|
/// Set during Sema. (May stay null if fallthrough is invalid.)
|
||||||
|
CaseStmt *getFallthroughSource() const {
|
||||||
|
return FallthroughSource;
|
||||||
|
}
|
||||||
|
void setFallthroughSource(CaseStmt *C) {
|
||||||
|
assert(!FallthroughSource && "fallthrough source already set?!");
|
||||||
|
FallthroughSource = C;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the CaseStmt block to which the fallthrough transfers control.
|
/// Get the CaseStmt block to which the fallthrough transfers control.
|
||||||
/// Set during Sema.
|
/// Set during Sema.
|
||||||
CaseStmt *getFallthroughDest() const {
|
CaseStmt *getFallthroughDest() const {
|
||||||
|
|||||||
@@ -1075,7 +1075,8 @@ void PatternMatchEmission::emitWildcardDispatch(ClauseMatrix &clauses,
|
|||||||
|
|
||||||
bool hasMultipleItems = false;
|
bool hasMultipleItems = false;
|
||||||
if (auto *caseStmt = dyn_cast<CaseStmt>(stmt)) {
|
if (auto *caseStmt = dyn_cast<CaseStmt>(stmt)) {
|
||||||
hasMultipleItems = caseStmt->getCaseLabelItems().size() > 1;
|
hasMultipleItems = clauses[row].hasFallthroughTo() ||
|
||||||
|
caseStmt->getCaseLabelItems().size() > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind the rest of the patterns.
|
// Bind the rest of the patterns.
|
||||||
@@ -2383,7 +2384,20 @@ void PatternMatchEmission::emitAddressOnlyAllocations() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
SILType ty = SGF.getLoweredType(V->getType());
|
SILType ty = SGF.getLoweredType(V->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() == V->getName()) {
|
||||||
|
ty = var.getSecond().value->getType();
|
||||||
|
if (var.getSecond().box) {
|
||||||
|
ty = ty.getObjectType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ty.isAddressOnly(SGF.F.getModule())) {
|
if (ty.isAddressOnly(SGF.F.getModule())) {
|
||||||
assert(!Temporaries[V]);
|
assert(!Temporaries[V]);
|
||||||
Temporaries[V] = SGF.emitTemporaryAllocation(V, ty);
|
Temporaries[V] = SGF.emitTemporaryAllocation(V, ty);
|
||||||
@@ -2617,7 +2631,7 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
|
|||||||
// statement.
|
// statement.
|
||||||
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock);
|
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock);
|
||||||
Cleanups.emitBranchAndCleanups(sharedDest, caseBlock);
|
Cleanups.emitBranchAndCleanups(sharedDest, caseBlock);
|
||||||
} else if (caseBlock->getCaseLabelItems().size() > 1) {
|
} else if (row.hasFallthroughTo() || caseBlock->getCaseLabelItems().size() > 1) {
|
||||||
JumpDest sharedDest =
|
JumpDest sharedDest =
|
||||||
emission.getSharedCaseBlockDest(caseBlock);
|
emission.getSharedCaseBlockDest(caseBlock);
|
||||||
|
|
||||||
@@ -2630,15 +2644,15 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
|
|||||||
ArrayRef<CaseLabelItem> labelItems = caseBlock->getCaseLabelItems();
|
ArrayRef<CaseLabelItem> labelItems = caseBlock->getCaseLabelItems();
|
||||||
SmallVector<SILValue, 4> args;
|
SmallVector<SILValue, 4> args;
|
||||||
SmallVector<VarDecl *, 4> expectedVarOrder;
|
SmallVector<VarDecl *, 4> expectedVarOrder;
|
||||||
SmallVector<VarDecl *, 4> Vars;
|
SmallVector<VarDecl *, 4> vars;
|
||||||
labelItems[0].getPattern()->collectVariables(expectedVarOrder);
|
labelItems[0].getPattern()->collectVariables(expectedVarOrder);
|
||||||
row.getCasePattern()->collectVariables(Vars);
|
row.getCasePattern()->collectVariables(vars);
|
||||||
|
|
||||||
SILModule &M = F.getModule();
|
SILModule &M = F.getModule();
|
||||||
for (auto expected : expectedVarOrder) {
|
for (auto expected : expectedVarOrder) {
|
||||||
if (!expected->hasName())
|
if (!expected->hasName())
|
||||||
continue;
|
continue;
|
||||||
for (auto var : Vars) {
|
for (auto *var : vars) {
|
||||||
if (var->hasName() && var->getName() == expected->getName()) {
|
if (var->hasName() && var->getName() == expected->getName()) {
|
||||||
SILValue value = VarLocs[var].value;
|
SILValue value = VarLocs[var].value;
|
||||||
SILType type = value->getType();
|
SILType type = value->getType();
|
||||||
@@ -2754,8 +2768,42 @@ void SILGenFunction::emitSwitchFallthrough(FallthroughStmt *S) {
|
|||||||
// Get the destination block.
|
// Get the destination block.
|
||||||
CaseStmt *caseStmt = S->getFallthroughDest();
|
CaseStmt *caseStmt = S->getFallthroughDest();
|
||||||
JumpDest sharedDest =
|
JumpDest sharedDest =
|
||||||
context->Emission.getSharedCaseBlockDest(caseStmt);
|
context->Emission.getSharedCaseBlockDest(caseStmt);
|
||||||
Cleanups.emitBranchAndCleanups(sharedDest, S);
|
|
||||||
|
if (!caseStmt->hasBoundDecls()) {
|
||||||
|
Cleanups.emitBranchAndCleanups(sharedDest, S);
|
||||||
|
} else {
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
for (auto *expected : expectedVarOrder) {
|
||||||
|
if (!expected->hasName())
|
||||||
|
continue;
|
||||||
|
for (auto var : VarLocs) {
|
||||||
|
auto varDecl = dyn_cast<VarDecl>(var.getFirst());
|
||||||
|
if (varDecl && varDecl->hasName() && varDecl->getName() == expected->getName()) {
|
||||||
|
SILValue value = var.getSecond().value;
|
||||||
|
|
||||||
|
if (value->getType().isAddressOnly(M)) {
|
||||||
|
context->Emission.emitAddressOnlyInitialization(expected, value);
|
||||||
|
} else if (var.getSecond().box) {
|
||||||
|
auto &lowering = getTypeLowering(value->getType());
|
||||||
|
auto argValue = lowering.emitLoad(B, CurrentSILLoc, value, LoadOwnershipQualifier::Copy);
|
||||||
|
args.push_back(argValue);
|
||||||
|
} else {
|
||||||
|
auto argValue = B.emitCopyValueOperation(CurrentSILLoc, value);
|
||||||
|
args.push_back(argValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Cleanups.emitBranchAndCleanups(sharedDest, S, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2355,6 +2355,29 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A fallthrough dest case's bound variable means the source case's
|
||||||
|
// var of the same name is read.
|
||||||
|
if (auto *fallthroughStmt = dyn_cast<FallthroughStmt>(S)) {
|
||||||
|
if (auto *sourceCase = fallthroughStmt->getFallthroughSource()) {
|
||||||
|
SmallVector<VarDecl *, 4> sourceVars;
|
||||||
|
auto sourcePattern = sourceCase->getCaseLabelItems()[0].getPattern();
|
||||||
|
sourcePattern->collectVariables(sourceVars);
|
||||||
|
|
||||||
|
auto destCase = fallthroughStmt->getFallthroughDest();
|
||||||
|
auto destPattern = destCase->getCaseLabelItems()[0].getPattern();
|
||||||
|
destPattern->forEachVariable([&](VarDecl *V) {
|
||||||
|
if (!V->hasName())
|
||||||
|
return;
|
||||||
|
for (auto *var : sourceVars) {
|
||||||
|
if (var->hasName() && var->getName() == V->getName()) {
|
||||||
|
VarDecls[var] |= RK_Read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { true, S };
|
return { true, S };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,7 +303,9 @@ public:
|
|||||||
/// The destination block for a 'fallthrough' statement. Null if the switch
|
/// The destination block for a 'fallthrough' statement. Null if the switch
|
||||||
/// scope depth is zero or if we are checking the final 'case' of the current
|
/// scope depth is zero or if we are checking the final 'case' of the current
|
||||||
/// switch.
|
/// switch.
|
||||||
|
CaseStmt /*nullable*/ *FallthroughSource = nullptr;
|
||||||
CaseStmt /*nullable*/ *FallthroughDest = nullptr;
|
CaseStmt /*nullable*/ *FallthroughDest = nullptr;
|
||||||
|
FallthroughStmt /*nullable*/ *PreviousFallthrough = nullptr;
|
||||||
|
|
||||||
SourceLoc EndTypeCheckLoc;
|
SourceLoc EndTypeCheckLoc;
|
||||||
|
|
||||||
@@ -818,9 +820,9 @@ public:
|
|||||||
TC.diagnose(S->getLoc(), diag::fallthrough_from_last_case);
|
TC.diagnose(S->getLoc(), diag::fallthrough_from_last_case);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (FallthroughDest->hasBoundDecls())
|
S->setFallthroughSource(FallthroughSource);
|
||||||
TC.diagnose(S->getLoc(), diag::fallthrough_into_case_with_var_binding);
|
|
||||||
S->setFallthroughDest(FallthroughDest);
|
S->setFallthroughDest(FallthroughDest);
|
||||||
|
PreviousFallthrough = S;
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,10 +841,12 @@ public:
|
|||||||
AddLabeledStmt labelNest(*this, S);
|
AddLabeledStmt labelNest(*this, S);
|
||||||
|
|
||||||
auto cases = S->getCases();
|
auto cases = S->getCases();
|
||||||
|
CaseStmt *previousBlock = nullptr;
|
||||||
for (auto i = cases.begin(), e = cases.end(); i != e; ++i) {
|
for (auto i = cases.begin(), e = cases.end(); i != e; ++i) {
|
||||||
auto *caseBlock = *i;
|
auto *caseBlock = *i;
|
||||||
// Fallthrough transfers control to the next case block. In the
|
// Fallthrough transfers control to the next case block. In the
|
||||||
// final case block, it is invalid.
|
// final case block, it is invalid.
|
||||||
|
FallthroughSource = caseBlock;
|
||||||
FallthroughDest = std::next(i) == e ? nullptr : *std::next(i);
|
FallthroughDest = std::next(i) == e ? nullptr : *std::next(i);
|
||||||
|
|
||||||
for (auto &labelItem : caseBlock->getMutableCaseLabelItems()) {
|
for (auto &labelItem : caseBlock->getMutableCaseLabelItems()) {
|
||||||
@@ -868,12 +872,12 @@ public:
|
|||||||
// was in the first label item's pattern.
|
// was in the first label item's pattern.
|
||||||
auto firstPattern = caseBlock->getCaseLabelItems()[0].getPattern();
|
auto firstPattern = caseBlock->getCaseLabelItems()[0].getPattern();
|
||||||
if (pattern != firstPattern) {
|
if (pattern != firstPattern) {
|
||||||
SmallVector<VarDecl *, 4> Vars;
|
SmallVector<VarDecl *, 4> vars;
|
||||||
firstPattern->collectVariables(Vars);
|
firstPattern->collectVariables(vars);
|
||||||
pattern->forEachVariable([&](VarDecl *VD) {
|
pattern->forEachVariable([&](VarDecl *VD) {
|
||||||
if (!VD->hasName())
|
if (!VD->hasName())
|
||||||
return;
|
return;
|
||||||
for (auto expected : Vars) {
|
for (auto *expected : vars) {
|
||||||
if (expected->hasName() && expected->getName() == VD->getName()) {
|
if (expected->hasName() && expected->getName() == VD->getName()) {
|
||||||
if (!VD->getType()->isEqual(expected->getType())) {
|
if (!VD->getType()->isEqual(expected->getType())) {
|
||||||
TC.diagnose(VD->getLoc(), diag::type_mismatch_multiple_pattern_list,
|
TC.diagnose(VD->getLoc(), diag::type_mismatch_multiple_pattern_list,
|
||||||
@@ -887,18 +891,54 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the guard expression, if present.
|
// Check the guard expression, if present.
|
||||||
if (auto *guard = labelItem.getGuardExpr()) {
|
if (auto *guard = labelItem.getGuardExpr()) {
|
||||||
hadError |= TC.typeCheckCondition(guard, DC);
|
hadError |= TC.typeCheckCondition(guard, DC);
|
||||||
labelItem.setGuardExpr(guard);
|
labelItem.setGuardExpr(guard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the previous case fellthrough, similarly check that that case's bindings
|
||||||
|
// includes our first label item's pattern bindings and types.
|
||||||
|
if (PreviousFallthrough) {
|
||||||
|
auto firstPattern = caseBlock->getCaseLabelItems()[0].getPattern();
|
||||||
|
SmallVector<VarDecl *, 4> Vars;
|
||||||
|
firstPattern->collectVariables(Vars);
|
||||||
|
|
||||||
|
for (auto &labelItem : previousBlock->getCaseLabelItems()) {
|
||||||
|
const Pattern *pattern = labelItem.getPattern();
|
||||||
|
SmallVector<VarDecl *, 4> PreviousVars;
|
||||||
|
pattern->collectVariables(PreviousVars);
|
||||||
|
for (auto expected : Vars) {
|
||||||
|
bool matched = false;
|
||||||
|
if (!expected->hasName())
|
||||||
|
continue;
|
||||||
|
for (auto previous: PreviousVars) {
|
||||||
|
if (previous->hasName() && expected->getName() == previous->getName()) {
|
||||||
|
if (!previous->getType()->isEqual(expected->getType())) {
|
||||||
|
TC.diagnose(previous->getLoc(), diag::type_mismatch_fallthrough_pattern_list,
|
||||||
|
previous->getType(), expected->getType());
|
||||||
|
previous->markInvalid();
|
||||||
|
expected->markInvalid();
|
||||||
|
}
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matched) {
|
||||||
|
TC.diagnose(PreviousFallthrough->getLoc(),
|
||||||
|
diag::fallthrough_into_case_with_var_binding, expected->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Type-check the body statements.
|
// Type-check the body statements.
|
||||||
|
PreviousFallthrough = nullptr;
|
||||||
Stmt *body = caseBlock->getBody();
|
Stmt *body = caseBlock->getBody();
|
||||||
hadError |= typeCheckStmt(body);
|
hadError |= typeCheckStmt(body);
|
||||||
caseBlock->setBody(body);
|
caseBlock->setBody(body);
|
||||||
|
previousBlock = caseBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S->isImplicit()) {
|
if (!S->isImplicit()) {
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ func foo(x: E, intVal: Int) {
|
|||||||
// 'fallthrough' target.
|
// 'fallthrough' target.
|
||||||
switch intVal {
|
switch intVal {
|
||||||
case 1:
|
case 1:
|
||||||
fallthrough // expected-error {{'fallthrough' cannot transfer control to a case label that declares variables}}
|
fallthrough // expected-error {{'fallthrough' from a case which doesn't bind variable 'val'}}
|
||||||
#if ENABLE_C
|
#if ENABLE_C
|
||||||
case let val:
|
case let val:
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -251,14 +251,31 @@ func patternVarUsedInAnotherPattern(x: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallthroughs can't transfer control into a case label with bindings.
|
// Fallthroughs can only transfer control into a case label with bindings if the previous case binds a superset of those vars.
|
||||||
switch t {
|
switch t {
|
||||||
case (1, 2):
|
case (1, 2):
|
||||||
fallthrough // expected-error {{'fallthrough' cannot transfer control to a case label that declares variables}}
|
fallthrough // expected-error {{'fallthrough' from a case which doesn't bind variable 'a'}} expected-error {{'fallthrough' from a case which doesn't bind variable 'b'}}
|
||||||
case (var a, var b): // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}} expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
|
case (var a, var b): // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}} expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
|
||||||
t = (b, a)
|
t = (b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch t { // specifically notice on next line that we shouldn't complain that a is unused - just never mutated
|
||||||
|
case (var a, let b): // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
|
||||||
|
t = (b, b)
|
||||||
|
fallthrough // ok - notice that subset of bound variables falling through is fine
|
||||||
|
case (2, let a):
|
||||||
|
t = (a, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func patternVarDiffType(x: Int, y: Double) {
|
||||||
|
switch (x, y) {
|
||||||
|
case (1, let a): // expected-error {{pattern variable bound to type 'Double', fallthrough case bound to type 'Int'}}
|
||||||
|
fallthrough
|
||||||
|
case (let a, _):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func test_label(x : Int) {
|
func test_label(x : Int) {
|
||||||
Gronk: // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}}
|
Gronk: // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}}
|
||||||
switch x {
|
switch x {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ func e() {}
|
|||||||
func f() {}
|
func f() {}
|
||||||
func g() {}
|
func g() {}
|
||||||
|
|
||||||
|
func z(_ i: Int) {}
|
||||||
|
|
||||||
// CHECK-LABEL: sil hidden @$S18switch_fallthrough5test1yyF
|
// CHECK-LABEL: sil hidden @$S18switch_fallthrough5test1yyF
|
||||||
func test1() {
|
func test1() {
|
||||||
switch foo() {
|
switch foo() {
|
||||||
@@ -134,3 +136,38 @@ func test4() {
|
|||||||
// CHECK-NEXT: tuple ()
|
// CHECK-NEXT: tuple ()
|
||||||
// CHECK-NEXT: return
|
// CHECK-NEXT: return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallthrough into case block with binding // CHECK-LABEL: sil hidden @$S18switch_fallthrough5test5yyF
|
||||||
|
func test5() {
|
||||||
|
switch (foo(), bar()) {
|
||||||
|
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], {{bb[0-9]+}}
|
||||||
|
// CHECK: [[YES_CASE1]]:
|
||||||
|
case (var n, foo()):
|
||||||
|
// Check that the var is boxed and unboxed and the final value is the one that falls through into the next case
|
||||||
|
// CHECK: [[BOX:%.*]] = alloc_box ${ var Int }, var, name "n"
|
||||||
|
// CHECK: [[N_BOX:%.*]] = project_box [[BOX]] : ${ var Int }, 0
|
||||||
|
// CHECK: function_ref @$S18switch_fallthrough1ayyF
|
||||||
|
// CHECK: [[N:%.*]] = load [trivial] [[N_BOX]] : $*Int
|
||||||
|
// CHECK: destroy_value [[BOX]] : ${ var Int }
|
||||||
|
// CHECK: br [[CASE2:bb[0-9]+]]([[N]] : $Int)
|
||||||
|
a()
|
||||||
|
fallthrough
|
||||||
|
case (foo(), let n):
|
||||||
|
// CHECK: cond_br {{%.*}}, [[YES_SECOND_CONDITION:bb[0-9]+]], {{bb[0-9]+}}
|
||||||
|
// CHECK: [[YES_SECOND_CONDITION]]:
|
||||||
|
// CHECK: debug_value [[SECOND_N:%.*]] : $Int, let, name "n"
|
||||||
|
// CHECK: br [[CASE2]]([[SECOND_N]] : $Int)
|
||||||
|
|
||||||
|
// CHECK: [[CASE2]]([[INCOMING_N:%.*]] : @trivial $Int):
|
||||||
|
// CHECK: [[Z:%.*]] = function_ref @$S18switch_fallthrough1zyySiF
|
||||||
|
// CHECK apply [[Z]]([[INCOMING_N]]) : $@convention(thin) (Int) -> ()
|
||||||
|
// CHECK: br [[CONT:bb[0-9]+]]
|
||||||
|
z(n)
|
||||||
|
case (_, _):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// CHECK: [[CONT]]:
|
||||||
|
// CHECK: function_ref @$S18switch_fallthrough1eyyF
|
||||||
|
e()
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,3 +131,70 @@ func multipleLabelsVar(e: E) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil hidden @$S34switch_multiple_entry_address_only20fallthroughWithValue1eyAA1EO_tF : $@convention(thin) (@in E) -> ()
|
||||||
|
func fallthroughWithValue(e: E) {
|
||||||
|
// CHECK: bb0
|
||||||
|
// CHECK: [[X_PHI:%.*]] = alloc_stack $Any
|
||||||
|
// CHECK-NEXT: [[E_COPY:%.*]] = alloc_stack $E
|
||||||
|
// CHECK-NEXT: copy_addr %0 to [initialization] [[E_COPY]]
|
||||||
|
// CHECK-NEXT: switch_enum_addr [[E_COPY]] : $*E, case #E.a!enumelt.1: bb1, case #E.b!enumelt.1: bb2, default bb4
|
||||||
|
|
||||||
|
// CHECK: bb1:
|
||||||
|
// CHECK-NEXT: [[E_PAYLOAD:%.*]] = unchecked_take_enum_data_addr [[E_COPY]] : $*E, #E.a!enumelt.1
|
||||||
|
// CHECK-NEXT: [[ORIGINAL_ANY_BOX:%.*]] = alloc_stack $Any
|
||||||
|
// CHECK-NEXT: copy_addr [take] [[E_PAYLOAD]] to [initialization] [[ORIGINAL_ANY_BOX]]
|
||||||
|
// CHECK-NEXT: [[ANY_BOX:%.*]] = alloc_stack $Any
|
||||||
|
// CHECK-NEXT: copy_addr [[ORIGINAL_ANY_BOX]] to [initialization] [[ANY_BOX]]
|
||||||
|
// CHECK: [[FN1:%.*]] = function_ref @$S34switch_multiple_entry_address_only8takesAnyyyypF
|
||||||
|
// CHECK-NEXT: apply [[FN1]]([[ANY_BOX]]
|
||||||
|
// CHECK-NEXT: dealloc_stack [[ANY_BOX]]
|
||||||
|
// CHECK-NEXT: copy_addr [[ORIGINAL_ANY_BOX]] to [initialization] [[X_PHI]]
|
||||||
|
// CHECK-NEXT: destroy_addr [[ORIGINAL_ANY_BOX]]
|
||||||
|
// CHECK-NEXT: dealloc_stack [[ORIGINAL_ANY_BOX]]
|
||||||
|
// CHECK-NEXT: dealloc_stack [[E_COPY]]
|
||||||
|
// CHECK-NEXT: br bb3
|
||||||
|
|
||||||
|
// CHECK: bb2:
|
||||||
|
// CHECK-NEXT: [[E_PAYLOAD:%.*]] = unchecked_take_enum_data_addr [[E_COPY]] : $*E, #E.b!enumelt.1
|
||||||
|
// CHECK-NEXT: [[ANY_BOX:%.*]] = alloc_stack $Any
|
||||||
|
// CHECK-NEXT: copy_addr [take] [[E_PAYLOAD]] to [initialization] [[ANY_BOX]]
|
||||||
|
// CHECK-NEXT: copy_addr [[ANY_BOX]] to [initialization] [[X_PHI]]
|
||||||
|
// CHECK-NEXT: destroy_addr [[ANY_BOX]]
|
||||||
|
// CHECK-NEXT: dealloc_stack [[ANY_BOX]]
|
||||||
|
// CHECK-NEXT: dealloc_stack [[E_COPY]]
|
||||||
|
// CHECK-NEXT: br bb3
|
||||||
|
|
||||||
|
// CHECK: bb3:
|
||||||
|
// CHECK-NEXT: [[ANY_BOX:%.*]] = alloc_stack $Any
|
||||||
|
// CHECK-NEXT: copy_addr [[X_PHI]] to [initialization] [[ANY_BOX]]
|
||||||
|
// CHECK: [[FN2:%.*]] = function_ref @$S34switch_multiple_entry_address_only8takesAnyyyypF
|
||||||
|
// CHECK-NEXT: apply [[FN2]]([[ANY_BOX]]
|
||||||
|
// CHECK-NEXT: dealloc_stack [[ANY_BOX]]
|
||||||
|
// CHECK-NEXT: destroy_addr [[X_PHI]]
|
||||||
|
// CHECK-NEXT: br bb6
|
||||||
|
|
||||||
|
// CHECK: bb4:
|
||||||
|
// CHECK-NEXT: br bb5
|
||||||
|
|
||||||
|
// CHECK: bb5:
|
||||||
|
// CHECK-NEXT: destroy_addr [[E_COPY]]
|
||||||
|
// CHECK-NEXT: dealloc_stack [[E_COPY]]
|
||||||
|
// CHECK-NEXT: br bb6
|
||||||
|
|
||||||
|
// CHECK: bb6:
|
||||||
|
// CHECK-NEXT: dealloc_stack [[X_PHI]]
|
||||||
|
// CHECK-NEXT: destroy_addr %0
|
||||||
|
// CHECK-NEXT: tuple ()
|
||||||
|
// CHECK-NEXT: return
|
||||||
|
|
||||||
|
switch e {
|
||||||
|
case .a(let x):
|
||||||
|
takesAny(x)
|
||||||
|
fallthrough
|
||||||
|
case .b(let x):
|
||||||
|
takesAny(x)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user