[v3] c++: P2448 - Relaxing some constexpr restrictions [PR106649]
Checks
Commit Message
On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote:
> On 11/15/22 19:30, Marek Polacek wrote:
> > @@ -996,19 +1040,26 @@ register_constexpr_fundef (const constexpr_fundef &value)
> > **slot = value;
> > }
> > -/* FUN is a non-constexpr function called in a context that requires a
> > - constant expression. If it comes from a constexpr template, explain why
> > - the instantiation isn't constexpr. */
> > +/* FUN is a non-constexpr (or, with -Wno-invalid-constexpr, a constexpr
> > + function called in a context that requires a constant expression).
> > + If it comes from a constexpr template, explain why the instantiation
> > + isn't constexpr. */
>
> The "if it comes from a constexpr template" wording has needed an update for
> a while now.
Probably ever since r178519. I've added "Otherwise, explain why the function
cannot be used in a constexpr context." Is that acceptable?
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
> > @@ -0,0 +1,43 @@
> > +// PR c++/106649
> > +// P2448 - Relaxing some constexpr restrictions
> > +// { dg-do compile { target c++23 } }
> > +// { dg-options "-Winvalid-constexpr" }
> > +// A copy/move assignment operator for a class X that is defaulted and
> > +// not defined as deleted is implicitly defined when it is odr-used,
> > +// when it is needed for constant evaluation, or when it is explicitly
> > +// defaulted after its first declaration.
> > +// The implicitly-defined copy/move assignment operator is constexpr.
> > +
> > +struct S {
> > + constexpr S() {}
> > + S& operator=(const S&) = default; // #1
> > + S& operator=(S&&) = default; // #2
> > +};
> > +
> > +struct U {
> > + constexpr U& operator=(const U&) = default;
> > + constexpr U& operator=(U&&) = default;
> > +};
> > +
> > +/* FIXME: If we only declare #1 and #2, and default them here:
> > +
> > + S& S::operator=(const S&) = default;
> > + S& S::operator=(S&&) = default;
> > +
> > +then they aren't constexpr. This sounds like a bug:
> > +<https://gcc.gnu.org/PR107598>. */
>
> As I commented on the PR, I don't think this is actually a bug, so let's
> omit this FIXME.
I'm glad I didn't really attempt to "fix" it (the inform message is flawed
and should be improved). Thanks for taking a look.
Here's a version with the two comments updated.
Ok?
-- >8 --
This patch implements C++23 P2448, which lifts more restrictions on the
constexpr keyword. It's effectively going the way of being just a hint
(hello, inline!).
This gist is relatively simple: in C++23, a constexpr function's return
type/parameter type doesn't have to be a literal type; and you can have
a constexpr function for which no invocation satisfies the requirements
of a core constant expression. For example,
void f(int& i); // not constexpr
constexpr void g(int& i) {
f(i); // unconditionally calls a non-constexpr function
}
is now OK, even though there isn't an invocation of 'g' that would be
a constant expression. Maybe 'f' will be made constexpr soon, or maybe
this depends on the version of C++ used, and similar. The patch is
unfortunately not that trivial. The important bit is to use the new
require_potential_rvalue_constant_expression_fncheck in
maybe_save_constexpr_fundef (and where appropriate). It has a new flag
that says that we're checking the body of a constexpr function, and in
that case it's OK to find constructs that aren't a constant expression.
Since it's useful to be able to check for problematic constructs even
in C++23, this patch implements a new warning, -Winvalid-constexpr,
which is a pedwarn turned on by default in C++20 and earlier, and which
can be turned on in C++23 as well, in which case it's an ordinary warning.
This I implemented by using the new function constexpr_error, used in
p_c_e_1 and friends. (In some cases I believe fundef_p will be always
false (= hard error), but it made sense to me to be consistent and use
constexpr_error throughout p_c_e_1.)
While working on this I think I found a bug, see constexpr-nonlit15.C
and <https://gcc.gnu.org/PR107598>. This patch doesn't address that.
This patch includes changes to diagnose the problem if the user doesn't
use -Winvalid-constexpr and calls a constexpr function that in fact isn't
constexpr-ready yet: maybe_save_constexpr_fundef registers the function
if warn_invalid_constexpr is 0 and explain_invalid_constexpr_fn then
gives the diagnostic.
PR c++/106649
gcc/c-family/ChangeLog:
* c-cppbuiltin.cc (c_cpp_builtins): Update value of __cpp_constexpr for
C++23.
* c-opts.cc (c_common_post_options): Set warn_invalid_constexpr
depending on cxx_dialect.
* c.opt (Winvalid-constexpr): New option.
gcc/cp/ChangeLog:
* constexpr.cc (constexpr_error): New function.
(is_valid_constexpr_fn): Use constexpr_error.
(maybe_save_constexpr_fundef): Call
require_potential_rvalue_constant_expression_fncheck rather than
require_potential_rvalue_constant_expression. Register the
function if -Wno-invalid-constexpr was specified.
(explain_invalid_constexpr_fn): Don't return early if a function marked
'constexpr' that isn't actually a constant expression was called.
(non_const_var_error): Add a bool parameter. Use constexpr_error.
(inline_asm_in_constexpr_error): Likewise.
(cxx_eval_constant_expression): Adjust calls to non_const_var_error
and inline_asm_in_constexpr_error.
(potential_constant_expression_1): Add a bool parameter. Use
constexpr_error.
(require_potential_rvalue_constant_expression_fncheck): New function.
* cp-tree.h (require_potential_rvalue_constant_expression_fncheck):
Declare.
* method.cc (struct comp_info): Call
require_potential_rvalue_constant_expression_fncheck rather than
require_potential_rvalue_constant_expression.
gcc/ChangeLog:
* doc/invoke.texi: Document -Winvalid-constexpr.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/constexpr-ctor2.C: Expect an error in c++20_down only.
* g++.dg/cpp0x/constexpr-default-ctor.C: Likewise.
* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
* g++.dg/cpp0x/constexpr-ex1.C: Likewise.
* g++.dg/cpp0x/constexpr-friend.C: Likewise.
* g++.dg/cpp0x/constexpr-generated1.C: Likewise.
* g++.dg/cpp0x/constexpr-ice5.C: Likewise.
* g++.dg/cpp0x/constexpr-ice6.C: Likewise.
* g++.dg/cpp0x/constexpr-memfn1.C: Likewise.
* g++.dg/cpp0x/constexpr-neg2.C: Likewise.
* g++.dg/cpp0x/constexpr-non-const-arg.C: Likewise.
* g++.dg/cpp0x/constexpr-reinterpret1.C: Likewise.
* g++.dg/cpp0x/pr65327.C: Likewise.
* g++.dg/cpp1y/constexpr-105050.C: Likewise.
* g++.dg/cpp1y/constexpr-89285-2.C: Likewise.
* g++.dg/cpp1y/constexpr-89285.C: Likewise.
* g++.dg/cpp1y/constexpr-89785-2.C: Likewise.
* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
* g++.dg/cpp1y/constexpr-nsdmi7b.C: Likewise.
* g++.dg/cpp1y/constexpr-throw.C: Likewise.
* g++.dg/cpp23/constexpr-nonlit3.C: Remove dg-error.
* g++.dg/cpp23/constexpr-nonlit6.C: Call the test functions.
* g++.dg/cpp23/feat-cxx2b.C: Adjust the expected value of
__cpp_constexpr.
* g++.dg/cpp2a/consteval3.C: Remove dg-error.
* g++.dg/cpp2a/constexpr-new7.C: Expect an error in c++20_down only.
* g++.dg/cpp2a/constexpr-try5.C: Remove dg-error.
* g++.dg/cpp2a/spaceship-constexpr1.C: Expect an error in c++20_down
only.
* g++.dg/cpp2a/spaceship-eq3.C: Likewise.
* g++.dg/diagnostic/constexpr1.C: Remove dg-error.
* g++.dg/gomp/pr79664.C: Use -Winvalid-constexpr -pedantic-errors.
* g++.dg/ubsan/vptr-4.C: Likewise.
* g++.dg/cpp23/constexpr-nonlit10.C: New test.
* g++.dg/cpp23/constexpr-nonlit11.C: New test.
* g++.dg/cpp23/constexpr-nonlit12.C: New test.
* g++.dg/cpp23/constexpr-nonlit13.C: New test.
* g++.dg/cpp23/constexpr-nonlit14.C: New test.
* g++.dg/cpp23/constexpr-nonlit15.C: New test.
* g++.dg/cpp23/constexpr-nonlit16.C: New test.
* g++.dg/cpp23/constexpr-nonlit8.C: New test.
* g++.dg/cpp23/constexpr-nonlit9.C: New test.
---
gcc/c-family/c-cppbuiltin.cc | 2 +-
gcc/c-family/c-opts.cc | 4 +
gcc/c-family/c.opt | 4 +
gcc/cp/constexpr.cc | 299 ++++++++++++------
gcc/cp/cp-tree.h | 1 +
gcc/cp/method.cc | 8 +-
gcc/doc/invoke.texi | 28 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C | 2 +-
.../g++.dg/cpp0x/constexpr-default-ctor.C | 6 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 3 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 2 +-
.../g++.dg/cpp0x/constexpr-generated1.C | 2 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C | 2 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C | 4 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C | 4 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C | 4 +-
.../g++.dg/cpp0x/constexpr-non-const-arg.C | 2 +-
.../g++.dg/cpp0x/constexpr-reinterpret1.C | 2 +-
gcc/testsuite/g++.dg/cpp0x/pr65327.C | 2 +-
gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C | 2 +-
.../g++.dg/cpp1y/constexpr-89285-2.C | 2 +-
gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C | 2 +-
.../g++.dg/cpp1y/constexpr-89785-2.C | 4 +-
gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C | 2 +-
.../g++.dg/cpp1y/constexpr-nsdmi7b.C | 2 +-
gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C | 6 +-
.../g++.dg/cpp23/constexpr-nonlit10.C | 96 ++++++
.../g++.dg/cpp23/constexpr-nonlit11.C | 53 ++++
.../g++.dg/cpp23/constexpr-nonlit12.C | 24 ++
.../g++.dg/cpp23/constexpr-nonlit13.C | 14 +
.../g++.dg/cpp23/constexpr-nonlit14.C | 26 ++
.../g++.dg/cpp23/constexpr-nonlit15.C | 35 ++
.../g++.dg/cpp23/constexpr-nonlit16.C | 23 ++
.../g++.dg/cpp23/constexpr-nonlit3.C | 2 +-
.../g++.dg/cpp23/constexpr-nonlit6.C | 13 +-
.../g++.dg/cpp23/constexpr-nonlit8.C | 96 ++++++
.../g++.dg/cpp23/constexpr-nonlit9.C | 53 ++++
gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +-
gcc/testsuite/g++.dg/cpp2a/consteval3.C | 1 -
gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 4 +-
gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C | 2 -
.../g++.dg/cpp2a/spaceship-constexpr1.C | 2 +-
gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 4 +-
gcc/testsuite/g++.dg/diagnostic/constexpr1.C | 2 -
gcc/testsuite/g++.dg/gomp/pr79664.C | 2 +-
gcc/testsuite/g++.dg/ubsan/vptr-4.C | 2 +-
47 files changed, 715 insertions(+), 146 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C
base-commit: 7026d0455dce1092975d4884f450a12a6ed205c7
Comments
On 11/16/22 11:06, Marek Polacek wrote:
> On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote:
>> On 11/15/22 19:30, Marek Polacek wrote:
>>> @@ -996,19 +1040,26 @@ register_constexpr_fundef (const constexpr_fundef &value)
>>> **slot = value;
>>> }
>>> -/* FUN is a non-constexpr function called in a context that requires a
>>> - constant expression. If it comes from a constexpr template, explain why
>>> - the instantiation isn't constexpr. */
>>> +/* FUN is a non-constexpr (or, with -Wno-invalid-constexpr, a constexpr
>>> + function called in a context that requires a constant expression).
>>> + If it comes from a constexpr template, explain why the instantiation
>>> + isn't constexpr. */
>>
>> The "if it comes from a constexpr template" wording has needed an update for
>> a while now.
>
> Probably ever since r178519. I've added "Otherwise, explain why the function
> cannot be used in a constexpr context." Is that acceptable?
>
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
>>> @@ -0,0 +1,43 @@
>>> +// PR c++/106649
>>> +// P2448 - Relaxing some constexpr restrictions
>>> +// { dg-do compile { target c++23 } }
>>> +// { dg-options "-Winvalid-constexpr" }
>>> +// A copy/move assignment operator for a class X that is defaulted and
>>> +// not defined as deleted is implicitly defined when it is odr-used,
>>> +// when it is needed for constant evaluation, or when it is explicitly
>>> +// defaulted after its first declaration.
>>> +// The implicitly-defined copy/move assignment operator is constexpr.
>>> +
>>> +struct S {
>>> + constexpr S() {}
>>> + S& operator=(const S&) = default; // #1
>>> + S& operator=(S&&) = default; // #2
>>> +};
>>> +
>>> +struct U {
>>> + constexpr U& operator=(const U&) = default;
>>> + constexpr U& operator=(U&&) = default;
>>> +};
>>> +
>>> +/* FIXME: If we only declare #1 and #2, and default them here:
>>> +
>>> + S& S::operator=(const S&) = default;
>>> + S& S::operator=(S&&) = default;
>>> +
>>> +then they aren't constexpr. This sounds like a bug:
>>> +<https://gcc.gnu.org/PR107598>. */
>>
>> As I commented on the PR, I don't think this is actually a bug, so let's
>> omit this FIXME.
>
> I'm glad I didn't really attempt to "fix" it (the inform message is flawed
> and should be improved). Thanks for taking a look.
>
> Here's a version with the two comments updated.
>
> Ok?
OK.
> -- >8 --
> This patch implements C++23 P2448, which lifts more restrictions on the
> constexpr keyword. It's effectively going the way of being just a hint
> (hello, inline!).
>
> This gist is relatively simple: in C++23, a constexpr function's return
> type/parameter type doesn't have to be a literal type; and you can have
> a constexpr function for which no invocation satisfies the requirements
> of a core constant expression. For example,
>
> void f(int& i); // not constexpr
>
> constexpr void g(int& i) {
> f(i); // unconditionally calls a non-constexpr function
> }
>
> is now OK, even though there isn't an invocation of 'g' that would be
> a constant expression. Maybe 'f' will be made constexpr soon, or maybe
> this depends on the version of C++ used, and similar. The patch is
> unfortunately not that trivial. The important bit is to use the new
> require_potential_rvalue_constant_expression_fncheck in
> maybe_save_constexpr_fundef (and where appropriate). It has a new flag
> that says that we're checking the body of a constexpr function, and in
> that case it's OK to find constructs that aren't a constant expression.
>
> Since it's useful to be able to check for problematic constructs even
> in C++23, this patch implements a new warning, -Winvalid-constexpr,
> which is a pedwarn turned on by default in C++20 and earlier, and which
> can be turned on in C++23 as well, in which case it's an ordinary warning.
> This I implemented by using the new function constexpr_error, used in
> p_c_e_1 and friends. (In some cases I believe fundef_p will be always
> false (= hard error), but it made sense to me to be consistent and use
> constexpr_error throughout p_c_e_1.)
>
> While working on this I think I found a bug, see constexpr-nonlit15.C
> and <https://gcc.gnu.org/PR107598>. This patch doesn't address that.
>
> This patch includes changes to diagnose the problem if the user doesn't
> use -Winvalid-constexpr and calls a constexpr function that in fact isn't
> constexpr-ready yet: maybe_save_constexpr_fundef registers the function
> if warn_invalid_constexpr is 0 and explain_invalid_constexpr_fn then
> gives the diagnostic.
>
> PR c++/106649
>
> gcc/c-family/ChangeLog:
>
> * c-cppbuiltin.cc (c_cpp_builtins): Update value of __cpp_constexpr for
> C++23.
> * c-opts.cc (c_common_post_options): Set warn_invalid_constexpr
> depending on cxx_dialect.
> * c.opt (Winvalid-constexpr): New option.
>
> gcc/cp/ChangeLog:
>
> * constexpr.cc (constexpr_error): New function.
> (is_valid_constexpr_fn): Use constexpr_error.
> (maybe_save_constexpr_fundef): Call
> require_potential_rvalue_constant_expression_fncheck rather than
> require_potential_rvalue_constant_expression. Register the
> function if -Wno-invalid-constexpr was specified.
> (explain_invalid_constexpr_fn): Don't return early if a function marked
> 'constexpr' that isn't actually a constant expression was called.
> (non_const_var_error): Add a bool parameter. Use constexpr_error.
> (inline_asm_in_constexpr_error): Likewise.
> (cxx_eval_constant_expression): Adjust calls to non_const_var_error
> and inline_asm_in_constexpr_error.
> (potential_constant_expression_1): Add a bool parameter. Use
> constexpr_error.
> (require_potential_rvalue_constant_expression_fncheck): New function.
> * cp-tree.h (require_potential_rvalue_constant_expression_fncheck):
> Declare.
> * method.cc (struct comp_info): Call
> require_potential_rvalue_constant_expression_fncheck rather than
> require_potential_rvalue_constant_expression.
>
> gcc/ChangeLog:
>
> * doc/invoke.texi: Document -Winvalid-constexpr.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/cpp0x/constexpr-ctor2.C: Expect an error in c++20_down only.
> * g++.dg/cpp0x/constexpr-default-ctor.C: Likewise.
> * g++.dg/cpp0x/constexpr-diag3.C: Likewise.
> * g++.dg/cpp0x/constexpr-ex1.C: Likewise.
> * g++.dg/cpp0x/constexpr-friend.C: Likewise.
> * g++.dg/cpp0x/constexpr-generated1.C: Likewise.
> * g++.dg/cpp0x/constexpr-ice5.C: Likewise.
> * g++.dg/cpp0x/constexpr-ice6.C: Likewise.
> * g++.dg/cpp0x/constexpr-memfn1.C: Likewise.
> * g++.dg/cpp0x/constexpr-neg2.C: Likewise.
> * g++.dg/cpp0x/constexpr-non-const-arg.C: Likewise.
> * g++.dg/cpp0x/constexpr-reinterpret1.C: Likewise.
> * g++.dg/cpp0x/pr65327.C: Likewise.
> * g++.dg/cpp1y/constexpr-105050.C: Likewise.
> * g++.dg/cpp1y/constexpr-89285-2.C: Likewise.
> * g++.dg/cpp1y/constexpr-89285.C: Likewise.
> * g++.dg/cpp1y/constexpr-89785-2.C: Likewise.
> * g++.dg/cpp1y/constexpr-neg1.C: Likewise.
> * g++.dg/cpp1y/constexpr-nsdmi7b.C: Likewise.
> * g++.dg/cpp1y/constexpr-throw.C: Likewise.
> * g++.dg/cpp23/constexpr-nonlit3.C: Remove dg-error.
> * g++.dg/cpp23/constexpr-nonlit6.C: Call the test functions.
> * g++.dg/cpp23/feat-cxx2b.C: Adjust the expected value of
> __cpp_constexpr.
> * g++.dg/cpp2a/consteval3.C: Remove dg-error.
> * g++.dg/cpp2a/constexpr-new7.C: Expect an error in c++20_down only.
> * g++.dg/cpp2a/constexpr-try5.C: Remove dg-error.
> * g++.dg/cpp2a/spaceship-constexpr1.C: Expect an error in c++20_down
> only.
> * g++.dg/cpp2a/spaceship-eq3.C: Likewise.
> * g++.dg/diagnostic/constexpr1.C: Remove dg-error.
> * g++.dg/gomp/pr79664.C: Use -Winvalid-constexpr -pedantic-errors.
> * g++.dg/ubsan/vptr-4.C: Likewise.
> * g++.dg/cpp23/constexpr-nonlit10.C: New test.
> * g++.dg/cpp23/constexpr-nonlit11.C: New test.
> * g++.dg/cpp23/constexpr-nonlit12.C: New test.
> * g++.dg/cpp23/constexpr-nonlit13.C: New test.
> * g++.dg/cpp23/constexpr-nonlit14.C: New test.
> * g++.dg/cpp23/constexpr-nonlit15.C: New test.
> * g++.dg/cpp23/constexpr-nonlit16.C: New test.
> * g++.dg/cpp23/constexpr-nonlit8.C: New test.
> * g++.dg/cpp23/constexpr-nonlit9.C: New test.
> ---
> gcc/c-family/c-cppbuiltin.cc | 2 +-
> gcc/c-family/c-opts.cc | 4 +
> gcc/c-family/c.opt | 4 +
> gcc/cp/constexpr.cc | 299 ++++++++++++------
> gcc/cp/cp-tree.h | 1 +
> gcc/cp/method.cc | 8 +-
> gcc/doc/invoke.texi | 28 +-
> gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C | 2 +-
> .../g++.dg/cpp0x/constexpr-default-ctor.C | 6 +-
> gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +-
> gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 3 +-
> gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 2 +-
> .../g++.dg/cpp0x/constexpr-generated1.C | 2 +-
> gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C | 2 +-
> gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C | 4 +-
> gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C | 4 +-
> gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C | 4 +-
> .../g++.dg/cpp0x/constexpr-non-const-arg.C | 2 +-
> .../g++.dg/cpp0x/constexpr-reinterpret1.C | 2 +-
> gcc/testsuite/g++.dg/cpp0x/pr65327.C | 2 +-
> gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C | 2 +-
> .../g++.dg/cpp1y/constexpr-89285-2.C | 2 +-
> gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C | 2 +-
> .../g++.dg/cpp1y/constexpr-89785-2.C | 4 +-
> gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C | 2 +-
> .../g++.dg/cpp1y/constexpr-nsdmi7b.C | 2 +-
> gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C | 6 +-
> .../g++.dg/cpp23/constexpr-nonlit10.C | 96 ++++++
> .../g++.dg/cpp23/constexpr-nonlit11.C | 53 ++++
> .../g++.dg/cpp23/constexpr-nonlit12.C | 24 ++
> .../g++.dg/cpp23/constexpr-nonlit13.C | 14 +
> .../g++.dg/cpp23/constexpr-nonlit14.C | 26 ++
> .../g++.dg/cpp23/constexpr-nonlit15.C | 35 ++
> .../g++.dg/cpp23/constexpr-nonlit16.C | 23 ++
> .../g++.dg/cpp23/constexpr-nonlit3.C | 2 +-
> .../g++.dg/cpp23/constexpr-nonlit6.C | 13 +-
> .../g++.dg/cpp23/constexpr-nonlit8.C | 96 ++++++
> .../g++.dg/cpp23/constexpr-nonlit9.C | 53 ++++
> gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +-
> gcc/testsuite/g++.dg/cpp2a/consteval3.C | 1 -
> gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 4 +-
> gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C | 2 -
> .../g++.dg/cpp2a/spaceship-constexpr1.C | 2 +-
> gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 4 +-
> gcc/testsuite/g++.dg/diagnostic/constexpr1.C | 2 -
> gcc/testsuite/g++.dg/gomp/pr79664.C | 2 +-
> gcc/testsuite/g++.dg/ubsan/vptr-4.C | 2 +-
> 47 files changed, 715 insertions(+), 146 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C
> create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C
>
> diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
> index f0be0598190..26447d02097 100644
> --- a/gcc/c-family/c-cppbuiltin.cc
> +++ b/gcc/c-family/c-cppbuiltin.cc
> @@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile)
> /* Set feature test macros for C++23. */
> cpp_define (pfile, "__cpp_size_t_suffix=202011L");
> cpp_define (pfile, "__cpp_if_consteval=202106L");
> - cpp_define (pfile, "__cpp_constexpr=202110L");
> + cpp_define (pfile, "__cpp_constexpr=202207L");
> cpp_define (pfile, "__cpp_multidimensional_subscript=202211L");
> cpp_define (pfile, "__cpp_named_character_escapes=202207L");
> cpp_define (pfile, "__cpp_static_call_operator=202207L");
> diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
> index bc1f85e3b85..70745aa4e7c 100644
> --- a/gcc/c-family/c-opts.cc
> +++ b/gcc/c-family/c-opts.cc
> @@ -1059,6 +1059,10 @@ c_common_post_options (const char **pfilename)
> if (flag_sized_deallocation == -1)
> flag_sized_deallocation = (cxx_dialect >= cxx14);
>
> + /* Pedwarn about invalid constexpr functions before C++23. */
> + if (warn_invalid_constexpr == -1)
> + warn_invalid_constexpr = (cxx_dialect < cxx23);
> +
> /* char8_t support is implicitly enabled in C++20 and C2X. */
> if (flag_char8_t == -1)
> flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc2x;
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 63a300ecd7c..3daeab85531 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -817,6 +817,10 @@ Wint-to-pointer-cast
> C ObjC C++ ObjC++ Var(warn_int_to_pointer_cast) Init(1) Warning
> Warn when there is a cast to a pointer from an integer of a different size.
>
> +Winvalid-constexpr
> +C++ ObjC++ Var(warn_invalid_constexpr) Init(-1) Warning
> +Warn when a function never produces a constant expression.
> +
> Winvalid-offsetof
> C++ ObjC++ Var(warn_invalid_offsetof) Init(1) Warning
> Warn about invalid uses of the \"offsetof\" macro.
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index e665839f5b1..a390cf921f3 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -139,6 +139,42 @@ ensure_literal_type_for_constexpr_object (tree decl)
> return decl;
> }
>
> +/* Issue a diagnostic with text GMSGID for constructs that are invalid in
> + constexpr functions. CONSTEXPR_FUNDEF_P is true if we're checking
> + a constexpr function body; if so, don't report hard errors and issue
> + a pedwarn pre-C++23, or a warning in C++23, if requested by
> + -Winvalid-constexpr. Otherwise, we're not in the context where we are
> + checking if a function can be marked 'constexpr', so give a hard error. */
> +
> +ATTRIBUTE_GCC_DIAG(3,4)
> +static bool
> +constexpr_error (location_t location, bool constexpr_fundef_p,
> + const char *gmsgid, ...)
> +{
> + diagnostic_info diagnostic;
> + va_list ap;
> + rich_location richloc (line_table, location);
> + va_start (ap, gmsgid);
> + bool ret;
> + if (!constexpr_fundef_p)
> + {
> + /* Report an error that cannot be suppressed. */
> + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
> + ret = diagnostic_report_diagnostic (global_dc, &diagnostic);
> + }
> + else if (warn_invalid_constexpr)
> + {
> + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
> + cxx_dialect < cxx23 ? DK_PEDWARN : DK_WARNING);
> + diagnostic.option_index = OPT_Winvalid_constexpr;
> + ret = diagnostic_report_diagnostic (global_dc, &diagnostic);
> + }
> + else
> + ret = false;
> + va_end (ap);
> + return ret;
> +}
> +
> struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
> {
> static hashval_t hash (const constexpr_fundef *);
> @@ -208,9 +244,11 @@ is_valid_constexpr_fn (tree fun, bool complain)
> if (complain)
> {
> auto_diagnostic_group d;
> - error ("invalid type for parameter %d of %<constexpr%> "
> - "function %q+#D", DECL_PARM_INDEX (parm), fun);
> - explain_non_literal_class (TREE_TYPE (parm));
> + if (constexpr_error (input_location, /*constexpr_fundef_p*/true,
> + "invalid type for parameter %d of "
> + "%<constexpr%> function %q+#D",
> + DECL_PARM_INDEX (parm), fun))
> + explain_non_literal_class (TREE_TYPE (parm));
> }
> }
> }
> @@ -242,9 +280,10 @@ is_valid_constexpr_fn (tree fun, bool complain)
> if (complain)
> {
> auto_diagnostic_group d;
> - error ("invalid return type %qT of %<constexpr%> function %q+D",
> - rettype, fun);
> - explain_non_literal_class (rettype);
> + if (constexpr_error (input_location, /*constexpr_fundef_p*/true,
> + "invalid return type %qT of %<constexpr%> "
> + "function %q+D", rettype, fun))
> + explain_non_literal_class (rettype);
> }
> }
>
> @@ -918,7 +957,7 @@ maybe_save_constexpr_fundef (tree fun)
>
> bool potential = potential_rvalue_constant_expression (massaged);
> if (!potential && complain)
> - require_potential_rvalue_constant_expression (massaged);
> + require_potential_rvalue_constant_expression_fncheck (massaged);
>
> if (DECL_CONSTRUCTOR_P (fun) && potential
> && !DECL_DEFAULTED_FN (fun))
> @@ -933,11 +972,16 @@ maybe_save_constexpr_fundef (tree fun)
> massaged = DECL_SAVED_TREE (fun);
> potential = potential_rvalue_constant_expression (massaged);
> if (!potential && complain)
> - require_potential_rvalue_constant_expression (massaged);
> + require_potential_rvalue_constant_expression_fncheck (massaged);
> }
> }
>
> - if (!potential && complain)
> + if (!potential && complain
> + /* If -Wno-invalid-constexpr was specified, we haven't complained
> + about non-constant expressions yet. Register the function and
> + complain in explain_invalid_constexpr_fn if the function is
> + called. */
> + && warn_invalid_constexpr != 0)
> return;
>
> if (implicit)
> @@ -996,19 +1040,27 @@ register_constexpr_fundef (const constexpr_fundef &value)
> **slot = value;
> }
>
> -/* FUN is a non-constexpr function called in a context that requires a
> - constant expression. If it comes from a constexpr template, explain why
> - the instantiation isn't constexpr. */
> +/* FUN is a non-constexpr (or, with -Wno-invalid-constexpr, a constexpr
> + function called in a context that requires a constant expression).
> + If it comes from a constexpr template, explain why the instantiation
> + isn't constexpr. Otherwise, explain why the function cannot be used
> + in a constexpr context. */
>
> void
> explain_invalid_constexpr_fn (tree fun)
> {
> static hash_set<tree> *diagnosed;
> tree body;
> + /* In C++23, a function marked 'constexpr' may not actually be a constant
> + expression. We haven't diagnosed the problem yet: -Winvalid-constexpr
> + wasn't enabled. The function was called, so diagnose why it cannot be
> + used in a constant expression. */
> + if (warn_invalid_constexpr == 0 && DECL_DECLARED_CONSTEXPR_P (fun))
> + /* Go on. */;
> /* Only diagnose defaulted functions, lambdas, or instantiations. */
> - if (!DECL_DEFAULTED_FN (fun)
> - && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
> - && !is_instantiation_of_constexpr (fun))
> + else if (!DECL_DEFAULTED_FN (fun)
> + && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
> + && !is_instantiation_of_constexpr (fun))
> {
> inform (DECL_SOURCE_LOCATION (fun), "%qD declared here", fun);
> return;
> @@ -5612,11 +5664,12 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
> }
>
> /* Complain about R, a VAR_DECL, not being usable in a constant expression.
> + FUNDEF_P is true if we're checking a constexpr function body.
> Shared between potential_constant_expression and
> cxx_eval_constant_expression. */
>
> static void
> -non_const_var_error (location_t loc, tree r)
> +non_const_var_error (location_t loc, tree r, bool fundef_p)
> {
> auto_diagnostic_group d;
> tree type = TREE_TYPE (r);
> @@ -5625,20 +5678,21 @@ non_const_var_error (location_t loc, tree r)
> || DECL_NAME (r) == heap_vec_uninit_identifier
> || DECL_NAME (r) == heap_vec_identifier)
> {
> - error_at (loc, "the content of uninitialized storage is not usable "
> - "in a constant expression");
> - inform (DECL_SOURCE_LOCATION (r), "allocated here");
> + if (constexpr_error (loc, fundef_p, "the content of uninitialized "
> + "storage is not usable in a constant expression"))
> + inform (DECL_SOURCE_LOCATION (r), "allocated here");
> return;
> }
> if (DECL_NAME (r) == heap_deleted_identifier)
> {
> - error_at (loc, "use of allocated storage after deallocation in a "
> - "constant expression");
> - inform (DECL_SOURCE_LOCATION (r), "allocated here");
> + if (constexpr_error (loc, fundef_p, "use of allocated storage after "
> + "deallocation in a constant expression"))
> + inform (DECL_SOURCE_LOCATION (r), "allocated here");
> return;
> }
> - error_at (loc, "the value of %qD is not usable in a constant "
> - "expression", r);
> + if (!constexpr_error (loc, fundef_p, "the value of %qD is not usable in "
> + "a constant expression", r))
> + return;
> /* Avoid error cascade. */
> if (DECL_INITIAL (r) == error_mark_node)
> return;
> @@ -6697,15 +6751,17 @@ lookup_placeholder (const constexpr_ctx *ctx, value_cat lval, tree type)
> return ob;
> }
>
> -/* Complain about an attempt to evaluate inline assembly. */
> +/* Complain about an attempt to evaluate inline assembly. If FUNDEF_P is
> + true, we're checking a constexpr function body. */
>
> static void
> -inline_asm_in_constexpr_error (location_t loc)
> +inline_asm_in_constexpr_error (location_t loc, bool fundef_p)
> {
> auto_diagnostic_group d;
> - error_at (loc, "inline assembly is not a constant expression");
> - inform (loc, "only unevaluated inline assembly is allowed in a "
> - "%<constexpr%> function in C++20");
> + if (constexpr_error (loc, fundef_p, "inline assembly is not a "
> + "constant expression"))
> + inform (loc, "only unevaluated inline assembly is allowed in a "
> + "%<constexpr%> function in C++20");
> }
>
> /* We're getting the constant value of DECL in a manifestly constant-evaluated
> @@ -6983,7 +7039,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
> if (DECL_P (r))
> {
> if (!ctx->quiet)
> - non_const_var_error (loc, r);
> + non_const_var_error (loc, r, /*fundef_p*/false);
> *non_constant_p = true;
> }
> break;
> @@ -7874,7 +7930,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
>
> case ASM_EXPR:
> if (!ctx->quiet)
> - inline_asm_in_constexpr_error (loc);
> + inline_asm_in_constexpr_error (loc, /*constexpr_fundef_p*/false);
> *non_constant_p = true;
> return t;
>
> @@ -8759,7 +8815,8 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
> diagnostic as appropriate under control of FLAGS. If WANT_RVAL is true,
> an lvalue-rvalue conversion is implied. If NOW is true, we want to
> consider the expression in the current context, independent of constexpr
> - substitution.
> + substitution. If FUNDEF_P is true, we're checking a constexpr function body
> + and hard errors should not be reported by constexpr_error.
>
> C++0x [expr.const] used to say
>
> @@ -8776,10 +8833,12 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
>
> static bool
> potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> - tsubst_flags_t flags, tree *jump_target)
> + bool fundef_p, tsubst_flags_t flags,
> + tree *jump_target)
> {
> #define RECUR(T,RV) \
> - potential_constant_expression_1 ((T), (RV), strict, now, flags, jump_target)
> + potential_constant_expression_1 ((T), (RV), strict, now, fundef_p, flags, \
> + jump_target)
>
> enum { any = false, rval = true };
> int i;
> @@ -8801,8 +8860,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (TREE_THIS_VOLATILE (t) && want_rval)
> {
> if (flags & tf_error)
> - error_at (loc, "lvalue-to-rvalue conversion of a volatile lvalue "
> - "%qE with type %qT", t, TREE_TYPE (t));
> + constexpr_error (loc, fundef_p, "lvalue-to-rvalue conversion of "
> + "a volatile lvalue %qE with type %qT", t,
> + TREE_TYPE (t));
> return false;
> }
> if (CONSTANT_CLASS_P (t))
> @@ -8861,7 +8921,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> /* An empty class has no data to read. */
> return true;
> if (flags & tf_error)
> - error ("%qE is not a constant expression", t);
> + constexpr_error (input_location, fundef_p,
> + "%qE is not a constant expression", t);
> return false;
> }
> return true;
> @@ -8910,7 +8971,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> {
> /* fold_call_expr can't do anything with IFN calls. */
> if (flags & tf_error)
> - error_at (loc, "call to internal function %qE", t);
> + constexpr_error (loc, fundef_p,
> + "call to internal function %qE", t);
> return false;
> }
> }
> @@ -8940,12 +9002,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> || !is_std_construct_at (current_function_decl))
> && !cxx_dynamic_cast_fn_p (fun))
> {
> - if (flags & tf_error)
> - {
> - error_at (loc, "call to non-%<constexpr%> function %qD",
> - fun);
> - explain_invalid_constexpr_fn (fun);
> - }
> + if ((flags & tf_error)
> + && constexpr_error (loc, fundef_p,
> + "call to non-%<constexpr%> "
> + "function %qD", fun))
> + explain_invalid_constexpr_fn (fun);
> return false;
> }
> /* A call to a non-static member function takes the address
> @@ -8962,8 +9023,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> constexpr substitution might not use the value. */
> bool sub_now = false;
> if (!potential_constant_expression_1 (x, rval, strict,
> - sub_now, flags,
> - jump_target))
> + sub_now, fundef_p,
> + flags, jump_target))
> return false;
> i = 1;
> }
> @@ -8997,7 +9058,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> substitution might not use the value of the argument. */
> bool sub_now = false;
> if (!potential_constant_expression_1 (x, rv, strict,
> - sub_now, flags, jump_target))
> + sub_now, fundef_p, flags,
> + jump_target))
> return false;
> }
> return true;
> @@ -9035,9 +9097,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (flags & tf_error)
> {
> tree cap = DECL_CAPTURED_VARIABLE (t);
> - error ("lambda capture of %qE is not a constant expression",
> - cap);
> - if (decl_constant_var_p (cap))
> + if (constexpr_error (input_location, fundef_p,
> + "lambda capture of %qE is not a "
> + "constant expression", cap)
> + && decl_constant_var_p (cap))
> inform (input_location, "because it is used as a glvalue");
> }
> return false;
> @@ -9060,8 +9123,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> && COMPLETE_TYPE_P (TREE_TYPE (t))
> && !is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false))
> {
> - if (flags & tf_error)
> - non_const_var_error (loc, t);
> + if (flags & tf_error)
> + non_const_var_error (loc, t, fundef_p);
> return false;
> }
> return true;
> @@ -9070,7 +9133,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (REINTERPRET_CAST_P (t))
> {
> if (flags & tf_error)
> - error_at (loc, "%<reinterpret_cast%> is not a constant expression");
> + constexpr_error (loc, fundef_p, "%<reinterpret_cast%> is not a "
> + "constant expression");
> return false;
> }
> /* FALLTHRU */
> @@ -9092,8 +9156,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> && !integer_zerop (from))
> {
> if (flags & tf_error)
> - error_at (loc,
> - "%<reinterpret_cast%> from integer to pointer");
> + constexpr_error (loc, fundef_p,
> + "%<reinterpret_cast%> from integer to "
> + "pointer");
> return false;
> }
> }
> @@ -9165,7 +9230,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (!var_in_maybe_constexpr_fn (x))
> {
> if (flags & tf_error)
> - error_at (loc, "use of %<this%> in a constant expression");
> + constexpr_error (loc, fundef_p, "use of %<this%> in a "
> + "constant expression");
> return false;
> }
> return true;
> @@ -9313,8 +9379,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> /* In C++17 lambdas can be constexpr, don't give up yet. */
> return true;
> else if (flags & tf_error)
> - error_at (loc, "lambda-expression is not a constant expression "
> - "before C++17");
> + constexpr_error (loc, fundef_p, "lambda-expression is not a "
> + "constant expression before C++17");
> return false;
>
> case NEW_EXPR:
> @@ -9325,8 +9391,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> /* In C++20, new-expressions are potentially constant. */
> return true;
> else if (flags & tf_error)
> - error_at (loc, "new-expression is not a constant expression "
> - "before C++20");
> + constexpr_error (loc, fundef_p, "new-expression is not a "
> + "constant expression before C++20");
> return false;
>
> case DYNAMIC_CAST_EXPR:
> @@ -9375,12 +9441,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> case AT_ENCODE_EXPR:
> fail:
> if (flags & tf_error)
> - error_at (loc, "expression %qE is not a constant expression", t);
> + constexpr_error (loc, fundef_p, "expression %qE is not a constant "
> + "expression", t);
> return false;
>
> case ASM_EXPR:
> if (flags & tf_error)
> - inline_asm_in_constexpr_error (loc);
> + inline_asm_in_constexpr_error (loc, fundef_p);
> return false;
>
> case OBJ_TYPE_REF:
> @@ -9388,8 +9455,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> /* In C++20 virtual calls can be constexpr, don't give up yet. */
> return true;
> else if (flags & tf_error)
> - error_at (loc,
> - "virtual functions cannot be %<constexpr%> before C++20");
> + constexpr_error (loc, fundef_p, "virtual functions cannot be "
> + "%<constexpr%> before C++20");
> return false;
>
> case TYPEID_EXPR:
> @@ -9404,8 +9471,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> && TYPE_POLYMORPHIC_P (TREE_TYPE (e)))
> {
> if (flags & tf_error)
> - error_at (loc, "%<typeid%> is not a constant expression "
> - "because %qE is of polymorphic type", e);
> + constexpr_error (loc, fundef_p, "%<typeid%> is not a "
> + "constant expression because %qE is "
> + "of polymorphic type", e);
> return false;
> }
> return true;
> @@ -9465,9 +9533,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> constant expression. */
> {
> if (flags & tf_error)
> - error_at (loc,
> - "cast to non-integral type %qT in a constant expression",
> - TREE_TYPE (t));
> + constexpr_error (loc, fundef_p,
> + "cast to non-integral type %qT in a constant "
> + "expression", TREE_TYPE (t));
> return false;
> }
> /* This might be a conversion from a class to a (potentially) literal
> @@ -9523,15 +9591,17 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (CP_DECL_THREAD_LOCAL_P (tmp) && !DECL_REALLY_EXTERN (tmp))
> {
> if (flags & tf_error)
> - error_at (DECL_SOURCE_LOCATION (tmp), "%qD defined "
> - "%<thread_local%> in %<constexpr%> context", tmp);
> + constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p,
> + "%qD defined %<thread_local%> in "
> + "%<constexpr%> context", tmp);
> return false;
> }
> else if (TREE_STATIC (tmp))
> {
> if (flags & tf_error)
> - error_at (DECL_SOURCE_LOCATION (tmp), "%qD defined "
> - "%<static%> in %<constexpr%> context", tmp);
> + constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p,
> + "%qD defined %<static%> in %<constexpr%> "
> + "context", tmp);
> return false;
> }
> else if (!check_for_uninitialized_const_var
> @@ -9554,9 +9624,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (flags & tf_error)
> {
> auto_diagnostic_group d;
> - error_at (loc, "temporary of non-literal type %qT in a "
> - "constant expression", TREE_TYPE (t));
> - explain_non_literal_class (TREE_TYPE (t));
> + if (constexpr_error (loc, fundef_p,
> + "temporary of non-literal type %qT in a "
> + "constant expression", TREE_TYPE (t)))
> + explain_non_literal_class (TREE_TYPE (t));
> }
> return false;
> }
> @@ -9603,7 +9674,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (integer_zerop (denom))
> {
> if (flags & tf_error)
> - error ("division by zero is not a constant expression");
> + constexpr_error (input_location, fundef_p,
> + "division by zero is not a constant expression");
> return false;
> }
> else
> @@ -9704,7 +9776,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (COND_EXPR_IS_VEC_DELETE (t) && cxx_dialect < cxx20)
> {
> if (flags & tf_error)
> - error_at (loc, "%<delete[]%> is not a constant expression");
> + constexpr_error (loc, fundef_p, "%<delete[]%> is not a "
> + "constant expression");
> return false;
> }
> /* Fall through. */
> @@ -9734,7 +9807,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> {
> tree this_jump_target = tmp;
> if (potential_constant_expression_1 (TREE_OPERAND (t, i),
> - want_rval, strict, now,
> + want_rval, strict, now, fundef_p,
> tf_none, &this_jump_target))
> {
> if (returns (&this_jump_target))
> @@ -9772,9 +9845,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (flags & tf_error)
> {
> if (TREE_CODE (t) == IF_STMT)
> - error_at (loc, "neither branch of %<if%> is a constant expression");
> + constexpr_error (loc, fundef_p, "neither branch of %<if%> is a "
> + "constant expression");
> else
> - error_at (loc, "expression %qE is not a constant expression", t);
> + constexpr_error (loc, fundef_p, "expression %qE is not a "
> + "constant expression", t);
> }
> return false;
>
> @@ -9783,8 +9858,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> return true;
> if (flags & tf_error)
> {
> - error_at (loc, "non-constant array initialization");
> - diagnose_non_constexpr_vec_init (t);
> + if (constexpr_error (loc, fundef_p, "non-constant array "
> + "initialization"))
> + diagnose_non_constexpr_vec_init (t);
> }
> return false;
>
> @@ -9813,7 +9889,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> return true;
> }
> if (flags & tf_error)
> - error_at (loc, "%<goto%> is not a constant expression");
> + constexpr_error (loc, fundef_p, "%<goto%> is not a constant "
> + "expression");
> return false;
> }
>
> @@ -9822,8 +9899,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
> return true;
> else if (flags & tf_error)
> - error_at (loc, "label definition in %<constexpr%> function only "
> - "available with %<-std=c++2b%> or %<-std=gnu++2b%>");
> + constexpr_error (loc, fundef_p, "label definition in %<constexpr%> "
> + "function only available with %<-std=c++2b%> or "
> + "%<-std=gnu++2b%>");
> return false;
>
> case ANNOTATE_EXPR:
> @@ -9861,7 +9939,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
>
> bool
> potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> - tsubst_flags_t flags)
> + bool fundef_p, tsubst_flags_t flags)
> {
> if (flags & tf_error)
> {
> @@ -9869,13 +9947,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> efficiently in some cases (currently only for TRUTH_*_EXPR). If
> that fails, replay the check noisily to give errors. */
> flags &= ~tf_error;
> - if (potential_constant_expression_1 (t, want_rval, strict, now, flags))
> + if (potential_constant_expression_1 (t, want_rval, strict, now, fundef_p,
> + flags))
> return true;
> flags |= tf_error;
> }
>
> tree target = NULL_TREE;
> - return potential_constant_expression_1 (t, want_rval, strict, now,
> + return potential_constant_expression_1 (t, want_rval, strict, now, fundef_p,
> flags, &target);
> }
>
> @@ -9884,7 +9963,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> bool
> potential_constant_expression (tree t)
> {
> - return potential_constant_expression_1 (t, false, true, false, tf_none);
> + return potential_constant_expression_1 (t, /*want_rval*/false, /*strict*/true,
> + /*now*/false, /*fundef_p*/false,
> + tf_none);
> }
>
> /* As above, but require a constant rvalue. */
> @@ -9892,7 +9973,9 @@ potential_constant_expression (tree t)
> bool
> potential_rvalue_constant_expression (tree t)
> {
> - return potential_constant_expression_1 (t, true, true, false, tf_none);
> + return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
> + /*now*/false, /*fundef_p*/false,
> + tf_none);
> }
>
> /* Like above, but complain about non-constant expressions. */
> @@ -9900,7 +9983,8 @@ potential_rvalue_constant_expression (tree t)
> bool
> require_potential_constant_expression (tree t)
> {
> - return potential_constant_expression_1 (t, false, true, false,
> + return potential_constant_expression_1 (t, /*want_rval*/false, /*strict*/true,
> + /*now*/false, /*fundef_p*/false,
> tf_warning_or_error);
> }
>
> @@ -9909,7 +9993,18 @@ require_potential_constant_expression (tree t)
> bool
> require_potential_rvalue_constant_expression (tree t)
> {
> - return potential_constant_expression_1 (t, true, true, false,
> + return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
> + /*now*/false, /*fundef_p*/false,
> + tf_warning_or_error);
> +}
> +
> +/* Like require_potential_rvalue_constant_expression, but fundef_p is true. */
> +
> +bool
> +require_potential_rvalue_constant_expression_fncheck (tree t)
> +{
> + return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
> + /*now*/false, /*fundef_p*/true,
> tf_warning_or_error);
> }
>
> @@ -9918,7 +10013,8 @@ require_potential_rvalue_constant_expression (tree t)
> bool
> require_rvalue_constant_expression (tree t)
> {
> - return potential_constant_expression_1 (t, true, true, true,
> + return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
> + /*now*/true, /*fundef_p*/false,
> tf_warning_or_error);
> }
>
> @@ -9932,7 +10028,9 @@ require_rvalue_constant_expression (tree t)
> bool
> is_constant_expression (tree t)
> {
> - return potential_constant_expression_1 (t, false, true, true, tf_none);
> + return potential_constant_expression_1 (t, /*want_rval*/false, /*strict*/true,
> + /*now*/true, /*fundef_p*/false,
> + tf_none);
> }
>
> /* As above, but expect an rvalue. */
> @@ -9940,7 +10038,9 @@ is_constant_expression (tree t)
> bool
> is_rvalue_constant_expression (tree t)
> {
> - return potential_constant_expression_1 (t, true, true, true, tf_none);
> + return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
> + /*now*/true, /*fundef_p*/false,
> + tf_none);
> }
>
> /* Like above, but complain about non-constant expressions. */
> @@ -9948,7 +10048,8 @@ is_rvalue_constant_expression (tree t)
> bool
> require_constant_expression (tree t)
> {
> - return potential_constant_expression_1 (t, false, true, true,
> + return potential_constant_expression_1 (t, /*want_rval*/false, /*strict*/true,
> + /*now*/true, /*fundef_p*/false,
> tf_warning_or_error);
> }
>
> @@ -9958,7 +10059,9 @@ require_constant_expression (tree t)
> bool
> is_static_init_expression (tree t)
> {
> - return potential_constant_expression_1 (t, false, false, true, tf_none);
> + return potential_constant_expression_1 (t, /*want_rval*/false,
> + /*strict*/false, /*now*/true,
> + /*fundef_p*/false, tf_none);
> }
>
> /* Returns true if T is a potential constant expression that is not
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 07f96ea861f..811a8345712 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -8450,6 +8450,7 @@ extern bool require_potential_constant_expression (tree);
> extern bool require_constant_expression (tree);
> extern bool require_rvalue_constant_expression (tree);
> extern bool require_potential_rvalue_constant_expression (tree);
> +extern bool require_potential_rvalue_constant_expression_fncheck (tree);
> extern tree cxx_constant_value (tree, tree = NULL_TREE,
> tsubst_flags_t = tf_error);
> inline tree cxx_constant_value (tree t, tsubst_flags_t complain)
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index c217d7e5aad..1e962b6e3b1 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -1332,7 +1332,7 @@ struct comp_info
> && !potential_rvalue_constant_expression (expr))
> {
> if (was_constexp)
> - require_potential_rvalue_constant_expression (expr);
> + require_potential_rvalue_constant_expression_fncheck (expr);
> else
> constexp = false;
> }
> @@ -2670,13 +2670,17 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
> requirements of a constexpr constructor (7.1.5), the
> implicitly-defined default constructor is constexpr.
>
> + C++20:
> The implicitly-defined copy/move assignment operator is constexpr if
> - X is a literal type, and
> - the assignment operator selected to copy/move each direct base class
> subobject is a constexpr function, and
> - for each non-static data member of X that is of class type (or array
> thereof), the assignment operator selected to copy/move that
> - member is a constexpr function. */
> + member is a constexpr function.
> +
> + C++23:
> + The implicitly-defined copy/move assignment operator is constexpr. */
> if (constexpr_p)
> *constexpr_p = (SFK_CTOR_P (sfk)
> || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14)
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 96f0b6d08bd..31d031cd25c 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -256,7 +256,7 @@ in the following sections.
> -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol
> -Weffc++ -Wno-exceptions -Wextra-semi -Wno-inaccessible-base @gol
> -Wno-inherited-variadic-ctor -Wno-init-list-lifetime @gol
> --Winvalid-imported-macros @gol
> +-Winvalid-constexpr -Winvalid-imported-macros @gol
> -Wno-invalid-offsetof -Wno-literal-suffix @gol
> -Wmismatched-new-delete -Wmismatched-tags @gol
> -Wmultiple-inheritance -Wnamespaces -Wnarrowing @gol
> @@ -3766,6 +3766,32 @@ the variable declaration statement.
>
> @end itemize
>
> +@item -Winvalid-constexpr
> +@opindex Winvalid-constexpr
> +@opindex Wno-invalid-constexpr
> +
> +Warn when a function never produces a constant expression. In C++20
> +and earlier, for every @code{constexpr} function and function template,
> +there must be at least one set of function arguments in at least one
> +instantiation such that an invocation of the function or constructor
> +could be an evaluated subexpression of a core constant expression.
> +C++23 removed this restriction, so it's possible to have a function
> +or a function template marked @code{constexpr} for which no invocation
> +satisfies the requirements of a core constant expression.
> +
> +This warning is enabled as a pedantic warning by default in C++20 and
> +earlier. In C++23, @option{-Winvalid-constexpr} can be turned on, in
> +which case it will be an ordinary warning. For example:
> +
> +@smallexample
> +void f (int& i);
> +constexpr void
> +g (int& i)
> +@{
> + f(i); // warns by default in C++20, in C++23 only with -Winvalid-constexpr
> +@}
> +@end smallexample
> +
> @item -Winvalid-imported-macros
> @opindex Winvalid-imported-macros
> @opindex Wno-invalid-imported-macros
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C
> index 30b01091fd1..eabc586385f 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C
> @@ -7,5 +7,5 @@ struct A
>
> struct B : A
> {
> - constexpr B(): A() { } // { dg-error "A::A" }
> + constexpr B(): A() { } // { dg-error "A::A" "" { target c++20_down } }
> };
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
> index 8d352d0bb99..2f9fbfb596a 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
> @@ -7,6 +7,6 @@ struct A {
> struct B: A { };
> constexpr int f(B b) { return b.i; }
>
> -struct C { C(); }; // { dg-message "" }
> -struct D: C { }; // { dg-message "" }
> -constexpr int g(D d) { return 42; } // { dg-error "invalid type" }
> +struct C { C(); }; // { dg-message "" "" { target c++20_down } }
> +struct D: C { }; // { dg-message "" "" { target c++20_down } }
> +constexpr int g(D d) { return 42; } // { dg-error "invalid type" "" { target c++20_down } }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
> index c167bb1d8bc..5eedf42ba36 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
> @@ -37,7 +37,7 @@ struct base // { dg-message "no .constexpr. constructor" "" { target { !
>
> struct derived : public base // { dg-message "base class" "" { target { ! implicit_constexpr } } }
> {
> - constexpr derived(): base() { } // { dg-error "non-.constexpr. function" "" { target { ! implicit_constexpr } } }
> + constexpr derived(): base() { } // { dg-error "non-.constexpr. function" "" { target { { ! implicit_constexpr } && c++20_down } } }
> };
>
> constexpr derived obj; // { dg-error "not literal" "" { target { ! implicit_constexpr } } }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
> index 1d5c58b4090..48281a47784 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
> @@ -87,7 +87,8 @@ struct resource {
> }
> };
> constexpr resource f(resource d)
> -{ return d; } // { dg-error "non-.constexpr." "" { target { ! implicit_constexpr } } }
> +{ return d; } // { dg-error "non-.constexpr." "" { target { { ! implicit_constexpr } && c++20_down } } }
> +// { dg-error "non-.constexpr." "" { target c++23 } .-2 }
> constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { ! implicit_constexpr } } }
>
> // 4.4 floating-point constant expressions
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
> index 85dfca4ff1d..3d171822855 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
> @@ -5,7 +5,7 @@ struct A { A(); };
>
> struct B {
> friend constexpr int f(B) { return 0; } // OK
> - friend constexpr int f(A) { return 0; } // { dg-error "constexpr" }
> + friend constexpr int f(A) { return 0; } // { dg-error "constexpr" "" { target c++20_down } }
> };
>
> template <class T>
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-generated1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-generated1.C
> index 4b0d68bf661..98235719546 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-generated1.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-generated1.C
> @@ -9,7 +9,7 @@ int g();
>
> // We should complain about this.
> template<> constexpr int A<int>::f()
> -{ return g(); } // { dg-error "non-.constexpr." }
> +{ return g(); } // { dg-error "non-.constexpr." "" { target c++20_down } }
>
> // But not about this.
> struct B
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C
> index e934421c2f4..70327fc414a 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C
> @@ -9,5 +9,5 @@ struct A
> struct B
> {
> A a[1];
> - constexpr B() : a() {} // { dg-error "non-constant|non-.constexpr." "" { target { ! implicit_constexpr } } }
> + constexpr B() : a() {} // { dg-error "non-constant|non-.constexpr." "" { target { { ! implicit_constexpr } && c++20_down } } }
> };
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C
> index bf95b2443c7..7eabd333758 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C
> @@ -6,6 +6,6 @@ struct A
> A(int);
> };
>
> -struct B : A {}; // { dg-message "" }
> +struct B : A {}; // { dg-message "" "" { target c++20_down } }
>
> -constexpr int foo(B) { return 0; } // { dg-error "invalid type" }
> +constexpr int foo(B) { return 0; } // { dg-error "invalid type" "" { target c++20_down } }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
> index 37255282ded..0c95961c730 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
> @@ -13,6 +13,6 @@ constexpr X X::g(X x) { return x; }
> struct Y
> {
> Y() { }
> - constexpr Y f(Y y) { return y; } // { dg-error "constexpr" "" { target { ! implicit_constexpr } } }
> - static constexpr Y g(Y y) { return y; } // { dg-error "constexpr" "" { target { ! implicit_constexpr } } }
> + constexpr Y f(Y y) { return y; } // { dg-error "constexpr" "" { target { { ! implicit_constexpr } && c++20_down } } }
> + static constexpr Y g(Y y) { return y; } // { dg-error "constexpr" "" { target { { ! implicit_constexpr } && c++20_down } } }
> };
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C
> index 793b4c3f5d3..47f7fb05e29 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C
> @@ -18,10 +18,10 @@ constexpr int three = one() ? 3 : nonconst_func(0);
> constexpr int bogus() { return zero () ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" }
>
> // Correctly rejected (not sure why).
> -constexpr int correct_error() { return nonconst_func(0); } // { dg-error "nonconst_func" }
> +constexpr int correct_error() { return nonconst_func(0); } // { dg-error "nonconst_func" "" { target c++20_down } }
>
> // Correctly rejected.
> constexpr int z = bogus(); // { dg-error "" }
>
> // This is also correctly rejected.
> -constexpr int correct_failure() { return 0 ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" }
> +constexpr int correct_failure() { return 0 ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" "" { target c++20_down } }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C
> index 0f68643f145..abbc70368d4 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C
> @@ -10,7 +10,7 @@ struct B {
> int global; // { dg-message "not const" }
>
> struct D : B {
> - constexpr D() : B(global) { } // { dg-error "global|argument" }
> + constexpr D() : B(global) { } // { dg-error "global|argument" "" { target c++20_down } }
> };
>
> struct A2 {
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C
> index d7d244f752d..4e19cd36a9f 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C
> @@ -17,7 +17,7 @@ public:
> constexpr static Inner & getInner()
> /* I am surprised this is considered a constexpr */
> {
> - return *((Inner *)4); // { dg-error "reinterpret_cast" }
> + return *((Inner *)4); // { dg-error "reinterpret_cast" "" { target c++20_down } }
> }
> };
>
> diff --git a/gcc/testsuite/g++.dg/cpp0x/pr65327.C b/gcc/testsuite/g++.dg/cpp0x/pr65327.C
> index e8149953ffd..b3ef57eec5f 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/pr65327.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/pr65327.C
> @@ -14,5 +14,5 @@ foo ()
> constexpr volatile int // { dg-warning "deprecated" "" { target c++2a } }
> bar ()
> {
> - return i; // { dg-error "lvalue-to-rvalue conversion of a volatile lvalue" }
> + return i; // { dg-error "lvalue-to-rvalue conversion of a volatile lvalue" "" { target c++20_down } }
> }
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C
> index e0688fbd38e..e5d53c9817b 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C
> @@ -5,7 +5,7 @@ void g();
> void h();
>
> constexpr void f(int* p, int* q) {
> - if (p != q && *p < 0) // { dg-error "neither branch of 'if' is a constant expression" }
> + if (p != q && *p < 0) // { dg-error "neither branch of 'if' is a constant expression" "" { target c++20_down } }
> g();
> else
> h();
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285-2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285-2.C
> index ea44daa849e..7b129fcee7a 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285-2.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285-2.C
> @@ -10,7 +10,7 @@ struct B {
> int *c = &x->a;
> while (*c)
> c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
> - *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error "reinterpret_cast" }
> + *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error "reinterpret_cast" "" { target c++20_down } }
> }
> };
> struct C : A {
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> index 26aab9b6a50..fe0b8570ca2 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> @@ -10,7 +10,7 @@ struct B {
> int *c = &x->a;
> while (*c)
> c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
> - *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error "reinterpret_cast" }
> + *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error "reinterpret_cast" "" { target c++20_down } }
> }
> };
> struct C : A {
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C
> index 5cd46c791af..7afd9d24e98 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C
> @@ -11,7 +11,7 @@ foo (int x)
> case 2:
> break;
> }
> - throw 42; // { dg-error "is not a constant expression" }
> + throw 42; // { dg-error "is not a constant expression" "" { target c++20_down } }
> return 0;
> }
>
> @@ -29,7 +29,7 @@ bar (int x)
> continue;
> break;
> }
> - throw -42; // { dg-error "is not a constant expression" }
> + throw -42; // { dg-error "is not a constant expression" "" { target c++20_down } }
> }
> while (0);
> return x;
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> index 8e9d1ea4943..53b5dd50f51 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> @@ -3,7 +3,7 @@
> struct A { A(); };
>
> constexpr int f(int i) {
> - static int j = i; // { dg-error "static" }
> + static int j = i; // { dg-error "static" "" { target c++20_down } }
> thread_local int l = i; // { dg-error "thread_local" "" { target c++20_down } }
> goto foo; // { dg-error "goto" "" { target c++20_down } }
> foo:
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi7b.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi7b.C
> index ec10ddd2be8..a410e482664 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi7b.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi7b.C
> @@ -21,7 +21,7 @@ bar()
> A a = foo();
> a.p->n = 5;
> return a;
> -} // { dg-error "non-.constexpr." }
> +} // { dg-error "non-.constexpr." "" { target c++20_down } }
>
> constexpr int
> baz()
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C
> index 3bbc8ac1b88..35928744686 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C
> @@ -7,18 +7,18 @@ constexpr void f1() {
>
> constexpr void f2() {
> if (true)
> - throw; // { dg-error "not a constant expression" }
> + throw; // { dg-error "not a constant expression" "" { target c++20_down } }
> }
>
> constexpr void f3() {
> if (false)
> ;
> else
> - throw; // { dg-error "not a constant expression" }
> + throw; // { dg-error "not a constant expression" "" { target c++20_down } }
> }
>
> constexpr void f4() {
> - throw; // { dg-error "not a constant expression" }
> + throw; // { dg-error "not a constant expression" "" { target c++20_down } }
> }
>
> constexpr int fun(int n) {
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
> new file mode 100644
> index 00000000000..48706f7b66e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
> @@ -0,0 +1,96 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++23 } }
> +// { dg-options "-Winvalid-constexpr -pedantic-errors" }
> +
> +// No constexpr constructors = not a literal type.
> +struct NonLiteral {
> + NonLiteral() {}
> +};
> +
> +// C++23: It is possible to write a constexpr function for which no
> +// invocation satisfies the requirements of a core constant expression.
> +constexpr NonLiteral
> +fn0 (int) // { dg-warning "invalid return type" }
> +{
> + return NonLiteral{};
> +}
> +
> +constexpr int
> +fn1 (NonLiteral) // { dg-warning "invalid type" }
> +{
> + return 42;
> +}
> +
> +// From P2448.
> +void f(int& i) {
> + i = 0;
> +}
> +
> +constexpr void g(int& i) {
> + f(i); // { dg-warning "call to" }
> +}
> +
> +// [dcl.constexpr] used to have this.
> +constexpr int f(bool b)
> + { return b ? throw 0 : 0; } // OK
> +constexpr int f() { return f(true); } // ill-formed, no diagnostic required
> +
> +struct B {
> + constexpr B(int) : i(0) { }
> + int i;
> +};
> +
> +int global;
> +
> +struct D : B {
> + constexpr D() : B(global) { } // { dg-warning "not usable" }
> + // ill-formed, no diagnostic required
> + // lvalue-to-rvalue conversion on non-constant global
> +};
> +
> +// If no specialization of the template would satisfy the requirements
> +// for a constexpr function when considered as a non-template function,
> +// the template is ill-formed, no diagnostic required.
> +template<typename>
> +constexpr void
> +fn2 ()
> +{
> + int i = 42;
> + f (i);
> +}
> +
> +void
> +fn3 ()
> +{
> + fn2<int>();
> +}
> +
> +constexpr volatile int cvi = 10;
> +
> +constexpr int
> +fn4 ()
> +{
> + return cvi; // { dg-warning "lvalue-to-rvalue conversion" }
> +}
> +
> +constexpr unsigned int
> +fn5 (int *p)
> +{
> + unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" }
> + return *q;
> +}
> +
> +constexpr int
> +fn6 (int i)
> +{
> + void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" }
> + return 42;
> +}
> +
> +constexpr int
> +fn7 (int i)
> +{
> + static int s = i; // { dg-warning "static" }
> + return s;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
> new file mode 100644
> index 00000000000..a7114bc66cb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
> @@ -0,0 +1,53 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++23 } }
> +// { dg-options "-Winvalid-constexpr -pedantic-errors" }
> +
> +// [dcl.constexpr]/4 used to say:
> +// The definition of a constexpr constructor whose function-body
> +// is not = delete shall additionally satisfy the following requirements:
> +// (4.1) for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
> +// (4.2) for a delegating constructor, the target constructor shall be a constexpr constructor.
> +
> +// This continues to be OK.
> +struct Length {
> + constexpr explicit Length(int i = 0) : val(i) { }
> +private:
> + int val;
> +};
> +
> +struct X {
> + X() {}
> + X(int i_) : i(i_) {}
> + int i;
> +};
> +
> +struct S {
> + X x;
> + // Calls a non-constexpr constructor X::X(int).
> + constexpr S(int i) : x(i) { } // { dg-warning "call to" }
> + S(int, int) { }
> + // Target constructor isn't constexpr.
> + constexpr S() : S(42, 42) { } // { dg-warning "call to" }
> +};
> +
> +namespace N1 {
> +struct X {
> + void x();
> +};
> +struct Y {
> + X x;
> + constexpr void y() { x.x(); } // { dg-warning "call to" }
> +};
> +}
> +
> +void g();
> +
> +struct A {
> + constexpr A() { g(); } // { dg-warning "call to" }
> +};
> +
> +struct B {
> + constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" }
> + constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" }
> +};
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
> new file mode 100644
> index 00000000000..8f003b80190
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
> @@ -0,0 +1,24 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++23 } }
> +// Test that we get a diagnostic even in C++23 if you do call the function.
> +
> +constexpr unsigned int
> +fn0 (const int *p)
> +{
> + return *reinterpret_cast<unsigned const int *>(p); // { dg-error ".reinterpret_cast. is not a constant expression" }
> +}
> +
> +constexpr void *
> +fn1 (int i)
> +{
> + return (void *) 1LL; // { dg-error ".reinterpret_cast." }
> +}
> +
> +void
> +g ()
> +{
> + constexpr int i = 42;
> + constexpr auto a1 = fn0 (&i);
> + constexpr auto a2 = fn1 (i); // { dg-error "called in a constant expression" }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
> new file mode 100644
> index 00000000000..7997e8e2c3c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
> @@ -0,0 +1,14 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++23 } }
> +// { dg-options "-Winvalid-constexpr" }
> +
> +constexpr volatile int i = 10;
> +
> +constexpr int
> +bar ()
> +{
> + return i; // { dg-warning "lvalue-to-rvalue conversion of a volatile lvalue" }
> +}
> +
> +constexpr int x = bar (); // { dg-error "called in a constant expression" }
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
> new file mode 100644
> index 00000000000..f79ff15cbe2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
> @@ -0,0 +1,26 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++20 } }
> +// { dg-options "" }
> +// The definition of a constexpr destructor whose function-body is not
> +// =delete shall additionally satisfy the following requirement:
> +// (5.1) for every subobject of class type or (possibly multi-dimensional)
> +// array thereof, that class type shall have a constexpr destructor.
> +
> +struct B {
> + B() { }
> + ~B() { }
> +};
> +
> +struct T : B {
> + constexpr ~T() { } // { dg-warning "call to" "" { target c++20_down } }
> +};
> +
> +struct S {
> + constexpr S() = default; // was error: implicit S() is not constexpr, now OK
> + ~S() noexcept(false) = default; // OK, despite mismatched exception specification
> +private:
> + int i;
> + S(S&); // OK: private copy constructor
> +};
> +S::S(S&) = default; // OK: defines copy constructor
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
> new file mode 100644
> index 00000000000..aa35fa88189
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
> @@ -0,0 +1,35 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++23 } }
> +// { dg-options "-Winvalid-constexpr" }
> +// A copy/move assignment operator for a class X that is defaulted and
> +// not defined as deleted is implicitly defined when it is odr-used,
> +// when it is needed for constant evaluation, or when it is explicitly
> +// defaulted after its first declaration.
> +// The implicitly-defined copy/move assignment operator is constexpr.
> +
> +struct S {
> + constexpr S() {}
> + S& operator=(const S&) = default;
> + S& operator=(S&&) = default;
> +};
> +
> +struct U {
> + constexpr U& operator=(const U&) = default;
> + constexpr U& operator=(U&&) = default;
> +};
> +
> +constexpr void
> +g ()
> +{
> + S a;
> + S b;
> + b = a;
> + b = S{};
> +
> + U u, v;
> + u = v;
> + u = U{};
> +}
> +
> +static_assert ((g(), true), "");
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C
> new file mode 100644
> index 00000000000..a6c4d19ffc6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C
> @@ -0,0 +1,23 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++20 } }
> +// { dg-options "" }
> +
> +template <typename T>
> +struct Wrapper {
> + constexpr Wrapper() = default;
> + constexpr Wrapper(Wrapper const&) = default;
> + constexpr Wrapper(T const& t) : t(t) { }
> +
> + constexpr T get() const { return t; }
> + constexpr bool operator==(Wrapper const&) const = default; // { dg-warning "call to" "" { target c++20_down } }
> +private:
> + T t;
> +};
> +
> +struct X {
> + X();
> + bool operator==(X const&) const;
> +};
> +
> +Wrapper<X> x;
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C
> index 3b5585dcd84..2238db91157 100644
> --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C
> @@ -5,6 +5,6 @@ constexpr int
> foo ()
> {
> goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
> -lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 }
> +lab:
> return 1;
> }
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C
> index fbeb83075b0..a1436938318 100644
> --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C
> @@ -18,8 +18,19 @@ bar ()
> }
>
> constexpr int
> -baz (int x)
> +baz ()
> {
> thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" }
> return ++a;
> }
> +
> +// In C++23, we get errors about the non-constant expressions only if we
> +// actually call the functions in a constexpr context.
> +
> +void
> +test ()
> +{
> + constexpr int a = foo (); // { dg-error "constant expression" }
> + constexpr int b = bar (); // { dg-error "constant expression" }
> + constexpr int c = baz (); // { dg-error "constant expression" }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C
> new file mode 100644
> index 00000000000..3fb1b93bd07
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C
> @@ -0,0 +1,96 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++14 } }
> +// { dg-options "" }
> +
> +// No constexpr constructors = not a literal type.
> +struct NonLiteral {
> + NonLiteral() {}
> +};
> +
> +// C++23: It is possible to write a constexpr function for which no
> +// invocation satisfies the requirements of a core constant expression.
> +constexpr NonLiteral
> +fn0 (int) // { dg-warning "invalid return type" "" { target c++20_down } }
> +{
> + return NonLiteral{};
> +}
> +
> +constexpr int
> +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target c++20_down } }
> +{
> + return 42;
> +}
> +
> +// From P2448.
> +void f(int& i) {
> + i = 0;
> +}
> +
> +constexpr void g(int& i) {
> + f(i); // { dg-warning "call to" "" { target c++20_down } }
> +}
> +
> +// [dcl.constexpr] used to have this.
> +constexpr int f(bool b)
> + { return b ? throw 0 : 0; } // OK
> +constexpr int f() { return f(true); } // ill-formed, no diagnostic required
> +
> +struct B {
> + constexpr B(int) : i(0) { }
> + int i;
> +};
> +
> +int global;
> +
> +struct D : B {
> + constexpr D() : B(global) { } // { dg-warning "not usable" "" { target c++20_down } }
> + // ill-formed, no diagnostic required
> + // lvalue-to-rvalue conversion on non-constant global
> +};
> +
> +// If no specialization of the template would satisfy the requirements
> +// for a constexpr function when considered as a non-template function,
> +// the template is ill-formed, no diagnostic required.
> +template<typename>
> +constexpr void
> +fn2 ()
> +{
> + int i = 42;
> + f (i);
> +}
> +
> +void
> +fn3 ()
> +{
> + fn2<int>();
> +}
> +
> +constexpr volatile int cvi = 10;
> +
> +constexpr int
> +fn4 ()
> +{
> + return cvi; // { dg-warning "lvalue-to-rvalue conversion" "" { target c++20_down } }
> +}
> +
> +constexpr unsigned int
> +fn5 (int *p)
> +{
> + unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" "" { target c++20_down } }
> + return *q;
> +}
> +
> +constexpr int
> +fn6 (int i)
> +{
> + void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" "" { target c++20_down } }
> + return 42;
> +}
> +
> +constexpr int
> +fn7 (int i)
> +{
> + static int s = i; // { dg-error "static" "" { target c++20_down } }
> + return s;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C
> new file mode 100644
> index 00000000000..228e90f14c1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C
> @@ -0,0 +1,53 @@
> +// PR c++/106649
> +// P2448 - Relaxing some constexpr restrictions
> +// { dg-do compile { target c++14 } }
> +// { dg-options "" }
> +
> +// [dcl.constexpr]/4 used to say:
> +// The definition of a constexpr constructor whose function-body
> +// is not = delete shall additionally satisfy the following requirements:
> +// (4.1) for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
> +// (4.2) for a delegating constructor, the target constructor shall be a constexpr constructor.
> +
> +// This continues to be OK.
> +struct Length {
> + constexpr explicit Length(int i = 0) : val(i) { }
> +private:
> + int val;
> +};
> +
> +struct X {
> + X() {}
> + X(int i_) : i(i_) {}
> + int i;
> +};
> +
> +struct S {
> + X x;
> + // Calls a non-constexpr constructor X::X(int).
> + constexpr S(int i) : x(i) { } // { dg-warning "call to" "" { target c++20_down } }
> + S(int, int) { }
> + // Target constructor isn't constexpr.
> + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target c++20_down } }
> +};
> +
> +namespace N1 {
> +struct X {
> + void x();
> +};
> +struct Y {
> + X x;
> + constexpr void y() { x.x(); } // { dg-warning "call to" "" { target c++20_down } }
> +};
> +}
> +
> +void g();
> +
> +struct A {
> + constexpr A() { g(); } // { dg-warning "call to" "" { target c++20_down } }
> +};
> +
> +struct B {
> + constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } }
> + constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } }
> +};
> diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
> index 85e66871fd1..3ce36e45cb7 100644
> --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
> +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
> @@ -134,8 +134,8 @@
>
> #ifndef __cpp_constexpr
> # error "__cpp_constexpr"
> -#elif __cpp_constexpr != 202110
> -# error "__cpp_constexpr != 202110"
> +#elif __cpp_constexpr != 202207
> +# error "__cpp_constexpr != 202207"
> #endif
>
> #ifndef __cpp_decltype_auto
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C
> index 83463868668..627ab142d5a 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C
> @@ -57,7 +57,6 @@ consteval int
> f13 (int x)
> {
> static int a = 5; // { dg-error "'a' defined 'static' in 'consteval' function only available with" "" { target c++20_only } }
> - // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 }
> thread_local int b = 6; // { dg-error "'b' defined 'thread_local' in 'consteval' function only available with" "" { target c++20_only } }
> return x;
> }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C
> index bb60a8ee91b..b2c98853882 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C
> @@ -13,7 +13,7 @@ void *operator new (std::size_t) noexcept;
> constexpr bool
> foo ()
> {
> - auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" }
> + auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } }
> *p = 1;
> ::operator delete (p);
> return false;
> @@ -24,7 +24,7 @@ struct S { constexpr S () : s (0) {} int s; };
> constexpr bool
> bar ()
> {
> - auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" }
> + auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } }
> auto q = new (p) S ();
> q->s++;
> q->~S ();
> diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
> index 216634dc56c..eb66105d7c4 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C
> @@ -6,7 +6,6 @@ constexpr int foo ()
> try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
> int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
> static double b = 1.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 }
> goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
> l:;
> return 0;
> @@ -22,7 +21,6 @@ constexpr int bar ()
> {
> int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
> static long double b = 3.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 }
> goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
> l:;
> try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C
> index dff59271a1e..fb62ecbfdb5 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C
> @@ -9,7 +9,7 @@ struct A
> struct B
> {
> A a;
> - bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { ! implicit_constexpr } } }
> + bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { { ! implicit_constexpr } && c++20_down } } }
> };
>
> constexpr bool x = B() == B(); // { dg-error "non-.constexpr" "" { target { ! implicit_constexpr } } }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
> index 7a517a8016c..69eaa7b9b20 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
> @@ -7,8 +7,8 @@ struct A {
> struct D
> {
> A i;
> - bool operator==(const D& x) const = default; // { dg-error "A::operator==" }
> - bool operator!=(const D& z) const = default; // { dg-error "D::operator==" }
> + bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } }
> + bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } }
> };
>
> constexpr D d{A()};
> diff --git a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C
> index c962a60c847..19242d15ba8 100644
> --- a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C
> +++ b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C
> @@ -1,7 +1,5 @@
> // { dg-do compile { target c++11 } }
>
> constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. defined .thread_local." "" { target c++20_down } }
> -// { dg-error "40:.i. defined .thread_local. in .constexpr. context" "" { target c++23 } .-1 }
>
> constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. defined .static." "" { target c++20_down } }
> -// { dg-error "34:.i. defined .static. in .constexpr. context" "" { target c++23 } .-1 }
> diff --git a/gcc/testsuite/g++.dg/gomp/pr79664.C b/gcc/testsuite/g++.dg/gomp/pr79664.C
> index 582eedb6d6d..f4c30c0b3f4 100644
> --- a/gcc/testsuite/g++.dg/gomp/pr79664.C
> +++ b/gcc/testsuite/g++.dg/gomp/pr79664.C
> @@ -1,6 +1,6 @@
> // PR c++/79664
> // { dg-do compile }
> -// { dg-options "-std=c++14 -fopenmp" }
> +// { dg-options "-std=c++14 -fopenmp -Winvalid-constexpr -pedantic-errors" }
>
> constexpr int
> f1 ()
> diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-4.C b/gcc/testsuite/g++.dg/ubsan/vptr-4.C
> index a21d3d60a3a..1efd3f77a55 100644
> --- a/gcc/testsuite/g++.dg/ubsan/vptr-4.C
> +++ b/gcc/testsuite/g++.dg/ubsan/vptr-4.C
> @@ -1,7 +1,7 @@
> // Verify that -fsanitize=vptr downcast instrumentation works properly
> // inside of constexpr.
> // { dg-do compile }
> -// { dg-options "-std=c++11 -fsanitize=vptr" }
> +// { dg-options "-std=c++11 -fsanitize=vptr -Winvalid-constexpr -pedantic-errors" }
>
> struct S {
> constexpr S() : a(0) {}
>
> base-commit: 7026d0455dce1092975d4884f450a12a6ed205c7
On 11/16/22 15:27, Jason Merrill wrote:
> On 11/16/22 11:06, Marek Polacek wrote:
>> On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote:
>>> On 11/15/22 19:30, Marek Polacek wrote:
>>>> @@ -996,19 +1040,26 @@ register_constexpr_fundef (const
>>>> constexpr_fundef &value)
>>>> **slot = value;
>>>> }
>>>> -/* FUN is a non-constexpr function called in a context that requires a
>>>> - constant expression. If it comes from a constexpr template,
>>>> explain why
>>>> - the instantiation isn't constexpr. */
>>>> +/* FUN is a non-constexpr (or, with -Wno-invalid-constexpr, a
>>>> constexpr
>>>> + function called in a context that requires a constant expression).
>>>> + If it comes from a constexpr template, explain why the
>>>> instantiation
>>>> + isn't constexpr. */
>>>
>>> The "if it comes from a constexpr template" wording has needed an
>>> update for
>>> a while now.
>>
>> Probably ever since r178519. I've added "Otherwise, explain why the
>> function
>> cannot be used in a constexpr context." Is that acceptable?
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
>>>> @@ -0,0 +1,43 @@
>>>> +// PR c++/106649
>>>> +// P2448 - Relaxing some constexpr restrictions
>>>> +// { dg-do compile { target c++23 } }
>>>> +// { dg-options "-Winvalid-constexpr" }
>>>> +// A copy/move assignment operator for a class X that is defaulted and
>>>> +// not defined as deleted is implicitly defined when it is odr-used,
>>>> +// when it is needed for constant evaluation, or when it is explicitly
>>>> +// defaulted after its first declaration.
>>>> +// The implicitly-defined copy/move assignment operator is constexpr.
>>>> +
>>>> +struct S {
>>>> + constexpr S() {}
>>>> + S& operator=(const S&) = default; // #1
>>>> + S& operator=(S&&) = default; // #2
>>>> +};
>>>> +
>>>> +struct U {
>>>> + constexpr U& operator=(const U&) = default;
>>>> + constexpr U& operator=(U&&) = default;
>>>> +};
>>>> +
>>>> +/* FIXME: If we only declare #1 and #2, and default them here:
>>>> +
>>>> + S& S::operator=(const S&) = default;
>>>> + S& S::operator=(S&&) = default;
>>>> +
>>>> +then they aren't constexpr. This sounds like a bug:
>>>> +<https://gcc.gnu.org/PR107598>. */
>>>
>>> As I commented on the PR, I don't think this is actually a bug, so let's
>>> omit this FIXME.
>>
>> I'm glad I didn't really attempt to "fix" it (the inform message is
>> flawed
>> and should be improved). Thanks for taking a look.
>>
>> Here's a version with the two comments updated.
>>
>> Ok?
>
> OK.
Since this patch I'm seeing these failures:
FAIL: g++.dg/cpp0x/constexpr-ex1.C -std=c++23 -fimplicit-constexpr at
line 91 (test for errors, line 89)
FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23
-fimplicit-constexpr (test for warnings, line 14)
FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23
-fimplicit-constexpr (test for warnings, line 20)
FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23
-fimplicit-constexpr (test for warnings, line 28)
FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23
-fimplicit-constexpr (test for warnings, line 31)
FAIL: g++.dg/cpp2a/spaceship-eq3.C -std=c++23 -fimplicit-constexpr
(test for excess errors)
Jason
On Fri, Nov 18, 2022 at 09:26:26PM -0500, Jason Merrill wrote:
> On 11/16/22 15:27, Jason Merrill wrote:
> > On 11/16/22 11:06, Marek Polacek wrote:
> > > On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote:
> > > > On 11/15/22 19:30, Marek Polacek wrote:
> > > > > @@ -996,19 +1040,26 @@ register_constexpr_fundef (const
> > > > > constexpr_fundef &value)
> > > > > **slot = value;
> > > > > }
> > > > > -/* FUN is a non-constexpr function called in a context that requires a
> > > > > - constant expression. If it comes from a constexpr
> > > > > template, explain why
> > > > > - the instantiation isn't constexpr. */
> > > > > +/* FUN is a non-constexpr (or, with -Wno-invalid-constexpr,
> > > > > a constexpr
> > > > > + function called in a context that requires a constant expression).
> > > > > + If it comes from a constexpr template, explain why the
> > > > > instantiation
> > > > > + isn't constexpr. */
> > > >
> > > > The "if it comes from a constexpr template" wording has needed
> > > > an update for
> > > > a while now.
> > >
> > > Probably ever since r178519. I've added "Otherwise, explain why the
> > > function
> > > cannot be used in a constexpr context." Is that acceptable?
> > > > > --- /dev/null
> > > > > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
> > > > > @@ -0,0 +1,43 @@
> > > > > +// PR c++/106649
> > > > > +// P2448 - Relaxing some constexpr restrictions
> > > > > +// { dg-do compile { target c++23 } }
> > > > > +// { dg-options "-Winvalid-constexpr" }
> > > > > +// A copy/move assignment operator for a class X that is defaulted and
> > > > > +// not defined as deleted is implicitly defined when it is odr-used,
> > > > > +// when it is needed for constant evaluation, or when it is explicitly
> > > > > +// defaulted after its first declaration.
> > > > > +// The implicitly-defined copy/move assignment operator is constexpr.
> > > > > +
> > > > > +struct S {
> > > > > + constexpr S() {}
> > > > > + S& operator=(const S&) = default; // #1
> > > > > + S& operator=(S&&) = default; // #2
> > > > > +};
> > > > > +
> > > > > +struct U {
> > > > > + constexpr U& operator=(const U&) = default;
> > > > > + constexpr U& operator=(U&&) = default;
> > > > > +};
> > > > > +
> > > > > +/* FIXME: If we only declare #1 and #2, and default them here:
> > > > > +
> > > > > + S& S::operator=(const S&) = default;
> > > > > + S& S::operator=(S&&) = default;
> > > > > +
> > > > > +then they aren't constexpr. This sounds like a bug:
> > > > > +<https://gcc.gnu.org/PR107598>. */
> > > >
> > > > As I commented on the PR, I don't think this is actually a bug, so let's
> > > > omit this FIXME.
> > >
> > > I'm glad I didn't really attempt to "fix" it (the inform message is
> > > flawed
> > > and should be improved). Thanks for taking a look.
> > >
> > > Here's a version with the two comments updated.
> > >
> > > Ok?
> >
> > OK.
>
> Since this patch I'm seeing these failures:
>
> FAIL: g++.dg/cpp0x/constexpr-ex1.C -std=c++23 -fimplicit-constexpr at line
> 91 (test for errors, line 89)
> FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr
> (test for warnings, line 14)
> FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr
> (test for warnings, line 20)
> FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr
> (test for warnings, line 28)
> FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr
> (test for warnings, line 31)
> FAIL: g++.dg/cpp2a/spaceship-eq3.C -std=c++23 -fimplicit-constexpr (test
> for excess errors)
Ah, sorry. The following patch fixes those fails.
Ok?
-- >8 --
Some of the new tests were failing with -fimplicit-constexpr. This
patch adjusts the expected diagnostic. Tested with
GXX_TESTSUITE_STDS=98,11,14,17,20,23 make check-c++ RUNTESTFLAGS="--target_board=unix\{,-fimplicit-constexpr\} dg.exp=spaceship-eq3.C"
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/constexpr-ex1.C: Adjust dg-error.
* g++.dg/cpp23/constexpr-nonlit10.C: Adjust dg-warning.
* g++.dg/cpp23/constexpr-nonlit11.C: Likewise.
* g++.dg/cpp2a/spaceship-eq3.C: Add dg-error.
---
gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 6 +++---
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C | 4 ++--
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C | 4 ++--
gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 1 +
4 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
index 48281a47784..383d38a42d4 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
@@ -87,8 +87,8 @@ struct resource {
}
};
constexpr resource f(resource d)
-{ return d; } // { dg-error "non-.constexpr." "" { target { { ! implicit_constexpr } && c++20_down } } }
-// { dg-error "non-.constexpr." "" { target c++23 } .-2 }
-constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { ! implicit_constexpr } } }
+{ return d; } // { dg-error "non-.constexpr." "" { target { { { ! implicit_constexpr } && c++20_down } || c++11_only } } }
+// { dg-error "non-.constexpr." "" { target { c++23 && { ! implicit_constexpr } } } .-2 }
+constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { { ! implicit_constexpr } || c++11_only } } }
// 4.4 floating-point constant expressions
diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
index 48706f7b66e..31d4b873bbf 100644
--- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
+++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
@@ -11,13 +11,13 @@ struct NonLiteral {
// C++23: It is possible to write a constexpr function for which no
// invocation satisfies the requirements of a core constant expression.
constexpr NonLiteral
-fn0 (int) // { dg-warning "invalid return type" }
+fn0 (int) // { dg-warning "invalid return type" "" { target { ! implicit_constexpr } } }
{
return NonLiteral{};
}
constexpr int
-fn1 (NonLiteral) // { dg-warning "invalid type" }
+fn1 (NonLiteral) // { dg-warning "invalid type" "" { target { ! implicit_constexpr } } }
{
return 42;
}
diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
index a7114bc66cb..e08809f873c 100644
--- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
+++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
@@ -25,10 +25,10 @@ struct X {
struct S {
X x;
// Calls a non-constexpr constructor X::X(int).
- constexpr S(int i) : x(i) { } // { dg-warning "call to" }
+ constexpr S(int i) : x(i) { } // { dg-warning "call to" "" { target { ! implicit_constexpr } } }
S(int, int) { }
// Target constructor isn't constexpr.
- constexpr S() : S(42, 42) { } // { dg-warning "call to" }
+ constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target { ! implicit_constexpr } } }
};
namespace N1 {
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
index 69eaa7b9b20..246839f1f95 100644
--- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
+++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
@@ -9,6 +9,7 @@ struct D
A i;
bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } }
bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } }
+// { dg-error "called" "" { target { c++23 && implicit_constexpr } } .-1 }
};
constexpr D d{A()};
base-commit: d19aa6af6634b1e97f38431ad091f3b3f12baf2f
On 12/2/22 14:48, Marek Polacek wrote:
> On Fri, Nov 18, 2022 at 09:26:26PM -0500, Jason Merrill wrote:
>> On 11/16/22 15:27, Jason Merrill wrote:
>>> On 11/16/22 11:06, Marek Polacek wrote:
>>>> On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote:
>>>>> On 11/15/22 19:30, Marek Polacek wrote:
>>>>>> @@ -996,19 +1040,26 @@ register_constexpr_fundef (const
>>>>>> constexpr_fundef &value)
>>>>>> **slot = value;
>>>>>> }
>>>>>> -/* FUN is a non-constexpr function called in a context that requires a
>>>>>> - constant expression. If it comes from a constexpr
>>>>>> template, explain why
>>>>>> - the instantiation isn't constexpr. */
>>>>>> +/* FUN is a non-constexpr (or, with -Wno-invalid-constexpr,
>>>>>> a constexpr
>>>>>> + function called in a context that requires a constant expression).
>>>>>> + If it comes from a constexpr template, explain why the
>>>>>> instantiation
>>>>>> + isn't constexpr. */
>>>>>
>>>>> The "if it comes from a constexpr template" wording has needed
>>>>> an update for
>>>>> a while now.
>>>>
>>>> Probably ever since r178519. I've added "Otherwise, explain why the
>>>> function
>>>> cannot be used in a constexpr context." Is that acceptable?
>>>>>> --- /dev/null
>>>>>> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
>>>>>> @@ -0,0 +1,43 @@
>>>>>> +// PR c++/106649
>>>>>> +// P2448 - Relaxing some constexpr restrictions
>>>>>> +// { dg-do compile { target c++23 } }
>>>>>> +// { dg-options "-Winvalid-constexpr" }
>>>>>> +// A copy/move assignment operator for a class X that is defaulted and
>>>>>> +// not defined as deleted is implicitly defined when it is odr-used,
>>>>>> +// when it is needed for constant evaluation, or when it is explicitly
>>>>>> +// defaulted after its first declaration.
>>>>>> +// The implicitly-defined copy/move assignment operator is constexpr.
>>>>>> +
>>>>>> +struct S {
>>>>>> + constexpr S() {}
>>>>>> + S& operator=(const S&) = default; // #1
>>>>>> + S& operator=(S&&) = default; // #2
>>>>>> +};
>>>>>> +
>>>>>> +struct U {
>>>>>> + constexpr U& operator=(const U&) = default;
>>>>>> + constexpr U& operator=(U&&) = default;
>>>>>> +};
>>>>>> +
>>>>>> +/* FIXME: If we only declare #1 and #2, and default them here:
>>>>>> +
>>>>>> + S& S::operator=(const S&) = default;
>>>>>> + S& S::operator=(S&&) = default;
>>>>>> +
>>>>>> +then they aren't constexpr. This sounds like a bug:
>>>>>> +<https://gcc.gnu.org/PR107598>. */
>>>>>
>>>>> As I commented on the PR, I don't think this is actually a bug, so let's
>>>>> omit this FIXME.
>>>>
>>>> I'm glad I didn't really attempt to "fix" it (the inform message is
>>>> flawed
>>>> and should be improved). Thanks for taking a look.
>>>>
>>>> Here's a version with the two comments updated.
>>>>
>>>> Ok?
>>>
>>> OK.
>>
>> Since this patch I'm seeing these failures:
>>
>> FAIL: g++.dg/cpp0x/constexpr-ex1.C -std=c++23 -fimplicit-constexpr at line
>> 91 (test for errors, line 89)
>> FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr
>> (test for warnings, line 14)
>> FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr
>> (test for warnings, line 20)
>> FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr
>> (test for warnings, line 28)
>> FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr
>> (test for warnings, line 31)
>> FAIL: g++.dg/cpp2a/spaceship-eq3.C -std=c++23 -fimplicit-constexpr (test
>> for excess errors)
>
> Ah, sorry. The following patch fixes those fails.
>
> Ok?
OK, thanks.
> -- >8 --
> Some of the new tests were failing with -fimplicit-constexpr. This
> patch adjusts the expected diagnostic. Tested with
>
> GXX_TESTSUITE_STDS=98,11,14,17,20,23 make check-c++ RUNTESTFLAGS="--target_board=unix\{,-fimplicit-constexpr\} dg.exp=spaceship-eq3.C"
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/cpp0x/constexpr-ex1.C: Adjust dg-error.
> * g++.dg/cpp23/constexpr-nonlit10.C: Adjust dg-warning.
> * g++.dg/cpp23/constexpr-nonlit11.C: Likewise.
> * g++.dg/cpp2a/spaceship-eq3.C: Add dg-error.
> ---
> gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 6 +++---
> gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C | 4 ++--
> gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C | 4 ++--
> gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 1 +
> 4 files changed, 8 insertions(+), 7 deletions(-)
>
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
> index 48281a47784..383d38a42d4 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
> @@ -87,8 +87,8 @@ struct resource {
> }
> };
> constexpr resource f(resource d)
> -{ return d; } // { dg-error "non-.constexpr." "" { target { { ! implicit_constexpr } && c++20_down } } }
> -// { dg-error "non-.constexpr." "" { target c++23 } .-2 }
> -constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { ! implicit_constexpr } } }
> +{ return d; } // { dg-error "non-.constexpr." "" { target { { { ! implicit_constexpr } && c++20_down } || c++11_only } } }
> +// { dg-error "non-.constexpr." "" { target { c++23 && { ! implicit_constexpr } } } .-2 }
> +constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { { ! implicit_constexpr } || c++11_only } } }
>
> // 4.4 floating-point constant expressions
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
> index 48706f7b66e..31d4b873bbf 100644
> --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
> @@ -11,13 +11,13 @@ struct NonLiteral {
> // C++23: It is possible to write a constexpr function for which no
> // invocation satisfies the requirements of a core constant expression.
> constexpr NonLiteral
> -fn0 (int) // { dg-warning "invalid return type" }
> +fn0 (int) // { dg-warning "invalid return type" "" { target { ! implicit_constexpr } } }
> {
> return NonLiteral{};
> }
>
> constexpr int
> -fn1 (NonLiteral) // { dg-warning "invalid type" }
> +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target { ! implicit_constexpr } } }
> {
> return 42;
> }
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
> index a7114bc66cb..e08809f873c 100644
> --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
> @@ -25,10 +25,10 @@ struct X {
> struct S {
> X x;
> // Calls a non-constexpr constructor X::X(int).
> - constexpr S(int i) : x(i) { } // { dg-warning "call to" }
> + constexpr S(int i) : x(i) { } // { dg-warning "call to" "" { target { ! implicit_constexpr } } }
> S(int, int) { }
> // Target constructor isn't constexpr.
> - constexpr S() : S(42, 42) { } // { dg-warning "call to" }
> + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target { ! implicit_constexpr } } }
> };
>
> namespace N1 {
> diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
> index 69eaa7b9b20..246839f1f95 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
> @@ -9,6 +9,7 @@ struct D
> A i;
> bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } }
> bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } }
> +// { dg-error "called" "" { target { c++23 && implicit_constexpr } } .-1 }
> };
>
> constexpr D d{A()};
>
> base-commit: d19aa6af6634b1e97f38431ad091f3b3f12baf2f
@@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile)
/* Set feature test macros for C++23. */
cpp_define (pfile, "__cpp_size_t_suffix=202011L");
cpp_define (pfile, "__cpp_if_consteval=202106L");
- cpp_define (pfile, "__cpp_constexpr=202110L");
+ cpp_define (pfile, "__cpp_constexpr=202207L");
cpp_define (pfile, "__cpp_multidimensional_subscript=202211L");
cpp_define (pfile, "__cpp_named_character_escapes=202207L");
cpp_define (pfile, "__cpp_static_call_operator=202207L");
@@ -1059,6 +1059,10 @@ c_common_post_options (const char **pfilename)
if (flag_sized_deallocation == -1)
flag_sized_deallocation = (cxx_dialect >= cxx14);
+ /* Pedwarn about invalid constexpr functions before C++23. */
+ if (warn_invalid_constexpr == -1)
+ warn_invalid_constexpr = (cxx_dialect < cxx23);
+
/* char8_t support is implicitly enabled in C++20 and C2X. */
if (flag_char8_t == -1)
flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc2x;
@@ -817,6 +817,10 @@ Wint-to-pointer-cast
C ObjC C++ ObjC++ Var(warn_int_to_pointer_cast) Init(1) Warning
Warn when there is a cast to a pointer from an integer of a different size.
+Winvalid-constexpr
+C++ ObjC++ Var(warn_invalid_constexpr) Init(-1) Warning
+Warn when a function never produces a constant expression.
+
Winvalid-offsetof
C++ ObjC++ Var(warn_invalid_offsetof) Init(1) Warning
Warn about invalid uses of the \"offsetof\" macro.
@@ -139,6 +139,42 @@ ensure_literal_type_for_constexpr_object (tree decl)
return decl;
}
+/* Issue a diagnostic with text GMSGID for constructs that are invalid in
+ constexpr functions. CONSTEXPR_FUNDEF_P is true if we're checking
+ a constexpr function body; if so, don't report hard errors and issue
+ a pedwarn pre-C++23, or a warning in C++23, if requested by
+ -Winvalid-constexpr. Otherwise, we're not in the context where we are
+ checking if a function can be marked 'constexpr', so give a hard error. */
+
+ATTRIBUTE_GCC_DIAG(3,4)
+static bool
+constexpr_error (location_t location, bool constexpr_fundef_p,
+ const char *gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+ rich_location richloc (line_table, location);
+ va_start (ap, gmsgid);
+ bool ret;
+ if (!constexpr_fundef_p)
+ {
+ /* Report an error that cannot be suppressed. */
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
+ ret = diagnostic_report_diagnostic (global_dc, &diagnostic);
+ }
+ else if (warn_invalid_constexpr)
+ {
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
+ cxx_dialect < cxx23 ? DK_PEDWARN : DK_WARNING);
+ diagnostic.option_index = OPT_Winvalid_constexpr;
+ ret = diagnostic_report_diagnostic (global_dc, &diagnostic);
+ }
+ else
+ ret = false;
+ va_end (ap);
+ return ret;
+}
+
struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
{
static hashval_t hash (const constexpr_fundef *);
@@ -208,9 +244,11 @@ is_valid_constexpr_fn (tree fun, bool complain)
if (complain)
{
auto_diagnostic_group d;
- error ("invalid type for parameter %d of %<constexpr%> "
- "function %q+#D", DECL_PARM_INDEX (parm), fun);
- explain_non_literal_class (TREE_TYPE (parm));
+ if (constexpr_error (input_location, /*constexpr_fundef_p*/true,
+ "invalid type for parameter %d of "
+ "%<constexpr%> function %q+#D",
+ DECL_PARM_INDEX (parm), fun))
+ explain_non_literal_class (TREE_TYPE (parm));
}
}
}
@@ -242,9 +280,10 @@ is_valid_constexpr_fn (tree fun, bool complain)
if (complain)
{
auto_diagnostic_group d;
- error ("invalid return type %qT of %<constexpr%> function %q+D",
- rettype, fun);
- explain_non_literal_class (rettype);
+ if (constexpr_error (input_location, /*constexpr_fundef_p*/true,
+ "invalid return type %qT of %<constexpr%> "
+ "function %q+D", rettype, fun))
+ explain_non_literal_class (rettype);
}
}
@@ -918,7 +957,7 @@ maybe_save_constexpr_fundef (tree fun)
bool potential = potential_rvalue_constant_expression (massaged);
if (!potential && complain)
- require_potential_rvalue_constant_expression (massaged);
+ require_potential_rvalue_constant_expression_fncheck (massaged);
if (DECL_CONSTRUCTOR_P (fun) && potential
&& !DECL_DEFAULTED_FN (fun))
@@ -933,11 +972,16 @@ maybe_save_constexpr_fundef (tree fun)
massaged = DECL_SAVED_TREE (fun);
potential = potential_rvalue_constant_expression (massaged);
if (!potential && complain)
- require_potential_rvalue_constant_expression (massaged);
+ require_potential_rvalue_constant_expression_fncheck (massaged);
}
}
- if (!potential && complain)
+ if (!potential && complain
+ /* If -Wno-invalid-constexpr was specified, we haven't complained
+ about non-constant expressions yet. Register the function and
+ complain in explain_invalid_constexpr_fn if the function is
+ called. */
+ && warn_invalid_constexpr != 0)
return;
if (implicit)
@@ -996,19 +1040,27 @@ register_constexpr_fundef (const constexpr_fundef &value)
**slot = value;
}
-/* FUN is a non-constexpr function called in a context that requires a
- constant expression. If it comes from a constexpr template, explain why
- the instantiation isn't constexpr. */
+/* FUN is a non-constexpr (or, with -Wno-invalid-constexpr, a constexpr
+ function called in a context that requires a constant expression).
+ If it comes from a constexpr template, explain why the instantiation
+ isn't constexpr. Otherwise, explain why the function cannot be used
+ in a constexpr context. */
void
explain_invalid_constexpr_fn (tree fun)
{
static hash_set<tree> *diagnosed;
tree body;
+ /* In C++23, a function marked 'constexpr' may not actually be a constant
+ expression. We haven't diagnosed the problem yet: -Winvalid-constexpr
+ wasn't enabled. The function was called, so diagnose why it cannot be
+ used in a constant expression. */
+ if (warn_invalid_constexpr == 0 && DECL_DECLARED_CONSTEXPR_P (fun))
+ /* Go on. */;
/* Only diagnose defaulted functions, lambdas, or instantiations. */
- if (!DECL_DEFAULTED_FN (fun)
- && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
- && !is_instantiation_of_constexpr (fun))
+ else if (!DECL_DEFAULTED_FN (fun)
+ && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
+ && !is_instantiation_of_constexpr (fun))
{
inform (DECL_SOURCE_LOCATION (fun), "%qD declared here", fun);
return;
@@ -5612,11 +5664,12 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
}
/* Complain about R, a VAR_DECL, not being usable in a constant expression.
+ FUNDEF_P is true if we're checking a constexpr function body.
Shared between potential_constant_expression and
cxx_eval_constant_expression. */
static void
-non_const_var_error (location_t loc, tree r)
+non_const_var_error (location_t loc, tree r, bool fundef_p)
{
auto_diagnostic_group d;
tree type = TREE_TYPE (r);
@@ -5625,20 +5678,21 @@ non_const_var_error (location_t loc, tree r)
|| DECL_NAME (r) == heap_vec_uninit_identifier
|| DECL_NAME (r) == heap_vec_identifier)
{
- error_at (loc, "the content of uninitialized storage is not usable "
- "in a constant expression");
- inform (DECL_SOURCE_LOCATION (r), "allocated here");
+ if (constexpr_error (loc, fundef_p, "the content of uninitialized "
+ "storage is not usable in a constant expression"))
+ inform (DECL_SOURCE_LOCATION (r), "allocated here");
return;
}
if (DECL_NAME (r) == heap_deleted_identifier)
{
- error_at (loc, "use of allocated storage after deallocation in a "
- "constant expression");
- inform (DECL_SOURCE_LOCATION (r), "allocated here");
+ if (constexpr_error (loc, fundef_p, "use of allocated storage after "
+ "deallocation in a constant expression"))
+ inform (DECL_SOURCE_LOCATION (r), "allocated here");
return;
}
- error_at (loc, "the value of %qD is not usable in a constant "
- "expression", r);
+ if (!constexpr_error (loc, fundef_p, "the value of %qD is not usable in "
+ "a constant expression", r))
+ return;
/* Avoid error cascade. */
if (DECL_INITIAL (r) == error_mark_node)
return;
@@ -6697,15 +6751,17 @@ lookup_placeholder (const constexpr_ctx *ctx, value_cat lval, tree type)
return ob;
}
-/* Complain about an attempt to evaluate inline assembly. */
+/* Complain about an attempt to evaluate inline assembly. If FUNDEF_P is
+ true, we're checking a constexpr function body. */
static void
-inline_asm_in_constexpr_error (location_t loc)
+inline_asm_in_constexpr_error (location_t loc, bool fundef_p)
{
auto_diagnostic_group d;
- error_at (loc, "inline assembly is not a constant expression");
- inform (loc, "only unevaluated inline assembly is allowed in a "
- "%<constexpr%> function in C++20");
+ if (constexpr_error (loc, fundef_p, "inline assembly is not a "
+ "constant expression"))
+ inform (loc, "only unevaluated inline assembly is allowed in a "
+ "%<constexpr%> function in C++20");
}
/* We're getting the constant value of DECL in a manifestly constant-evaluated
@@ -6983,7 +7039,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
if (DECL_P (r))
{
if (!ctx->quiet)
- non_const_var_error (loc, r);
+ non_const_var_error (loc, r, /*fundef_p*/false);
*non_constant_p = true;
}
break;
@@ -7874,7 +7930,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case ASM_EXPR:
if (!ctx->quiet)
- inline_asm_in_constexpr_error (loc);
+ inline_asm_in_constexpr_error (loc, /*constexpr_fundef_p*/false);
*non_constant_p = true;
return t;
@@ -8759,7 +8815,8 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
diagnostic as appropriate under control of FLAGS. If WANT_RVAL is true,
an lvalue-rvalue conversion is implied. If NOW is true, we want to
consider the expression in the current context, independent of constexpr
- substitution.
+ substitution. If FUNDEF_P is true, we're checking a constexpr function body
+ and hard errors should not be reported by constexpr_error.
C++0x [expr.const] used to say
@@ -8776,10 +8833,12 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
static bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
- tsubst_flags_t flags, tree *jump_target)
+ bool fundef_p, tsubst_flags_t flags,
+ tree *jump_target)
{
#define RECUR(T,RV) \
- potential_constant_expression_1 ((T), (RV), strict, now, flags, jump_target)
+ potential_constant_expression_1 ((T), (RV), strict, now, fundef_p, flags, \
+ jump_target)
enum { any = false, rval = true };
int i;
@@ -8801,8 +8860,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (TREE_THIS_VOLATILE (t) && want_rval)
{
if (flags & tf_error)
- error_at (loc, "lvalue-to-rvalue conversion of a volatile lvalue "
- "%qE with type %qT", t, TREE_TYPE (t));
+ constexpr_error (loc, fundef_p, "lvalue-to-rvalue conversion of "
+ "a volatile lvalue %qE with type %qT", t,
+ TREE_TYPE (t));
return false;
}
if (CONSTANT_CLASS_P (t))
@@ -8861,7 +8921,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
/* An empty class has no data to read. */
return true;
if (flags & tf_error)
- error ("%qE is not a constant expression", t);
+ constexpr_error (input_location, fundef_p,
+ "%qE is not a constant expression", t);
return false;
}
return true;
@@ -8910,7 +8971,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
{
/* fold_call_expr can't do anything with IFN calls. */
if (flags & tf_error)
- error_at (loc, "call to internal function %qE", t);
+ constexpr_error (loc, fundef_p,
+ "call to internal function %qE", t);
return false;
}
}
@@ -8940,12 +9002,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
|| !is_std_construct_at (current_function_decl))
&& !cxx_dynamic_cast_fn_p (fun))
{
- if (flags & tf_error)
- {
- error_at (loc, "call to non-%<constexpr%> function %qD",
- fun);
- explain_invalid_constexpr_fn (fun);
- }
+ if ((flags & tf_error)
+ && constexpr_error (loc, fundef_p,
+ "call to non-%<constexpr%> "
+ "function %qD", fun))
+ explain_invalid_constexpr_fn (fun);
return false;
}
/* A call to a non-static member function takes the address
@@ -8962,8 +9023,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
constexpr substitution might not use the value. */
bool sub_now = false;
if (!potential_constant_expression_1 (x, rval, strict,
- sub_now, flags,
- jump_target))
+ sub_now, fundef_p,
+ flags, jump_target))
return false;
i = 1;
}
@@ -8997,7 +9058,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
substitution might not use the value of the argument. */
bool sub_now = false;
if (!potential_constant_expression_1 (x, rv, strict,
- sub_now, flags, jump_target))
+ sub_now, fundef_p, flags,
+ jump_target))
return false;
}
return true;
@@ -9035,9 +9097,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (flags & tf_error)
{
tree cap = DECL_CAPTURED_VARIABLE (t);
- error ("lambda capture of %qE is not a constant expression",
- cap);
- if (decl_constant_var_p (cap))
+ if (constexpr_error (input_location, fundef_p,
+ "lambda capture of %qE is not a "
+ "constant expression", cap)
+ && decl_constant_var_p (cap))
inform (input_location, "because it is used as a glvalue");
}
return false;
@@ -9060,8 +9123,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
&& COMPLETE_TYPE_P (TREE_TYPE (t))
&& !is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false))
{
- if (flags & tf_error)
- non_const_var_error (loc, t);
+ if (flags & tf_error)
+ non_const_var_error (loc, t, fundef_p);
return false;
}
return true;
@@ -9070,7 +9133,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (REINTERPRET_CAST_P (t))
{
if (flags & tf_error)
- error_at (loc, "%<reinterpret_cast%> is not a constant expression");
+ constexpr_error (loc, fundef_p, "%<reinterpret_cast%> is not a "
+ "constant expression");
return false;
}
/* FALLTHRU */
@@ -9092,8 +9156,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
&& !integer_zerop (from))
{
if (flags & tf_error)
- error_at (loc,
- "%<reinterpret_cast%> from integer to pointer");
+ constexpr_error (loc, fundef_p,
+ "%<reinterpret_cast%> from integer to "
+ "pointer");
return false;
}
}
@@ -9165,7 +9230,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (!var_in_maybe_constexpr_fn (x))
{
if (flags & tf_error)
- error_at (loc, "use of %<this%> in a constant expression");
+ constexpr_error (loc, fundef_p, "use of %<this%> in a "
+ "constant expression");
return false;
}
return true;
@@ -9313,8 +9379,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
/* In C++17 lambdas can be constexpr, don't give up yet. */
return true;
else if (flags & tf_error)
- error_at (loc, "lambda-expression is not a constant expression "
- "before C++17");
+ constexpr_error (loc, fundef_p, "lambda-expression is not a "
+ "constant expression before C++17");
return false;
case NEW_EXPR:
@@ -9325,8 +9391,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
/* In C++20, new-expressions are potentially constant. */
return true;
else if (flags & tf_error)
- error_at (loc, "new-expression is not a constant expression "
- "before C++20");
+ constexpr_error (loc, fundef_p, "new-expression is not a "
+ "constant expression before C++20");
return false;
case DYNAMIC_CAST_EXPR:
@@ -9375,12 +9441,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
case AT_ENCODE_EXPR:
fail:
if (flags & tf_error)
- error_at (loc, "expression %qE is not a constant expression", t);
+ constexpr_error (loc, fundef_p, "expression %qE is not a constant "
+ "expression", t);
return false;
case ASM_EXPR:
if (flags & tf_error)
- inline_asm_in_constexpr_error (loc);
+ inline_asm_in_constexpr_error (loc, fundef_p);
return false;
case OBJ_TYPE_REF:
@@ -9388,8 +9455,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
/* In C++20 virtual calls can be constexpr, don't give up yet. */
return true;
else if (flags & tf_error)
- error_at (loc,
- "virtual functions cannot be %<constexpr%> before C++20");
+ constexpr_error (loc, fundef_p, "virtual functions cannot be "
+ "%<constexpr%> before C++20");
return false;
case TYPEID_EXPR:
@@ -9404,8 +9471,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
&& TYPE_POLYMORPHIC_P (TREE_TYPE (e)))
{
if (flags & tf_error)
- error_at (loc, "%<typeid%> is not a constant expression "
- "because %qE is of polymorphic type", e);
+ constexpr_error (loc, fundef_p, "%<typeid%> is not a "
+ "constant expression because %qE is "
+ "of polymorphic type", e);
return false;
}
return true;
@@ -9465,9 +9533,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
constant expression. */
{
if (flags & tf_error)
- error_at (loc,
- "cast to non-integral type %qT in a constant expression",
- TREE_TYPE (t));
+ constexpr_error (loc, fundef_p,
+ "cast to non-integral type %qT in a constant "
+ "expression", TREE_TYPE (t));
return false;
}
/* This might be a conversion from a class to a (potentially) literal
@@ -9523,15 +9591,17 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (CP_DECL_THREAD_LOCAL_P (tmp) && !DECL_REALLY_EXTERN (tmp))
{
if (flags & tf_error)
- error_at (DECL_SOURCE_LOCATION (tmp), "%qD defined "
- "%<thread_local%> in %<constexpr%> context", tmp);
+ constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p,
+ "%qD defined %<thread_local%> in "
+ "%<constexpr%> context", tmp);
return false;
}
else if (TREE_STATIC (tmp))
{
if (flags & tf_error)
- error_at (DECL_SOURCE_LOCATION (tmp), "%qD defined "
- "%<static%> in %<constexpr%> context", tmp);
+ constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p,
+ "%qD defined %<static%> in %<constexpr%> "
+ "context", tmp);
return false;
}
else if (!check_for_uninitialized_const_var
@@ -9554,9 +9624,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (flags & tf_error)
{
auto_diagnostic_group d;
- error_at (loc, "temporary of non-literal type %qT in a "
- "constant expression", TREE_TYPE (t));
- explain_non_literal_class (TREE_TYPE (t));
+ if (constexpr_error (loc, fundef_p,
+ "temporary of non-literal type %qT in a "
+ "constant expression", TREE_TYPE (t)))
+ explain_non_literal_class (TREE_TYPE (t));
}
return false;
}
@@ -9603,7 +9674,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (integer_zerop (denom))
{
if (flags & tf_error)
- error ("division by zero is not a constant expression");
+ constexpr_error (input_location, fundef_p,
+ "division by zero is not a constant expression");
return false;
}
else
@@ -9704,7 +9776,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (COND_EXPR_IS_VEC_DELETE (t) && cxx_dialect < cxx20)
{
if (flags & tf_error)
- error_at (loc, "%<delete[]%> is not a constant expression");
+ constexpr_error (loc, fundef_p, "%<delete[]%> is not a "
+ "constant expression");
return false;
}
/* Fall through. */
@@ -9734,7 +9807,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
{
tree this_jump_target = tmp;
if (potential_constant_expression_1 (TREE_OPERAND (t, i),
- want_rval, strict, now,
+ want_rval, strict, now, fundef_p,
tf_none, &this_jump_target))
{
if (returns (&this_jump_target))
@@ -9772,9 +9845,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (flags & tf_error)
{
if (TREE_CODE (t) == IF_STMT)
- error_at (loc, "neither branch of %<if%> is a constant expression");
+ constexpr_error (loc, fundef_p, "neither branch of %<if%> is a "
+ "constant expression");
else
- error_at (loc, "expression %qE is not a constant expression", t);
+ constexpr_error (loc, fundef_p, "expression %qE is not a "
+ "constant expression", t);
}
return false;
@@ -9783,8 +9858,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return true;
if (flags & tf_error)
{
- error_at (loc, "non-constant array initialization");
- diagnose_non_constexpr_vec_init (t);
+ if (constexpr_error (loc, fundef_p, "non-constant array "
+ "initialization"))
+ diagnose_non_constexpr_vec_init (t);
}
return false;
@@ -9813,7 +9889,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return true;
}
if (flags & tf_error)
- error_at (loc, "%<goto%> is not a constant expression");
+ constexpr_error (loc, fundef_p, "%<goto%> is not a constant "
+ "expression");
return false;
}
@@ -9822,8 +9899,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
return true;
else if (flags & tf_error)
- error_at (loc, "label definition in %<constexpr%> function only "
- "available with %<-std=c++2b%> or %<-std=gnu++2b%>");
+ constexpr_error (loc, fundef_p, "label definition in %<constexpr%> "
+ "function only available with %<-std=c++2b%> or "
+ "%<-std=gnu++2b%>");
return false;
case ANNOTATE_EXPR:
@@ -9861,7 +9939,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
- tsubst_flags_t flags)
+ bool fundef_p, tsubst_flags_t flags)
{
if (flags & tf_error)
{
@@ -9869,13 +9947,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
efficiently in some cases (currently only for TRUTH_*_EXPR). If
that fails, replay the check noisily to give errors. */
flags &= ~tf_error;
- if (potential_constant_expression_1 (t, want_rval, strict, now, flags))
+ if (potential_constant_expression_1 (t, want_rval, strict, now, fundef_p,
+ flags))
return true;
flags |= tf_error;
}
tree target = NULL_TREE;
- return potential_constant_expression_1 (t, want_rval, strict, now,
+ return potential_constant_expression_1 (t, want_rval, strict, now, fundef_p,
flags, &target);
}
@@ -9884,7 +9963,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
bool
potential_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, false, true, false, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/false, /*strict*/true,
+ /*now*/false, /*fundef_p*/false,
+ tf_none);
}
/* As above, but require a constant rvalue. */
@@ -9892,7 +9973,9 @@ potential_constant_expression (tree t)
bool
potential_rvalue_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, true, true, false, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/false, /*fundef_p*/false,
+ tf_none);
}
/* Like above, but complain about non-constant expressions. */
@@ -9900,7 +9983,8 @@ potential_rvalue_constant_expression (tree t)
bool
require_potential_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, false, true, false,
+ return potential_constant_expression_1 (t, /*want_rval*/false, /*strict*/true,
+ /*now*/false, /*fundef_p*/false,
tf_warning_or_error);
}
@@ -9909,7 +9993,18 @@ require_potential_constant_expression (tree t)
bool
require_potential_rvalue_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, true, true, false,
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/false, /*fundef_p*/false,
+ tf_warning_or_error);
+}
+
+/* Like require_potential_rvalue_constant_expression, but fundef_p is true. */
+
+bool
+require_potential_rvalue_constant_expression_fncheck (tree t)
+{
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/false, /*fundef_p*/true,
tf_warning_or_error);
}
@@ -9918,7 +10013,8 @@ require_potential_rvalue_constant_expression (tree t)
bool
require_rvalue_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, true, true, true,
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/true, /*fundef_p*/false,
tf_warning_or_error);
}
@@ -9932,7 +10028,9 @@ require_rvalue_constant_expression (tree t)
bool
is_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, false, true, true, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/false, /*strict*/true,
+ /*now*/true, /*fundef_p*/false,
+ tf_none);
}
/* As above, but expect an rvalue. */
@@ -9940,7 +10038,9 @@ is_constant_expression (tree t)
bool
is_rvalue_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, true, true, true, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/true, /*fundef_p*/false,
+ tf_none);
}
/* Like above, but complain about non-constant expressions. */
@@ -9948,7 +10048,8 @@ is_rvalue_constant_expression (tree t)
bool
require_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, false, true, true,
+ return potential_constant_expression_1 (t, /*want_rval*/false, /*strict*/true,
+ /*now*/true, /*fundef_p*/false,
tf_warning_or_error);
}
@@ -9958,7 +10059,9 @@ require_constant_expression (tree t)
bool
is_static_init_expression (tree t)
{
- return potential_constant_expression_1 (t, false, false, true, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/false,
+ /*strict*/false, /*now*/true,
+ /*fundef_p*/false, tf_none);
}
/* Returns true if T is a potential constant expression that is not
@@ -8450,6 +8450,7 @@ extern bool require_potential_constant_expression (tree);
extern bool require_constant_expression (tree);
extern bool require_rvalue_constant_expression (tree);
extern bool require_potential_rvalue_constant_expression (tree);
+extern bool require_potential_rvalue_constant_expression_fncheck (tree);
extern tree cxx_constant_value (tree, tree = NULL_TREE,
tsubst_flags_t = tf_error);
inline tree cxx_constant_value (tree t, tsubst_flags_t complain)
@@ -1332,7 +1332,7 @@ struct comp_info
&& !potential_rvalue_constant_expression (expr))
{
if (was_constexp)
- require_potential_rvalue_constant_expression (expr);
+ require_potential_rvalue_constant_expression_fncheck (expr);
else
constexp = false;
}
@@ -2670,13 +2670,17 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
requirements of a constexpr constructor (7.1.5), the
implicitly-defined default constructor is constexpr.
+ C++20:
The implicitly-defined copy/move assignment operator is constexpr if
- X is a literal type, and
- the assignment operator selected to copy/move each direct base class
subobject is a constexpr function, and
- for each non-static data member of X that is of class type (or array
thereof), the assignment operator selected to copy/move that
- member is a constexpr function. */
+ member is a constexpr function.
+
+ C++23:
+ The implicitly-defined copy/move assignment operator is constexpr. */
if (constexpr_p)
*constexpr_p = (SFK_CTOR_P (sfk)
|| (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14)
@@ -256,7 +256,7 @@ in the following sections.
-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol
-Weffc++ -Wno-exceptions -Wextra-semi -Wno-inaccessible-base @gol
-Wno-inherited-variadic-ctor -Wno-init-list-lifetime @gol
--Winvalid-imported-macros @gol
+-Winvalid-constexpr -Winvalid-imported-macros @gol
-Wno-invalid-offsetof -Wno-literal-suffix @gol
-Wmismatched-new-delete -Wmismatched-tags @gol
-Wmultiple-inheritance -Wnamespaces -Wnarrowing @gol
@@ -3766,6 +3766,32 @@ the variable declaration statement.
@end itemize
+@item -Winvalid-constexpr
+@opindex Winvalid-constexpr
+@opindex Wno-invalid-constexpr
+
+Warn when a function never produces a constant expression. In C++20
+and earlier, for every @code{constexpr} function and function template,
+there must be at least one set of function arguments in at least one
+instantiation such that an invocation of the function or constructor
+could be an evaluated subexpression of a core constant expression.
+C++23 removed this restriction, so it's possible to have a function
+or a function template marked @code{constexpr} for which no invocation
+satisfies the requirements of a core constant expression.
+
+This warning is enabled as a pedantic warning by default in C++20 and
+earlier. In C++23, @option{-Winvalid-constexpr} can be turned on, in
+which case it will be an ordinary warning. For example:
+
+@smallexample
+void f (int& i);
+constexpr void
+g (int& i)
+@{
+ f(i); // warns by default in C++20, in C++23 only with -Winvalid-constexpr
+@}
+@end smallexample
+
@item -Winvalid-imported-macros
@opindex Winvalid-imported-macros
@opindex Wno-invalid-imported-macros
@@ -7,5 +7,5 @@ struct A
struct B : A
{
- constexpr B(): A() { } // { dg-error "A::A" }
+ constexpr B(): A() { } // { dg-error "A::A" "" { target c++20_down } }
};
@@ -7,6 +7,6 @@ struct A {
struct B: A { };
constexpr int f(B b) { return b.i; }
-struct C { C(); }; // { dg-message "" }
-struct D: C { }; // { dg-message "" }
-constexpr int g(D d) { return 42; } // { dg-error "invalid type" }
+struct C { C(); }; // { dg-message "" "" { target c++20_down } }
+struct D: C { }; // { dg-message "" "" { target c++20_down } }
+constexpr int g(D d) { return 42; } // { dg-error "invalid type" "" { target c++20_down } }
@@ -37,7 +37,7 @@ struct base // { dg-message "no .constexpr. constructor" "" { target { !
struct derived : public base // { dg-message "base class" "" { target { ! implicit_constexpr } } }
{
- constexpr derived(): base() { } // { dg-error "non-.constexpr. function" "" { target { ! implicit_constexpr } } }
+ constexpr derived(): base() { } // { dg-error "non-.constexpr. function" "" { target { { ! implicit_constexpr } && c++20_down } } }
};
constexpr derived obj; // { dg-error "not literal" "" { target { ! implicit_constexpr } } }
@@ -87,7 +87,8 @@ struct resource {
}
};
constexpr resource f(resource d)
-{ return d; } // { dg-error "non-.constexpr." "" { target { ! implicit_constexpr } } }
+{ return d; } // { dg-error "non-.constexpr." "" { target { { ! implicit_constexpr } && c++20_down } } }
+// { dg-error "non-.constexpr." "" { target c++23 } .-2 }
constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { ! implicit_constexpr } } }
// 4.4 floating-point constant expressions
@@ -5,7 +5,7 @@ struct A { A(); };
struct B {
friend constexpr int f(B) { return 0; } // OK
- friend constexpr int f(A) { return 0; } // { dg-error "constexpr" }
+ friend constexpr int f(A) { return 0; } // { dg-error "constexpr" "" { target c++20_down } }
};
template <class T>
@@ -9,7 +9,7 @@ int g();
// We should complain about this.
template<> constexpr int A<int>::f()
-{ return g(); } // { dg-error "non-.constexpr." }
+{ return g(); } // { dg-error "non-.constexpr." "" { target c++20_down } }
// But not about this.
struct B
@@ -9,5 +9,5 @@ struct A
struct B
{
A a[1];
- constexpr B() : a() {} // { dg-error "non-constant|non-.constexpr." "" { target { ! implicit_constexpr } } }
+ constexpr B() : a() {} // { dg-error "non-constant|non-.constexpr." "" { target { { ! implicit_constexpr } && c++20_down } } }
};
@@ -6,6 +6,6 @@ struct A
A(int);
};
-struct B : A {}; // { dg-message "" }
+struct B : A {}; // { dg-message "" "" { target c++20_down } }
-constexpr int foo(B) { return 0; } // { dg-error "invalid type" }
+constexpr int foo(B) { return 0; } // { dg-error "invalid type" "" { target c++20_down } }
@@ -13,6 +13,6 @@ constexpr X X::g(X x) { return x; }
struct Y
{
Y() { }
- constexpr Y f(Y y) { return y; } // { dg-error "constexpr" "" { target { ! implicit_constexpr } } }
- static constexpr Y g(Y y) { return y; } // { dg-error "constexpr" "" { target { ! implicit_constexpr } } }
+ constexpr Y f(Y y) { return y; } // { dg-error "constexpr" "" { target { { ! implicit_constexpr } && c++20_down } } }
+ static constexpr Y g(Y y) { return y; } // { dg-error "constexpr" "" { target { { ! implicit_constexpr } && c++20_down } } }
};
@@ -18,10 +18,10 @@ constexpr int three = one() ? 3 : nonconst_func(0);
constexpr int bogus() { return zero () ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" }
// Correctly rejected (not sure why).
-constexpr int correct_error() { return nonconst_func(0); } // { dg-error "nonconst_func" }
+constexpr int correct_error() { return nonconst_func(0); } // { dg-error "nonconst_func" "" { target c++20_down } }
// Correctly rejected.
constexpr int z = bogus(); // { dg-error "" }
// This is also correctly rejected.
-constexpr int correct_failure() { return 0 ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" }
+constexpr int correct_failure() { return 0 ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" "" { target c++20_down } }
@@ -10,7 +10,7 @@ struct B {
int global; // { dg-message "not const" }
struct D : B {
- constexpr D() : B(global) { } // { dg-error "global|argument" }
+ constexpr D() : B(global) { } // { dg-error "global|argument" "" { target c++20_down } }
};
struct A2 {
@@ -17,7 +17,7 @@ public:
constexpr static Inner & getInner()
/* I am surprised this is considered a constexpr */
{
- return *((Inner *)4); // { dg-error "reinterpret_cast" }
+ return *((Inner *)4); // { dg-error "reinterpret_cast" "" { target c++20_down } }
}
};
@@ -14,5 +14,5 @@ foo ()
constexpr volatile int // { dg-warning "deprecated" "" { target c++2a } }
bar ()
{
- return i; // { dg-error "lvalue-to-rvalue conversion of a volatile lvalue" }
+ return i; // { dg-error "lvalue-to-rvalue conversion of a volatile lvalue" "" { target c++20_down } }
}
@@ -5,7 +5,7 @@ void g();
void h();
constexpr void f(int* p, int* q) {
- if (p != q && *p < 0) // { dg-error "neither branch of 'if' is a constant expression" }
+ if (p != q && *p < 0) // { dg-error "neither branch of 'if' is a constant expression" "" { target c++20_down } }
g();
else
h();
@@ -10,7 +10,7 @@ struct B {
int *c = &x->a;
while (*c)
c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
- *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error "reinterpret_cast" }
+ *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error "reinterpret_cast" "" { target c++20_down } }
}
};
struct C : A {
@@ -10,7 +10,7 @@ struct B {
int *c = &x->a;
while (*c)
c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
- *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error "reinterpret_cast" }
+ *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error "reinterpret_cast" "" { target c++20_down } }
}
};
struct C : A {
@@ -11,7 +11,7 @@ foo (int x)
case 2:
break;
}
- throw 42; // { dg-error "is not a constant expression" }
+ throw 42; // { dg-error "is not a constant expression" "" { target c++20_down } }
return 0;
}
@@ -29,7 +29,7 @@ bar (int x)
continue;
break;
}
- throw -42; // { dg-error "is not a constant expression" }
+ throw -42; // { dg-error "is not a constant expression" "" { target c++20_down } }
}
while (0);
return x;
@@ -3,7 +3,7 @@
struct A { A(); };
constexpr int f(int i) {
- static int j = i; // { dg-error "static" }
+ static int j = i; // { dg-error "static" "" { target c++20_down } }
thread_local int l = i; // { dg-error "thread_local" "" { target c++20_down } }
goto foo; // { dg-error "goto" "" { target c++20_down } }
foo:
@@ -21,7 +21,7 @@ bar()
A a = foo();
a.p->n = 5;
return a;
-} // { dg-error "non-.constexpr." }
+} // { dg-error "non-.constexpr." "" { target c++20_down } }
constexpr int
baz()
@@ -7,18 +7,18 @@ constexpr void f1() {
constexpr void f2() {
if (true)
- throw; // { dg-error "not a constant expression" }
+ throw; // { dg-error "not a constant expression" "" { target c++20_down } }
}
constexpr void f3() {
if (false)
;
else
- throw; // { dg-error "not a constant expression" }
+ throw; // { dg-error "not a constant expression" "" { target c++20_down } }
}
constexpr void f4() {
- throw; // { dg-error "not a constant expression" }
+ throw; // { dg-error "not a constant expression" "" { target c++20_down } }
}
constexpr int fun(int n) {
new file mode 100644
@@ -0,0 +1,96 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// { dg-options "-Winvalid-constexpr -pedantic-errors" }
+
+// No constexpr constructors = not a literal type.
+struct NonLiteral {
+ NonLiteral() {}
+};
+
+// C++23: It is possible to write a constexpr function for which no
+// invocation satisfies the requirements of a core constant expression.
+constexpr NonLiteral
+fn0 (int) // { dg-warning "invalid return type" }
+{
+ return NonLiteral{};
+}
+
+constexpr int
+fn1 (NonLiteral) // { dg-warning "invalid type" }
+{
+ return 42;
+}
+
+// From P2448.
+void f(int& i) {
+ i = 0;
+}
+
+constexpr void g(int& i) {
+ f(i); // { dg-warning "call to" }
+}
+
+// [dcl.constexpr] used to have this.
+constexpr int f(bool b)
+ { return b ? throw 0 : 0; } // OK
+constexpr int f() { return f(true); } // ill-formed, no diagnostic required
+
+struct B {
+ constexpr B(int) : i(0) { }
+ int i;
+};
+
+int global;
+
+struct D : B {
+ constexpr D() : B(global) { } // { dg-warning "not usable" }
+ // ill-formed, no diagnostic required
+ // lvalue-to-rvalue conversion on non-constant global
+};
+
+// If no specialization of the template would satisfy the requirements
+// for a constexpr function when considered as a non-template function,
+// the template is ill-formed, no diagnostic required.
+template<typename>
+constexpr void
+fn2 ()
+{
+ int i = 42;
+ f (i);
+}
+
+void
+fn3 ()
+{
+ fn2<int>();
+}
+
+constexpr volatile int cvi = 10;
+
+constexpr int
+fn4 ()
+{
+ return cvi; // { dg-warning "lvalue-to-rvalue conversion" }
+}
+
+constexpr unsigned int
+fn5 (int *p)
+{
+ unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" }
+ return *q;
+}
+
+constexpr int
+fn6 (int i)
+{
+ void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" }
+ return 42;
+}
+
+constexpr int
+fn7 (int i)
+{
+ static int s = i; // { dg-warning "static" }
+ return s;
+}
new file mode 100644
@@ -0,0 +1,53 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// { dg-options "-Winvalid-constexpr -pedantic-errors" }
+
+// [dcl.constexpr]/4 used to say:
+// The definition of a constexpr constructor whose function-body
+// is not = delete shall additionally satisfy the following requirements:
+// (4.1) for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
+// (4.2) for a delegating constructor, the target constructor shall be a constexpr constructor.
+
+// This continues to be OK.
+struct Length {
+ constexpr explicit Length(int i = 0) : val(i) { }
+private:
+ int val;
+};
+
+struct X {
+ X() {}
+ X(int i_) : i(i_) {}
+ int i;
+};
+
+struct S {
+ X x;
+ // Calls a non-constexpr constructor X::X(int).
+ constexpr S(int i) : x(i) { } // { dg-warning "call to" }
+ S(int, int) { }
+ // Target constructor isn't constexpr.
+ constexpr S() : S(42, 42) { } // { dg-warning "call to" }
+};
+
+namespace N1 {
+struct X {
+ void x();
+};
+struct Y {
+ X x;
+ constexpr void y() { x.x(); } // { dg-warning "call to" }
+};
+}
+
+void g();
+
+struct A {
+ constexpr A() { g(); } // { dg-warning "call to" }
+};
+
+struct B {
+ constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" }
+ constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" }
+};
new file mode 100644
@@ -0,0 +1,24 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// Test that we get a diagnostic even in C++23 if you do call the function.
+
+constexpr unsigned int
+fn0 (const int *p)
+{
+ return *reinterpret_cast<unsigned const int *>(p); // { dg-error ".reinterpret_cast. is not a constant expression" }
+}
+
+constexpr void *
+fn1 (int i)
+{
+ return (void *) 1LL; // { dg-error ".reinterpret_cast." }
+}
+
+void
+g ()
+{
+ constexpr int i = 42;
+ constexpr auto a1 = fn0 (&i);
+ constexpr auto a2 = fn1 (i); // { dg-error "called in a constant expression" }
+}
new file mode 100644
@@ -0,0 +1,14 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// { dg-options "-Winvalid-constexpr" }
+
+constexpr volatile int i = 10;
+
+constexpr int
+bar ()
+{
+ return i; // { dg-warning "lvalue-to-rvalue conversion of a volatile lvalue" }
+}
+
+constexpr int x = bar (); // { dg-error "called in a constant expression" }
new file mode 100644
@@ -0,0 +1,26 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++20 } }
+// { dg-options "" }
+// The definition of a constexpr destructor whose function-body is not
+// =delete shall additionally satisfy the following requirement:
+// (5.1) for every subobject of class type or (possibly multi-dimensional)
+// array thereof, that class type shall have a constexpr destructor.
+
+struct B {
+ B() { }
+ ~B() { }
+};
+
+struct T : B {
+ constexpr ~T() { } // { dg-warning "call to" "" { target c++20_down } }
+};
+
+struct S {
+ constexpr S() = default; // was error: implicit S() is not constexpr, now OK
+ ~S() noexcept(false) = default; // OK, despite mismatched exception specification
+private:
+ int i;
+ S(S&); // OK: private copy constructor
+};
+S::S(S&) = default; // OK: defines copy constructor
new file mode 100644
@@ -0,0 +1,35 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// { dg-options "-Winvalid-constexpr" }
+// A copy/move assignment operator for a class X that is defaulted and
+// not defined as deleted is implicitly defined when it is odr-used,
+// when it is needed for constant evaluation, or when it is explicitly
+// defaulted after its first declaration.
+// The implicitly-defined copy/move assignment operator is constexpr.
+
+struct S {
+ constexpr S() {}
+ S& operator=(const S&) = default;
+ S& operator=(S&&) = default;
+};
+
+struct U {
+ constexpr U& operator=(const U&) = default;
+ constexpr U& operator=(U&&) = default;
+};
+
+constexpr void
+g ()
+{
+ S a;
+ S b;
+ b = a;
+ b = S{};
+
+ U u, v;
+ u = v;
+ u = U{};
+}
+
+static_assert ((g(), true), "");
new file mode 100644
@@ -0,0 +1,23 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++20 } }
+// { dg-options "" }
+
+template <typename T>
+struct Wrapper {
+ constexpr Wrapper() = default;
+ constexpr Wrapper(Wrapper const&) = default;
+ constexpr Wrapper(T const& t) : t(t) { }
+
+ constexpr T get() const { return t; }
+ constexpr bool operator==(Wrapper const&) const = default; // { dg-warning "call to" "" { target c++20_down } }
+private:
+ T t;
+};
+
+struct X {
+ X();
+ bool operator==(X const&) const;
+};
+
+Wrapper<X> x;
@@ -5,6 +5,6 @@ constexpr int
foo ()
{
goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
-lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 }
+lab:
return 1;
}
@@ -18,8 +18,19 @@ bar ()
}
constexpr int
-baz (int x)
+baz ()
{
thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" }
return ++a;
}
+
+// In C++23, we get errors about the non-constant expressions only if we
+// actually call the functions in a constexpr context.
+
+void
+test ()
+{
+ constexpr int a = foo (); // { dg-error "constant expression" }
+ constexpr int b = bar (); // { dg-error "constant expression" }
+ constexpr int c = baz (); // { dg-error "constant expression" }
+}
new file mode 100644
@@ -0,0 +1,96 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+// No constexpr constructors = not a literal type.
+struct NonLiteral {
+ NonLiteral() {}
+};
+
+// C++23: It is possible to write a constexpr function for which no
+// invocation satisfies the requirements of a core constant expression.
+constexpr NonLiteral
+fn0 (int) // { dg-warning "invalid return type" "" { target c++20_down } }
+{
+ return NonLiteral{};
+}
+
+constexpr int
+fn1 (NonLiteral) // { dg-warning "invalid type" "" { target c++20_down } }
+{
+ return 42;
+}
+
+// From P2448.
+void f(int& i) {
+ i = 0;
+}
+
+constexpr void g(int& i) {
+ f(i); // { dg-warning "call to" "" { target c++20_down } }
+}
+
+// [dcl.constexpr] used to have this.
+constexpr int f(bool b)
+ { return b ? throw 0 : 0; } // OK
+constexpr int f() { return f(true); } // ill-formed, no diagnostic required
+
+struct B {
+ constexpr B(int) : i(0) { }
+ int i;
+};
+
+int global;
+
+struct D : B {
+ constexpr D() : B(global) { } // { dg-warning "not usable" "" { target c++20_down } }
+ // ill-formed, no diagnostic required
+ // lvalue-to-rvalue conversion on non-constant global
+};
+
+// If no specialization of the template would satisfy the requirements
+// for a constexpr function when considered as a non-template function,
+// the template is ill-formed, no diagnostic required.
+template<typename>
+constexpr void
+fn2 ()
+{
+ int i = 42;
+ f (i);
+}
+
+void
+fn3 ()
+{
+ fn2<int>();
+}
+
+constexpr volatile int cvi = 10;
+
+constexpr int
+fn4 ()
+{
+ return cvi; // { dg-warning "lvalue-to-rvalue conversion" "" { target c++20_down } }
+}
+
+constexpr unsigned int
+fn5 (int *p)
+{
+ unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" "" { target c++20_down } }
+ return *q;
+}
+
+constexpr int
+fn6 (int i)
+{
+ void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" "" { target c++20_down } }
+ return 42;
+}
+
+constexpr int
+fn7 (int i)
+{
+ static int s = i; // { dg-error "static" "" { target c++20_down } }
+ return s;
+}
new file mode 100644
@@ -0,0 +1,53 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+// [dcl.constexpr]/4 used to say:
+// The definition of a constexpr constructor whose function-body
+// is not = delete shall additionally satisfy the following requirements:
+// (4.1) for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
+// (4.2) for a delegating constructor, the target constructor shall be a constexpr constructor.
+
+// This continues to be OK.
+struct Length {
+ constexpr explicit Length(int i = 0) : val(i) { }
+private:
+ int val;
+};
+
+struct X {
+ X() {}
+ X(int i_) : i(i_) {}
+ int i;
+};
+
+struct S {
+ X x;
+ // Calls a non-constexpr constructor X::X(int).
+ constexpr S(int i) : x(i) { } // { dg-warning "call to" "" { target c++20_down } }
+ S(int, int) { }
+ // Target constructor isn't constexpr.
+ constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target c++20_down } }
+};
+
+namespace N1 {
+struct X {
+ void x();
+};
+struct Y {
+ X x;
+ constexpr void y() { x.x(); } // { dg-warning "call to" "" { target c++20_down } }
+};
+}
+
+void g();
+
+struct A {
+ constexpr A() { g(); } // { dg-warning "call to" "" { target c++20_down } }
+};
+
+struct B {
+ constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } }
+ constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } }
+};
@@ -134,8 +134,8 @@
#ifndef __cpp_constexpr
# error "__cpp_constexpr"
-#elif __cpp_constexpr != 202110
-# error "__cpp_constexpr != 202110"
+#elif __cpp_constexpr != 202207
+# error "__cpp_constexpr != 202207"
#endif
#ifndef __cpp_decltype_auto
@@ -57,7 +57,6 @@ consteval int
f13 (int x)
{
static int a = 5; // { dg-error "'a' defined 'static' in 'consteval' function only available with" "" { target c++20_only } }
- // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 }
thread_local int b = 6; // { dg-error "'b' defined 'thread_local' in 'consteval' function only available with" "" { target c++20_only } }
return x;
}
@@ -13,7 +13,7 @@ void *operator new (std::size_t) noexcept;
constexpr bool
foo ()
{
- auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" }
+ auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } }
*p = 1;
::operator delete (p);
return false;
@@ -24,7 +24,7 @@ struct S { constexpr S () : s (0) {} int s; };
constexpr bool
bar ()
{
- auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" }
+ auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } }
auto q = new (p) S ();
q->s++;
q->~S ();
@@ -6,7 +6,6 @@ constexpr int foo ()
try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
static double b = 1.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } }
- // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 }
goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
l:;
return 0;
@@ -22,7 +21,6 @@ constexpr int bar ()
{
int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
static long double b = 3.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } }
- // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 }
goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
l:;
try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
@@ -9,7 +9,7 @@ struct A
struct B
{
A a;
- bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { ! implicit_constexpr } } }
+ bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { { ! implicit_constexpr } && c++20_down } } }
};
constexpr bool x = B() == B(); // { dg-error "non-.constexpr" "" { target { ! implicit_constexpr } } }
@@ -7,8 +7,8 @@ struct A {
struct D
{
A i;
- bool operator==(const D& x) const = default; // { dg-error "A::operator==" }
- bool operator!=(const D& z) const = default; // { dg-error "D::operator==" }
+ bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } }
+ bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } }
};
constexpr D d{A()};
@@ -1,7 +1,5 @@
// { dg-do compile { target c++11 } }
constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. defined .thread_local." "" { target c++20_down } }
-// { dg-error "40:.i. defined .thread_local. in .constexpr. context" "" { target c++23 } .-1 }
constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. defined .static." "" { target c++20_down } }
-// { dg-error "34:.i. defined .static. in .constexpr. context" "" { target c++23 } .-1 }
@@ -1,6 +1,6 @@
// PR c++/79664
// { dg-do compile }
-// { dg-options "-std=c++14 -fopenmp" }
+// { dg-options "-std=c++14 -fopenmp -Winvalid-constexpr -pedantic-errors" }
constexpr int
f1 ()
@@ -1,7 +1,7 @@
// Verify that -fsanitize=vptr downcast instrumentation works properly
// inside of constexpr.
// { dg-do compile }
-// { dg-options "-std=c++11 -fsanitize=vptr" }
+// { dg-options "-std=c++11 -fsanitize=vptr -Winvalid-constexpr -pedantic-errors" }
struct S {
constexpr S() : a(0) {}