Skip to content

Commit 3f944d0

Browse files
authored
Merge pull request #14724 from jrose-apple/obligatory-its-a-trap-joke
[SILGen] Generate a trap for unexpected cases in all @objc enums rdar://problem/20420436
2 parents 1785855 + 49e71c9 commit 3f944d0

File tree

6 files changed

+408
-175
lines changed

6 files changed

+408
-175
lines changed

lib/SILGen/SILGenPattern.cpp

Lines changed: 120 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,113 +1679,141 @@ void PatternMatchEmission::emitIsDispatch(ArrayRef<RowToSpecialize> rows,
16791679
[&] { (*innerFailure)(loc); }, rows[0].Count);
16801680
}
16811681

1682-
/// Perform specialized dispatch for a sequence of EnumElementPattern or an
1683-
/// OptionalSomePattern.
1684-
void PatternMatchEmission::emitEnumElementDispatchWithOwnership(
1685-
ArrayRef<RowToSpecialize> rows, ConsumableManagedValue src,
1686-
const SpecializationHandler &handleCase,
1687-
const FailureHandler &outerFailure) {
1688-
assert(src.getFinalConsumption() != CastConsumptionKind::TakeOnSuccess &&
1689-
"SIL ownership does not support TakeOnSuccess");
1690-
1691-
CanType sourceType = rows[0].Pattern->getType()->getCanonicalType();
1692-
1682+
namespace {
16931683
struct CaseInfo {
16941684
EnumElementDecl *FormalElement;
16951685
Pattern *FirstMatcher;
16961686
bool Irrefutable = false;
16971687
SmallVector<SpecializedRow, 2> SpecializedRows;
16981688
};
1689+
} // end anonymous namespace
16991690

1700-
SILBasicBlock *curBB = SGF.B.getInsertionBB();
1701-
1702-
// Collect the cases and specialized rows.
1703-
//
1704-
// These vectors are completely parallel, but the switch
1705-
// instructions want only the first information, so we split them up.
1706-
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 4> caseBBs;
1707-
SmallVector<CaseInfo, 4> caseInfos;
1708-
SILBasicBlock *defaultBB = nullptr;
1691+
/// Create destination blocks for switching over the cases in an enum defined
1692+
/// by \p rows.
1693+
static void generateEnumCaseBlocks(
1694+
SILGenFunction &SGF,
1695+
ArrayRef<RowToSpecialize> rows,
1696+
CanType sourceType,
1697+
SILBasicBlock *curBB,
1698+
SmallVectorImpl<std::pair<EnumElementDecl *, SILBasicBlock *>> &caseBBs,
1699+
SmallVectorImpl<ProfileCounter> &caseCounts,
1700+
SmallVectorImpl<CaseInfo> &caseInfos,
1701+
SILBasicBlock *&defaultBB) {
1702+
1703+
assert(caseBBs.empty());
1704+
assert(caseCounts.empty());
1705+
assert(caseInfos.empty());
1706+
assert(defaultBB == nullptr);
17091707

17101708
caseBBs.reserve(rows.size());
17111709
caseInfos.reserve(rows.size());
17121710

1713-
{
1714-
// Create destination blocks for all the cases.
1715-
llvm::DenseMap<EnumElementDecl *, unsigned> caseToIndex;
1716-
for (auto &row : rows) {
1717-
EnumElementDecl *formalElt;
1718-
Pattern *subPattern = nullptr;
1719-
if (auto eep = dyn_cast<EnumElementPattern>(row.Pattern)) {
1720-
formalElt = eep->getElementDecl();
1721-
subPattern = eep->getSubPattern();
1722-
} else {
1723-
auto *osp = cast<OptionalSomePattern>(row.Pattern);
1724-
formalElt = osp->getElementDecl();
1725-
subPattern = osp->getSubPattern();
1726-
}
1727-
auto elt = SGF.SGM.getLoweredEnumElementDecl(formalElt);
1711+
auto enumDecl = sourceType.getEnumOrBoundGenericEnum();
17281712

1729-
unsigned index = caseInfos.size();
1730-
auto insertionResult = caseToIndex.insert({elt, index});
1731-
if (!insertionResult.second) {
1732-
index = insertionResult.first->second;
1733-
} else {
1734-
curBB = SGF.createBasicBlock(curBB);
1735-
caseBBs.push_back({elt, curBB});
1736-
caseInfos.resize(caseInfos.size() + 1);
1737-
caseInfos.back().FormalElement = formalElt;
1738-
caseInfos.back().FirstMatcher = row.Pattern;
1739-
}
1740-
assert(caseToIndex[elt] == index);
1741-
assert(caseBBs[index].first == elt);
1742-
1743-
auto &info = caseInfos[index];
1744-
info.Irrefutable = (info.Irrefutable || row.Irrefutable);
1745-
info.SpecializedRows.resize(info.SpecializedRows.size() + 1);
1746-
auto &specRow = info.SpecializedRows.back();
1747-
specRow.RowIndex = row.RowIndex;
1748-
1749-
// Use the row pattern, if it has one.
1750-
if (subPattern) {
1751-
specRow.Patterns.push_back(subPattern);
1752-
// It's also legal to write:
1753-
// case .Some { ... }
1754-
// which is an implicit wildcard.
1755-
} else {
1756-
specRow.Patterns.push_back(nullptr);
1757-
}
1713+
llvm::SmallDenseMap<EnumElementDecl *, unsigned, 16> caseToIndex;
1714+
for (auto &row : rows) {
1715+
EnumElementDecl *formalElt;
1716+
Pattern *subPattern = nullptr;
1717+
if (auto eep = dyn_cast<EnumElementPattern>(row.Pattern)) {
1718+
formalElt = eep->getElementDecl();
1719+
subPattern = eep->getSubPattern();
1720+
} else {
1721+
auto *osp = cast<OptionalSomePattern>(row.Pattern);
1722+
formalElt = osp->getElementDecl();
1723+
subPattern = osp->getSubPattern();
17581724
}
1725+
auto elt = SGF.SGM.getLoweredEnumElementDecl(formalElt);
1726+
assert(elt->getParentEnum() == enumDecl);
17591727

1760-
// We always need a default block if the enum is resilient.
1761-
// If the enum is @_fixed_layout, we only need one if the
1762-
// switch is not exhaustive.
1763-
bool exhaustive = false;
1764-
auto enumDecl = sourceType.getEnumOrBoundGenericEnum();
1728+
unsigned index = caseInfos.size();
1729+
auto insertionResult = caseToIndex.insert({elt, index});
1730+
if (!insertionResult.second) {
1731+
index = insertionResult.first->second;
1732+
} else {
1733+
curBB = SGF.createBasicBlock(curBB);
1734+
caseBBs.push_back({elt, curBB});
1735+
caseInfos.push_back(CaseInfo());
1736+
caseInfos.back().FormalElement = formalElt;
1737+
caseInfos.back().FirstMatcher = row.Pattern;
1738+
caseCounts.push_back(row.Count);
1739+
}
1740+
assert(caseToIndex[elt] == index);
1741+
assert(caseBBs[index].first == elt);
17651742

1766-
if (!enumDecl->isResilient(SGF.SGM.M.getSwiftModule(),
1767-
SGF.F.getResilienceExpansion())) {
1768-
exhaustive = true;
1743+
auto &info = caseInfos[index];
1744+
info.Irrefutable = (info.Irrefutable || row.Irrefutable);
1745+
info.SpecializedRows.push_back(SpecializedRow());
1746+
auto &specRow = info.SpecializedRows.back();
1747+
specRow.RowIndex = row.RowIndex;
17691748

1770-
for (auto elt : enumDecl->getAllElements()) {
1771-
if (!caseToIndex.count(elt)) {
1772-
exhaustive = false;
1773-
break;
1774-
}
1775-
}
1749+
// Use the row pattern, if it has one.
1750+
if (subPattern) {
1751+
specRow.Patterns.push_back(subPattern);
1752+
// It's also legal to write:
1753+
// case .Some { ... }
1754+
// which is an implicit wildcard.
1755+
} else {
1756+
specRow.Patterns.push_back(nullptr);
17761757
}
1777-
1778-
if (!exhaustive)
1779-
defaultBB = SGF.createBasicBlock(curBB);
17801758
}
17811759

17821760
assert(caseBBs.size() == caseInfos.size());
17831761

1762+
// Check to see if the enum may have values beyond the cases we can see
1763+
// at compile-time. This includes future cases (for resilient enums) and
1764+
// random values crammed into C enums.
1765+
//
1766+
// Note: This relies on the fact that there are no "non-resilient" enums that
1767+
// are still non-exhaustive, except for @objc enums.
1768+
bool canAssumeExhaustive = !enumDecl->isObjC();
1769+
if (canAssumeExhaustive) {
1770+
canAssumeExhaustive =
1771+
!enumDecl->isResilient(SGF.SGM.SwiftModule,
1772+
SGF.F.getResilienceExpansion());
1773+
}
1774+
if (canAssumeExhaustive) {
1775+
// Check that Sema didn't let any cases slip through. (This can happen
1776+
// with @_downgrade_exhaustivity_check.)
1777+
canAssumeExhaustive = llvm::all_of(enumDecl->getAllElements(),
1778+
[&](const EnumElementDecl *elt) {
1779+
return caseToIndex.count(elt);
1780+
});
1781+
}
1782+
1783+
if (!canAssumeExhaustive)
1784+
defaultBB = SGF.createBasicBlock(curBB);
1785+
}
1786+
1787+
/// Perform specialized dispatch for a sequence of EnumElementPattern or an
1788+
/// OptionalSomePattern.
1789+
void PatternMatchEmission::emitEnumElementDispatchWithOwnership(
1790+
ArrayRef<RowToSpecialize> rows, ConsumableManagedValue src,
1791+
const SpecializationHandler &handleCase,
1792+
const FailureHandler &outerFailure) {
1793+
assert(src.getFinalConsumption() != CastConsumptionKind::TakeOnSuccess &&
1794+
"SIL ownership does not support TakeOnSuccess");
1795+
1796+
CanType sourceType = rows[0].Pattern->getType()->getCanonicalType();
1797+
1798+
// Collect the cases and specialized rows.
1799+
//
1800+
// These vectors are completely parallel, but the switch
1801+
// instructions want only the first information, so we split them up.
1802+
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 4> caseBBs;
1803+
SmallVector<ProfileCounter, 4> caseCounts;
1804+
SmallVector<CaseInfo, 4> caseInfos;
1805+
SILBasicBlock *defaultBB = nullptr;
1806+
1807+
generateEnumCaseBlocks(SGF, rows, sourceType, SGF.B.getInsertionBB(),
1808+
caseBBs, caseCounts, caseInfos, defaultBB);
1809+
17841810
SILLocation loc = PatternMatchStmt;
17851811
loc.setDebugLoc(rows[0].Pattern);
17861812
// SEMANTIC SIL TODO: Once we have the representation of a switch_enum that
17871813
// can take a +0 value, this extra copy should be a borrow.
17881814
SILValue srcValue = src.getFinalManagedValue().copy(SGF, loc).forward(SGF);
1815+
// FIXME: Pass caseCounts in here as well, as it is in
1816+
// emitEnumElementDispatch.
17891817
SGF.B.createSwitchEnum(loc, srcValue, defaultBB, caseBBs);
17901818

17911819
// Okay, now emit all the cases.
@@ -1910,15 +1938,6 @@ void PatternMatchEmission::emitEnumElementDispatch(
19101938

19111939
CanType sourceType = rows[0].Pattern->getType()->getCanonicalType();
19121940

1913-
struct CaseInfo {
1914-
EnumElementDecl *FormalElement;
1915-
Pattern *FirstMatcher;
1916-
bool Irrefutable = false;
1917-
SmallVector<SpecializedRow, 2> SpecializedRows;
1918-
};
1919-
1920-
SILBasicBlock *curBB = SGF.B.getInsertionBB();
1921-
19221941
// Collect the cases and specialized rows.
19231942
//
19241943
// These vectors are completely parallel, but the switch
@@ -1928,80 +1947,8 @@ void PatternMatchEmission::emitEnumElementDispatch(
19281947
SmallVector<ProfileCounter, 4> caseCounts;
19291948
SILBasicBlock *defaultBB = nullptr;
19301949

1931-
caseBBs.reserve(rows.size());
1932-
caseInfos.reserve(rows.size());
1933-
1934-
{
1935-
// Create destination blocks for all the cases.
1936-
llvm::DenseMap<EnumElementDecl*, unsigned> caseToIndex;
1937-
for (auto &row : rows) {
1938-
EnumElementDecl *formalElt;
1939-
Pattern *subPattern = nullptr;
1940-
if (auto eep = dyn_cast<EnumElementPattern>(row.Pattern)) {
1941-
formalElt = eep->getElementDecl();
1942-
subPattern = eep->getSubPattern();
1943-
} else {
1944-
auto *osp = cast<OptionalSomePattern>(row.Pattern);
1945-
formalElt = osp->getElementDecl();
1946-
subPattern = osp->getSubPattern();
1947-
}
1948-
auto elt = SGF.SGM.getLoweredEnumElementDecl(formalElt);
1949-
1950-
unsigned index = caseInfos.size();
1951-
auto insertionResult = caseToIndex.insert({elt, index});
1952-
if (!insertionResult.second) {
1953-
index = insertionResult.first->second;
1954-
} else {
1955-
curBB = SGF.createBasicBlock(curBB);
1956-
caseBBs.push_back({elt, curBB});
1957-
caseInfos.resize(caseInfos.size() + 1);
1958-
caseInfos.back().FormalElement = formalElt;
1959-
caseInfos.back().FirstMatcher = row.Pattern;
1960-
caseCounts.push_back(row.Count);
1961-
}
1962-
assert(caseToIndex[elt] == index);
1963-
assert(caseBBs[index].first == elt);
1964-
1965-
auto &info = caseInfos[index];
1966-
info.Irrefutable = (info.Irrefutable || row.Irrefutable);
1967-
info.SpecializedRows.resize(info.SpecializedRows.size() + 1);
1968-
auto &specRow = info.SpecializedRows.back();
1969-
specRow.RowIndex = row.RowIndex;
1970-
1971-
// Use the row pattern, if it has one.
1972-
if (subPattern) {
1973-
specRow.Patterns.push_back(subPattern);
1974-
// It's also legal to write:
1975-
// case .Some { ... }
1976-
// which is an implicit wildcard.
1977-
} else {
1978-
specRow.Patterns.push_back(nullptr);
1979-
}
1980-
}
1981-
1982-
// We always need a default block if the enum is resilient.
1983-
// If the enum is @_fixed_layout, we only need one if the
1984-
// switch is not exhaustive.
1985-
bool exhaustive = false;
1986-
auto enumDecl = sourceType.getEnumOrBoundGenericEnum();
1987-
1988-
if (!enumDecl->isResilient(SGF.SGM.M.getSwiftModule(),
1989-
SGF.F.getResilienceExpansion())) {
1990-
exhaustive = true;
1991-
1992-
for (auto elt : enumDecl->getAllElements()) {
1993-
if (!caseToIndex.count(elt)) {
1994-
exhaustive = false;
1995-
break;
1996-
}
1997-
}
1998-
}
1999-
2000-
if (!exhaustive)
2001-
defaultBB = SGF.createBasicBlock(curBB);
2002-
}
2003-
2004-
assert(caseBBs.size() == caseInfos.size());
1950+
generateEnumCaseBlocks(SGF, rows, sourceType, SGF.B.getInsertionBB(),
1951+
caseBBs, caseCounts, caseInfos, defaultBB);
20051952

20061953
// Emit the switch_enum{_addr} instruction.
20071954
bool addressOnlyEnum = src.getType().isAddress();
@@ -2600,17 +2547,21 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26002547
DEBUG(llvm::dbgs() << "emitting switch stmt\n";
26012548
S->print(llvm::dbgs());
26022549
llvm::dbgs() << '\n');
2603-
auto failure = [&](SILLocation location) {
2604-
// If we fail to match anything, we can just emit unreachable.
2605-
// This will be a dataflow error if we can reach here.
2606-
B.createUnreachable(S);
2550+
auto failure = [this](SILLocation location) {
2551+
// If we fail to match anything, we trap. This can happen with a switch
2552+
// over an @objc enum, which may contain any value of its underlying type.
2553+
// FIXME: Even if we can't say what the invalid value was, we should at
2554+
// least mention that this was because of a non-exhaustive enum.
2555+
B.createBuiltinTrap(location);
2556+
B.createUnreachable(location);
26072557
};
26082558

26092559
// If the subject expression is uninhabited, we're already dead.
26102560
// Emit an unreachable in place of the switch statement.
26112561
if (S->getSubjectExpr()->getType()->isStructurallyUninhabited()) {
26122562
emitIgnoredExpr(S->getSubjectExpr());
2613-
return failure(SILLocation(S));
2563+
B.createUnreachable(S);
2564+
return;
26142565
}
26152566

26162567
auto completionHandler = [&](PatternMatchEmission &emission,

test/IRGen/objc_enum_multi_file.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ func useFoo(_ x: Foo) -> Int32 {
1616
// CHECK-DAG: i32 0, label %[[CASE_A:.+]]
1717
// CHECK: ]
1818

19-
// CHECK: <label>:[[DEFAULT]]
20-
// CHECK-NEXT: unreachable
21-
2219
switch x {
2320
// CHECK: <label>:[[CASE_B]]
2421
// CHECK-NEXT: br label %[[FINAL:.+]]
@@ -36,6 +33,10 @@ func useFoo(_ x: Foo) -> Int32 {
3633
return 10
3734
}
3835

36+
// CHECK: <label>:[[DEFAULT]]
37+
// CHECK-NEXT: call void @llvm.trap()
38+
// CHECK-NEXT: unreachable
39+
3940
// CHECK: <label>:[[FINAL]]
4041
// CHECK: %[[RETVAL:.+]] = phi i32 [ 10, %[[CASE_A]] ], [ 15, %[[CASE_C]] ], [ 11, %[[CASE_B]] ]
4142
// CHECK: ret i32 %[[RETVAL]]
@@ -49,9 +50,6 @@ func useBar(_ x: Bar) -> Int32 {
4950
// CHECK-DAG: i32 5, label %[[CASE_A:.+]]
5051
// CHECK: ]
5152

52-
// CHECK: <label>:[[DEFAULT]]
53-
// CHECK-NEXT: unreachable
54-
5553
switch x {
5654
// CHECK: <label>:[[CASE_B]]
5755
// CHECK-NEXT: br label %[[FINAL:.+]]
@@ -69,6 +67,10 @@ func useBar(_ x: Bar) -> Int32 {
6967
return 10
7068
}
7169

70+
// CHECK: <label>:[[DEFAULT]]
71+
// CHECK-NEXT: call void @llvm.trap()
72+
// CHECK-NEXT: unreachable
73+
7274
// CHECK: <label>:[[FINAL]]
7375
// CHECK: %[[RETVAL:.+]] = phi i32 [ 10, %[[CASE_A]] ], [ 15, %[[CASE_C]] ], [ 11, %[[CASE_B]] ]
7476
// CHECK: ret i32 %[[RETVAL]]

0 commit comments

Comments
 (0)