diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f4f1bc67724a1..f25584cb89bcf 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8264,6 +8264,8 @@ def err_mixing_cxx_try_seh_try : Error< "in the same function as SEH '__try'">; def err_seh_try_unsupported : Error< "SEH '__try' is not supported on this target">; +def err_seh_try_dtor : Error<"cannot use C++ object with a destructor " + "in the same function as SEH '__try'">; def note_conflicting_try_here : Note< "conflicting %0 here">; def warn_jump_out_of_seh_finally : Warning< diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index f21e571e6e0ce..589a921b23a6d 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -399,6 +399,47 @@ static bool isNoexcept(const FunctionDecl *FD) { return false; } +//===----------------------------------------------------------------------===// +// Check for SEH __try in a function with C++ objects that have destructors. +//===----------------------------------------------------------------------===// + +static void emitDiagForSehTryUnwind(Sema &S, CFGElement &E) { + if (auto AD = E.getAs()) { + const auto *VD = AD->getVarDecl(); + S.Diag(VD->getLocation(), diag::err_seh_try_dtor); + } else if (auto TD = E.getAs()) { + const auto *E = TD->getBindTemporaryExpr(); + S.Diag(E->getBeginLoc(), diag::err_seh_try_dtor); + } else + llvm_unreachable("emitDiagForSehTryUnwind should only be used with " + "AutomaticObjectDtor or TemporaryDtor"); +} + +static void checkSehTryNeedsUnwind(Sema &S, const FunctionDecl *FD, + AnalysisDeclContext &AC) { + if (!FD->usesSEHTry()) + return; + CFG *BodyCFG = AC.getCFG(); + if (!BodyCFG) + return; + if (BodyCFG->getExit().pred_empty()) + return; + + llvm::BitVector Reachable(BodyCFG->getNumBlockIDs()); + clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), + Reachable); + for (CFGBlock *B : *BodyCFG) { + if (!Reachable[B->getBlockID()]) + continue; + for (CFGElement &E : *B) { + auto Kind = E.getKind(); + if (Kind == CFGElement::AutomaticObjectDtor || + Kind == CFGElement::TemporaryDtor) + emitDiagForSehTryUnwind(S, E); + } + } +} + //===----------------------------------------------------------------------===// // Check for missing return value. //===----------------------------------------------------------------------===// @@ -2821,6 +2862,10 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( if (S.getLangOpts().CPlusPlus && !fscope->isCoroutine() && isNoexcept(FD)) checkThrowInNonThrowingFunc(S, FD, AC); + if (S.getLangOpts().CPlusPlus) + if (const FunctionDecl *FD = dyn_cast(D)) + checkSehTryNeedsUnwind(S, FD, AC); + // If none of the previous checks caused a CFG build, trigger one here // for the logical error handler. if (LogicalErrorHandler::hasActiveDiagnostics(Diags, D->getBeginLoc())) { diff --git a/clang/test/Sema/seh-cxx-dtors.cpp b/clang/test/Sema/seh-cxx-dtors.cpp new file mode 100644 index 0000000000000..6e00fe8bd89a8 --- /dev/null +++ b/clang/test/Sema/seh-cxx-dtors.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fsyntax-only -fms-extensions -verify %s + +struct Foo { + ~Foo() {} +}; + +// These need '-fms-extensions' +void f1() { + Foo foo {}; // expected-error {{destructor}} + __try {} __except (1) {} +} + +void bar(Foo foo); + +void f2() { + bar(Foo {}); // expected-error {{destructor}} + __try {} __except (1) {} +}