This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: C++ PATCH to implement P1064R0, Virtual Function Calls in Constant Expressions (v2)
- From: Marek Polacek <polacek at redhat dot com>
- To: Jakub Jelinek <jakub at redhat dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>, Jason Merrill <jason at redhat dot com>
- Date: Fri, 14 Sep 2018 15:30:51 -0400
- Subject: Re: C++ PATCH to implement P1064R0, Virtual Function Calls in Constant Expressions (v2)
- References: <20180914171950.GE5587@redhat.com> <20180914173647.GR8250@tucnak>
On Fri, Sep 14, 2018 at 07:36:47PM +0200, Jakub Jelinek wrote:
> On Fri, Sep 14, 2018 at 01:19:50PM -0400, Marek Polacek wrote:
> > + /* We expect something in the form of &x.D.2103.D.2094; get x. */
> > + if (TREE_CODE (obj) != ADDR_EXPR)
> > + return t;
>
> Shouldn't it then be a gcc_assert instead, or code like:
> if (TREE_CODE (obj) != ADDR_EXPR)
> {
> if (!ctx->quiet)
> error (...);
> *non_constant_p = true;
> }
> to make it clear that we haven't handled it and don't consider it a constant
> expression?
Not an assert, but setting *non_constant_p is probably sensible. Thus:
v2: Set *non_constant_p if OBJ_TYPE_REF is not in expected format.
Bootstrapped/regtested on x86_64-linux.
2018-09-14 Marek Polacek <polacek@redhat.com>
P1064R0 - Allowing Virtual Function Calls in Constant Expressions
* call.c (build_over_call): Add FIXME.
* constexpr.c (cxx_eval_array_reference): Handle referencing the vtable.
(cxx_eval_constant_expression): Don't ignore _vptr's initializer.
(potential_constant_expression_1): Handle OBJ_TYPE_REF.
* decl.c (grokdeclarator): Change error to pedwarn. Only warn when
pedantic and not C++2a.
* g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error.
* g++.dg/cpp2a/constexpr-virtual1.C: New test.
* g++.dg/cpp2a/constexpr-virtual2.C: New test.
* g++.dg/cpp2a/constexpr-virtual3.C: New test.
* g++.dg/cpp2a/constexpr-virtual4.C: New test.
* g++.dg/cpp2a/constexpr-virtual5.C: New test.
* g++.dg/cpp2a/constexpr-virtual6.C: New test.
* g++.dg/cpp2a/constexpr-virtual7.C: New test.
* g++.dg/cpp2a/constexpr-virtual8.C: New test.
* g++.dg/cpp2a/constexpr-virtual9.C: New test.
* g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use
-pedantic-errors. Adjust dg-error.
diff --git gcc/cp/call.c gcc/cp/call.c
index 69503ca7920..6c70874af40 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -8401,7 +8401,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0
/* Don't mess with virtual lookup in instantiate_non_dependent_expr;
- virtual functions can't be constexpr. */
+ virtual functions can't be constexpr. FIXME Actually, no longer
+ true in C++2a. */
&& !in_template_function ())
{
tree t;
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 88c73787961..eb6b8fa1842 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -2414,16 +2414,27 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
bool *non_constant_p, bool *overflow_p)
{
tree oldary = TREE_OPERAND (t, 0);
+ tree oldidx = TREE_OPERAND (t, 1);
+
+ /* The virtual table isn't constexpr, but has static storage duration and its
+ initializer is a compile-time constant, so we handle referencing an element
+ in the table specially. */
+ if (TREE_TYPE (t) == vtable_entry_type)
+ {
+ VERIFY_CONSTANT (oldidx);
+ tree ctor = DECL_INITIAL (oldary);
+ return CONSTRUCTOR_ELT (ctor, tree_to_uhwi (oldidx))->value;
+ }
+
tree ary = cxx_eval_constant_expression (ctx, oldary,
lval,
non_constant_p, overflow_p);
- tree index, oldidx;
+ tree index;
HOST_WIDE_INT i = 0;
tree elem_type = NULL_TREE;
unsigned len = 0, elem_nchars = 1;
if (*non_constant_p)
return t;
- oldidx = TREE_OPERAND (t, 1);
index = cxx_eval_constant_expression (ctx, oldidx,
false,
non_constant_p, overflow_p);
@@ -4209,7 +4220,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
CONST_DECL for aggregate constants. */
if (lval)
return t;
+ /* is_really_empty_class doesn't take into account _vptr, so initializing
+ otherwise empty class with { } would overwrite the initializer that
+ initialize_vtable created for us. */
if (COMPLETE_TYPE_P (TREE_TYPE (t))
+ && !(DECL_INITIAL (t)
+ && TREE_CODE (DECL_INITIAL (t)) == CONSTRUCTOR
+ /* But if DECL_INITIAL was { }, do mark it as constant. */
+ && CONSTRUCTOR_NELTS (DECL_INITIAL (t)) > 0)
&& is_really_empty_class (TREE_TYPE (t)))
{
/* If the class is empty, we aren't actually loading anything. */
@@ -4778,7 +4796,6 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case MODOP_EXPR:
/* GCC internal stuff. */
case VA_ARG_EXPR:
- case OBJ_TYPE_REF:
case NON_DEPENDENT_EXPR:
case BASELINK:
case OFFSET_REF:
@@ -4788,6 +4805,34 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
*non_constant_p = true;
break;
+ case OBJ_TYPE_REF:
+ {
+ /* Virtual function call. Let the constexpr machinery figure out
+ the dynamic type. */
+ int token = tree_to_shwi (OBJ_TYPE_REF_TOKEN (t));
+ tree obj = OBJ_TYPE_REF_OBJECT (t);
+ obj = cxx_eval_constant_expression (ctx, obj, lval, non_constant_p,
+ overflow_p);
+ /* We expect something in the form of &x.D.2103.D.2094; get x. */
+ if (TREE_CODE (obj) != ADDR_EXPR)
+ {
+ if (!ctx->quiet)
+ error_at (cp_expr_loc_or_loc (t, input_location),
+ "expression %qE is not a constant expression", t);
+ *non_constant_p = true;
+ return t;
+ }
+ obj = TREE_OPERAND (obj, 0);
+ while (handled_component_p (obj))
+ obj = TREE_OPERAND (obj, 0);
+ tree objtype = TREE_TYPE (obj);
+ /* Find the function decl in the virtual functions list. TOKEN is
+ the DECL_VINDEX that says which function we're looking for. */
+ tree virtuals = BINFO_VIRTUALS (TYPE_BINFO (objtype));
+ r = TREE_VALUE (chain_index (token, virtuals));
+ break;
+ }
+
case PLACEHOLDER_EXPR:
/* Use of the value or address of the current object. */
if (tree ctor = lookup_placeholder (ctx, lval, TREE_TYPE (t)))
@@ -5871,7 +5916,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
case OACC_UPDATE:
/* GCC internal stuff. */
case VA_ARG_EXPR:
- case OBJ_TYPE_REF:
case TRANSACTION_EXPR:
case ASM_EXPR:
case AT_ENCODE_EXPR:
@@ -5880,6 +5924,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
error_at (loc, "expression %qE is not a constant expression", t);
return false;
+ case OBJ_TYPE_REF:
+ if (cxx_dialect >= cxx2a)
+ /* In C++2a 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++2a");
+ return false;
+
case TYPEID_EXPR:
/* -- a typeid expression whose operand is of polymorphic
class type; */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 50b60e89df5..da3749254e9 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -10854,12 +10854,13 @@ grokdeclarator (const cp_declarator *declarator,
storage_class = sc_none;
staticp = 0;
}
- if (constexpr_p)
+ if (constexpr_p && cxx_dialect < cxx2a)
{
gcc_rich_location richloc (declspecs->locations[ds_virtual]);
richloc.add_range (declspecs->locations[ds_constexpr]);
- error_at (&richloc, "member %qD cannot be declared both %<virtual%> "
- "and %<constexpr%>", dname);
+ pedwarn (&richloc, OPT_Wpedantic, "member %qD can be declared both "
+ "%<virtual%> and %<constexpr%> only in -std=c++2a or "
+ "-std=gnu++2a", dname);
}
}
friendp = decl_spec_seq_has_spec_p (declspecs, ds_friend);
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C
index 2465f9d9b4f..5f9ab4d9c28 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C
@@ -2,5 +2,5 @@
// { dg-do compile { target c++11 } }
struct S {
- constexpr virtual int f() { return 1; } // { dg-error "13:member .f. cannot be declared both .virtual. and .constexpr." }
+ constexpr virtual int f() { return 1; } // { dg-error "13:member .f. can be declared both .virtual. and .constexpr." "" { target c++17_down } }
};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C
index e69de29bb2d..fcf8cac6417 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C
@@ -0,0 +1,8 @@
+// P1064R0
+// { dg-do compile { target c++11 } }
+// { dg-options "-pedantic-errors" }
+
+struct X
+{
+ constexpr virtual int f() { return 0; } // { dg-error "member .f. can be declared both .virtual. and .constexpr. only" "" { target c++17_down } }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C
index e69de29bb2d..9d82c5c59ac 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C
@@ -0,0 +1,49 @@
+// P1064R0
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+struct X1
+{
+ virtual int f() const = 0;
+};
+
+struct X2: public X1
+{
+ constexpr virtual int f() const { return 2; }
+};
+
+struct X3: public X2
+{
+ virtual int f() const { return 3; }
+};
+
+struct X4: public X3
+{
+ constexpr virtual int f() const { return 4; }
+};
+
+constexpr int (X1::*pf)() const = &X1::f;
+
+constexpr X2 x2;
+static_assert(x2.f() == 2);
+static_assert((x2.*pf)() == 2);
+
+constexpr X1 const& r2 = x2;
+static_assert(r2.f() == 2);
+static_assert((r2.*pf)() == 2);
+
+constexpr X1 const* p2 = &x2;
+static_assert(p2->f() == 2);
+static_assert((p2->*pf)() == 2);
+
+constexpr X4 x4;
+static_assert(x4.f() == 4);
+static_assert((x4.*pf)() == 4);
+
+constexpr X1 const& r4 = x4;
+static_assert(r4.f() == 4);
+static_assert((r4.*pf)() == 4);
+
+constexpr X1 const* p4 = &x4;
+static_assert(p4->f() == 4);
+static_assert((p4->*pf)() == 4);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C
index e69de29bb2d..d71422fc4d0 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C
@@ -0,0 +1,52 @@
+// P1064R0
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+struct X1
+{
+ virtual int f() const = 0;
+};
+
+struct X2: public X1
+{
+ int i2 = 42;
+ constexpr virtual int f() const { return 2; }
+};
+
+struct X3: public X2
+{
+ int i3 = 42;
+ virtual int f() const { return 3; }
+};
+
+struct X4: public X3
+{
+ int i4 = 42;
+ constexpr virtual int f() const { return 4; }
+};
+
+constexpr int (X1::*pf)() const = &X1::f;
+
+constexpr X2 x2;
+static_assert(x2.f() == 2);
+static_assert((x2.*pf)() == 2);
+
+constexpr X1 const& r2 = x2;
+static_assert(r2.f() == 2);
+static_assert((r2.*pf)() == 2);
+
+constexpr X1 const* p2 = &x2;
+static_assert(p2->f() == 2);
+static_assert((p2->*pf)() == 2);
+
+constexpr X4 x4;
+static_assert(x4.f() == 4);
+static_assert((x4.*pf)() == 4);
+
+constexpr X1 const& r4 = x4;
+static_assert(r4.f() == 4);
+static_assert((r4.*pf)() == 4);
+
+constexpr X1 const* p4 = &x4;
+static_assert(p4->f() == 4);
+static_assert((p4->*pf)() == 4);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C
index e69de29bb2d..2038bebc6d1 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C
@@ -0,0 +1,57 @@
+// P1064R0
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+struct X1
+{
+ virtual int f1() const = 0;
+ virtual int f2() const = 0;
+ virtual int f3() const = 0;
+};
+
+struct X2: public X1
+{
+ constexpr virtual int f1() const { return 21; }
+ constexpr virtual int f2() const { return 22; }
+ constexpr virtual int f3() const { return 23; }
+};
+
+struct X3: public X2
+{
+ virtual int f1() const { return 31; }
+ virtual int f2() const { return 32; }
+ virtual int f3() const { return 33; }
+};
+
+struct X4: public X3
+{
+ constexpr virtual int f1() const { return 41; }
+ constexpr virtual int f2() const { return 42; }
+ constexpr virtual int f3() const { return 43; }
+};
+
+constexpr int (X1::*pf)() const = &X1::f2;
+
+constexpr X2 x2;
+static_assert(x2.f2() == 22);
+static_assert((x2.*pf)() == 22);
+
+constexpr X1 const& r2 = x2;
+static_assert(r2.f2() == 22);
+static_assert((r2.*pf)() == 22);
+
+constexpr X1 const* p2 = &x2;
+static_assert(p2->f2() == 22);
+static_assert((p2->*pf)() == 22);
+
+constexpr X4 x4;
+static_assert(x4.f2() == 42);
+static_assert((x4.*pf)() == 42);
+
+constexpr X1 const& r4 = x4;
+static_assert(r4.f2() == 42);
+static_assert((r4.*pf)() == 42);
+
+constexpr X1 const* p4 = &x4;
+static_assert(p4->f2() == 42);
+static_assert((p4->*pf)() == 42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C
index e69de29bb2d..6d27990a8b6 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C
@@ -0,0 +1,60 @@
+// P1064R0
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+struct X1
+{
+ virtual int f1() const = 0;
+ virtual int f2() const = 0;
+ virtual int f3() const = 0;
+};
+
+struct X2: public X1
+{
+ int i2 = 42;
+ constexpr virtual int f1() const { return 21; }
+ constexpr virtual int f2() const { return 22; }
+ constexpr virtual int f3() const { return 23; }
+};
+
+struct X3: public X2
+{
+ int i3 = 42;
+ virtual int f1() const { return 31; }
+ virtual int f2() const { return 32; }
+ virtual int f3() const { return 33; }
+};
+
+struct X4: public X3
+{
+ int i4 = 42;
+ constexpr virtual int f1() const { return 41; }
+ constexpr virtual int f2() const { return 42; }
+ constexpr virtual int f3() const { return 43; }
+};
+
+constexpr int (X1::*pf)() const = &X1::f2;
+
+constexpr X2 x2;
+static_assert(x2.f2() == 22);
+static_assert((x2.*pf)() == 22);
+
+constexpr X1 const& r2 = x2;
+static_assert(r2.f2() == 22);
+static_assert((r2.*pf)() == 22);
+
+constexpr X1 const* p2 = &x2;
+static_assert(p2->f2() == 22);
+static_assert((p2->*pf)() == 22);
+
+constexpr X4 x4;
+static_assert(x4.f2() == 42);
+static_assert((x4.*pf)() == 42);
+
+constexpr X1 const& r4 = x4;
+static_assert(r4.f2() == 42);
+static_assert((r4.*pf)() == 42);
+
+constexpr X1 const* p4 = &x4;
+static_assert(p4->f2() == 42);
+static_assert((p4->*pf)() == 42);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C
index e69de29bb2d..ece5e703c32 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C
@@ -0,0 +1,25 @@
+// P1064R0
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+struct X1
+{
+ constexpr virtual X1 const *f() const { return this; }
+};
+
+struct Y
+{
+ int m = 0;
+};
+
+struct X2: public Y, public X1
+{
+ constexpr virtual X2 const *f() const { return this; }
+};
+
+constexpr X1 x1;
+static_assert(x1.f() == &x1);
+
+constexpr X2 x2;
+constexpr X1 const& r2 = x2;
+static_assert(r2.f() == &r2);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C
index e69de29bb2d..b0f499608ef 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C
@@ -0,0 +1,87 @@
+// P1064R0
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+struct X1
+{
+ virtual int f() const = 0;
+};
+
+struct X2: public X1
+{
+ constexpr virtual int f() const { return 2; }
+};
+
+struct X3: public X2
+{
+ virtual int f() const { return 3; }
+};
+
+struct X4: public X3
+{
+ constexpr virtual int f() const { return 4; }
+};
+
+constexpr int (X1::*pf)() const = &X1::f;
+
+constexpr X2 x2;
+
+struct S
+{
+ int i, j;
+ constexpr S() : i(x2.f()), j((x2.*pf)()) { }
+};
+
+static_assert(S().i == 2);
+static_assert(S().j == 2);
+
+constexpr X1 const& r2 = x2;
+
+struct S2
+{
+ int i, j;
+ constexpr S2() : i(r2.f()), j((r2.*pf)()) { }
+};
+
+static_assert(S2().i == 2);
+static_assert(S2().j == 2);
+
+constexpr X1 const* p2 = &x2;
+struct S3
+{
+ int i, j;
+ constexpr S3() : i(p2->f()), j((p2->*pf)()) { }
+};
+
+static_assert(S3().i == 2);
+static_assert(S3().j == 2);
+
+constexpr X4 x4;
+struct S4
+{
+ int i, j;
+ constexpr S4() : i(x4.f()), j((x4.*pf)()) { }
+};
+
+static_assert(S4().i == 4);
+static_assert(S4().j == 4);
+
+constexpr X1 const& r4 = x4;
+struct S5
+{
+ int i, j;
+ constexpr S5() : i(r4.f()), j((r4.*pf)()) { }
+};
+
+static_assert(S5().i == 4);
+static_assert(S5().j == 4);
+
+constexpr X1 const* p4 = &x4;
+struct S6
+{
+ int i, j;
+ constexpr S6() : i(p4->f()), j((p4->*pf)()) { }
+};
+
+static_assert(S6().i == 4);
+static_assert(S6().j == 4);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C
index e69de29bb2d..4a7cc972a91 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C
@@ -0,0 +1,50 @@
+// P1064R0
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+template<typename T>
+struct X1
+{
+ virtual T f() const = 0;
+};
+
+struct X2: public X1<int>
+{
+ constexpr virtual int f() const { return 2; }
+};
+
+struct X3: public X2
+{
+ virtual int f() const { return 3; }
+};
+
+struct X4: public X3
+{
+ constexpr virtual int f() const { return 4; }
+};
+
+constexpr int (X1<int>::*pf)() const = &X1<int>::f;
+
+constexpr X2 x2;
+static_assert(x2.f() == 2);
+static_assert((x2.*pf)() == 2);
+
+constexpr X1<int> const& r2 = x2;
+static_assert(r2.f() == 2);
+static_assert((r2.*pf)() == 2);
+
+constexpr X1<int> const* p2 = &x2;
+static_assert(p2->f() == 2);
+static_assert((p2->*pf)() == 2);
+
+constexpr X4 x4;
+static_assert(x4.f() == 4);
+static_assert((x4.*pf)() == 4);
+
+constexpr X1<int> const& r4 = x4;
+static_assert(r4.f() == 4);
+static_assert((r4.*pf)() == 4);
+
+constexpr X1<int> const* p4 = &x4;
+static_assert(p4->f() == 4);
+static_assert((p4->*pf)() == 4);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C
index e69de29bb2d..3a12adc2659 100644
--- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C
@@ -0,0 +1,83 @@
+// P1064R0
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+struct X1
+{
+ virtual int f() const = 0;
+ virtual int f(int) const = 0;
+ virtual int f(int, int) const = 0;
+};
+
+struct X2: public X1
+{
+ constexpr virtual int f() const { return 2; }
+ constexpr virtual int f(int) const { return 12; }
+ constexpr virtual int f(int, int) const { return 22; }
+};
+
+struct X3: public X2
+{
+ virtual int f() const { return 3; }
+ virtual int f(int) const { return 13; }
+ virtual int f(int, int) const { return 23; }
+};
+
+struct X4: public X3
+{
+ constexpr virtual int f() const { return 4; }
+ constexpr virtual int f(int) const { return 14; }
+ constexpr virtual int f(int, int) const { return 24; }
+};
+
+constexpr int (X1::*pf)() const = &X1::f;
+constexpr int (X1::*pf1)(int) const = &X1::f;
+constexpr int (X1::*pf2)(int, int) const = &X1::f;
+
+constexpr X2 x2;
+static_assert(x2.f() == 2);
+static_assert((x2.*pf)() == 2);
+static_assert(x2.f(1) == 12);
+static_assert((x2.*pf1)(1) == 12);
+static_assert(x2.f(1, 2) == 22);
+static_assert((x2.*pf2)(1, 2) == 22);
+
+constexpr X1 const& r2 = x2;
+static_assert(r2.f() == 2);
+static_assert((r2.*pf)() == 2);
+static_assert(r2.f(1) == 12);
+static_assert((r2.*pf1)(1) == 12);
+static_assert(r2.f(1, 2) == 22);
+static_assert((r2.*pf2)(1, 2) == 22);
+
+constexpr X1 const* p2 = &x2;
+static_assert(p2->f() == 2);
+static_assert((p2->*pf)() == 2);
+static_assert(p2->f(1) == 12);
+static_assert((p2->*pf1)(1) == 12);
+static_assert(p2->f(1, 2) == 22);
+static_assert((p2->*pf2)(1, 2) == 22);
+
+constexpr X4 x4;
+static_assert(x4.f() == 4);
+static_assert((x4.*pf)() == 4);
+static_assert(x4.f(1) == 14);
+static_assert((x4.*pf1)(1) == 14);
+static_assert(x4.f(1, 2) == 24);
+static_assert((x4.*pf2)(1, 2) == 24);
+
+constexpr X1 const& r4 = x4;
+static_assert(r4.f() == 4);
+static_assert((r4.*pf)() == 4);
+static_assert(r4.f(1) == 14);
+static_assert((r4.*pf1)(1) == 14);
+static_assert(r4.f(1, 2) == 24);
+static_assert((r4.*pf2)(1, 2) == 24);
+
+constexpr X1 const* p4 = &x4;
+static_assert(p4->f() == 4);
+static_assert((p4->*pf)() == 4);
+static_assert(p4->f(1) == 14);
+static_assert((p4->*pf1)(1) == 14);
+static_assert(p4->f(1, 2) == 24);
+static_assert((p4->*pf2)(1, 2) == 24);
diff --git gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C
index 2c83236cae9..9223c692737 100644
--- gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C
+++ gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C
@@ -1,14 +1,15 @@
-// { dg-options "-fdiagnostics-show-caret" }
+// { dg-options "-fdiagnostics-show-caret -pedantic-errors" }
// { dg-do compile { target c++11 } }
+// { dg-skip-if "virtual constexpr" { *-*-* } { "-std=gnu++2a" } { "" } }
struct S
{
- virtual constexpr void foo(); // { dg-error "3:member .foo. cannot be declared both .virtual. and .constexpr." }
+ virtual constexpr void foo(); // { dg-error "3:member .foo. can be declared both .virtual. and .constexpr." }
/* { dg-begin-multiline-output "" }
virtual constexpr void foo();
^~~~~~~ ~~~~~~~~~
{ dg-end-multiline-output "" } */
- constexpr virtual void bar(); // { dg-error "13:member .bar. cannot be declared both .virtual. and .constexpr." }
+ constexpr virtual void bar(); // { dg-error "13:member .bar. can be declared both .virtual. and .constexpr." }
/* { dg-begin-multiline-output "" }
constexpr virtual void bar();
~~~~~~~~~ ^~~~~~~