Skip to content

Commit

Permalink
[constexpr][c++2a] Try-catch blocks in constexpr functions
Browse files Browse the repository at this point in the history
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

llvm-svn: 348789
  • Loading branch information
bcardosolopes committed Dec 10, 2018
1 parent 5ec1460 commit 5c1399a
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 23 deletions.
17 changes: 17 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2357,6 +2357,13 @@ def warn_cxx11_compat_constexpr_body_invalid_stmt : Warning<
"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>;
Expand Down Expand Up @@ -2409,6 +2416,16 @@ def note_constexpr_body_previous_return : Note<
"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<
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4279,6 +4279,9 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
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);
}
}

Expand Down
63 changes: 49 additions & 14 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1803,7 +1803,7 @@ static void CheckConstexprCtorInitializer(Sema &SemaRef,
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:
Expand Down Expand Up @@ -1840,7 +1840,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
CompoundStmt *CompStmt = cast<CompoundStmt>(S);
for (auto *BodyIt : CompStmt->body()) {
if (!CheckConstexprFunctionStmt(SemaRef, Dcl, BodyIt, ReturnStmts,
Cxx1yLoc))
Cxx1yLoc, Cxx2aLoc))
return false;
}
return true;
Expand All @@ -1858,11 +1858,11 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,

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;
}
Expand All @@ -1881,7 +1881,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
for (Stmt *SubStmt : S->children())
if (SubStmt &&
!CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts,
Cxx1yLoc))
Cxx1yLoc, Cxx2aLoc))
return false;
return true;

Expand All @@ -1896,10 +1896,30 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
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;
Expand All @@ -1920,6 +1940,8 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
///
/// \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
Expand All @@ -1930,22 +1952,35 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) {
// 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
Expand Down
27 changes: 23 additions & 4 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down
13 changes: 10 additions & 3 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
8 changes: 7 additions & 1 deletion clang/test/CXX/drs/dr6xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -953,13 +953,15 @@ <h2 id="cxx20">C++2a implementation status</h2>
<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>
Expand Down

0 comments on commit 5c1399a

Please sign in to comment.