bruno created this revision. bruno added reviewers: rsmith, ahatanak, erik.pilkington. Herald added subscribers: dexonsmith, jkorous.
Implement support for try-catch blocks in constexpr functions, as proposed in http://wg21.link/P1002 and voted in San Diego for c++20. The idea is that we can still never throw inside constexpr, so the catch block is never entered. A try-catch block like this: try { f(); } catch (...) { } is then morally equivalent to just { f(); } Same idea should apply for function/constructor try blocks. rdar://problem/45530773 https://reviews.llvm.org/D55097 Files: lib/AST/ExprConstant.cpp lib/Sema/SemaDeclCXX.cpp test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp test/CXX/drs/dr6xx.cpp www/cxx_status.html
Index: www/cxx_status.html =================================================================== --- www/cxx_status.html +++ www/cxx_status.html @@ -953,13 +953,15 @@ <tr> <td rowspan=4>Relaxations of <tt>constexpr</tt> restrictions</td> <td><a href="http://wg21.link/p1064r0">P1064R0</a></td> - <td rowspan=4 class="none" align="center">No</td> + <td class="none" align="center">No</td> </tr> <tr> <!-- from San Diego --> <td><a href="http://wg21.link/p1002r1">P1002R1</a></td> + <td class="full" align="center">Clang 8</td> </tr> <tr> <td><a href="http://wg21.link/p1327r1">P1327R1</a></td> + <td rowspan=2 class="none" align="center">No</td> </tr> <tr> <td><a href="http://wg21.link/p1330r0">P1330R0</a></td> Index: test/CXX/drs/dr6xx.cpp =================================================================== --- test/CXX/drs/dr6xx.cpp +++ test/CXX/drs/dr6xx.cpp @@ -492,7 +492,10 @@ struct C { constexpr C(NonLiteral); constexpr C(NonLiteral, int) {} // expected-error {{not a literal type}} - constexpr C() try {} catch (...) {} // expected-error {{function try block}} + constexpr C() try {} catch (...) {} +#if __cplusplus <= 201703L + // expected-error@-2 {{function try block}} +#endif }; struct D { Index: test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp =================================================================== --- test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp +++ test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -verify -std=c++11 -fcxx-exceptions -Werror=c++1y-extensions %s // RUN: %clang_cc1 -verify -std=c++1y -fcxx-exceptions -DCXX1Y %s +// RUN: %clang_cc1 -verify -std=c++2a -fcxx-exceptions -DCXX1Y -DCXX2A %s namespace N { typedef char C; @@ -49,7 +50,10 @@ // - its function-body shall not be a function-try-block; struct U { constexpr U() - try // expected-error {{function try block not allowed in constexpr constructor}} + try +#ifndef CXX2A + // expected-error@-2 {{function try block not allowed in constexpr constructor}} +#endif : u() { } catch (...) { throw; Index: test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp =================================================================== --- test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp +++ test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++11 -Werror=c++1y-extensions %s // RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++1y -DCXX1Y %s +// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++2a -DCXX1Y -DCXX2A %s namespace N { typedef char C; @@ -78,7 +79,12 @@ }; struct T3 { constexpr T3 &operator=(const T3&) const = default; - // expected-error@-1 {{an explicitly-defaulted copy assignment operator may not have 'const' or 'volatile' qualifiers}} +#ifndef CXX2A + // expected-error@-2 {{an explicitly-defaulted copy assignment operator may not have 'const' or 'volatile' qualifiers}} +#else + // expected-warning@-4 {{explicitly defaulted copy assignment operator is implicitly deleted}} + // expected-note@-5 {{function is implicitly deleted because its declared type does not match the type of an implicit copy assignment operator}} +#endif }; #endif struct U { @@ -131,7 +137,10 @@ } constexpr int DisallowedStmtsCXX1Y_3() { // - a try-block, - try {} catch (...) {} // expected-error {{statement not allowed in constexpr function}} + try {} catch (...) {} +#ifndef CXX2A + // expected-error@-2 {{statement not allowed in constexpr function}} +#endif return 0; } constexpr int DisallowedStmtsCXX1Y_4() { Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -1900,6 +1900,27 @@ return false; return true; + case Stmt::CXXTryStmtClass: + if (!SemaRef.getLangOpts().CPlusPlus2a) + break; + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getBeginLoc(); + for (Stmt *SubStmt : S->children()) + if (SubStmt && + !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts, + Cxx1yLoc)) + return false; + return true; + + case Stmt::CXXCatchStmtClass: + if (!SemaRef.getLangOpts().CPlusPlus2a) + break; + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getBeginLoc(); + // In case we got a valid constexpr try block, the catch block can be + // ignored since it will never be evaluated in a constexpr context. + return true; + default: if (!isa<Expr>(S)) break; @@ -1920,6 +1941,8 @@ /// /// \return true if the body is OK, false if we have diagnosed a problem. bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { + SmallVector<SourceLocation, 4> ReturnStmts; + if (isa<CXXTryStmt>(Body)) { // C++11 [dcl.constexpr]p3: // The definition of a constexpr function shall satisfy the following @@ -1930,12 +1953,25 @@ // C++11 [dcl.constexpr]p4: // In the definition of a constexpr constructor, [...] // - its function-body shall not be a function-try-block; - Diag(Body->getBeginLoc(), diag::err_constexpr_function_try_block) - << isa<CXXConstructorDecl>(Dcl); - return false; - } + if (!getLangOpts().CPlusPlus2a) { + Diag(Body->getBeginLoc(), diag::err_constexpr_function_try_block) + << isa<CXXConstructorDecl>(Dcl); + return false; + } - SmallVector<SourceLocation, 4> ReturnStmts; + // constexpr function try blocks are allowed in c++2a, assuming that the + // inner statements do also apply to general constexpr rules. + SourceLocation Cxx1yLoc; + for (Stmt *SubStmt : Body->children()) + if (SubStmt && + !CheckConstexprFunctionStmt(*this, Dcl, SubStmt, ReturnStmts, + Cxx1yLoc)) + return false; + if (Cxx1yLoc.isValid()) + Diag(Cxx1yLoc, diag::warn_cxx11_compat_constexpr_body_invalid_stmt) + << isa<CXXConstructorDecl>(Dcl); + return true; + } // - its function-body shall be [...] a compound-statement that contains only // [... list of cases ...] Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -4274,6 +4274,23 @@ case Stmt::CaseStmtClass: case Stmt::DefaultStmtClass: return EvaluateStmt(Result, Info, cast<SwitchCase>(S)->getSubStmt(), Case); + case Stmt::CXXTryStmtClass: { + // Evaluate try blocks by evaluating all sub statements and keep track + // whether there's a return. + EvalStmtResult ESR = ESR_Succeeded; + for (const Stmt *SubStmt : S->children()) { + EvalStmtResult SubStmtESR = EvaluateStmt(Result, Info, SubStmt, Case); + if (SubStmtESR != ESR_Succeeded && SubStmtESR != ESR_Returned) + return ESR_Failed; + if (SubStmtESR == ESR_Returned) + ESR = SubStmtESR; + } + return ESR; + } + case Stmt::CXXCatchStmtClass: + // No need to evaluate catch since it will be ignored in case the try block + // is successfully evaluated. + return ESR_Succeeded; } }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits