Author: bruno Date: Mon Dec 10 11:03:12 2018 New Revision: 348789 URL: http://llvm.org/viewvc/llvm-project?rev=348789&view=rev Log: [constexpr][c++2a] Try-catch blocks in constexpr functions
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 Differential Revision: https://reviews.llvm.org/D55097 Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/lib/AST/ExprConstant.cpp cfe/trunk/lib/Sema/SemaDeclCXX.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp cfe/trunk/test/CXX/drs/dr6xx.cpp cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=348789&r1=348788&r2=348789&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Dec 10 11:03:12 2018 @@ -2357,6 +2357,13 @@ def warn_cxx11_compat_constexpr_body_inv "use of this statement in a constexpr %select{function|constructor}0 " "is incompatible with C++ standards before C++14">, InGroup<CXXPre14Compat>, DefaultIgnore; +def ext_constexpr_body_invalid_stmt_cxx2a : ExtWarn< + "use of this statement in a constexpr %select{function|constructor}0 " + "is a C++2a extension">, InGroup<CXX2a>; +def warn_cxx17_compat_constexpr_body_invalid_stmt : Warning< + "use of this statement in a constexpr %select{function|constructor}0 " + "is incompatible with C++ standards before C++2a">, + InGroup<CXXPre2aCompat>, DefaultIgnore; def ext_constexpr_type_definition : ExtWarn< "type definition in a constexpr %select{function|constructor}0 " "is a C++14 extension">, InGroup<CXX14>; @@ -2409,6 +2416,16 @@ def note_constexpr_body_previous_return "previous return statement is here">; def err_constexpr_function_try_block : Error< "function try block not allowed in constexpr %select{function|constructor}0">; + +// c++2a function try blocks in constexpr +def ext_constexpr_function_try_block_cxx2a : ExtWarn< + "function try block in constexpr %select{function|constructor}0 is " + "a C++2a extension">, InGroup<CXX2a>; +def warn_cxx17_compat_constexpr_function_try_block : Warning< + "function try block in constexpr %select{function|constructor}0 is " + "incompatible with C++ standards before C++2a">, + InGroup<CXXPre2aCompat>, DefaultIgnore; + def err_constexpr_union_ctor_no_init : Error< "constexpr union constructor does not initialize any member">; def err_constexpr_ctor_missing_init : Error< Modified: cfe/trunk/lib/AST/ExprConstant.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=348789&r1=348788&r2=348789&view=diff ============================================================================== --- cfe/trunk/lib/AST/ExprConstant.cpp (original) +++ cfe/trunk/lib/AST/ExprConstant.cpp Mon Dec 10 11:03:12 2018 @@ -4279,6 +4279,9 @@ static EvalStmtResult EvaluateStmt(StmtR 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. + return EvaluateStmt(Result, Info, cast<CXXTryStmt>(S)->getTryBlock(), Case); } } Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=348789&r1=348788&r2=348789&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon Dec 10 11:03:12 2018 @@ -1803,7 +1803,7 @@ static void CheckConstexprCtorInitialize static bool CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, SmallVectorImpl<SourceLocation> &ReturnStmts, - SourceLocation &Cxx1yLoc) { + SourceLocation &Cxx1yLoc, SourceLocation &Cxx2aLoc) { // - its function-body shall be [...] a compound-statement that contains only switch (S->getStmtClass()) { case Stmt::NullStmtClass: @@ -1840,7 +1840,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef CompoundStmt *CompStmt = cast<CompoundStmt>(S); for (auto *BodyIt : CompStmt->body()) { if (!CheckConstexprFunctionStmt(SemaRef, Dcl, BodyIt, ReturnStmts, - Cxx1yLoc)) + Cxx1yLoc, Cxx2aLoc)) return false; } return true; @@ -1858,11 +1858,11 @@ CheckConstexprFunctionStmt(Sema &SemaRef IfStmt *If = cast<IfStmt>(S); if (!CheckConstexprFunctionStmt(SemaRef, Dcl, If->getThen(), ReturnStmts, - Cxx1yLoc)) + Cxx1yLoc, Cxx2aLoc)) return false; if (If->getElse() && !CheckConstexprFunctionStmt(SemaRef, Dcl, If->getElse(), ReturnStmts, - Cxx1yLoc)) + Cxx1yLoc, Cxx2aLoc)) return false; return true; } @@ -1881,7 +1881,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef for (Stmt *SubStmt : S->children()) if (SubStmt && !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts, - Cxx1yLoc)) + Cxx1yLoc, Cxx2aLoc)) return false; return true; @@ -1896,10 +1896,30 @@ CheckConstexprFunctionStmt(Sema &SemaRef for (Stmt *SubStmt : S->children()) if (SubStmt && !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts, - Cxx1yLoc)) + Cxx1yLoc, Cxx2aLoc)) return false; return true; + case Stmt::CXXTryStmtClass: + if (Cxx2aLoc.isInvalid()) + Cxx2aLoc = S->getBeginLoc(); + for (Stmt *SubStmt : S->children()) { + if (SubStmt && + !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts, + Cxx1yLoc, Cxx2aLoc)) + return false; + } + return true; + + case Stmt::CXXCatchStmtClass: + // Do not bother checking the language mode (already covered by the + // try block check). + if (!CheckConstexprFunctionStmt(SemaRef, Dcl, + cast<CXXCatchStmt>(S)->getHandlerBlock(), + ReturnStmts, Cxx1yLoc, Cxx2aLoc)) + return false; + return true; + default: if (!isa<Expr>(S)) break; @@ -1920,6 +1940,8 @@ CheckConstexprFunctionStmt(Sema &SemaRef /// /// \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,22 +1952,35 @@ bool Sema::CheckConstexprFunctionBody(co // 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) + // + // This restriction is lifted in C++2a, as long as inner statements also + // apply the general constexpr rules. + Diag(Body->getBeginLoc(), + !getLangOpts().CPlusPlus2a + ? diag::ext_constexpr_function_try_block_cxx2a + : diag::warn_cxx17_compat_constexpr_function_try_block) << isa<CXXConstructorDecl>(Dcl); - return false; } - SmallVector<SourceLocation, 4> ReturnStmts; - // - its function-body shall be [...] a compound-statement that contains only // [... list of cases ...] - CompoundStmt *CompBody = cast<CompoundStmt>(Body); - SourceLocation Cxx1yLoc; - for (auto *BodyIt : CompBody->body()) { - if (!CheckConstexprFunctionStmt(*this, Dcl, BodyIt, ReturnStmts, Cxx1yLoc)) + // + // Note that walking the children here is enough to properly check for + // CompoundStmt and CXXTryStmt body. + SourceLocation Cxx1yLoc, Cxx2aLoc; + for (Stmt *SubStmt : Body->children()) { + if (SubStmt && + !CheckConstexprFunctionStmt(*this, Dcl, SubStmt, ReturnStmts, + Cxx1yLoc, Cxx2aLoc)) return false; } + if (Cxx2aLoc.isValid()) + Diag(Cxx2aLoc, + getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_constexpr_body_invalid_stmt + : diag::ext_constexpr_body_invalid_stmt_cxx2a) + << isa<CXXConstructorDecl>(Dcl); if (Cxx1yLoc.isValid()) Diag(Cxx1yLoc, getLangOpts().CPlusPlus14 Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp?rev=348789&r1=348788&r2=348789&view=diff ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp (original) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp Mon Dec 10 11:03:12 2018 @@ -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++11 -Werror=c++1y-extensions -Werror=c++2a-extensions %s +// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++1y -DCXX1Y -Werror=c++2a-extensions %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 T2 { }; 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 { @@ -129,9 +135,22 @@ constexpr int DisallowedStmtsCXX1Y_2() { x: return 0; } +constexpr int DisallowedStmtsCXX1Y_2_1() { + try { + return 0; + } catch (...) { + merp: goto merp; // expected-error {{statement not allowed in constexpr function}} + } +} constexpr int DisallowedStmtsCXX1Y_3() { // - a try-block, - try {} catch (...) {} // expected-error {{statement not allowed in constexpr function}} + try {} catch (...) {} +#ifndef CXX2A + // expected-error@-2 {{use of this statement in a constexpr function is a C++2a extension}} +#ifndef CXX1Y + // expected-error@-4 {{use of this statement in a constexpr function is a C++14 extension}} +#endif +#endif return 0; } constexpr int DisallowedStmtsCXX1Y_4() { Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp?rev=348789&r1=348788&r2=348789&view=diff ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp (original) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp Mon Dec 10 11:03:12 2018 @@ -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++11 -fcxx-exceptions -Werror=c++1y-extensions -Werror=c++2a-extensions %s +// RUN: %clang_cc1 -verify -std=c++1y -fcxx-exceptions -DCXX1Y -Werror=c++2a-extensions %s +// RUN: %clang_cc1 -verify -std=c++2a -fcxx-exceptions -DCXX1Y -DCXX2A %s namespace N { typedef char C; @@ -49,8 +50,14 @@ namespace IndirectVBase { // - 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 in constexpr constructor is a C++2a extension}} +#endif : u() { +#ifndef CXX1Y + // expected-error@-2 {{use of this statement in a constexpr constructor is a C++14 extension}} +#endif } catch (...) { throw; } Modified: cfe/trunk/test/CXX/drs/dr6xx.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr6xx.cpp?rev=348789&r1=348788&r2=348789&view=diff ============================================================================== --- cfe/trunk/test/CXX/drs/dr6xx.cpp (original) +++ cfe/trunk/test/CXX/drs/dr6xx.cpp Mon Dec 10 11:03:12 2018 @@ -492,7 +492,13 @@ namespace dr647 { // dr647: yes 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 in constexpr constructor is a C++2a extension}} +#endif +#if __cplusplus < 201402L + // expected-error@-5 {{use of this statement in a constexpr constructor is a C++14 extension}} +#endif }; struct D { Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=348789&r1=348788&r2=348789&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Mon Dec 10 11:03:12 2018 @@ -953,13 +953,15 @@ as the draft C++2a standard evolves. <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">SVN</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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits