Skip to content

Commit 2a7eee8

Browse files
authored
Merge pull request #8908 from CodaFi/space-engine
Redo Exhaustiveness Analysis
2 parents e326728 + 1dfab98 commit 2a7eee8

38 files changed

+1633
-375
lines changed

include/swift/AST/ASTPrinter.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,6 @@ uint8_t getKeywordLen(tok keyword);
322322
/// Get <#code#>;
323323
StringRef getCodePlaceholder();
324324

325-
/// Given an array of enum element decls, print them as case statements with
326-
/// placeholders as contents.
327-
void printEnumElementsAsCases(llvm::DenseSet<EnumElementDecl*> &UnhandledElements,
328-
llvm::raw_ostream &OS);
329-
330325
} // namespace swift
331326

332327
#endif // LLVM_SWIFT_AST_ASTPRINTER_H

include/swift/AST/DiagnosticsSema.def

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,14 +2754,6 @@ ERROR(trailing_closure_requires_parens,none,
27542754
"trailing closure requires parentheses for disambiguation in this"
27552755
" context", ())
27562756

2757-
ERROR(empty_switch_stmt,none,
2758-
"%select{'switch' statement body must have at least one 'case' or 'default' block; |}0"
2759-
"do you want to add "
2760-
"%select{missing cases?|a default case?}1",(bool, bool))
2761-
ERROR(non_exhaustive_switch,none,
2762-
"%select{switch must be exhaustive, consider adding |do you want to add }0"
2763-
"%select{missing cases|a default clause}1"
2764-
"%select{|?}0", (bool, bool))
27652757
//------------------------------------------------------------------------------
27662758
// Type Check Patterns
27672759
//------------------------------------------------------------------------------
@@ -3595,6 +3587,21 @@ WARNING(debug_long_closure_body, none,
35953587
"closure took %0ms to type-check (limit: %1ms)",
35963588
(unsigned, unsigned))
35973589

3590+
//------------------------------------------------------------------------------
3591+
// Pattern match diagnostics
3592+
//------------------------------------------------------------------------------
3593+
3594+
3595+
ERROR(empty_switch_stmt,none,
3596+
"'switch' statement body must have at least one 'case' or 'default' "
3597+
"block; do you want to add a default case?",())
3598+
ERROR(non_exhaustive_switch,none,
3599+
"%select{switch must be exhaustive, consider adding |do you want to add }0"
3600+
"%select{missing cases|a default clause}1"
3601+
"%select{:|?}0 %2", (bool, bool, StringRef))
3602+
NOTE(missing_particular_case,none,
3603+
"missing case: '%0'", (StringRef))
3604+
35983605
#ifndef DIAG_NO_UNDEF
35993606
# if defined(DIAG)
36003607
# undef DIAG

include/swift/AST/DiagnosticsSema.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ namespace swift {
3737
extern detail::DiagWithArguments<void Signature>::type ID;
3838
#include "DiagnosticsSema.def"
3939
}
40-
void diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS);
4140
}
4241

4342
#endif

lib/AST/ASTPrinter.cpp

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -611,60 +611,6 @@ uint8_t swift::getKeywordLen(tok keyword) {
611611

612612
StringRef swift::getCodePlaceholder() { return "<#code#>"; }
613613

614-
void swift::
615-
printEnumElementsAsCases(llvm::DenseSet<EnumElementDecl*> &UnhandledElements,
616-
llvm::raw_ostream &OS) {
617-
// Sort the missing elements to a vector because set does not guarantee orders.
618-
SmallVector<EnumElementDecl*, 4> SortedElements;
619-
SortedElements.insert(SortedElements.begin(), UnhandledElements.begin(),
620-
UnhandledElements.end());
621-
std::sort(SortedElements.begin(),SortedElements.end(),
622-
[](EnumElementDecl *LHS, EnumElementDecl *RHS) {
623-
return LHS->getNameStr().compare(RHS->getNameStr()) < 0;
624-
});
625-
626-
auto printPayloads = [](EnumElementDecl *EE, llvm::raw_ostream &OS) {
627-
// If the enum element has no payloads, return.
628-
auto TL = EE->getArgumentTypeLoc();
629-
if (TL.isNull())
630-
return;
631-
TypeRepr* TR = EE->getArgumentTypeLoc().getTypeRepr();
632-
if (auto *TTR = dyn_cast<TupleTypeRepr>(TR)) {
633-
SmallVector<Identifier, 4> NameBuffer;
634-
ArrayRef<Identifier> Names;
635-
if (TTR->hasElementNames()) {
636-
// Get the name from the tuple repr, if exist.
637-
Names = TTR->getElementNames();
638-
} else {
639-
// Create same amount of empty names to the elements.
640-
NameBuffer.resize(TTR->getNumElements());
641-
Names = llvm::makeArrayRef(NameBuffer);
642-
}
643-
OS << "(";
644-
// Print each element in the pattern match.
645-
for (unsigned I = 0, N = Names.size(); I < N; I ++) {
646-
auto Id = Names[I];
647-
if (Id.empty())
648-
OS << "_";
649-
else
650-
OS << tok::kw_let << " " << Id.str();
651-
if (I + 1 != N) {
652-
OS << ", ";
653-
}
654-
}
655-
OS << ")";
656-
}
657-
};
658-
659-
// Print each enum element name.
660-
std::for_each(SortedElements.begin(), SortedElements.end(),
661-
[&](EnumElementDecl *EE) {
662-
OS << tok::kw_case << " ." << EE->getNameStr();
663-
printPayloads(EE, OS);
664-
OS <<": " << getCodePlaceholder() << "\n";
665-
});
666-
}
667-
668614
ASTPrinter &operator<<(ASTPrinter &printer, tok keyword) {
669615
SmallString<16> Buffer;
670616
llvm::raw_svector_ostream OS(Buffer);

lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,6 @@ static void diagnoseUnreachable(const SILInstruction *I,
8383
return;
8484
}
8585

86-
// A non-exhaustive switch would also produce an unreachable instruction.
87-
if (L.isASTNode<SwitchStmt>()) {
88-
diagnoseMissingCases(Context, L.getAsASTNode<SwitchStmt>());
89-
return;
90-
}
91-
9286
if (auto *Guard = L.getAsASTNode<GuardStmt>()) {
9387
diagnose(Context, Guard->getBody()->getEndLoc(),
9488
diag::guard_body_must_not_fallthrough);

lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ add_swift_library(swiftSema STATIC
4848
TypeCheckREPL.cpp
4949
TypeCheckRequest.cpp
5050
TypeCheckStmt.cpp
51+
TypeCheckSwitchStmt.cpp
5152
TypeCheckType.cpp
5253
TypeChecker.cpp
5354
LINK_LIBRARIES

lib/Sema/TypeCheckStmt.cpp

Lines changed: 13 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -197,80 +197,6 @@ namespace {
197197
};
198198
} // end anonymous namespace
199199

200-
/// If the pattern handles an enum element, remove the enum element from the
201-
/// given set. If seeing uncertain patterns, e.g. any pattern, return true;
202-
/// otherwise return false.
203-
static bool
204-
removedHandledEnumElements(Pattern *P,
205-
llvm::DenseSet<EnumElementDecl*> &UnhandledElements) {
206-
if (auto *EEP = dyn_cast<EnumElementPattern>(P)) {
207-
UnhandledElements.erase(EEP->getElementDecl());
208-
} else if (auto *VP = dyn_cast<VarPattern>(P)) {
209-
return removedHandledEnumElements(VP->getSubPattern(), UnhandledElements);
210-
} else {
211-
return true;
212-
}
213-
return false;
214-
};
215-
216-
void swift::
217-
diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS) {
218-
bool Empty = SwitchS->getCases().empty();
219-
SourceLoc StartLoc = SwitchS->getStartLoc();
220-
SourceLoc EndLoc = SwitchS->getEndLoc();
221-
StringRef Placeholder = getCodePlaceholder();
222-
SmallString<32> Buffer;
223-
llvm::raw_svector_ostream OS(Buffer);
224-
225-
bool InEditor = Context.LangOpts.DiagnosticsEditorMode;
226-
227-
auto DefaultDiag = [&]() {
228-
OS << tok::kw_default << ": " << Placeholder << "\n";
229-
Context.Diags.diagnose(StartLoc, Empty ? diag::empty_switch_stmt :
230-
diag::non_exhaustive_switch, InEditor, true).fixItInsert(EndLoc,
231-
Buffer.str());
232-
};
233-
// To find the subject enum decl for this switch statement.
234-
EnumDecl *SubjectED = nullptr;
235-
if (auto SubjectTy = SwitchS->getSubjectExpr()->getType()) {
236-
if (auto *ND = SubjectTy->getAnyNominal()) {
237-
SubjectED = ND->getAsEnumOrEnumExtensionContext();
238-
}
239-
}
240-
241-
// The switch is not about an enum decl, add "default:" instead.
242-
if (!SubjectED) {
243-
DefaultDiag();
244-
return;
245-
}
246-
247-
// Assume enum elements are not handled in the switch statement.
248-
llvm::DenseSet<EnumElementDecl*> UnhandledElements;
249-
250-
SubjectED->getAllElements(UnhandledElements);
251-
for (auto Current : SwitchS->getCases()) {
252-
// For each handled enum element, remove it from the bucket.
253-
for (auto Item : Current->getCaseLabelItems()) {
254-
if (removedHandledEnumElements(Item.getPattern(), UnhandledElements)) {
255-
DefaultDiag();
256-
return;
257-
}
258-
}
259-
}
260-
261-
// No unhandled cases, so we are not sure the exact case to add, fall back to
262-
// default instead.
263-
if (UnhandledElements.empty()) {
264-
DefaultDiag();
265-
return;
266-
}
267-
268-
printEnumElementsAsCases(UnhandledElements, OS);
269-
Context.Diags.diagnose(StartLoc, Empty ? diag::empty_switch_stmt :
270-
diag::non_exhaustive_switch, InEditor, false).fixItInsert(EndLoc,
271-
Buffer.str());
272-
}
273-
274200
static void setAutoClosureDiscriminators(DeclContext *DC, Stmt *S) {
275201
S->walk(ContextualizeClosures(DC));
276202
}
@@ -928,9 +854,11 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
928854
}
929855

930856
Stmt *visitSwitchStmt(SwitchStmt *S) {
857+
bool hadError = false;
858+
931859
// Type-check the subject expression.
932860
Expr *subjectExpr = S->getSubjectExpr();
933-
TC.typeCheckExpression(subjectExpr, DC);
861+
hadError |= TC.typeCheckExpression(subjectExpr, DC);
934862
if (Expr *newSubjectExpr = TC.coerceToMaterializable(subjectExpr))
935863
subjectExpr = newSubjectExpr;
936864
S->setSubjectExpr(subjectExpr);
@@ -940,9 +868,6 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
940868
AddSwitchNest switchNest(*this);
941869
AddLabeledStmt labelNest(*this, S);
942870

943-
// Reject switch statements with empty blocks.
944-
if (S->getCases().empty())
945-
diagnoseMissingCases(TC.Context, S);
946871
for (unsigned i = 0, e = S->getCases().size(); i < e; ++i) {
947872
auto *caseBlock = S->getCases()[i];
948873
// Fallthrough transfers control to the next case block. In the
@@ -958,6 +883,8 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
958883
// Coerce the pattern to the subject's type.
959884
if (TC.coercePatternToType(pattern, DC, subjectType,
960885
TR_InExpression)) {
886+
hadError = true;
887+
961888
// If that failed, mark any variables binding pieces of the pattern
962889
// as invalid to silence follow-on errors.
963890
pattern->forEachVariable([&](VarDecl *VD) {
@@ -992,17 +919,21 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
992919

993920
// Check the guard expression, if present.
994921
if (auto *guard = labelItem.getGuardExpr()) {
995-
TC.typeCheckCondition(guard, DC);
922+
hadError |= TC.typeCheckCondition(guard, DC);
996923
labelItem.setGuardExpr(guard);
997924
}
998925
}
999926

1000927
// Type-check the body statements.
1001928
Stmt *body = caseBlock->getBody();
1002-
typeCheckStmt(body);
929+
hadError |= typeCheckStmt(body);
1003930
caseBlock->setBody(body);
1004931
}
1005-
932+
933+
if (!S->isImplicit()) {
934+
TC.checkSwitchExhaustiveness(S, /*limitChecking*/hadError);
935+
}
936+
1006937
return S;
1007938
}
1008939

@@ -1057,8 +988,8 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
1057988
// There is nothing more to do.
1058989
return S;
1059990
}
991+
1060992
};
1061-
1062993
} // end anonymous namespace
1063994

1064995
bool TypeChecker::typeCheckCatchPattern(CatchStmt *S, DeclContext *DC) {

0 commit comments

Comments
 (0)