<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
margin-bottom:.0001pt;
font-size:12.0pt;
font-family:"Times New Roman",serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:blue;
text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
{mso-style-priority:99;
color:purple;
text-decoration:underline;}
span.EmailStyle17
{mso-style-type:personal-reply;
font-family:"Calibri",sans-serif;
color:#1F497D;}
.MsoChpDefault
{mso-style-type:export-only;
font-family:"Calibri",sans-serif;}
@page WordSection1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
{page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="blue" vlink="purple">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:#1F497D">Hi Richard:
<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:#1F497D">I’m not sure if you noticed this, but my coworker did submit a review here:
<a href="https://reviews.llvm.org/D34671">https://reviews.llvm.org/D34671</a> with the changes you suggested.
<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:#1F497D">Thanks,<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:#1F497D">Erich<o:p></o:p></span></p>
<p class="MsoNormal"><a name="_MailEndCompose"><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:#1F497D"><o:p> </o:p></span></a></p>
<p class="MsoNormal"><b><span style="font-size:11.0pt;font-family:"Calibri",sans-serif">From:</span></b><span style="font-size:11.0pt;font-family:"Calibri",sans-serif"> metafoo@gmail.com [mailto:metafoo@gmail.com]
<b>On Behalf Of </b>Richard Smith<br>
<b>Sent:</b> Monday, June 26, 2017 2:25 PM<br>
<b>To:</b> Keane, Erich <erich.keane@intel.com><br>
<b>Cc:</b> cfe-commits <cfe-commits@lists.llvm.org><br>
<b>Subject:</b> Re: r306149 - Emit warning when throw exception in destruct or dealloc functions which has a<o:p></o:p></span></p>
<p class="MsoNormal"><o:p> </o:p></p>
<div>
<div>
<div>
<p class="MsoNormal">On 23 June 2017 at 13:22, Erich Keane via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<o:p></o:p></p>
<blockquote style="border:none;border-left:solid #CCCCCC 1.0pt;padding:0in 0in 0in 6.0pt;margin-left:4.8pt;margin-right:0in">
<p class="MsoNormal">Author: erichkeane<br>
Date: Fri Jun 23 15:22:19 2017<br>
New Revision: 306149<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=306149&view=rev" target="_blank">
http://llvm.org/viewvc/llvm-project?rev=306149&view=rev</a><br>
Log:<br>
Emit warning when throw exception in destruct or dealloc functions which has a<br>
(possible implicit) noexcept specifier<br>
<br>
Throwing in the destructor is not good (C++11 change try to not allow see below).<br>
But in reality, those codes are exist.<br>
C++11 [class.dtor]p3:<br>
<br>
A declaration of a destructor that does not have an exception-specification is<br>
implicitly considered to have the same exception specification as an implicit<br>
declaration.<br>
<br>
With this change, the application worked before may now run into runtime<br>
termination. My goal here is to emit a warning to provide only possible info to<br>
where the code may need to be changed.<br>
<br>
First there is no way, in compile time to identify the “throw” really throw out<br>
of the function. Things like the call which throw out… To keep this simple,<br>
when “throw” is seen, checking its enclosing function(only destructor and<br>
dealloc functions) with noexcept(true) specifier emit warning.<br>
<br>
Here is implementation detail:<br>
A new member function CheckCXXThrowInNonThrowingFunc is added for class Sema<br>
in Sema.h. It is used in the call to both BuildCXXThrow and<br>
TransformCXXThrowExpr.<br>
<br>
The function basic check if the enclosing function with non-throwing noexcept<br>
specifer, if so emit warning for it.<br>
<br>
The example of warning message like:<br>
k1.cpp:18:3: warning: ''~dependent_warn'' has a (possible implicit) non-throwing<br>
<br>
noexcept specifier. Throwing exception may cause termination.<br>
[-Wthrow-in-dtor]<br>
throw 1;<br>
^<br>
<br>
k1.cpp:43:30: note: in instantiation of member function<br>
<br>
'dependent_warn<noexcept_fun>::~dependent_warn' requested here<br>
<br>
dependent_warn<noexcept_fun> f; // cause warning<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D33333" target="_blank">
https://reviews.llvm.org/D33333</a><br>
<br>
<br>
Modified:<br>
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp<br>
cfe/trunk/test/CXX/except/except.spec/p11.cpp<br>
<br>
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=306149&r1=306148&r2=306149&view=diff" target="_blank">
http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=306149&r1=306148&r2=306149&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)<br>
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jun 23 15:22:19 2017<br>
@@ -6351,6 +6351,15 @@ def err_exceptions_disabled : Error<<br>
"cannot use '%0' with exceptions disabled">;<br>
def err_objc_exceptions_disabled : Error<<br>
"cannot use '%0' with Objective-C exceptions disabled">;<br>
+def warn_throw_in_noexcept_func<br>
+ : Warning<"%0 has a non-throwing exception specification but can still "<br>
+ "throw, resulting in unexpected program termination">,<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
<div>
<p class="MsoNormal">How do you know it's unexpected? :) You also don't know that this leads to program termination: a set_unexpected handler could do something else, in principle. I would just delete the ", resulting in unexpected program termination" part
here.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<blockquote style="border:none;border-left:solid #CCCCCC 1.0pt;padding:0in 0in 0in 6.0pt;margin-left:4.8pt;margin-right:0in">
<p class="MsoNormal">+ InGroup<Exceptions>;<br>
+def note_throw_in_dtor<br>
+ : Note<"destructor or deallocator has a (possibly implicit) non-throwing "<br>
+ "excepton specification">;<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
<div>
<p class="MsoNormal">Please figure out which case we're actually in, and just mention that one. You can use "hasImplicitExceptionSpec" in SemaExceptionSpec.cpp to determine whether the exception specification is implicit.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
<div>
<p class="MsoNormal">Also, typo "excepton".<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<blockquote style="border:none;border-left:solid #CCCCCC 1.0pt;padding:0in 0in 0in 6.0pt;margin-left:4.8pt;margin-right:0in">
<p class="MsoNormal">+def note_throw_in_function<br>
+ : Note<"non-throwing function declare here">;<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
<div>
<p class="MsoNormal">declare -> declared, but something like "function declared non-throwing here" would be preferable<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<blockquote style="border:none;border-left:solid #CCCCCC 1.0pt;padding:0in 0in 0in 6.0pt;margin-left:4.8pt;margin-right:0in">
<p class="MsoNormal"> def err_seh_try_outside_functions : Error<<br>
"cannot use SEH '__try' in blocks, captured regions, or Obj-C method decls">;<br>
def err_mixing_cxx_try_seh_try : Error<<br>
<br>
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=306149&r1=306148&r2=306149&view=diff" target="_blank">
http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=306149&r1=306148&r2=306149&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)<br>
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Fri Jun 23 15:22:19 2017<br>
@@ -279,6 +279,150 @@ static void checkRecursiveFunction(Sema<br>
}<br>
<br>
//===----------------------------------------------------------------------===//<br>
+// Check for throw in a non-throwing function.<br>
+//===----------------------------------------------------------------------===//<br>
+enum ThrowState {<br>
+ FoundNoPathForThrow,<br>
+ FoundPathForThrow,<br>
+ FoundPathWithNoThrowOutFunction,<br>
+};<br>
+<br>
+static bool isThrowCaught(const CXXThrowExpr *Throw,<br>
+ const CXXCatchStmt *Catch) {<br>
+ const Type *ThrowType = nullptr;<br>
+ if (Throw->getSubExpr())<br>
+ ThrowType = Throw->getSubExpr()->getType().getTypePtrOrNull();<br>
+ if (!ThrowType)<br>
+ return false;<br>
+ const Type *CaughtType = Catch->getCaughtType().getTypePtrOrNull();<br>
+ if (!CaughtType)<br>
+ return true;<br>
+ if (ThrowType->isReferenceType())<br>
+ ThrowType = ThrowType->castAs<ReferenceType>()<br>
+ ->getPointeeType()<br>
+ ->getUnqualifiedDesugaredType();<br>
+ if (CaughtType->isReferenceType())<br>
+ CaughtType = CaughtType->castAs<ReferenceType>()<br>
+ ->getPointeeType()<br>
+ ->getUnqualifiedDesugaredType();<br>
+ if (CaughtType == ThrowType)<br>
+ return true;<br>
+ const CXXRecordDecl *CaughtAsRecordType =<br>
+ CaughtType->getPointeeCXXRecordDecl();<br>
+ const CXXRecordDecl *ThrowTypeAsRecordType = ThrowType->getAsCXXRecordDecl();<br>
+ if (CaughtAsRecordType && ThrowTypeAsRecordType)<br>
+ return ThrowTypeAsRecordType->isDerivedFrom(CaughtAsRecordType);<br>
+ return false;<br>
+}<br>
+<br>
+static bool isThrowCaughtByHandlers(const CXXThrowExpr *CE,<br>
+ const CXXTryStmt *TryStmt) {<br>
+ for (unsigned H = 0, E = TryStmt->getNumHandlers(); H < E; ++H) {<br>
+ if (isThrowCaught(CE, TryStmt->getHandler(H)))<br>
+ return true;<br>
+ }<br>
+ return false;<br>
+}<br>
+<br>
+static bool doesThrowEscapePath(CFGBlock Block, SourceLocation &OpLoc) {<br>
+ for (const auto &B : Block) {<br>
+ if (B.getKind() != CFGElement::Statement)<br>
+ continue;<br>
+ const auto *CE = dyn_cast<CXXThrowExpr>(B.getAs<CFGStmt>()->getStmt());<br>
+ if (!CE)<br>
+ continue;<br>
+<br>
+ OpLoc = CE->getThrowLoc();<br>
+ for (const auto &I : Block.succs()) {<br>
+ if (!I.isReachable())<br>
+ continue;<br>
+ if (const auto *Terminator =<br>
+ dyn_cast_or_null<CXXTryStmt>(I->getTerminator()))<br>
+ if (isThrowCaughtByHandlers(CE, Terminator))<br>
+ return false;<br>
+ }<br>
+ return true;<br>
+ }<br>
+ return false;<br>
+}<br>
+<br>
+static bool hasThrowOutNonThrowingFunc(SourceLocation &OpLoc, CFG *BodyCFG) {<br>
+<br>
+ unsigned ExitID = BodyCFG->getExit().getBlockID();<br>
+<br>
+ SmallVector<ThrowState, 16> States(BodyCFG->getNumBlockIDs(),<br>
+ FoundNoPathForThrow);<br>
+ States[BodyCFG->getEntry().getBlockID()] = FoundPathWithNoThrowOutFunction;<br>
+<br>
+ SmallVector<CFGBlock *, 16> Stack;<br>
+ Stack.push_back(&BodyCFG->getEntry());<br>
+ while (!Stack.empty()) {<br>
+ CFGBlock *CurBlock = Stack.back();<br>
+ Stack.pop_back();<br>
+<br>
+ unsigned ID = CurBlock->getBlockID();<br>
+ ThrowState CurState = States[ID];<br>
+ if (CurState == FoundPathWithNoThrowOutFunction) {<br>
+ if (ExitID == ID)<br>
+ continue;<br>
+<br>
+ if (doesThrowEscapePath(*CurBlock, OpLoc))<br>
+ CurState = FoundPathForThrow;<br>
+ }<br>
+<br>
+ // Loop over successor blocks and add them to the Stack if their state<br>
+ // changes.<br>
+ for (const auto &I : CurBlock->succs())<br>
+ if (I.isReachable()) {<br>
+ unsigned NextID = I->getBlockID();<br>
+ if (NextID == ExitID && CurState == FoundPathForThrow) {<br>
+ States[NextID] = CurState;<br>
+ } else if (States[NextID] < CurState) {<br>
+ States[NextID] = CurState;<br>
+ Stack.push_back(I);<br>
+ }<br>
+ }<br>
+ }<br>
+ // Return true if the exit node is reachable, and only reachable through<br>
+ // a throw expression.<br>
+ return States[ExitID] == FoundPathForThrow;<br>
+}<br>
+<br>
+static void EmitDiagForCXXThrowInNonThrowingFunc(Sema &S, SourceLocation OpLoc,<br>
+ const FunctionDecl *FD) {<br>
+ if (!S.getSourceManager().isInSystemHeader(OpLoc)) {<br>
+ S.Diag(OpLoc, diag::warn_throw_in_noexcept_func) << FD;<br>
+ if (S.getLangOpts().CPlusPlus11 &&<br>
+ (isa<CXXDestructorDecl>(FD) ||<br>
+ FD->getDeclName().getCXXOverloadedOperator() == OO_Delete ||<br>
+ FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete))<br>
+ S.Diag(FD->getLocation(), diag::note_throw_in_dtor);<br>
+ else<br>
+ S.Diag(FD->getLocation(), diag::note_throw_in_function);<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
<div>
<p class="MsoNormal">Please point this diagnostic at the exception specification when its location can be computed (use FD->getExceptionSpecSourceRange()).<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<blockquote style="border:none;border-left:solid #CCCCCC 1.0pt;padding:0in 0in 0in 6.0pt;margin-left:4.8pt;margin-right:0in">
<p class="MsoNormal">+ }<br>
+}<br>
+<br>
+static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD,<br>
+ AnalysisDeclContext &AC) {<br>
+ CFG *BodyCFG = AC.getCFG();<br>
+ if (!BodyCFG)<br>
+ return;<br>
+ if (BodyCFG->getExit().pred_empty())<br>
+ return;<br>
+ SourceLocation OpLoc;<br>
+ if (hasThrowOutNonThrowingFunc(OpLoc, BodyCFG))<br>
+ EmitDiagForCXXThrowInNonThrowingFunc(S, OpLoc, FD);<br>
+}<br>
+<br>
+static bool isNoexcept(const FunctionDecl *FD) {<br>
+ const auto *FPT = FD->getType()->castAs<FunctionProtoType>();<br>
+ if (FPT->getExceptionSpecType() != EST_None &&<br>
+ FPT->isNothrow(FD->getASTContext()))<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
<div>
<p class="MsoNormal">Why the EST_None special case here? The isNothrow check would handle that just fine.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<blockquote style="border:none;border-left:solid #CCCCCC 1.0pt;padding:0in 0in 0in 6.0pt;margin-left:4.8pt;margin-right:0in">
<p class="MsoNormal">+ return true;<br>
+ return false;<br>
+}<br>
+<br>
+//===----------------------------------------------------------------------===//<br>
// Check for missing return value.<br>
//===----------------------------------------------------------------------===//<br>
<br>
@@ -2127,6 +2271,12 @@ AnalysisBasedWarnings::IssueWarnings(sem<br>
}<br>
}<br>
<br>
+ // Check for throw out of non-throwing function.<br>
+ if (!Diags.isIgnored(diag::warn_throw_in_noexcept_func, D->getLocStart()))<br>
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))<br>
+ if (S.getLangOpts().CPlusPlus && isNoexcept(FD))<br>
+ checkThrowInNonThrowingFunc(S, FD, AC);<br>
+<br>
// If none of the previous checks caused a CFG build, trigger one here<br>
// for -Wtautological-overlap-compare<br>
if (!Diags.isIgnored(diag::warn_tautological_overlap_comparison,<br>
<br>
Modified: cfe/trunk/test/CXX/except/except.spec/p11.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/except/except.spec/p11.cpp?rev=306149&r1=306148&r2=306149&view=diff" target="_blank">
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/except/except.spec/p11.cpp?rev=306149&r1=306148&r2=306149&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/except/except.spec/p11.cpp (original)<br>
+++ cfe/trunk/test/CXX/except/except.spec/p11.cpp Fri Jun 23 15:22:19 2017<br>
@@ -1,12 +1,11 @@<br>
// RUN: %clang_cc1 -std=c++11 -fexceptions -fcxx-exceptions -fsyntax-only -verify %s<br>
-// expected-no-diagnostics<br>
<br>
// This is the "let the user shoot themselves in the foot" clause.<br>
-void f() noexcept {<br>
- throw 0; // no-error<br>
+void f() noexcept { // expected-note {{non-throwing function declare here}}<br>
+ throw 0; // expected-warning {{has a non-throwing exception specification but}}<br>
}<br>
-void g() throw() {<br>
- throw 0; // no-error<br>
+void g() throw() { // expected-note {{non-throwing function declare here}}<br>
+ throw 0; // expected-warning {{has a non-throwing exception specification but}}<br>
}<br>
void h() throw(int) {<br>
throw 0.0; // no-error<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><o:p></o:p></p>
</blockquote>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
</div>
</div>
</body>
</html>