Skip to content

Commit 42c5955

Browse files
committed
Introduce the Space Engine
Implement exhaustiveness checking in Sema with rich error messages. The algorithm used is a variant of the one described in Fengyun Liu's paper "A Generic Algorithm for Checking Exhaustivity of Pattern Matching" published in the EPFL conference, and Luc Maranget's seminal paper "Warnings for Pattern Matching" The Space Engine views pattern matching as a problem of projecting the scrutinee of a pattern-match into a "Space", then iteratively constructing a Space from the cases. Taking the difference of this master space and the covered spaces yields the "holes" left over or reveals a completely covered space. The algorithm also extends trivially to redundancy checks in patterns, but that check is already implemented in SILGen and this algorithm does not improve upon it.
1 parent 0ad6f73 commit 42c5955

File tree

6 files changed

+1014
-154
lines changed

6 files changed

+1014
-154
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: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2750,14 +2750,6 @@ ERROR(trailing_closure_requires_parens,none,
27502750
"trailing closure requires parentheses for disambiguation in this"
27512751
" context", ())
27522752

2753-
ERROR(empty_switch_stmt,none,
2754-
"%select{'switch' statement body must have at least one 'case' or 'default' block; |}0"
2755-
"do you want to add "
2756-
"%select{missing cases?|a default case?}1",(bool, bool))
2757-
ERROR(non_exhaustive_switch,none,
2758-
"%select{switch must be exhaustive, consider adding |do you want to add }0"
2759-
"%select{missing cases|a default clause}1"
2760-
"%select{|?}0", (bool, bool))
27612753
//------------------------------------------------------------------------------
27622754
// Type Check Patterns
27632755
//------------------------------------------------------------------------------
@@ -3591,6 +3583,19 @@ WARNING(debug_long_closure_body, none,
35913583
"closure took %0ms to type-check (limit: %1ms)",
35923584
(unsigned, unsigned))
35933585

3586+
//------------------------------------------------------------------------------
3587+
// Pattern match diagnostics
3588+
//------------------------------------------------------------------------------
3589+
3590+
3591+
ERROR(empty_switch_stmt,none,
3592+
"'switch' statement body must have at least one 'case' or 'default' "
3593+
"block; do you want to add a default case?",())
3594+
ERROR(non_exhaustive_switch,none,
3595+
"%select{switch must be exhaustive, consider adding |do you want to add }0"
3596+
"%select{missing cases|a default clause}1"
3597+
"%select{:|?}0 %2", (bool, bool, StringRef))
3598+
35943599
#ifndef DIAG_NO_UNDEF
35953600
# if defined(DIAG)
35963601
# 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);

0 commit comments

Comments
 (0)