Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/pending_changes/gh-356-cpp11-constexpr-cpp14-body.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[GH #356]
C++11 constexpr templates with C++14-style statements

In GNU (12.0 and later) and Clang (3.3 and later) C++11 modes, a statement
that C++14 allows in a constexpr constructor or function of a template (an
"if" statement, for example) is accepted with a warning. The constructor or
function was nevertheless treated as not constexpr, so it could not be used
in constant expressions. For example, with --c++11 --gnu_version 150000:

template <class T> struct A {
T x;
constexpr A(T i) : x(i) {
if (sizeof(T) == 0) {} // Warning
}
};
constexpr A<int> a(5); // Previously an error.

This is now fixed for constexpr constructors and, in Clang mode (like
Clang), for constexpr functions. As with GCC, such a constexpr function
still cannot be used in constant expressions in GNU mode.
32 changes: 25 additions & 7 deletions src/statements.c
Original file line number Diff line number Diff line change
Expand Up @@ -7806,6 +7806,7 @@ this statement was preceded by the GNU keyword __extension__.
a_boolean prev_was_label = FALSE;
a_boolean get_another_statement;
a_boolean can_appear_in_constexpr_body = relaxed_constexpr_enabled;
a_boolean allowed_by_relaxed_constexpr = TRUE;
an_error_severity in_constexpr_body_sev = es_error;
a_source_position start_pos;
an_il_entity_list_entry_ptr
Expand Down Expand Up @@ -7891,13 +7892,15 @@ this statement was preceded by the GNU keyword __extension__.
case tok_for_each:
/* "for each" statement (ECMA-372 section 16.2.1). */
for_each_statement();
can_appear_in_constexpr_body = FALSE;
can_appear_in_constexpr_body = allowed_by_relaxed_constexpr = FALSE;
break;
#endif /* MICROSOFT_EXTENSIONS_ALLOWED */
case tok_goto:
/* Goto statement. */
goto_statement();
if (!cpp23_mode) can_appear_in_constexpr_body = FALSE;
if (!cpp23_mode) {
can_appear_in_constexpr_body = allowed_by_relaxed_constexpr = FALSE;
} /* if */
break;
case tok_continue:
/* Continue statement. */
Expand All @@ -7921,7 +7924,7 @@ this statement was preceded by the GNU keyword __extension__.
case tok_microsoft_asm:
/* Asm "declaration" or Microsoft mode asm block. */
asm_statement();
can_appear_in_constexpr_body = cpp20_mode;
can_appear_in_constexpr_body = allowed_by_relaxed_constexpr = cpp20_mode;
if (!can_appear_in_constexpr_body && cpp14_mode &&
gpp_version_is(>= 100000)) {
/* Newer versions of GCC only warn about asm declarations in constexpr
Expand All @@ -7933,18 +7936,19 @@ this statement was preceded by the GNU keyword __extension__.
/* C++ try block. */
try_block_statement((a_statement_ptr)NULL,
/*explicit_return_type=*/FALSE);
can_appear_in_constexpr_body = constexpr_try_enabled;
can_appear_in_constexpr_body = allowed_by_relaxed_constexpr =
constexpr_try_enabled;
break;
#if MICROSOFT_EXTENSIONS_ALLOWED
case tok_microsoft_try:
/* Microsoft try-finally or try-except statement. */
microsoft_try_statement();
can_appear_in_constexpr_body = FALSE;
can_appear_in_constexpr_body = allowed_by_relaxed_constexpr = FALSE;
break;
case tok_leave:
/* Microsoft __leave. */
leave_statement();
can_appear_in_constexpr_body = FALSE;
can_appear_in_constexpr_body = allowed_by_relaxed_constexpr = FALSE;
break;
#endif /* MICROSOFT_EXTENSIONS_ALLOWED */
case tok_case:
Expand Down Expand Up @@ -8091,6 +8095,9 @@ this statement was preceded by the GNU keyword __extension__.
sssep->prefix_attributes = NULL;
} /* if */
decl_statement(marked_as_gnu_extension, &can_appear_in_constexpr_body);
/* Declarations are not checked against the C++14 rules here, so do
not assume that they would be valid in C++14. */
allowed_by_relaxed_constexpr = can_appear_in_constexpr_body;
if (!can_appear_in_constexpr_body && relaxed_constexpr_allowed()) {
/* This can be true in Clang-C++11-mode system headers. */
in_constexpr_body_sev = es_warning;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -8144,6 +8151,7 @@ this statement was preceded by the GNU keyword __extension__.
} /* switch */
if (current_rp != NULL && current_rp->is_constexpr &&
!can_appear_in_constexpr_body) {
a_boolean constexpr_still_possible = FALSE;
if (current_rp->is_declared_constexpr || current_rp->is_consteval) {
/* Report an error if the statement is not one that is allowed in a
constexpr function or constexpr constructor. */
Expand All @@ -8156,14 +8164,24 @@ this statement was preceded by the GNU keyword __extension__.
} else if ((gpp_version_is(>=120000) || clangcpp_version_is(>=30300)) &&
current_rp->is_template_function) {
in_constexpr_body_sev = es_warning;
/* GCC and Clang accept a statement that C++14 allows in a constexpr
constructor as an extension, and the constructor can still be used
in constant expressions. Clang does the same for a constexpr
function; GCC does not. */
constexpr_still_possible =
allowed_by_relaxed_constexpr &&
(clangcpp_version_is(>=30300) ||
special_kind_is(current_rp, sfk_constructor));
} /* if */
pos_diagnostic(in_constexpr_body_sev,
special_kind_is(current_rp, sfk_constructor) ?
ec_invalid_statement_in_constexpr_constructor :
ec_invalid_statement_in_constexpr_function,
&start_pos);
} /* if */
scope_stack[depth_innermost_function_scope].constexpr_ruled_out = TRUE;
if (!constexpr_still_possible) {
scope_stack[depth_innermost_function_scope].constexpr_ruled_out = TRUE;
} /* if */
} /* if */
/* Loop if we just got a label and not an actual statement. */
if (get_another_statement) {
Expand Down
48 changes: 48 additions & 0 deletions tests/tests/changes/.GH356.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
fe_only -DTEST_NUMBER=1 --c++11 --gnu_version 150000 Test_name.c
"Test_name.c", line 15: warning: statement may not appear in a constexpr constructor
if (sizeof(T) == 0) {}
^

"Test_name.c", line 24: warning: statement may not appear in a constexpr constructor
constexpr B(T i) : x(i) { return; }
^

"Test_name.c", line 31: warning: statement may not appear in a constexpr function
if (t > 0) {}
^

"Test_name.c", line 32: warning: statement may not appear in a constexpr function
switch (sizeof(T)) { default: break; }
^

"Test_name.c", line 32: warning: statement may not appear in a constexpr function
switch (sizeof(T)) { default: break; }
^

"Test_name.c", line 32: warning: statement may not appear in a constexpr function
switch (sizeof(T)) { default: break; }
^

"Test_name.c", line 35: error: expression must have a constant value
static_assert(f(1) == 1, ""); // Error in GNU mode only.
^
"Test_name.c", line 35: note: this call cannot be evaluated because the target function function "f(T) [with T=int]" (declared at line 30) is not constexpr or not completely defined yet
static_assert(f(1) == 1, ""); // Error in GNU mode only.
^

"Test_name.c", line 39: warning: statement may not appear in a constexpr function
goto done;
^

"Test_name.c", line 40: warning: statement may not appear in a constexpr function
done:
^

"Test_name.c", line 43: error: expression must have a constant value
static_assert(g(1) == 1, ""); // Error: g is not usable here.
^
"Test_name.c", line 43: note: this call cannot be evaluated because the target function function "g(T) [with T=int]" (declared at line 38) is not constexpr or not completely defined yet
static_assert(g(1) == 1, ""); // Error: g is not usable here.
^

2 errors detected in the compilation of "Test_name.c".
41 changes: 41 additions & 0 deletions tests/tests/changes/.GH356.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
fe_only -DTEST_NUMBER=2 --c++11 --clang_version 190000 Test_name.c
"Test_name.c", line 15: warning: statement may not appear in a constexpr constructor
if (sizeof(T) == 0) {}
^

"Test_name.c", line 24: warning: statement may not appear in a constexpr constructor
constexpr B(T i) : x(i) { return; }
^

"Test_name.c", line 31: warning: statement may not appear in a constexpr function
if (t > 0) {}
^

"Test_name.c", line 32: warning: statement may not appear in a constexpr function
switch (sizeof(T)) { default: break; }
^

"Test_name.c", line 32: warning: statement may not appear in a constexpr function
switch (sizeof(T)) { default: break; }
^

"Test_name.c", line 32: warning: statement may not appear in a constexpr function
switch (sizeof(T)) { default: break; }
^

"Test_name.c", line 39: warning: statement may not appear in a constexpr function
goto done;
^

"Test_name.c", line 40: warning: statement may not appear in a constexpr function
done:
^

"Test_name.c", line 43: error: expression must have a constant value
static_assert(g(1) == 1, ""); // Error: g is not usable here.
^
"Test_name.c", line 43: note: this call cannot be evaluated because the target function function "g(T) [with T=int]" (declared at line 38) is not constexpr or not completely defined yet
static_assert(g(1) == 1, ""); // Error: g is not usable here.
^

1 error detected in the compilation of "Test_name.c".
43 changes: 43 additions & 0 deletions tests/tests/changes/GH356.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//type:fn
//options:--c++11 --gnu_version 150000:--c++11 --clang_version 190000
//remark:[GH #356] C++11 constexpr template with a C++14-style body
//
// In GNU and Clang C++11 modes, a statement that C++14 allows in a constexpr
// constructor of a template only gets a warning, and the constructor can
// still be used in constant expressions. For a constexpr function template,
// that is only the case in Clang mode (GCC rejects the call). A goto, which
// C++14 does not allow either, makes the function unusable in both modes.

template <class T>
struct A {
T x;
constexpr A(T i) : x(i) {
if (sizeof(T) == 0) {}
}
};
constexpr A<int> a(5);
static_assert(a.x == 5, "");

template <class T>
struct B {
T x;
constexpr B(T i) : x(i) { return; }
};
constexpr B<int> b(6);
static_assert(b.x == 6, "");

template <class T>
constexpr int f(T t) {
if (t > 0) {}
switch (sizeof(T)) { default: break; }
return 1;
}
static_assert(f(1) == 1, ""); // Error in GNU mode only.

template <class T>
constexpr int g(T t) {
goto done;
done:
return 1;
}
static_assert(g(1) == 1, ""); // Error: g is not usable here.
Loading